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