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