]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
Fix bat seg fault
[bacula/bacula] / bacula / src / dird / ua_output.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 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    memset(&mr, 0, sizeof(mr));
284
285    Dmsg1(20, "list: %s\n", cmd);
286
287    if (!ua->db) {
288       ua->error_msg(_("Hey! DB is NULL\n"));
289    }
290
291    /* Apply any limit */
292    j = find_arg_with_value(ua, NT_("limit"));
293    if (j >= 0) {
294       jr.limit = atoi(ua->argv[j]);
295    }
296
297    /* Scan arguments looking for things to do */
298    for (i=1; i<ua->argc; i++) {
299       /* List JOBS */
300       if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
301          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
302
303          /* List JOBTOTALS */
304       } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
305          db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
306
307       /* List JOBID=nn */
308       } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
309          if (ua->argv[i]) {
310             jobid = str_to_int64(ua->argv[i]);
311             if (jobid > 0) {
312                jr.JobId = jobid;
313                db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
314             }
315          }
316
317       /* List JOB=xxx */
318       } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
319                   strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
320          bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
321          jr.JobId = 0;
322          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
323
324       /* List UJOBID=xxx */
325       } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
326          bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
327          jr.JobId = 0;
328          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
329
330       /* List Base files */
331       } else if (strcasecmp(ua->argk[i], NT_("basefiles")) == 0) {
332          /* TODO: cleanup this block */
333          for (j=i+1; j<ua->argc; j++) {
334             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
335                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
336                jr.JobId = 0;
337                db_get_job_record(ua->jcr, ua->db, &jr);
338                jobid = jr.JobId;
339             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
340                jobid = str_to_int64(ua->argv[j]);
341             } else {
342                continue;
343             }
344             if (jobid > 0) {
345                db_list_base_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
346             }
347          }
348       
349       /* List FILES */
350       } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
351
352          for (j=i+1; j<ua->argc; j++) {
353             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
354                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
355                jr.JobId = 0;
356                db_get_job_record(ua->jcr, ua->db, &jr);
357                jobid = jr.JobId;
358             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
359                jobid = str_to_int64(ua->argv[j]);
360             } else {
361                continue;
362             }
363             if (jobid > 0) {
364                db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
365             }
366          }
367
368       /* List JOBMEDIA */
369       } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
370          bool done = false;
371          for (j=i+1; j<ua->argc; j++) {
372             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
373                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
374                jr.JobId = 0;
375                db_get_job_record(ua->jcr, ua->db, &jr);
376                jobid = jr.JobId;
377             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
378                jobid = str_to_int64(ua->argv[j]);
379             } else {
380                continue;
381             }
382             db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
383             done = true;
384          }
385          if (!done) {
386             /* List for all jobs (jobid=0) */
387             db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
388          }
389
390       /* List JOBLOG */
391       } else if (strcasecmp(ua->argk[i], NT_("joblog")) == 0) {
392          bool done = false;
393          for (j=i+1; j<ua->argc; j++) {
394             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
395                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
396                jr.JobId = 0;
397                db_get_job_record(ua->jcr, ua->db, &jr);
398                jobid = jr.JobId;
399             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
400                jobid = str_to_int64(ua->argv[j]);
401             } else {
402                continue;
403             }
404             db_list_joblog_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
405             done = true;
406          }
407          if (!done) {
408             /* List for all jobs (jobid=0) */
409             db_list_joblog_records(ua->jcr, ua->db, 0, prtit, ua, llist);
410          }
411
412
413       /* List POOLS */
414       } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
415                  strcasecmp(ua->argk[i], NT_("pools")) == 0) {
416          POOL_DBR pr;
417          memset(&pr, 0, sizeof(pr));
418          if (ua->argv[i]) {
419             bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
420          }
421          db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
422
423       } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
424          db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
425
426
427       /* List MEDIA or VOLUMES */
428       } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
429                  strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
430                  strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
431          bool done = false;
432          for (j=i+1; j<ua->argc; j++) {
433             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
434                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
435                jr.JobId = 0;
436                db_get_job_record(ua->jcr, ua->db, &jr);
437                jobid = jr.JobId;
438             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
439                jobid = str_to_int64(ua->argv[j]);
440             } else {
441                continue;
442             }
443             VolumeName = get_pool_memory(PM_FNAME);
444             n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
445             ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
446             free_pool_memory(VolumeName);
447             done = true;
448          }
449          /* if no job or jobid keyword found, then we list all media */
450          if (!done) {
451             int num_pools;
452             uint32_t *ids;
453             /* List a specific volume? */
454             if (ua->argv[i]) {
455                bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
456                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
457                return 1;
458             }
459             /* Is a specific pool wanted? */
460             for (i=1; i<ua->argc; i++) {
461                if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
462                   if (!get_pool_dbr(ua, &pr)) {
463                      ua->error_msg(_("No Pool specified.\n"));
464                      return 1;
465                   }
466                   mr.PoolId = pr.PoolId;
467                   db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
468                   return 1;
469                }
470             }
471
472             /* List Volumes in all pools */
473             if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
474                ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"),
475                         db_strerror(ua->db));
476                return 1;
477             }
478             if (num_pools <= 0) {
479                return 1;
480             }
481             for (i=0; i < num_pools; i++) {
482                pr.PoolId = ids[i];
483                if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
484                   ua->send_msg(_("Pool: %s\n"), pr.Name);
485                }
486                mr.PoolId = ids[i];
487                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
488             }
489             free(ids);
490             return 1;
491          }
492       /* List next volume */
493       } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
494                  strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
495          n = 1;
496          j = find_arg_with_value(ua, NT_("days"));
497          if (j >= 0) {
498             n = atoi(ua->argv[j]);
499             if ((n < 0) || (n > 50)) {
500               ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n"));
501               n = 1;
502             }
503          }
504          list_nextvol(ua, n);
505       } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
506          char *jobids = NULL;
507          uint32_t limit=0;
508          for (j=i+1; j<ua->argc; j++) {
509             if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
510                if (is_a_number_list(ua->argv[j])) {
511                   jobids = ua->argv[j];
512                }
513             } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
514                limit = atoi(ua->argv[j]);
515             } 
516          }
517          db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
518       } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
519                  || strcasecmp(ua->argk[i], NT_("days")) == 0) {
520          /* Ignore it */
521       } else {
522          ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
523       }
524    }
525    return 1;
526 }
527
528 static bool list_nextvol(UAContext *ua, int ndays)
529 {
530    JOB *job;
531    JCR *jcr;          
532    USTORE store;
533    RUN *run;
534    utime_t runtime;
535    bool found = false;
536    MEDIA_DBR mr;
537    POOL_DBR pr;
538
539    memset(&mr, 0, sizeof(mr));
540    int i = find_arg_with_value(ua, "job");
541    if (i <= 0) {
542       if ((job = select_job_resource(ua)) == NULL) {
543          return false;
544       }
545    } else {
546       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
547       if (!job) {
548          Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
549          if ((job = select_job_resource(ua)) == NULL) {
550             return false;
551          }
552       }
553    }
554
555    jcr = new_jcr(sizeof(JCR), dird_free_jcr);
556    for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
557       if (!complete_jcr_for_job(jcr, job, run->pool)) {
558          found = false;
559          goto get_out;
560       }
561       if (!jcr->jr.PoolId) {
562          ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
563          continue;
564       }
565       memset(&pr, 0, sizeof(pr));
566       pr.PoolId = jcr->jr.PoolId;
567       if (!db_get_pool_record(jcr, jcr->db, &pr)) {
568          bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
569       }
570       mr.PoolId = jcr->jr.PoolId;
571       get_job_storage(&store, job, run);
572       mr.StorageId = store.store->StorageId;
573       /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
574       if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
575          ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
576             job->name(), pr.Name, level_to_str(run->level));
577       } else {
578          ua->send_msg(
579             _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
580             job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
581          found = true;
582       }
583    }
584
585 get_out:
586    db_close_database(jcr, jcr->db);
587    jcr->db = NULL;
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(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    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
716       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
717                  jcr->catalog->db_name);
718       if (jcr->db) {
719          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
720          db_close_database(jcr, jcr->db);
721          jcr->db = NULL;
722       }
723       return false;
724    }
725    bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
726    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
727       /* Try to create the pool */
728       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
729          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
730             db_strerror(jcr->db));
731          if (jcr->db) {
732             db_close_database(jcr, jcr->db);
733             jcr->db = NULL;
734          }
735          return false;
736       } else {
737          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
738       }
739    }
740    jcr->jr.PoolId = pr.PoolId;
741    return true;
742 }
743
744
745 static void con_lock_release(void *arg)
746 {
747    Vw(con_lock);
748 }
749
750 void do_messages(UAContext *ua, const char *cmd)
751 {
752    char msg[2000];
753    int mlen;
754    bool do_truncate = false;
755
756    Pw(con_lock);
757    pthread_cleanup_push(con_lock_release, (void *)NULL);
758    rewind(con_fd);
759    while (fgets(msg, sizeof(msg), con_fd)) {
760       mlen = strlen(msg);
761       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
762       strcpy(ua->UA_sock->msg, msg);
763       ua->UA_sock->msglen = mlen;
764       ua->UA_sock->send();
765       do_truncate = true;
766    }
767    if (do_truncate) {
768       (void)ftruncate(fileno(con_fd), 0L);
769    }
770    console_msg_pending = FALSE;
771    ua->user_notified_msg_pending = FALSE;
772    pthread_cleanup_pop(0);
773    Vw(con_lock);
774 }
775
776
777 int qmessagescmd(UAContext *ua, const char *cmd)
778 {
779    if (console_msg_pending && ua->auto_display_messages) {
780       do_messages(ua, cmd);
781    }
782    return 1;
783 }
784
785 int messagescmd(UAContext *ua, const char *cmd)
786 {
787    if (console_msg_pending) {
788       do_messages(ua, cmd);
789    } else {
790       ua->UA_sock->fsend(_("You have no messages.\n"));
791    }
792    return 1;
793 }
794
795 /*
796  * Callback routine for "printing" database file listing
797  */
798 void prtit(void *ctx, const char *msg)
799 {
800    UAContext *ua = (UAContext *)ctx;
801
802    ua->UA_sock->fsend("%s", msg);
803 }
804
805 /*
806  * Format message and send to other end.
807
808  * If the UA_sock is NULL, it means that there is no user
809  * agent, so we are being called from Bacula core. In
810  * that case direct the messages to the Job.
811  */
812 #ifdef HAVE_VA_COPY
813 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
814 {
815    BSOCK *bs = ua->UA_sock;
816    int maxlen, len;
817    POOLMEM *msg = NULL;
818    va_list ap;
819
820    if (bs) {
821       msg = bs->msg;
822    }
823    if (!msg) {
824       msg = get_pool_memory(PM_EMSG);
825    }
826
827 again:
828    maxlen = sizeof_pool_memory(msg) - 1;
829    va_copy(ap, arg_ptr);
830    len = bvsnprintf(msg, maxlen, fmt, ap);
831    va_end(ap);
832    if (len < 0 || len >= maxlen) {
833       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
834       goto again;
835    }
836
837    if (bs) {
838       bs->msg = msg;
839       bs->msglen = len;
840       bs->send();
841    } else {                           /* No UA, send to Job */
842       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
843       free_pool_memory(msg);
844    }
845
846 }
847
848 #else /* no va_copy() -- brain damaged version of variable arguments */
849
850 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
851 {
852    BSOCK *bs = ua->UA_sock;
853    int maxlen, len;
854    POOLMEM *msg = NULL;
855
856    if (bs) {
857       msg = bs->msg;
858    }
859    if (!msg) {
860       msg = get_memory(5000);
861    }
862
863    maxlen = sizeof_pool_memory(msg) - 1;
864    if (maxlen < 4999) {
865       msg = realloc_pool_memory(msg, 5000);
866       maxlen = 4999;
867    }
868    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
869    if (len < 0 || len >= maxlen) {
870       pm_strcpy(msg, _("Message too long to display.\n"));
871       len = strlen(msg);
872    }
873
874    if (bs) {
875       bs->msg = msg;
876       bs->msglen = len;
877       bs->send();
878    } else {                           /* No UA, send to Job */
879       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
880       free_pool_memory(msg);
881    }
882
883 }
884 #endif
885  
886 void bsendmsg(void *ctx, const char *fmt, ...)
887 {
888    va_list arg_ptr;
889    va_start(arg_ptr, fmt);
890    bmsg((UAContext *)ctx, fmt, arg_ptr);
891    va_end(arg_ptr);
892 }
893
894 /*
895  * The following UA methods are mainly intended for GUI
896  * programs
897  */
898 /*
899  * This is a message that should be displayed on the user's 
900  *  console.
901  */
902 void UAContext::send_msg(const char *fmt, ...)
903 {
904    va_list arg_ptr;
905    va_start(arg_ptr, fmt);
906    bmsg(this, fmt, arg_ptr);
907    va_end(arg_ptr);
908 }
909
910
911 /*
912  * This is an error condition with a command. The gui should put
913  *  up an error or critical dialog box.  The command is aborted.
914  */
915 void UAContext::error_msg(const char *fmt, ...)
916 {
917    BSOCK *bs = UA_sock;
918    va_list arg_ptr;
919
920    if (bs && api) bs->signal(BNET_ERROR_MSG);
921    va_start(arg_ptr, fmt);
922    bmsg(this, fmt, arg_ptr);
923    va_end(arg_ptr);
924 }
925
926 /*  
927  * This is a warning message, that should bring up a warning
928  *  dialog box on the GUI. The command is not aborted, but something
929  *  went wrong.
930  */
931 void UAContext::warning_msg(const char *fmt, ...)
932 {
933    BSOCK *bs = UA_sock;
934    va_list arg_ptr;
935
936    if (bs && api) bs->signal(BNET_WARNING_MSG);
937    va_start(arg_ptr, fmt);
938    bmsg(this, fmt, arg_ptr);
939    va_end(arg_ptr);
940 }
941
942 /* 
943  * This is an information message that should probably be put
944  *  into the status line of a GUI program.
945  */
946 void UAContext::info_msg(const char *fmt, ...)
947 {
948    BSOCK *bs = UA_sock;
949    va_list arg_ptr;
950
951    if (bs && api) bs->signal(BNET_INFO_MSG);
952    va_start(arg_ptr, fmt);
953    bmsg(this, fmt, arg_ptr);
954    va_end(arg_ptr);
955 }