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