]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
Merge branch 'master' into basejobv3
[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 Base files */
330       } else if (strcasecmp(ua->argk[i], NT_("basefiles")) == 0) {
331          /* TODO: cleanup this block */
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_base_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
345             }
346          }
347       
348       /* List FILES */
349       } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
350
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             if (jobid > 0) {
363                db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
364             }
365          }
366
367       /* List JOBMEDIA */
368       } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
369          bool done = false;
370          for (j=i+1; j<ua->argc; j++) {
371             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
372                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
373                jr.JobId = 0;
374                db_get_job_record(ua->jcr, ua->db, &jr);
375                jobid = jr.JobId;
376             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
377                jobid = str_to_int64(ua->argv[j]);
378             } else {
379                continue;
380             }
381             db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
382             done = true;
383          }
384          if (!done) {
385             /* List for all jobs (jobid=0) */
386             db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
387          }
388
389       /* List JOBLOG */
390       } else if (strcasecmp(ua->argk[i], NT_("joblog")) == 0) {
391          bool done = false;
392          for (j=i+1; j<ua->argc; j++) {
393             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
394                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
395                jr.JobId = 0;
396                db_get_job_record(ua->jcr, ua->db, &jr);
397                jobid = jr.JobId;
398             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
399                jobid = str_to_int64(ua->argv[j]);
400             } else {
401                continue;
402             }
403             db_list_joblog_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
404             done = true;
405          }
406          if (!done) {
407             /* List for all jobs (jobid=0) */
408             db_list_joblog_records(ua->jcr, ua->db, 0, prtit, ua, llist);
409          }
410
411
412       /* List POOLS */
413       } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
414                  strcasecmp(ua->argk[i], NT_("pools")) == 0) {
415          POOL_DBR pr;
416          memset(&pr, 0, sizeof(pr));
417          if (ua->argv[i]) {
418             bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
419          }
420          db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
421
422       } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
423          db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
424
425
426       /* List MEDIA or VOLUMES */
427       } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
428                  strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
429                  strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
430          bool done = false;
431          for (j=i+1; j<ua->argc; j++) {
432             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
433                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
434                jr.JobId = 0;
435                db_get_job_record(ua->jcr, ua->db, &jr);
436                jobid = jr.JobId;
437             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
438                jobid = str_to_int64(ua->argv[j]);
439             } else {
440                continue;
441             }
442             VolumeName = get_pool_memory(PM_FNAME);
443             n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
444             ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
445             free_pool_memory(VolumeName);
446             done = true;
447          }
448          /* if no job or jobid keyword found, then we list all media */
449          if (!done) {
450             int num_pools;
451             uint32_t *ids;
452             /* List a specific volume? */
453             if (ua->argv[i]) {
454                bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
455                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
456                return 1;
457             }
458             /* Is a specific pool wanted? */
459             for (i=1; i<ua->argc; i++) {
460                if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
461                   if (!get_pool_dbr(ua, &pr)) {
462                      ua->error_msg(_("No Pool specified.\n"));
463                      return 1;
464                   }
465                   mr.PoolId = pr.PoolId;
466                   db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
467                   return 1;
468                }
469             }
470
471             /* List Volumes in all pools */
472             if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
473                ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"),
474                         db_strerror(ua->db));
475                return 1;
476             }
477             if (num_pools <= 0) {
478                return 1;
479             }
480             for (i=0; i < num_pools; i++) {
481                pr.PoolId = ids[i];
482                if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
483                   ua->send_msg(_("Pool: %s\n"), pr.Name);
484                }
485                mr.PoolId = ids[i];
486                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
487             }
488             free(ids);
489             return 1;
490          }
491       /* List next volume */
492       } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
493                  strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
494          n = 1;
495          j = find_arg_with_value(ua, NT_("days"));
496          if (j >= 0) {
497             n = atoi(ua->argv[j]);
498             if ((n < 0) || (n > 50)) {
499               ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n"));
500               n = 1;
501             }
502          }
503          list_nextvol(ua, n);
504       } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
505          char *jobids=NULL;
506          uint32_t limit=0;
507          for (j=i+1; j<ua->argc; j++) {
508             if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
509                if (is_a_number_list(ua->argv[j])) {
510                   jobids = ua->argv[j];
511                }
512             } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
513                limit = atoi(ua->argv[j]);
514             } 
515          }
516          db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
517       } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
518                  || strcasecmp(ua->argk[i], NT_("days")) == 0) {
519          /* Ignore it */
520       } else {
521          ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
522       }
523    }
524    return 1;
525 }
526
527 static bool list_nextvol(UAContext *ua, int ndays)
528 {
529    JOB *job;
530    JCR *jcr;          
531    USTORE store;
532    RUN *run;
533    utime_t runtime;
534    bool found = false;
535    MEDIA_DBR mr;
536    POOL_DBR pr;
537
538    memset(&mr, 0, sizeof(mr));
539    int i = find_arg_with_value(ua, "job");
540    if (i <= 0) {
541       if ((job = select_job_resource(ua)) == NULL) {
542          return false;
543       }
544    } else {
545       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
546       if (!job) {
547          Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
548          if ((job = select_job_resource(ua)) == NULL) {
549             return false;
550          }
551       }
552    }
553
554    jcr = new_jcr(sizeof(JCR), dird_free_jcr);
555    for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
556       if (!complete_jcr_for_job(jcr, job, run->pool)) {
557          found = false;
558          goto get_out;
559       }
560       if (!jcr->jr.PoolId) {
561          ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
562          continue;
563       }
564       memset(&pr, 0, sizeof(pr));
565       pr.PoolId = jcr->jr.PoolId;
566       if (!db_get_pool_record(jcr, jcr->db, &pr)) {
567          bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
568       }
569       mr.PoolId = jcr->jr.PoolId;
570       get_job_storage(&store, job, run);
571       mr.StorageId = store.store->StorageId;
572       /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
573       if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
574          ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
575             job->name(), pr.Name, level_to_str(run->level));
576       } else {
577          ua->send_msg(
578             _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
579             job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
580          found = true;
581       }
582    }
583
584 get_out:
585    db_close_database(jcr, jcr->db);
586    jcr->db = NULL;
587    free_jcr(jcr);
588    if (!found) {
589       ua->error_msg(_("Could not find next Volume for Job %s.\n"),
590          job->hdr.name);
591       return false;
592    }
593    return true;
594 }
595
596
597 /*
598  * For a given job, we examine all his run records
599  *  to see if it is scheduled today or tomorrow.
600  */
601 RUN *find_next_run(RUN *run, JOB *job, utime_t &runtime, int ndays)
602 {
603    time_t now, future, endtime;
604    SCHED *sched;
605    struct tm tm, runtm;
606    int mday, wday, month, wom, i;
607    int woy;
608    int day;
609    int is_scheduled;
610
611    sched = job->schedule;
612    if (sched == NULL) {            /* scheduled? */
613       return NULL;                 /* no nothing to report */
614    }
615
616    /* Break down the time into components */
617    now = time(NULL);
618    endtime = now + (ndays * 60 * 60 * 24);
619
620    if (run == NULL) {
621       run = sched->run;
622    } else {
623       run = run->next;
624    }
625    for ( ; run; run=run->next) {
626       /*
627        * Find runs in next 24 hours.  Day 0 is today, so if
628        *   ndays=1, look at today and tomorrow.
629        */
630       for (day = 0; day <= ndays; day++) {
631          future = now + (day * 60 * 60 * 24);
632
633          /* Break down the time into components */
634          (void)localtime_r(&future, &tm);
635          mday = tm.tm_mday - 1;
636          wday = tm.tm_wday;
637          month = tm.tm_mon;
638          wom = mday / 7;
639          woy = tm_woy(future);
640
641          is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
642             bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
643             bit_is_set(woy, run->woy);
644  
645 #ifdef xxx
646          Pmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
647          Pmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
648          Pmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
649          Pmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
650          Pmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
651          Pmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
652 #endif
653
654          if (is_scheduled) { /* Jobs scheduled on that day */
655 #ifdef xxx
656             char buf[300], num[10];
657             bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
658             for (i=0; i<24; i++) {
659                if (bit_is_set(i, run->hour)) {
660                   bsnprintf(num, sizeof(num), "%d ", i);
661                   bstrncat(buf, num, sizeof(buf));
662                }
663             }
664             bstrncat(buf, "\n", sizeof(buf));
665             Pmsg1(000, "%s", buf);
666 #endif
667             /* find time (time_t) job is to be run */
668             (void)localtime_r(&future, &runtm);
669             for (i= 0; i < 24; i++) {
670                if (bit_is_set(i, run->hour)) {
671                   runtm.tm_hour = i;
672                   runtm.tm_min = run->minute;
673                   runtm.tm_sec = 0;
674                   runtime = mktime(&runtm);
675                   Dmsg2(200, "now=%d runtime=%lld\n", now, runtime);
676                   if ((runtime > now) && (runtime < endtime)) {
677                      Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
678                      return run;         /* found it, return run resource */
679                   }
680                }
681             }
682          }
683       }
684    } /* end for loop over runs */
685    /* Nothing found */
686    return NULL;
687 }
688
689 /*
690  * Fill in the remaining fields of the jcr as if it
691  *  is going to run the job.
692  */
693 bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
694 {
695    POOL_DBR pr;
696
697    memset(&pr, 0, sizeof(POOL_DBR));
698    set_jcr_defaults(jcr, job);
699    if (pool) {
700       jcr->pool = pool;               /* override */
701    }
702    if (jcr->db) {
703       Dmsg0(100, "complete_jcr close db\n");
704       db_close_database(jcr, jcr->db);
705       jcr->db = NULL;
706    }
707
708    Dmsg0(100, "complete_jcr open db\n");
709    jcr->db = jcr->db=db_init(jcr, jcr->catalog->db_driver, jcr->catalog->db_name, 
710                       jcr->catalog->db_user,
711                       jcr->catalog->db_password, jcr->catalog->db_address,
712                       jcr->catalog->db_port, jcr->catalog->db_socket,
713                       jcr->catalog->mult_db_connections);
714    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
715       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
716                  jcr->catalog->db_name);
717       if (jcr->db) {
718          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
719          db_close_database(jcr, jcr->db);
720          jcr->db = NULL;
721       }
722       return false;
723    }
724    bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
725    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
726       /* Try to create the pool */
727       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
728          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
729             db_strerror(jcr->db));
730          if (jcr->db) {
731             db_close_database(jcr, jcr->db);
732             jcr->db = NULL;
733          }
734          return false;
735       } else {
736          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
737       }
738    }
739    jcr->jr.PoolId = pr.PoolId;
740    return true;
741 }
742
743
744 static void con_lock_release(void *arg)
745 {
746    Vw(con_lock);
747 }
748
749 void do_messages(UAContext *ua, const char *cmd)
750 {
751    char msg[2000];
752    int mlen;
753    bool do_truncate = false;
754
755    Pw(con_lock);
756    pthread_cleanup_push(con_lock_release, (void *)NULL);
757    rewind(con_fd);
758    while (fgets(msg, sizeof(msg), con_fd)) {
759       mlen = strlen(msg);
760       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
761       strcpy(ua->UA_sock->msg, msg);
762       ua->UA_sock->msglen = mlen;
763       ua->UA_sock->send();
764       do_truncate = true;
765    }
766    if (do_truncate) {
767       (void)ftruncate(fileno(con_fd), 0L);
768    }
769    console_msg_pending = FALSE;
770    ua->user_notified_msg_pending = FALSE;
771    pthread_cleanup_pop(0);
772    Vw(con_lock);
773 }
774
775
776 int qmessagescmd(UAContext *ua, const char *cmd)
777 {
778    if (console_msg_pending && ua->auto_display_messages) {
779       do_messages(ua, cmd);
780    }
781    return 1;
782 }
783
784 int messagescmd(UAContext *ua, const char *cmd)
785 {
786    if (console_msg_pending) {
787       do_messages(ua, cmd);
788    } else {
789       ua->UA_sock->fsend(_("You have no messages.\n"));
790    }
791    return 1;
792 }
793
794 /*
795  * Callback routine for "printing" database file listing
796  */
797 void prtit(void *ctx, const char *msg)
798 {
799    UAContext *ua = (UAContext *)ctx;
800
801    ua->UA_sock->fsend("%s", msg);
802 }
803
804 /*
805  * Format message and send to other end.
806
807  * If the UA_sock is NULL, it means that there is no user
808  * agent, so we are being called from Bacula core. In
809  * that case direct the messages to the Job.
810  */
811 #ifdef HAVE_VA_COPY
812 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
813 {
814    BSOCK *bs = ua->UA_sock;
815    int maxlen, len;
816    POOLMEM *msg = NULL;
817    va_list ap;
818
819    if (bs) {
820       msg = bs->msg;
821    }
822    if (!msg) {
823       msg = get_pool_memory(PM_EMSG);
824    }
825
826 again:
827    maxlen = sizeof_pool_memory(msg) - 1;
828    va_copy(ap, arg_ptr);
829    len = bvsnprintf(msg, maxlen, fmt, ap);
830    va_end(ap);
831    if (len < 0 || len >= maxlen) {
832       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
833       goto again;
834    }
835
836    if (bs) {
837       bs->msg = msg;
838       bs->msglen = len;
839       bs->send();
840    } else {                           /* No UA, send to Job */
841       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
842       free_pool_memory(msg);
843    }
844
845 }
846
847 #else /* no va_copy() -- brain damaged version of variable arguments */
848
849 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
850 {
851    BSOCK *bs = ua->UA_sock;
852    int maxlen, len;
853    POOLMEM *msg = NULL;
854
855    if (bs) {
856       msg = bs->msg;
857    }
858    if (!msg) {
859       msg = get_memory(5000);
860    }
861
862    maxlen = sizeof_pool_memory(msg) - 1;
863    if (maxlen < 4999) {
864       msg = realloc_pool_memory(msg, 5000);
865       maxlen = 4999;
866    }
867    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
868    if (len < 0 || len >= maxlen) {
869       pm_strcpy(msg, _("Message too long to display.\n"));
870       len = strlen(msg);
871    }
872
873    if (bs) {
874       bs->msg = msg;
875       bs->msglen = len;
876       bs->send();
877    } else {                           /* No UA, send to Job */
878       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
879       free_pool_memory(msg);
880    }
881
882 }
883 #endif
884  
885 void bsendmsg(void *ctx, const char *fmt, ...)
886 {
887    va_list arg_ptr;
888    va_start(arg_ptr, fmt);
889    bmsg((UAContext *)ctx, fmt, arg_ptr);
890    va_end(arg_ptr);
891 }
892
893 /*
894  * The following UA methods are mainly intended for GUI
895  * programs
896  */
897 /*
898  * This is a message that should be displayed on the user's 
899  *  console.
900  */
901 void UAContext::send_msg(const char *fmt, ...)
902 {
903    va_list arg_ptr;
904    va_start(arg_ptr, fmt);
905    bmsg(this, fmt, arg_ptr);
906    va_end(arg_ptr);
907 }
908
909
910 /*
911  * This is an error condition with a command. The gui should put
912  *  up an error or critical dialog box.  The command is aborted.
913  */
914 void UAContext::error_msg(const char *fmt, ...)
915 {
916    BSOCK *bs = UA_sock;
917    va_list arg_ptr;
918
919    if (bs && api) bs->signal(BNET_ERROR_MSG);
920    va_start(arg_ptr, fmt);
921    bmsg(this, fmt, arg_ptr);
922    va_end(arg_ptr);
923 }
924
925 /*  
926  * This is a warning message, that should bring up a warning
927  *  dialog box on the GUI. The command is not aborted, but something
928  *  went wrong.
929  */
930 void UAContext::warning_msg(const char *fmt, ...)
931 {
932    BSOCK *bs = UA_sock;
933    va_list arg_ptr;
934
935    if (bs && api) bs->signal(BNET_WARNING_MSG);
936    va_start(arg_ptr, fmt);
937    bmsg(this, fmt, arg_ptr);
938    va_end(arg_ptr);
939 }
940
941 /* 
942  * This is an information message that should probably be put
943  *  into the status line of a GUI program.
944  */
945 void UAContext::info_msg(const char *fmt, ...)
946 {
947    BSOCK *bs = UA_sock;
948    va_list arg_ptr;
949
950    if (bs && api) bs->signal(BNET_INFO_MSG);
951    va_start(arg_ptr, fmt);
952    bmsg(this, fmt, arg_ptr);
953    va_end(arg_ptr);
954 }