]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
Partial modification of copyrights as per agreement with FSFE
[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->mult_db_connections,
819                 jcr->catalog->disable_batch_insert);
820    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
821       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
822                  jcr->catalog->db_name);
823       if (jcr->db) {
824          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
825          db_close_database(jcr, jcr->db);
826          jcr->db = NULL;
827       }
828       return false;
829    }
830    bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
831    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
832       /* Try to create the pool */
833       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
834          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
835             db_strerror(jcr->db));
836          if (jcr->db) {
837             db_close_database(jcr, jcr->db);
838             jcr->db = NULL;
839          }
840          return false;
841       } else {
842          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
843       }
844    }
845    jcr->jr.PoolId = pr.PoolId;
846    return true;
847 }
848
849
850 static void con_lock_release(void *arg)
851 {
852    Vw(con_lock);
853 }
854
855 void do_messages(UAContext *ua, const char *cmd)
856 {
857    char msg[2000];
858    int mlen;
859    bool do_truncate = false;
860
861    if (ua->jcr) {
862       dequeue_messages(ua->jcr);
863    }
864    Pw(con_lock);
865    pthread_cleanup_push(con_lock_release, (void *)NULL);
866    rewind(con_fd);
867    while (fgets(msg, sizeof(msg), con_fd)) {
868       mlen = strlen(msg);
869       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
870       strcpy(ua->UA_sock->msg, msg);
871       ua->UA_sock->msglen = mlen;
872       ua->UA_sock->send();
873       do_truncate = true;
874    }
875    if (do_truncate) {
876       (void)ftruncate(fileno(con_fd), 0L);
877    }
878    console_msg_pending = FALSE;
879    ua->user_notified_msg_pending = FALSE;
880    pthread_cleanup_pop(0);
881    Vw(con_lock);
882 }
883
884
885 int qmessagescmd(UAContext *ua, const char *cmd)
886 {
887    if (console_msg_pending && ua->auto_display_messages) {
888       do_messages(ua, cmd);
889    }
890    return 1;
891 }
892
893 int messagescmd(UAContext *ua, const char *cmd)
894 {
895    if (console_msg_pending) {
896       do_messages(ua, cmd);
897    } else {
898       ua->UA_sock->fsend(_("You have no messages.\n"));
899    }
900    return 1;
901 }
902
903 /*
904  * Callback routine for "printing" database file listing
905  */
906 void prtit(void *ctx, const char *msg)
907 {
908    UAContext *ua = (UAContext *)ctx;
909
910    if (ua) ua->send_msg("%s", msg);
911 }
912
913 /*
914  * Format message and send to other end.
915
916  * If the UA_sock is NULL, it means that there is no user
917  * agent, so we are being called from Bacula core. In
918  * that case direct the messages to the Job.
919  */
920 #ifdef HAVE_VA_COPY
921 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
922 {
923    BSOCK *bs = ua->UA_sock;
924    int maxlen, len;
925    POOLMEM *msg = NULL;
926    va_list ap;
927
928    if (bs) {
929       msg = bs->msg;
930    }
931    if (!msg) {
932       msg = get_pool_memory(PM_EMSG);
933    }
934
935 again:
936    maxlen = sizeof_pool_memory(msg) - 1;
937    va_copy(ap, arg_ptr);
938    len = bvsnprintf(msg, maxlen, fmt, ap);
939    va_end(ap);
940    if (len < 0 || len >= maxlen) {
941       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
942       goto again;
943    }
944
945    if (bs) {
946       bs->msg = msg;
947       bs->msglen = len;
948       bs->send();
949    } else {                           /* No UA, send to Job */
950       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
951       free_pool_memory(msg);
952    }
953
954 }
955
956 #else /* no va_copy() -- brain damaged version of variable arguments */
957
958 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
959 {
960    BSOCK *bs = ua->UA_sock;
961    int maxlen, len;
962    POOLMEM *msg = NULL;
963
964    if (bs) {
965       msg = bs->msg;
966    }
967    if (!msg) {
968       msg = get_memory(5000);
969    }
970
971    maxlen = sizeof_pool_memory(msg) - 1;
972    if (maxlen < 4999) {
973       msg = realloc_pool_memory(msg, 5000);
974       maxlen = 4999;
975    }
976    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
977    if (len < 0 || len >= maxlen) {
978       pm_strcpy(msg, _("Message too long to display.\n"));
979       len = strlen(msg);
980    }
981
982    if (bs) {
983       bs->msg = msg;
984       bs->msglen = len;
985       bs->send();
986    } else {                           /* No UA, send to Job */
987       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
988       free_pool_memory(msg);
989    }
990
991 }
992 #endif
993
994 void bsendmsg(void *ctx, const char *fmt, ...)
995 {
996    va_list arg_ptr;
997    va_start(arg_ptr, fmt);
998    bmsg((UAContext *)ctx, fmt, arg_ptr);
999    va_end(arg_ptr);
1000 }
1001
1002 /*
1003  * The following UA methods are mainly intended for GUI
1004  * programs
1005  */
1006 /*
1007  * This is a message that should be displayed on the user's
1008  *  console.
1009  */
1010 void UAContext::send_msg(const char *fmt, ...)
1011 {
1012    va_list arg_ptr;
1013    va_start(arg_ptr, fmt);
1014    bmsg(this, fmt, arg_ptr);
1015    va_end(arg_ptr);
1016 }
1017
1018
1019 /*
1020  * This is an error condition with a command. The gui should put
1021  *  up an error or critical dialog box.  The command is aborted.
1022  */
1023 void UAContext::error_msg(const char *fmt, ...)
1024 {
1025    BSOCK *bs = UA_sock;
1026    va_list arg_ptr;
1027
1028    if (bs && api) bs->signal(BNET_ERROR_MSG);
1029    va_start(arg_ptr, fmt);
1030    bmsg(this, fmt, arg_ptr);
1031    va_end(arg_ptr);
1032 }
1033
1034 /*
1035  * This is a warning message, that should bring up a warning
1036  *  dialog box on the GUI. The command is not aborted, but something
1037  *  went wrong.
1038  */
1039 void UAContext::warning_msg(const char *fmt, ...)
1040 {
1041    BSOCK *bs = UA_sock;
1042    va_list arg_ptr;
1043
1044    if (bs && api) bs->signal(BNET_WARNING_MSG);
1045    va_start(arg_ptr, fmt);
1046    bmsg(this, fmt, arg_ptr);
1047    va_end(arg_ptr);
1048 }
1049
1050 /*
1051  * This is an information message that should probably be put
1052  *  into the status line of a GUI program.
1053  */
1054 void UAContext::info_msg(const char *fmt, ...)
1055 {
1056    BSOCK *bs = UA_sock;
1057    va_list arg_ptr;
1058
1059    if (bs && api) bs->signal(BNET_INFO_MSG);
1060    va_start(arg_ptr, fmt);
1061    bmsg(this, fmt, arg_ptr);
1062    va_end(arg_ptr);
1063 }