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