]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
kes Make WritePartAfterJob the default so that the last DVD part
[bacula/bacula] / bacula / src / dird / ua_output.c
1 /*
2  *
3  *   Bacula Director -- User Agent Output Commands
4  *     I.e. messages, listing database, showing resources, ...
5  *
6  *     Kern Sibbald, September MM
7  *
8  *   Version $Id$
9  */
10
11 /*
12    Copyright (C) 2000-2006 Kern Sibbald
13
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License
16    version 2 as amended with additional clauses defined in the
17    file LICENSE in the main source directory.
18
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
22    the file LICENSE for additional details.
23
24  */
25
26 #include "bacula.h"
27 #include "dird.h"
28
29 /* Imported subroutines */
30
31 /* Imported variables */
32 extern int r_first;
33 extern int r_last;
34 extern RES_TABLE resources[];
35 extern RES **res_head;
36
37 /* Imported functions */
38
39 /* Forward referenced functions */
40 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist);
41 static bool list_nextvol(UAContext *ua, int ndays);
42
43 /*
44  * Turn auto display of console messages on/off
45  */
46 int autodisplay_cmd(UAContext *ua, const char *cmd)
47 {
48    static const char *kw[] = {
49       NT_("on"),
50       NT_("off"),
51       NULL};
52
53    switch (find_arg_keyword(ua, kw)) {
54    case 0:
55       ua->auto_display_messages = true;
56       break;
57    case 1:
58       ua->auto_display_messages = false;
59       break;
60    default:
61       bsendmsg(ua, _("ON or OFF keyword missing.\n"));
62       break;
63    }
64    return 1;
65 }
66
67 /*
68  * Turn GUI mode on/off
69  */
70 int gui_cmd(UAContext *ua, const char *cmd)
71 {
72    static const char *kw[] = {
73       NT_("on"),
74       NT_("off"),
75       NULL};
76
77    switch (find_arg_keyword(ua, kw)) {
78    case 0:
79       ua->jcr->gui = ua->gui = true;
80       break;
81    case 1:
82       ua->jcr->gui = ua->gui = false;
83       break;
84    default:
85       bsendmsg(ua, _("ON or OFF keyword missing.\n"));
86       break;
87    }
88    return 1;
89 }
90
91
92
93 struct showstruct {const char *res_name; int type;};
94 static struct showstruct reses[] = {
95    {NT_("directors"),  R_DIRECTOR},
96    {NT_("clients"),    R_CLIENT},
97    {NT_("counters"),   R_COUNTER},
98    {NT_("devices"),    R_DEVICE},
99    {NT_("jobs"),       R_JOB},
100    {NT_("storages"),   R_STORAGE},
101    {NT_("catalogs"),   R_CATALOG},
102    {NT_("schedules"),  R_SCHEDULE},
103    {NT_("filesets"),   R_FILESET},
104    {NT_("pools"),      R_POOL},
105    {NT_("messages"),   R_MSGS},
106    {NT_("all"),        -1},
107    {NT_("help"),       -2},
108    {NULL,           0}
109 };
110
111
112 /*
113  *  Displays Resources
114  *
115  *  show all
116  *  show <resource-keyword-name>  e.g. show directors
117  *  show <resource-keyword-name>=<name> e.g. show director=HeadMan
118  *
119  */
120 int show_cmd(UAContext *ua, const char *cmd)
121 {
122    int i, j, type, len;
123    int recurse;
124    char *res_name;
125    RES *res = NULL;
126
127    Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
128
129
130    LockRes();
131    for (i=1; i<ua->argc; i++) {
132       type = 0;
133       res_name = ua->argk[i];
134       if (!ua->argv[i]) {             /* was a name given? */
135          /* No name, dump all resources of specified type */
136          recurse = 1;
137          len = strlen(res_name);
138          for (j=0; reses[j].res_name; j++) {
139             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
140                type = reses[j].type;
141                if (type > 0) {
142                   res = res_head[type-r_first];
143                } else {
144                   res = NULL;
145                }
146                break;
147             }
148          }
149
150       } else {
151          /* Dump a single resource with specified name */
152          recurse = 0;
153          len = strlen(res_name);
154          for (j=0; reses[j].res_name; j++) {
155             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
156                type = reses[j].type;
157                res = (RES *)GetResWithName(type, ua->argv[i]);
158                if (!res) {
159                   type = -3;
160                }
161                break;
162             }
163          }
164       }
165
166       switch (type) {
167       case -1:                           /* all */
168          for (j=r_first; j<=r_last; j++) {
169             dump_resource(j, res_head[j-r_first], bsendmsg, ua);
170          }
171          break;
172       case -2:
173          bsendmsg(ua, _("Keywords for the show command are:\n"));
174          for (j=0; reses[j].res_name; j++) {
175             bsendmsg(ua, "%s\n", _(reses[j].res_name));
176          }
177          goto bail_out;
178       case -3:
179          bsendmsg(ua, _("%s resource %s not found.\n"), res_name, ua->argv[i]);
180          goto bail_out;
181       case 0:
182          bsendmsg(ua, _("Resource %s not found\n"), res_name);
183          goto bail_out;
184       default:
185          dump_resource(recurse?type:-type, res, bsendmsg, ua);
186          break;
187       }
188    }
189 bail_out:
190    UnlockRes();
191    return 1;
192 }
193
194
195
196
197 /*
198  *  List contents of database
199  *
200  *  list jobs           - lists all jobs run
201  *  list jobid=nnn      - list job data for jobid
202  *  list ujobid=uname   - list job data for unique jobid
203  *  list job=name       - list all jobs with "name"   
204  *  list jobname=name   - same as above 
205  *  list jobmedia jobid=<nn>
206  *  list jobmedia job=name
207  *  list files jobid=<nn> - list files saved for job nn
208  *  list files job=name
209  *  list pools          - list pool records
210  *  list jobtotals      - list totals for all jobs
211  *  list media          - list media for given pool (deprecated)
212  *  list volumes        - list Volumes
213  *  list clients        - list clients
214  *  list nextvol job=xx  - list the next vol to be used by job
215  *  list nextvolume job=xx - same as above.
216  *
217  */
218
219 /* Do long or full listing */
220 int llist_cmd(UAContext *ua, const char *cmd)
221 {
222    return do_list_cmd(ua, cmd, VERT_LIST);
223 }
224
225 /* Do short or summary listing */
226 int list_cmd(UAContext *ua, const char *cmd)
227 {
228    return do_list_cmd(ua, cmd, HORZ_LIST);
229 }
230
231 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
232 {
233    POOLMEM *VolumeName;
234    int jobid, n;
235    int i, j;
236    JOB_DBR jr;
237    POOL_DBR pr;
238    MEDIA_DBR mr;
239
240    if (!open_db(ua))
241       return 1;
242
243    memset(&jr, 0, sizeof(jr));
244    memset(&pr, 0, sizeof(pr));
245    memset(&mr, 0, sizeof(mr));
246
247    Dmsg1(20, "list: %s\n", cmd);
248
249    if (!ua->db) {
250       bsendmsg(ua, _("Hey! DB is NULL\n"));
251    }
252
253    /* Scan arguments looking for things to do */
254    for (i=1; i<ua->argc; i++) {
255       /* List JOBS */
256       if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
257          /* Apply any limit */
258          j = find_arg_with_value(ua, NT_("limit"));
259          if (j >= 0) {
260             jr.limit = atoi(ua->argv[j]);
261          }
262          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
263
264          /* List JOBTOTALS */
265       } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
266          db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
267
268       /* List JOBID=nn */
269       } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
270          if (ua->argv[i]) {
271             jobid = str_to_int64(ua->argv[i]);
272             if (jobid > 0) {
273                jr.JobId = jobid;
274                db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
275             }
276          }
277
278       /* List JOB=xxx */
279       } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
280                   strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
281          bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
282          jr.JobId = 0;
283          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
284
285       /* List UJOBID=xxx */
286       } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
287          bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
288          jr.JobId = 0;
289          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
290
291       /* List FILES */
292       } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
293
294          for (j=i+1; j<ua->argc; j++) {
295             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
296                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
297                jr.JobId = 0;
298                db_get_job_record(ua->jcr, ua->db, &jr);
299                jobid = jr.JobId;
300             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
301                jobid = str_to_int64(ua->argv[j]);
302             } else {
303                continue;
304             }
305             if (jobid > 0) {
306                db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
307             }
308          }
309
310       /* List JOBMEDIA */
311       } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
312          int done = FALSE;
313          for (j=i+1; j<ua->argc; j++) {
314             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
315                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
316                jr.JobId = 0;
317                db_get_job_record(ua->jcr, ua->db, &jr);
318                jobid = jr.JobId;
319             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
320                jobid = str_to_int64(ua->argv[j]);
321             } else {
322                continue;
323             }
324             db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
325             done = TRUE;
326          }
327          if (!done) {
328             /* List for all jobs (jobid=0) */
329             db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
330          }
331
332       /* List POOLS */
333       } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
334                  strcasecmp(ua->argk[i], NT_("pools")) == 0) {
335          POOL_DBR pr;
336          memset(&pr, 0, sizeof(pr));
337          if (ua->argv[i]) {
338             bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
339          }
340          db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
341
342       } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
343          db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
344
345
346       /* List MEDIA or VOLUMES */
347       } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
348                  strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
349                  strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
350          bool done = false;
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             VolumeName = get_pool_memory(PM_FNAME);
363             n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
364             bsendmsg(ua, _("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
365             free_pool_memory(VolumeName);
366             done = true;
367          }
368          /* if no job or jobid keyword found, then we list all media */
369          if (!done) {
370             int num_pools;
371             uint32_t *ids;
372             /* List a specific volume? */
373             if (ua->argv[i]) {
374                bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
375                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
376                return 1;
377             }
378             /* Is a specific pool wanted? */
379             for (i=1; i<ua->argc; i++) {
380                if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
381                   if (!get_pool_dbr(ua, &pr)) {
382                      bsendmsg(ua, _("No Pool specified.\n"));
383                      return 1;
384                   }
385                   mr.PoolId = pr.PoolId;
386                   db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
387                   return 1;
388                }
389             }
390
391             /* List Volumes in all pools */
392             if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
393                bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"),
394                         db_strerror(ua->db));
395                return 1;
396             }
397             if (num_pools <= 0) {
398                return 1;
399             }
400             for (i=0; i < num_pools; i++) {
401                pr.PoolId = ids[i];
402                if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
403                   bsendmsg(ua, _("Pool: %s\n"), pr.Name);
404                }
405                mr.PoolId = ids[i];
406                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
407             }
408             free(ids);
409             return 1;
410          }
411       /* List next volume */
412       } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
413                  strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
414          n = 1;
415          j = find_arg_with_value(ua, NT_("days"));
416          if (j >= 0) {
417             n = atoi(ua->argv[j]);
418             if ((n < 0) || (n > 50)) {
419               bsendmsg(ua, _("Ignoring illegal value for days.\n"));
420               n = 1;
421             }
422          }
423          list_nextvol(ua, n);
424       } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
425                  || strcasecmp(ua->argk[i], NT_("days")) == 0) {
426          /* Ignore it */
427       } else {
428          bsendmsg(ua, _("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
429       }
430    }
431    return 1;
432 }
433
434 static bool list_nextvol(UAContext *ua, int ndays)
435 {
436    JOB *job;
437    JCR *jcr = ua->jcr;
438    POOL *pool;
439    RUN *run;
440    time_t runtime;
441    bool found = false;
442    MEDIA_DBR mr;
443    POOL_DBR pr;
444
445    memset(&mr, 0, sizeof(mr));
446    int i = find_arg_with_value(ua, "job");
447    if (i <= 0) {
448       if ((job = select_job_resource(ua)) == NULL) {
449          return false;
450       }
451    } else {
452       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
453       if (!job) {
454          Jmsg(jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
455          if ((job = select_job_resource(ua)) == NULL) {
456             return false;
457          }
458       }
459    }
460    for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
461       pool = run->pool ? run->pool : NULL;
462       if (!complete_jcr_for_job(jcr, job, pool)) {
463          return false;
464       }
465       memset(&pr, 0, sizeof(pr));
466       pr.PoolId = jcr->jr.PoolId;
467       if (! db_get_pool_record(ua->jcr, ua->db, &pr)) {
468          bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
469       }
470       mr.PoolId = jcr->jr.PoolId;
471       if (run->storage) {
472          jcr->wstore = run->storage;
473       } else {
474          jcr->wstore = (STORE *)job->storage->first();
475       }
476       mr.StorageId = jcr->wstore->StorageId;
477       if (!find_next_volume_for_append(jcr, &mr, 1, false/*no create*/)) {
478          bsendmsg(ua, _("Could not find next Volume for Job %s (%s, %s).\n"),
479             job->hdr.name, pr.Name, level_to_str(run->level));
480       } else {
481          bsendmsg(ua,
482             _("The next Volume to be used by Job \"%s\" (%s, %s) will be %s\n"),
483             job->hdr.name, pr.Name, level_to_str(run->level), mr.VolumeName);
484          found = true;
485       }
486       if (jcr->db && jcr->db != ua->db) {
487          db_close_database(jcr, jcr->db);
488          jcr->db = NULL;
489       }
490    }
491    if (!found) {
492       bsendmsg(ua, _("Could not find next Volume for Job %s.\n"),
493          job->hdr.name);
494       return false;
495    }
496    return true;
497 }
498
499
500 /*
501  * For a given job, we examine all his run records
502  *  to see if it is scheduled today or tomorrow.
503  */
504 RUN *find_next_run(RUN *run, JOB *job, time_t &runtime, int ndays)
505 {
506    time_t now, future, endtime;
507    SCHED *sched;
508    struct tm tm, runtm;
509    int mday, wday, month, wom, i;
510    int woy;
511    int day;
512    int is_scheduled;
513
514    sched = job->schedule;
515    if (sched == NULL) {            /* scheduled? */
516       return NULL;                 /* no nothing to report */
517    }
518
519    /* Break down the time into components */
520    now = time(NULL);
521    endtime = now + (ndays * 60 * 60 * 24);
522
523    if (run == NULL) {
524       run = sched->run;
525    } else {
526       run = run->next;
527    }
528    for ( ; run; run=run->next) {
529       /*
530        * Find runs in next 24 hours.  Day 0 is today, so if
531        *   ndays=1, look at today and tomorrow.
532        */
533       for (day = 0; day <= ndays; day++) {
534          future = now + (day * 60 * 60 * 24);
535
536          /* Break down the time into components */
537          (void)localtime_r(&future, &tm);
538          mday = tm.tm_mday - 1;
539          wday = tm.tm_wday;
540          month = tm.tm_mon;
541          wom = mday / 7;
542          woy = tm_woy(future);
543
544          is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
545             bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
546             bit_is_set(woy, run->woy);
547  
548 #ifdef xxx
549          Dmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
550          Dmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
551          Dmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
552          Dmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
553          Dmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
554          Dmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
555 #endif
556
557          if (is_scheduled) { /* Jobs scheduled on that day */
558 #ifdef xxx
559             char buf[300], num[10];
560             bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
561             for (i=0; i<24; i++) {
562                if (bit_is_set(i, run->hour)) {
563                   bsnprintf(num, sizeof(num), "%d ", i);
564                   bstrncat(buf, num, sizeof(buf));
565                }
566             }
567             bstrncat(buf, "\n", sizeof(buf));
568             Dmsg1(000, "%s", buf);
569 #endif
570             /* find time (time_t) job is to be run */
571             (void)localtime_r(&future, &runtm);
572             for (i= 0; i < 24; i++) {
573                if (bit_is_set(i, run->hour)) {
574                   runtm.tm_hour = i;
575                   runtm.tm_min = run->minute;
576                   runtm.tm_sec = 0;
577                   runtime = mktime(&runtm);
578                   Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
579                   if ((runtime > now) && (runtime < endtime)) {
580                      Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
581                      return run;         /* found it, return run resource */
582                   }
583                }
584             }
585          }
586       }
587    } /* end for loop over runs */
588    /* Nothing found */
589    return NULL;
590 }
591
592 /*
593  * Fill in the remaining fields of the jcr as if it
594  *  is going to run the job.
595  */
596 int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
597 {
598    POOL_DBR pr;
599
600    memset(&pr, 0, sizeof(POOL_DBR));
601    set_jcr_defaults(jcr, job);
602    if (pool) {
603       jcr->pool = pool;               /* override */
604    }
605    jcr->db = jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
606                       jcr->catalog->db_password, jcr->catalog->db_address,
607                       jcr->catalog->db_port, jcr->catalog->db_socket,
608                       jcr->catalog->mult_db_connections);
609    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
610       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
611                  jcr->catalog->db_name);
612       if (jcr->db) {
613          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
614       }
615       return 0;
616    }
617    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
618    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
619       /* Try to create the pool */
620       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
621          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
622             db_strerror(jcr->db));
623          if (jcr->db) {
624             db_close_database(jcr, jcr->db);
625             jcr->db = NULL;
626          }
627          return 0;
628       } else {
629          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
630       }
631    }
632    jcr->jr.PoolId = pr.PoolId;
633    return 1;
634 }
635
636
637 static void con_lock_release(void *arg)
638 {
639    Vw(con_lock);
640 }
641
642 void do_messages(UAContext *ua, const char *cmd)
643 {
644    char msg[2000];
645    int mlen;
646    bool do_truncate = false;
647
648    Pw(con_lock);
649    pthread_cleanup_push(con_lock_release, (void *)NULL);
650    rewind(con_fd);
651    while (fgets(msg, sizeof(msg), con_fd)) {
652       mlen = strlen(msg);
653       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
654       strcpy(ua->UA_sock->msg, msg);
655       ua->UA_sock->msglen = mlen;
656       bnet_send(ua->UA_sock);
657       do_truncate = true;
658    }
659    if (do_truncate) {
660       (void)ftruncate(fileno(con_fd), 0L);
661    }
662    console_msg_pending = FALSE;
663    ua->user_notified_msg_pending = FALSE;
664    pthread_cleanup_pop(0);
665    Vw(con_lock);
666 }
667
668
669 int qmessagescmd(UAContext *ua, const char *cmd)
670 {
671    if (console_msg_pending && ua->auto_display_messages) {
672       do_messages(ua, cmd);
673    }
674    return 1;
675 }
676
677 int messagescmd(UAContext *ua, const char *cmd)
678 {
679    if (console_msg_pending) {
680       do_messages(ua, cmd);
681    } else {
682       bnet_fsend(ua->UA_sock, _("You have no messages.\n"));
683    }
684    return 1;
685 }
686
687 /*
688  * Callback routine for "printing" database file listing
689  */
690 void prtit(void *ctx, const char *msg)
691 {
692    UAContext *ua = (UAContext *)ctx;
693
694    bnet_fsend(ua->UA_sock, "%s", msg);
695 }
696
697 /*
698  * Format message and send to other end.
699
700  * If the UA_sock is NULL, it means that there is no user
701  * agent, so we are being called from Bacula core. In
702  * that case direct the messages to the Job.
703  */
704 void bsendmsg(void *ctx, const char *fmt, ...)
705 {
706    va_list arg_ptr;
707    UAContext *ua = (UAContext *)ctx;
708    BSOCK *bs = ua->UA_sock;
709    int maxlen, len;
710    POOLMEM *msg;
711
712    if (bs) {
713       msg = bs->msg;
714    } else {
715       msg = get_pool_memory(PM_EMSG);
716    }
717
718 again:
719    maxlen = sizeof_pool_memory(msg) - 1;
720    va_start(arg_ptr, fmt);
721    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
722    va_end(arg_ptr);
723    if (len < 0 || len >= maxlen) {
724       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
725       goto again;
726    }
727
728    if (bs) {
729       bs->msg = msg;
730       bs->msglen = len;
731       bnet_send(bs);
732    } else {                           /* No UA, send to Job */
733       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
734       free_pool_memory(msg);
735    }
736
737 }