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