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