]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
Merge branch 'master' of ssh://bacula.git.sourceforge.net/gitroot/bacula/bacula
[bacula/bacula] / bacula / src / dird / ua_output.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- User Agent Output Commands
31  *     I.e. messages, listing database, showing resources, ...
32  *
33  *     Kern Sibbald, September MM
34  *
35  *   Version $Id$
36  */
37
38 #include "bacula.h"
39 #include "dird.h"
40
41 /* Imported subroutines */
42
43 /* Imported variables */
44
45 /* Imported functions */
46
47 /* Forward referenced functions */
48 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist);
49 static bool list_nextvol(UAContext *ua, int ndays);
50
51 /*
52  * Turn auto display of console messages on/off
53  */
54 int autodisplay_cmd(UAContext *ua, const char *cmd)
55 {
56    static const char *kw[] = {
57       NT_("on"),
58       NT_("off"),
59       NULL};
60
61    switch (find_arg_keyword(ua, kw)) {
62    case 0:
63       ua->auto_display_messages = true;
64       break;
65    case 1:
66       ua->auto_display_messages = false;
67       break;
68    default:
69       ua->error_msg(_("ON or OFF keyword missing.\n"));
70       break;
71    }
72    return 1;
73 }
74
75 /*
76  * Turn GUI mode on/off
77  */
78 int gui_cmd(UAContext *ua, const char *cmd)
79 {
80    static const char *kw[] = {
81       NT_("on"),
82       NT_("off"),
83       NULL};
84
85    switch (find_arg_keyword(ua, kw)) {
86    case 0:
87       ua->jcr->gui = ua->gui = true;
88       break;
89    case 1:
90       ua->jcr->gui = ua->gui = false;
91       break;
92    default:
93       ua->error_msg(_("ON or OFF keyword missing.\n"));
94       break;
95    }
96    return 1;
97 }
98
99 /* 
100  * Enter with Resources locked 
101  */
102 static void show_disabled_jobs(UAContext *ua)
103 {
104    JOB *job;
105    bool first = true;
106    foreach_res(job, R_JOB) {   
107       if (!acl_access_ok(ua, Job_ACL, job->name())) {
108          continue;
109       }
110       if (!job->enabled) {
111          if (first) {
112             first = false;
113             ua->send_msg(_("Disabled Jobs:\n"));
114          }
115          ua->send_msg("   %s\n", job->name());
116      }
117   }
118   if (first) {
119      ua->send_msg(_("No disabled Jobs.\n"));
120   }
121 }
122
123 struct showstruct {const char *res_name; int type;};
124 static struct showstruct reses[] = {
125    {NT_("directors"),  R_DIRECTOR},
126    {NT_("clients"),    R_CLIENT},
127    {NT_("counters"),   R_COUNTER},
128    {NT_("devices"),    R_DEVICE},
129    {NT_("jobs"),       R_JOB},
130    {NT_("storages"),   R_STORAGE},
131    {NT_("catalogs"),   R_CATALOG},
132    {NT_("schedules"),  R_SCHEDULE},
133    {NT_("filesets"),   R_FILESET},
134    {NT_("pools"),      R_POOL},
135    {NT_("messages"),   R_MSGS},
136    {NT_("all"),        -1},
137    {NT_("help"),       -2},
138    {NULL,           0}
139 };
140
141
142 /*
143  *  Displays Resources
144  *
145  *  show all
146  *  show <resource-keyword-name>  e.g. show directors
147  *  show <resource-keyword-name>=<name> e.g. show director=HeadMan
148  *  show disabled    shows disabled jobs
149  *
150  */
151 int show_cmd(UAContext *ua, const char *cmd)
152 {
153    int i, j, type, len;
154    int recurse;
155    char *res_name;
156    RES *res = NULL;
157
158    Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
159
160
161    LockRes();
162    for (i=1; i<ua->argc; i++) {
163       if (strcasecmp(ua->argk[i], _("disabled")) == 0) {
164          show_disabled_jobs(ua);
165          goto bail_out;
166       }
167       type = 0;
168       res_name = ua->argk[i];
169       if (!ua->argv[i]) {             /* was a name given? */
170          /* No name, dump all resources of specified type */
171          recurse = 1;
172          len = strlen(res_name);
173          for (j=0; reses[j].res_name; j++) {
174             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
175                type = reses[j].type;
176                if (type > 0) {
177                   res = res_head[type-r_first];
178                } else {
179                   res = NULL;
180                }
181                break;
182             }
183          }
184
185       } else {
186          /* Dump a single resource with specified name */
187          recurse = 0;
188          len = strlen(res_name);
189          for (j=0; reses[j].res_name; j++) {
190             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
191                type = reses[j].type;
192                res = (RES *)GetResWithName(type, ua->argv[i]);
193                if (!res) {
194                   type = -3;
195                }
196                break;
197             }
198          }
199       }
200
201       switch (type) {
202       case -1:                           /* all */
203          for (j=r_first; j<=r_last; j++) {
204             dump_resource(j, res_head[j-r_first], bsendmsg, ua);
205          }
206          break;
207       case -2:
208          ua->send_msg(_("Keywords for the show command are:\n"));
209          for (j=0; reses[j].res_name; j++) {
210             ua->error_msg("%s\n", _(reses[j].res_name));
211          }
212          goto bail_out;
213       case -3:
214          ua->error_msg(_("%s resource %s not found.\n"), res_name, ua->argv[i]);
215          goto bail_out;
216       case 0:
217          ua->error_msg(_("Resource %s not found\n"), res_name);
218          goto bail_out;
219       default:
220          dump_resource(recurse?type:-type, res, bsendmsg, ua);
221          break;
222       }
223    }
224 bail_out:
225    UnlockRes();
226    return 1;
227 }
228
229
230
231
232 /*
233  *  List contents of database
234  *
235  *  list jobs           - lists all jobs run
236  *  list jobid=nnn      - list job data for jobid
237  *  list ujobid=uname   - list job data for unique jobid
238  *  list job=name       - list all jobs with "name"   
239  *  list jobname=name   - same as above 
240  *  list jobmedia jobid=<nn>
241  *  list jobmedia job=name
242  *  list joblog jobid=<nn>
243  *  list joblog job=name
244  *  list files jobid=<nn> - list files saved for job nn
245  *  list files job=name
246  *  list pools          - list pool records
247  *  list jobtotals      - list totals for all jobs
248  *  list media          - list media for given pool (deprecated)
249  *  list volumes        - list Volumes
250  *  list clients        - list clients
251  *  list nextvol job=xx  - list the next vol to be used by job
252  *  list nextvolume job=xx - same as above.
253  *  list copies jobid=x,y,z
254  *
255  */
256
257 /* Do long or full listing */
258 int llist_cmd(UAContext *ua, const char *cmd)
259 {
260    return do_list_cmd(ua, cmd, VERT_LIST);
261 }
262
263 /* Do short or summary listing */
264 int list_cmd(UAContext *ua, const char *cmd)
265 {
266    return do_list_cmd(ua, cmd, HORZ_LIST);
267 }
268
269 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
270 {
271    POOLMEM *VolumeName;
272    int jobid, n;
273    int i, j;
274    JOB_DBR jr;
275    POOL_DBR pr;
276    MEDIA_DBR mr;
277
278    if (!open_client_db(ua))
279       return 1;
280
281    memset(&jr, 0, sizeof(jr));
282    memset(&pr, 0, sizeof(pr));
283    memset(&mr, 0, sizeof(mr));
284
285    Dmsg1(20, "list: %s\n", cmd);
286
287    if (!ua->db) {
288       ua->error_msg(_("Hey! DB is NULL\n"));
289    }
290
291    /* Scan arguments looking for things to do */
292    for (i=1; i<ua->argc; i++) {
293       /* List JOBS */
294       if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
295          /* Apply any limit */
296          j = find_arg_with_value(ua, NT_("limit"));
297          if (j >= 0) {
298             jr.limit = atoi(ua->argv[j]);
299          }
300          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
301
302          /* List JOBTOTALS */
303       } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
304          db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
305
306       /* List JOBID=nn */
307       } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
308          if (ua->argv[i]) {
309             jobid = str_to_int64(ua->argv[i]);
310             if (jobid > 0) {
311                jr.JobId = jobid;
312                db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
313             }
314          }
315
316       /* List JOB=xxx */
317       } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
318                   strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
319          bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
320          jr.JobId = 0;
321          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
322
323       /* List UJOBID=xxx */
324       } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
325          bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
326          jr.JobId = 0;
327          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
328
329       /* List FILES */
330       } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
331
332          for (j=i+1; j<ua->argc; j++) {
333             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
334                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
335                jr.JobId = 0;
336                db_get_job_record(ua->jcr, ua->db, &jr);
337                jobid = jr.JobId;
338             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
339                jobid = str_to_int64(ua->argv[j]);
340             } else {
341                continue;
342             }
343             if (jobid > 0) {
344                db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
345             }
346          }
347
348       /* List JOBMEDIA */
349       } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
350          bool done = false;
351          for (j=i+1; j<ua->argc; j++) {
352             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
353                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
354                jr.JobId = 0;
355                db_get_job_record(ua->jcr, ua->db, &jr);
356                jobid = jr.JobId;
357             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
358                jobid = str_to_int64(ua->argv[j]);
359             } else {
360                continue;
361             }
362             db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
363             done = true;
364          }
365          if (!done) {
366             /* List for all jobs (jobid=0) */
367             db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
368          }
369
370       /* List JOBLOG */
371       } else if (strcasecmp(ua->argk[i], NT_("joblog")) == 0) {
372          bool done = false;
373          for (j=i+1; j<ua->argc; j++) {
374             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
375                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
376                jr.JobId = 0;
377                db_get_job_record(ua->jcr, ua->db, &jr);
378                jobid = jr.JobId;
379             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
380                jobid = str_to_int64(ua->argv[j]);
381             } else {
382                continue;
383             }
384             db_list_joblog_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
385             done = true;
386          }
387          if (!done) {
388             /* List for all jobs (jobid=0) */
389             db_list_joblog_records(ua->jcr, ua->db, 0, prtit, ua, llist);
390          }
391
392
393       /* List POOLS */
394       } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
395                  strcasecmp(ua->argk[i], NT_("pools")) == 0) {
396          POOL_DBR pr;
397          memset(&pr, 0, sizeof(pr));
398          if (ua->argv[i]) {
399             bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
400          }
401          db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
402
403       } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
404          db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
405
406
407       /* List MEDIA or VOLUMES */
408       } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
409                  strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
410                  strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
411          bool done = false;
412          for (j=i+1; j<ua->argc; j++) {
413             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
414                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
415                jr.JobId = 0;
416                db_get_job_record(ua->jcr, ua->db, &jr);
417                jobid = jr.JobId;
418             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
419                jobid = str_to_int64(ua->argv[j]);
420             } else {
421                continue;
422             }
423             VolumeName = get_pool_memory(PM_FNAME);
424             n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
425             ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
426             free_pool_memory(VolumeName);
427             done = true;
428          }
429          /* if no job or jobid keyword found, then we list all media */
430          if (!done) {
431             int num_pools;
432             uint32_t *ids;
433             /* List a specific volume? */
434             if (ua->argv[i]) {
435                bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
436                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
437                return 1;
438             }
439             /* Is a specific pool wanted? */
440             for (i=1; i<ua->argc; i++) {
441                if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
442                   if (!get_pool_dbr(ua, &pr)) {
443                      ua->error_msg(_("No Pool specified.\n"));
444                      return 1;
445                   }
446                   mr.PoolId = pr.PoolId;
447                   db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
448                   return 1;
449                }
450             }
451
452             /* List Volumes in all pools */
453             if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
454                ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"),
455                         db_strerror(ua->db));
456                return 1;
457             }
458             if (num_pools <= 0) {
459                return 1;
460             }
461             for (i=0; i < num_pools; i++) {
462                pr.PoolId = ids[i];
463                if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
464                   ua->send_msg(_("Pool: %s\n"), pr.Name);
465                }
466                mr.PoolId = ids[i];
467                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
468             }
469             free(ids);
470             return 1;
471          }
472       /* List next volume */
473       } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
474                  strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
475          n = 1;
476          j = find_arg_with_value(ua, NT_("days"));
477          if (j >= 0) {
478             n = atoi(ua->argv[j]);
479             if ((n < 0) || (n > 50)) {
480               ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n"));
481               n = 1;
482             }
483          }
484          list_nextvol(ua, n);
485       } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
486          char *jobids = NULL;
487          uint32_t limit=0;
488          for (j=i+1; j<ua->argc; j++) {
489             if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
490                if (is_a_number_list(ua->argv[j])) {
491                   jobids = ua->argv[j];
492                }
493             } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
494                limit = atoi(ua->argv[j]);
495             } 
496          }
497          db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
498       } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
499                  || strcasecmp(ua->argk[i], NT_("days")) == 0) {
500          /* Ignore it */
501       } else {
502          ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
503       }
504    }
505    return 1;
506 }
507
508 static bool list_nextvol(UAContext *ua, int ndays)
509 {
510    JOB *job;
511    JCR *jcr;          
512    USTORE store;
513    RUN *run;
514    utime_t runtime;
515    bool found = false;
516    MEDIA_DBR mr;
517    POOL_DBR pr;
518
519    memset(&mr, 0, sizeof(mr));
520    int i = find_arg_with_value(ua, "job");
521    if (i <= 0) {
522       if ((job = select_job_resource(ua)) == NULL) {
523          return false;
524       }
525    } else {
526       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
527       if (!job) {
528          Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
529          if ((job = select_job_resource(ua)) == NULL) {
530             return false;
531          }
532       }
533    }
534
535    jcr = new_jcr(sizeof(JCR), dird_free_jcr);
536    for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
537       if (!complete_jcr_for_job(jcr, job, run->pool)) {
538          found = false;
539          goto get_out;
540       }
541       if (!jcr->jr.PoolId) {
542          ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
543          continue;
544       }
545       memset(&pr, 0, sizeof(pr));
546       pr.PoolId = jcr->jr.PoolId;
547       if (!db_get_pool_record(jcr, jcr->db, &pr)) {
548          bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
549       }
550       mr.PoolId = jcr->jr.PoolId;
551       get_job_storage(&store, job, run);
552       mr.StorageId = store.store->StorageId;
553       /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
554       if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
555          ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
556             job->name(), pr.Name, level_to_str(run->level));
557       } else {
558          ua->send_msg(
559             _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
560             job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
561          found = true;
562       }
563    }
564
565 get_out:
566    db_close_database(jcr, jcr->db);
567    jcr->db = NULL;
568    free_jcr(jcr);
569    if (!found) {
570       ua->error_msg(_("Could not find next Volume for Job %s.\n"),
571          job->hdr.name);
572       return false;
573    }
574    return true;
575 }
576
577
578 /*
579  * For a given job, we examine all his run records
580  *  to see if it is scheduled today or tomorrow.
581  */
582 RUN *find_next_run(RUN *run, JOB *job, utime_t &runtime, int ndays)
583 {
584    time_t now, future, endtime;
585    SCHED *sched;
586    struct tm tm, runtm;
587    int mday, wday, month, wom, i;
588    int woy;
589    int day;
590    int is_scheduled;
591
592    sched = job->schedule;
593    if (sched == NULL) {            /* scheduled? */
594       return NULL;                 /* no nothing to report */
595    }
596
597    /* Break down the time into components */
598    now = time(NULL);
599    endtime = now + (ndays * 60 * 60 * 24);
600
601    if (run == NULL) {
602       run = sched->run;
603    } else {
604       run = run->next;
605    }
606    for ( ; run; run=run->next) {
607       /*
608        * Find runs in next 24 hours.  Day 0 is today, so if
609        *   ndays=1, look at today and tomorrow.
610        */
611       for (day = 0; day <= ndays; day++) {
612          future = now + (day * 60 * 60 * 24);
613
614          /* Break down the time into components */
615          (void)localtime_r(&future, &tm);
616          mday = tm.tm_mday - 1;
617          wday = tm.tm_wday;
618          month = tm.tm_mon;
619          wom = mday / 7;
620          woy = tm_woy(future);
621
622          is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
623             bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
624             bit_is_set(woy, run->woy);
625  
626 #ifdef xxx
627          Pmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
628          Pmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
629          Pmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
630          Pmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
631          Pmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
632          Pmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
633 #endif
634
635          if (is_scheduled) { /* Jobs scheduled on that day */
636 #ifdef xxx
637             char buf[300], num[10];
638             bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
639             for (i=0; i<24; i++) {
640                if (bit_is_set(i, run->hour)) {
641                   bsnprintf(num, sizeof(num), "%d ", i);
642                   bstrncat(buf, num, sizeof(buf));
643                }
644             }
645             bstrncat(buf, "\n", sizeof(buf));
646             Pmsg1(000, "%s", buf);
647 #endif
648             /* find time (time_t) job is to be run */
649             (void)localtime_r(&future, &runtm);
650             for (i= 0; i < 24; i++) {
651                if (bit_is_set(i, run->hour)) {
652                   runtm.tm_hour = i;
653                   runtm.tm_min = run->minute;
654                   runtm.tm_sec = 0;
655                   runtime = mktime(&runtm);
656                   Dmsg2(200, "now=%d runtime=%lld\n", now, runtime);
657                   if ((runtime > now) && (runtime < endtime)) {
658                      Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
659                      return run;         /* found it, return run resource */
660                   }
661                }
662             }
663          }
664       }
665    } /* end for loop over runs */
666    /* Nothing found */
667    return NULL;
668 }
669
670 /*
671  * Fill in the remaining fields of the jcr as if it
672  *  is going to run the job.
673  */
674 bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
675 {
676    POOL_DBR pr;
677
678    memset(&pr, 0, sizeof(POOL_DBR));
679    set_jcr_defaults(jcr, job);
680    if (pool) {
681       jcr->pool = pool;               /* override */
682    }
683    if (jcr->db) {
684       Dmsg0(100, "complete_jcr close db\n");
685       db_close_database(jcr, jcr->db);
686       jcr->db = NULL;
687    }
688
689    Dmsg0(100, "complete_jcr open db\n");
690    jcr->db = jcr->db=db_init(jcr, jcr->catalog->db_driver, jcr->catalog->db_name, 
691                       jcr->catalog->db_user,
692                       jcr->catalog->db_password, jcr->catalog->db_address,
693                       jcr->catalog->db_port, jcr->catalog->db_socket,
694                       jcr->catalog->mult_db_connections);
695    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
696       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
697                  jcr->catalog->db_name);
698       if (jcr->db) {
699          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
700          db_close_database(jcr, jcr->db);
701          jcr->db = NULL;
702       }
703       return false;
704    }
705    bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
706    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
707       /* Try to create the pool */
708       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
709          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
710             db_strerror(jcr->db));
711          if (jcr->db) {
712             db_close_database(jcr, jcr->db);
713             jcr->db = NULL;
714          }
715          return false;
716       } else {
717          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
718       }
719    }
720    jcr->jr.PoolId = pr.PoolId;
721    return true;
722 }
723
724
725 static void con_lock_release(void *arg)
726 {
727    Vw(con_lock);
728 }
729
730 void do_messages(UAContext *ua, const char *cmd)
731 {
732    char msg[2000];
733    int mlen;
734    bool do_truncate = false;
735
736    Pw(con_lock);
737    pthread_cleanup_push(con_lock_release, (void *)NULL);
738    rewind(con_fd);
739    while (fgets(msg, sizeof(msg), con_fd)) {
740       mlen = strlen(msg);
741       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
742       strcpy(ua->UA_sock->msg, msg);
743       ua->UA_sock->msglen = mlen;
744       ua->UA_sock->send();
745       do_truncate = true;
746    }
747    if (do_truncate) {
748       (void)ftruncate(fileno(con_fd), 0L);
749    }
750    console_msg_pending = FALSE;
751    ua->user_notified_msg_pending = FALSE;
752    pthread_cleanup_pop(0);
753    Vw(con_lock);
754 }
755
756
757 int qmessagescmd(UAContext *ua, const char *cmd)
758 {
759    if (console_msg_pending && ua->auto_display_messages) {
760       do_messages(ua, cmd);
761    }
762    return 1;
763 }
764
765 int messagescmd(UAContext *ua, const char *cmd)
766 {
767    if (console_msg_pending) {
768       do_messages(ua, cmd);
769    } else {
770       ua->UA_sock->fsend(_("You have no messages.\n"));
771    }
772    return 1;
773 }
774
775 /*
776  * Callback routine for "printing" database file listing
777  */
778 void prtit(void *ctx, const char *msg)
779 {
780    UAContext *ua = (UAContext *)ctx;
781
782    ua->UA_sock->fsend("%s", msg);
783 }
784
785 /*
786  * Format message and send to other end.
787
788  * If the UA_sock is NULL, it means that there is no user
789  * agent, so we are being called from Bacula core. In
790  * that case direct the messages to the Job.
791  */
792 #ifdef HAVE_VA_COPY
793 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
794 {
795    BSOCK *bs = ua->UA_sock;
796    int maxlen, len;
797    POOLMEM *msg = NULL;
798    va_list ap;
799
800    if (bs) {
801       msg = bs->msg;
802    }
803    if (!msg) {
804       msg = get_pool_memory(PM_EMSG);
805    }
806
807 again:
808    maxlen = sizeof_pool_memory(msg) - 1;
809    va_copy(ap, arg_ptr);
810    len = bvsnprintf(msg, maxlen, fmt, ap);
811    va_end(ap);
812    if (len < 0 || len >= maxlen) {
813       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
814       goto again;
815    }
816
817    if (bs) {
818       bs->msg = msg;
819       bs->msglen = len;
820       bs->send();
821    } else {                           /* No UA, send to Job */
822       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
823       free_pool_memory(msg);
824    }
825
826 }
827
828 #else /* no va_copy() -- brain damaged version of variable arguments */
829
830 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
831 {
832    BSOCK *bs = ua->UA_sock;
833    int maxlen, len;
834    POOLMEM *msg = NULL;
835
836    if (bs) {
837       msg = bs->msg;
838    }
839    if (!msg) {
840       msg = get_memory(5000);
841    }
842
843    maxlen = sizeof_pool_memory(msg) - 1;
844    if (maxlen < 4999) {
845       msg = realloc_pool_memory(msg, 5000);
846       maxlen = 4999;
847    }
848    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
849    if (len < 0 || len >= maxlen) {
850       pm_strcpy(msg, _("Message too long to display.\n"));
851       len = strlen(msg);
852    }
853
854    if (bs) {
855       bs->msg = msg;
856       bs->msglen = len;
857       bs->send();
858    } else {                           /* No UA, send to Job */
859       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
860       free_pool_memory(msg);
861    }
862
863 }
864 #endif
865  
866 void bsendmsg(void *ctx, const char *fmt, ...)
867 {
868    va_list arg_ptr;
869    va_start(arg_ptr, fmt);
870    bmsg((UAContext *)ctx, fmt, arg_ptr);
871    va_end(arg_ptr);
872 }
873
874 /*
875  * The following UA methods are mainly intended for GUI
876  * programs
877  */
878 /*
879  * This is a message that should be displayed on the user's 
880  *  console.
881  */
882 void UAContext::send_msg(const char *fmt, ...)
883 {
884    va_list arg_ptr;
885    va_start(arg_ptr, fmt);
886    bmsg(this, fmt, arg_ptr);
887    va_end(arg_ptr);
888 }
889
890
891 /*
892  * This is an error condition with a command. The gui should put
893  *  up an error or critical dialog box.  The command is aborted.
894  */
895 void UAContext::error_msg(const char *fmt, ...)
896 {
897    BSOCK *bs = UA_sock;
898    va_list arg_ptr;
899
900    if (bs && api) bs->signal(BNET_ERROR_MSG);
901    va_start(arg_ptr, fmt);
902    bmsg(this, fmt, arg_ptr);
903    va_end(arg_ptr);
904 }
905
906 /*  
907  * This is a warning message, that should bring up a warning
908  *  dialog box on the GUI. The command is not aborted, but something
909  *  went wrong.
910  */
911 void UAContext::warning_msg(const char *fmt, ...)
912 {
913    BSOCK *bs = UA_sock;
914    va_list arg_ptr;
915
916    if (bs && api) bs->signal(BNET_WARNING_MSG);
917    va_start(arg_ptr, fmt);
918    bmsg(this, fmt, arg_ptr);
919    va_end(arg_ptr);
920 }
921
922 /* 
923  * This is an information message that should probably be put
924  *  into the status line of a GUI program.
925  */
926 void UAContext::info_msg(const char *fmt, ...)
927 {
928    BSOCK *bs = UA_sock;
929    va_list arg_ptr;
930
931    if (bs && api) bs->signal(BNET_INFO_MSG);
932    va_start(arg_ptr, fmt);
933    bmsg(this, fmt, arg_ptr);
934    va_end(arg_ptr);
935 }