]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
27Feb06
[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 extern int console_msg_pending;
37 extern FILE *con_fd;
38 extern brwlock_t con_lock;
39
40 /* Imported functions */
41
42 /* Forward referenced functions */
43 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist);
44 static bool list_nextvol(UAContext *ua, int ndays);
45
46 /*
47  * Turn auto display of console messages on/off
48  */
49 int autodisplay_cmd(UAContext *ua, const char *cmd)
50 {
51    static const char *kw[] = {
52       N_("on"),
53       N_("off"),
54       NULL};
55
56    switch (find_arg_keyword(ua, kw)) {
57    case 0:
58       ua->auto_display_messages = true;
59       break;
60    case 1:
61       ua->auto_display_messages = false;
62       break;
63    default:
64       bsendmsg(ua, _("ON or OFF keyword missing.\n"));
65       break;
66    }
67    return 1;
68 }
69
70 /*
71  * Turn GUI mode on/off
72  */
73 int gui_cmd(UAContext *ua, const char *cmd)
74 {
75    static const char *kw[] = {
76       N_("on"),
77       N_("off"),
78       NULL};
79
80    switch (find_arg_keyword(ua, kw)) {
81    case 0:
82       ua->jcr->gui = ua->gui = true;
83       break;
84    case 1:
85       ua->jcr->gui = ua->gui = false;
86       break;
87    default:
88       bsendmsg(ua, _("ON or OFF keyword missing.\n"));
89       break;
90    }
91    return 1;
92 }
93
94
95
96 struct showstruct {const char *res_name; int type;};
97 static struct showstruct reses[] = {
98    {N_("directors"),  R_DIRECTOR},
99    {N_("clients"),    R_CLIENT},
100    {N_("counters"),   R_COUNTER},
101    {N_("devices"),    R_DEVICE},
102    {N_("jobs"),       R_JOB},
103    {N_("storages"),   R_STORAGE},
104    {N_("catalogs"),   R_CATALOG},
105    {N_("schedules"),  R_SCHEDULE},
106    {N_("filesets"),   R_FILESET},
107    {N_("pools"),      R_POOL},
108    {N_("messages"),   R_MSGS},
109    {N_("all"),        -1},
110    {N_("help"),       -2},
111    {NULL,           0}
112 };
113
114
115 /*
116  *  Displays Resources
117  *
118  *  show all
119  *  show <resource-keyword-name>  e.g. show directors
120  *  show <resource-keyword-name>=<name> e.g. show director=HeadMan
121  *
122  */
123 int show_cmd(UAContext *ua, const char *cmd)
124 {
125    int i, j, type, len;
126    int recurse;
127    char *res_name;
128    RES *res = NULL;
129
130    Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
131
132
133    LockRes();
134    for (i=1; i<ua->argc; i++) {
135       type = 0;
136       res_name = ua->argk[i];
137       if (!ua->argv[i]) {             /* was a name given? */
138          /* No name, dump all resources of specified type */
139          recurse = 1;
140          len = strlen(res_name);
141          for (j=0; reses[j].res_name; j++) {
142             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
143                type = reses[j].type;
144                if (type > 0) {
145                   res = res_head[type-r_first];
146                } else {
147                   res = NULL;
148                }
149                break;
150             }
151          }
152
153       } else {
154          /* Dump a single resource with specified name */
155          recurse = 0;
156          len = strlen(res_name);
157          for (j=0; reses[j].res_name; j++) {
158             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
159                type = reses[j].type;
160                res = (RES *)GetResWithName(type, ua->argv[i]);
161                if (!res) {
162                   type = -3;
163                }
164                break;
165             }
166          }
167       }
168
169       switch (type) {
170       case -1:                           /* all */
171          for (j=r_first; j<=r_last; j++) {
172             dump_resource(j, res_head[j-r_first], bsendmsg, ua);
173          }
174          break;
175       case -2:
176          bsendmsg(ua, _("Keywords for the show command are:\n"));
177          for (j=0; reses[j].res_name; j++) {
178             bsendmsg(ua, "%s\n", _(reses[j].res_name));
179          }
180          goto bail_out;
181       case -3:
182          bsendmsg(ua, _("%s resource %s not found.\n"), res_name, ua->argv[i]);
183          goto bail_out;
184       case 0:
185          bsendmsg(ua, _("Resource %s not found\n"), res_name);
186          goto bail_out;
187       default:
188          dump_resource(recurse?type:-type, res, bsendmsg, ua);
189          break;
190       }
191    }
192 bail_out:
193    UnlockRes();
194    return 1;
195 }
196
197
198
199
200 /*
201  *  List contents of database
202  *
203  *  list jobs           - lists all jobs run
204  *  list jobid=nnn      - list job data for jobid
205  *  list jobuid=uname   - list job data for unique jobid
206  *  list job=name       - list all jobs with "name"   
207  *  list jobname=name   - same as above 
208  *  list jobmedia jobid=<nn>
209  *  list jobmedia job=name
210  *  list files jobid=<nn> - list files saved for job nn
211  *  list files job=name
212  *  list pools          - list pool records
213  *  list jobtotals      - list totals for all jobs
214  *  list media          - list media for given pool (deprecated)
215  *  list volumes        - list Volumes
216  *  list clients        - list clients
217  *  list nextvol job=xx  - list the next vol to be used by job
218  *  list nextvolume job=xx - same as above.
219  *
220  */
221
222 /* Do long or full listing */
223 int llist_cmd(UAContext *ua, const char *cmd)
224 {
225    return do_list_cmd(ua, cmd, VERT_LIST);
226 }
227
228 /* Do short or summary listing */
229 int list_cmd(UAContext *ua, const char *cmd)
230 {
231    return do_list_cmd(ua, cmd, HORZ_LIST);
232 }
233
234 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
235 {
236    POOLMEM *VolumeName;
237    int jobid, n;
238    int i, j;
239    JOB_DBR jr;
240    POOL_DBR pr;
241    MEDIA_DBR mr;
242
243    if (!open_db(ua))
244       return 1;
245
246    memset(&jr, 0, sizeof(jr));
247    memset(&pr, 0, sizeof(pr));
248    memset(&mr, 0, sizeof(mr));
249
250    Dmsg1(20, "list: %s\n", cmd);
251
252    if (!ua->db) {
253       bsendmsg(ua, _("Hey! DB is NULL\n"));
254    }
255
256    /* Scan arguments looking for things to do */
257    for (i=1; i<ua->argc; i++) {
258       /* List JOBS */
259       if (strcasecmp(ua->argk[i], N_("jobs")) == 0) {
260          /* Apply any limit */
261          j = find_arg_with_value(ua, N_("limit"));
262          if (j >= 0) {
263             jr.limit = atoi(ua->argv[j]);
264          }
265          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
266
267          /* List JOBTOTALS */
268       } else if (strcasecmp(ua->argk[i], N_("jobtotals")) == 0) {
269          db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
270
271       /* List JOBID=nn */
272       } else if (strcasecmp(ua->argk[i], N_("jobid")) == 0) {
273          if (ua->argv[i]) {
274             jobid = str_to_int64(ua->argv[i]);
275             if (jobid > 0) {
276                jr.JobId = jobid;
277                db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
278             }
279          }
280
281       /* List JOB=xxx */
282       } else if ((strcasecmp(ua->argk[i], N_("job")) == 0 ||
283                   strcasecmp(ua->argk[i], N_("jobname")) == 0) && ua->argv[i]) {
284          bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
285          jr.JobId = 0;
286          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
287
288       /* List JOBUID=xxx */
289       } else if (strcasecmp(ua->argk[i], N_("jobuid")) == 0 && ua->argv[i]) {
290          bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
291          jr.JobId = 0;
292          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
293
294       /* List FILES */
295       } else if (strcasecmp(ua->argk[i], N_("files")) == 0) {
296
297          for (j=i+1; j<ua->argc; j++) {
298             if (strcasecmp(ua->argk[j], N_("jobuid")) == 0 && ua->argv[j]) {
299                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
300                jr.JobId = 0;
301                db_get_job_record(ua->jcr, ua->db, &jr);
302                jobid = jr.JobId;
303             } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
304                jobid = str_to_int64(ua->argv[j]);
305             } else {
306                continue;
307             }
308             if (jobid > 0) {
309                db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
310             }
311          }
312
313       /* List JOBMEDIA */
314       } else if (strcasecmp(ua->argk[i], N_("jobmedia")) == 0) {
315          int done = FALSE;
316          for (j=i+1; j<ua->argc; j++) {
317             if (strcasecmp(ua->argk[j], N_("jobuid")) == 0 && ua->argv[j]) {
318                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
319                jr.JobId = 0;
320                db_get_job_record(ua->jcr, ua->db, &jr);
321                jobid = jr.JobId;
322             } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
323                jobid = str_to_int64(ua->argv[j]);
324             } else {
325                continue;
326             }
327             db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
328             done = TRUE;
329          }
330          if (!done) {
331             /* List for all jobs (jobid=0) */
332             db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
333          }
334
335       /* List POOLS */
336       } else if (strcasecmp(ua->argk[i], N_("pool")) == 0 ||
337                  strcasecmp(ua->argk[i], N_("pools")) == 0) {
338          POOL_DBR pr;
339          memset(&pr, 0, sizeof(pr));
340          if (ua->argv[i]) {
341             bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
342          }
343          db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
344
345       } else if (strcasecmp(ua->argk[i], N_("clients")) == 0) {
346          db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
347
348
349       /* List MEDIA or VOLUMES */
350       } else if (strcasecmp(ua->argk[i], N_("media")) == 0 ||
351                  strcasecmp(ua->argk[i], N_("volume")) == 0 ||
352                  strcasecmp(ua->argk[i], N_("volumes")) == 0) {
353          bool done = false;
354          for (j=i+1; j<ua->argc; j++) {
355             if (strcasecmp(ua->argk[j], N_("jobuid")) == 0 && ua->argv[j]) {
356                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
357                jr.JobId = 0;
358                db_get_job_record(ua->jcr, ua->db, &jr);
359                jobid = jr.JobId;
360             } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
361                jobid = str_to_int64(ua->argv[j]);
362             } else {
363                continue;
364             }
365             VolumeName = get_pool_memory(PM_FNAME);
366             n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
367             bsendmsg(ua, _("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
368             free_pool_memory(VolumeName);
369             done = true;
370          }
371          /* if no job or jobid keyword found, then we list all media */
372          if (!done) {
373             int num_pools;
374             uint32_t *ids;
375             /* List a specific volume? */
376             if (ua->argv[i]) {
377                bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
378                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
379                return 1;
380             }
381             /* Is a specific pool wanted? */
382             for (i=1; i<ua->argc; i++) {
383                if (strcasecmp(ua->argk[i], N_("pool")) == 0) {
384                   if (!get_pool_dbr(ua, &pr)) {
385                      bsendmsg(ua, _("No Pool specified.\n"));
386                      return 1;
387                   }
388                   mr.PoolId = pr.PoolId;
389                   db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
390                   return 1;
391                }
392             }
393
394             /* List Volumes in all pools */
395             if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
396                bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"),
397                         db_strerror(ua->db));
398                return 1;
399             }
400             if (num_pools <= 0) {
401                return 1;
402             }
403             for (i=0; i < num_pools; i++) {
404                pr.PoolId = ids[i];
405                if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
406                   bsendmsg(ua, _("Pool: %s\n"), pr.Name);
407                }
408                mr.PoolId = ids[i];
409                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
410             }
411             free(ids);
412             return 1;
413          }
414       /* List next volume */
415       } else if (strcasecmp(ua->argk[i], N_("nextvol")) == 0 ||
416                  strcasecmp(ua->argk[i], N_("nextvolume")) == 0) {
417          n = 1;
418          j = find_arg_with_value(ua, N_("days"));
419          if (j >= 0) {
420             n = atoi(ua->argv[j]);
421             if ((n < 0) || (n > 50)) {
422               bsendmsg(ua, _("Ignoring illegal value for days.\n"));
423               n = 1;
424             }
425          }
426          list_nextvol(ua, n);
427       } else if (strcasecmp(ua->argk[i], N_("limit")) == 0
428                  || strcasecmp(ua->argk[i], N_("days")) == 0) {
429          /* Ignore it */
430       } else {
431          bsendmsg(ua, _("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
432       }
433    }
434    return 1;
435 }
436
437 static bool list_nextvol(UAContext *ua, int ndays)
438 {
439    JOB *job;
440    JCR *jcr = ua->jcr;
441    POOL *pool;
442    RUN *run;
443    time_t runtime;
444    bool found = false;
445    MEDIA_DBR mr;
446    POOL_DBR pr;
447
448    memset(&mr, 0, sizeof(mr));
449    int i = find_arg_with_value(ua, "job");
450    if (i <= 0) {
451       if ((job = select_job_resource(ua)) == NULL) {
452          return false;
453       }
454    } else {
455       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
456       if (!job) {
457          Jmsg(jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
458          if ((job = select_job_resource(ua)) == NULL) {
459             return false;
460          }
461       }
462    }
463    for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
464       pool = run->pool ? run->pool : NULL;
465       if (!complete_jcr_for_job(jcr, job, pool)) {
466          return false;
467       }
468       mr.PoolId = jcr->jr.PoolId;
469       if (run->storage) {
470          jcr->store = run->storage;
471       }
472       memset(&pr, 0, sizeof(pr));
473       pr.PoolId = jcr->jr.PoolId;
474       if (! db_get_pool_record(ua->jcr, ua->db, &pr)) {
475          strcpy(pr.Name, "*UnknownPool*");
476       }
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          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             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 }