]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
kes Add back code to open tape device nonblocking, but if rewind fails
[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  * Fill in the remaining fields of the jcr as if it
593  *  is going to run the job.
594  */
595 int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
596 {
597    POOL_DBR pr;
598
599    memset(&pr, 0, sizeof(POOL_DBR));
600    set_jcr_defaults(jcr, job);
601    if (pool) {
602       jcr->pool = pool;               /* override */
603    }
604    jcr->db = jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
605                       jcr->catalog->db_password, jcr->catalog->db_address,
606                       jcr->catalog->db_port, jcr->catalog->db_socket,
607                       jcr->catalog->mult_db_connections);
608    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
609       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
610                  jcr->catalog->db_name);
611       if (jcr->db) {
612          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
613       }
614       return 0;
615    }
616    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
617    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
618       /* Try to create the pool */
619       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
620          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
621             db_strerror(jcr->db));
622          if (jcr->db) {
623             db_close_database(jcr, jcr->db);
624             jcr->db = NULL;
625          }
626          return 0;
627       } else {
628          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
629       }
630    }
631    jcr->jr.PoolId = pr.PoolId;
632    return 1;
633 }
634
635
636 static void con_lock_release(void *arg)
637 {
638    Vw(con_lock);
639 }
640
641 void do_messages(UAContext *ua, const char *cmd)
642 {
643    char msg[2000];
644    int mlen;
645    bool do_truncate = false;
646
647    Pw(con_lock);
648    pthread_cleanup_push(con_lock_release, (void *)NULL);
649    rewind(con_fd);
650    while (fgets(msg, sizeof(msg), con_fd)) {
651       mlen = strlen(msg);
652       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
653       strcpy(ua->UA_sock->msg, msg);
654       ua->UA_sock->msglen = mlen;
655       bnet_send(ua->UA_sock);
656       do_truncate = true;
657    }
658    if (do_truncate) {
659       (void)ftruncate(fileno(con_fd), 0L);
660    }
661    console_msg_pending = FALSE;
662    ua->user_notified_msg_pending = FALSE;
663    pthread_cleanup_pop(0);
664    Vw(con_lock);
665 }
666
667
668 int qmessagescmd(UAContext *ua, const char *cmd)
669 {
670    if (console_msg_pending && ua->auto_display_messages) {
671       do_messages(ua, cmd);
672    }
673    return 1;
674 }
675
676 int messagescmd(UAContext *ua, const char *cmd)
677 {
678    if (console_msg_pending) {
679       do_messages(ua, cmd);
680    } else {
681       bnet_fsend(ua->UA_sock, _("You have no messages.\n"));
682    }
683    return 1;
684 }
685
686 /*
687  * Callback routine for "printing" database file listing
688  */
689 void prtit(void *ctx, const char *msg)
690 {
691    UAContext *ua = (UAContext *)ctx;
692
693    bnet_fsend(ua->UA_sock, "%s", msg);
694 }
695
696 /*
697  * Format message and send to other end.
698
699  * If the UA_sock is NULL, it means that there is no user
700  * agent, so we are being called from Bacula core. In
701  * that case direct the messages to the Job.
702  */
703 void bsendmsg(void *ctx, const char *fmt, ...)
704 {
705    va_list arg_ptr;
706    UAContext *ua = (UAContext *)ctx;
707    BSOCK *bs = ua->UA_sock;
708    int maxlen, len;
709    POOLMEM *msg;
710
711    if (bs) {
712       msg = bs->msg;
713    } else {
714       msg = get_pool_memory(PM_EMSG);
715    }
716
717 again:
718    maxlen = sizeof_pool_memory(msg) - 1;
719    va_start(arg_ptr, fmt);
720    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
721    va_end(arg_ptr);
722    if (len < 0 || len >= maxlen) {
723       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
724       goto again;
725    }
726
727    if (bs) {
728       bs->msg = msg;
729       bs->msglen = len;
730       bnet_send(bs);
731    } else {                           /* No UA, send to Job */
732       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
733       free_pool_memory(msg);
734    }
735
736 }