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