]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
- Implement first cut of Migration.
[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 job=name       - list job data for job
206  *  list jobmedia jobid=<nn>
207  *  list jobmedia job=name
208  *  list files jobid=<nn> - list files saved for job nn
209  *  list files job=name
210  *  list pools          - list pool records
211  *  list jobtotals      - list totals for all jobs
212  *  list media          - list media for given pool (deprecated)
213  *  list volumes        - list Volumes
214  *  list clients        - list clients
215  *  list nextvol job=xx  - list the next vol to be used by job
216  *  list nextvolume job=xx - same as above.
217  *
218  */
219
220 /* Do long or full listing */
221 int llist_cmd(UAContext *ua, const char *cmd)
222 {
223    return do_list_cmd(ua, cmd, VERT_LIST);
224 }
225
226 /* Do short or summary listing */
227 int list_cmd(UAContext *ua, const char *cmd)
228 {
229    return do_list_cmd(ua, cmd, HORZ_LIST);
230 }
231
232 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
233 {
234    POOLMEM *VolumeName;
235    int jobid, n;
236    int i, j;
237    JOB_DBR jr;
238    POOL_DBR pr;
239    MEDIA_DBR mr;
240
241    if (!open_db(ua))
242       return 1;
243
244    memset(&jr, 0, sizeof(jr));
245    memset(&pr, 0, sizeof(pr));
246    memset(&mr, 0, sizeof(mr));
247
248    Dmsg1(20, "list: %s\n", cmd);
249
250    if (!ua->db) {
251       bsendmsg(ua, _("Hey! DB is NULL\n"));
252    }
253
254    /* Scan arguments looking for things to do */
255    for (i=1; i<ua->argc; i++) {
256       /* List JOBS */
257       if (strcasecmp(ua->argk[i], N_("jobs")) == 0) {
258          /* Apply any limit */
259          j = find_arg_with_value(ua, N_("limit"));
260          if (j >= 0) {
261             jr.limit = atoi(ua->argv[j]);
262          }
263          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
264
265          /* List JOBTOTALS */
266       } else if (strcasecmp(ua->argk[i], N_("jobtotals")) == 0) {
267          db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
268
269       /* List JOBID=nn */
270       } else if (strcasecmp(ua->argk[i], N_("jobid")) == 0) {
271          if (ua->argv[i]) {
272             jobid = str_to_int64(ua->argv[i]);
273             if (jobid > 0) {
274                jr.JobId = jobid;
275                db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
276             }
277          }
278
279       /* List JOB=xxx */
280       } else if (strcasecmp(ua->argk[i], N_("job")) == 0 && ua->argv[i]) {
281          bstrncpy(jr.Job, 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 JOBNAME=xxx */
286       } else if (strcasecmp(ua->argk[i], N_("job")) == 0 && ua->argv[i]) {
287          bstrncpy(jr.Name, 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
292       /* List FILES */
293       } else if (strcasecmp(ua->argk[i], N_("files")) == 0) {
294
295          for (j=i+1; j<ua->argc; j++) {
296             if (strcasecmp(ua->argk[j], N_("job")) == 0 && ua->argv[j]) {
297                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
298                jr.JobId = 0;
299                db_get_job_record(ua->jcr, ua->db, &jr);
300                jobid = jr.JobId;
301             } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
302                jobid = str_to_int64(ua->argv[j]);
303             } else {
304                continue;
305             }
306             if (jobid > 0) {
307                db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
308             }
309          }
310
311       /* List JOBMEDIA */
312       } else if (strcasecmp(ua->argk[i], N_("jobmedia")) == 0) {
313          int done = FALSE;
314          for (j=i+1; j<ua->argc; j++) {
315             if (strcasecmp(ua->argk[j], N_("job")) == 0 && ua->argv[j]) {
316                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
317                jr.JobId = 0;
318                db_get_job_record(ua->jcr, ua->db, &jr);
319                jobid = jr.JobId;
320             } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
321                jobid = str_to_int64(ua->argv[j]);
322             } else {
323                continue;
324             }
325             db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
326             done = TRUE;
327          }
328          if (!done) {
329             /* List for all jobs (jobid=0) */
330             db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
331          }
332
333       /* List POOLS */
334       } else if (strcasecmp(ua->argk[i], N_("pool")) == 0 ||
335                  strcasecmp(ua->argk[i], N_("pools")) == 0) {
336          POOL_DBR pr;
337          memset(&pr, 0, sizeof(pr));
338          if (ua->argv[i]) {
339             bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
340          }
341          db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
342
343       } else if (strcasecmp(ua->argk[i], N_("clients")) == 0) {
344          db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
345
346
347       /* List MEDIA or VOLUMES */
348       } else if (strcasecmp(ua->argk[i], N_("media")) == 0 ||
349                  strcasecmp(ua->argk[i], N_("volume")) == 0 ||
350                  strcasecmp(ua->argk[i], N_("volumes")) == 0) {
351          bool done = false;
352          for (j=i+1; j<ua->argc; j++) {
353             if (strcasecmp(ua->argk[j], N_("job")) == 0 && ua->argv[j]) {
354                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
355                jr.JobId = 0;
356                db_get_job_record(ua->jcr, ua->db, &jr);
357                jobid = jr.JobId;
358             } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
359                jobid = str_to_int64(ua->argv[j]);
360             } else {
361                continue;
362             }
363             VolumeName = get_pool_memory(PM_FNAME);
364             n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
365             bsendmsg(ua, _("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
366             free_pool_memory(VolumeName);
367             done = true;
368          }
369          /* if no job or jobid keyword found, then we list all media */
370          if (!done) {
371             int num_pools;
372             uint32_t *ids;
373             /* List a specific volume? */
374             if (ua->argv[i]) {
375                bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
376                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
377                return 1;
378             }
379             /* Is a specific pool wanted? */
380             for (i=1; i<ua->argc; i++) {
381                if (strcasecmp(ua->argk[i], N_("pool")) == 0) {
382                   if (!get_pool_dbr(ua, &pr)) {
383                      bsendmsg(ua, _("No Pool specified.\n"));
384                      return 1;
385                   }
386                   mr.PoolId = pr.PoolId;
387                   db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
388                   return 1;
389                }
390             }
391
392             /* List Volumes in all pools */
393             if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
394                bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"),
395                         db_strerror(ua->db));
396                return 1;
397             }
398             if (num_pools <= 0) {
399                return 1;
400             }
401             for (i=0; i < num_pools; i++) {
402                pr.PoolId = ids[i];
403                if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
404                   bsendmsg(ua, _("Pool: %s\n"), pr.Name);
405                }
406                mr.PoolId = ids[i];
407                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
408             }
409             free(ids);
410             return 1;
411          }
412       /* List next volume */
413       } else if (strcasecmp(ua->argk[i], N_("nextvol")) == 0 ||
414                  strcasecmp(ua->argk[i], N_("nextvolume")) == 0) {
415          n = 1;
416          j = find_arg_with_value(ua, N_("days"));
417          if (j >= 0) {
418             n = atoi(ua->argv[j]);
419             if ((n < 0) || (n > 50)) {
420               bsendmsg(ua, _("Ignoring illegal value for days.\n"));
421               n = 1;
422             }
423          }
424          list_nextvol(ua, n);
425       } else if (strcasecmp(ua->argk[i], N_("limit")) == 0
426                  || strcasecmp(ua->argk[i], N_("days")) == 0) {
427          /* Ignore it */
428       } else {
429          bsendmsg(ua, _("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
430       }
431    }
432    return 1;
433 }
434
435 static bool list_nextvol(UAContext *ua, int ndays)
436 {
437    JOB *job;
438    JCR *jcr = ua->jcr;
439    POOL *pool;
440    RUN *run;
441    time_t runtime;
442    bool found = false;
443    MEDIA_DBR mr;
444    POOL_DBR pr;
445
446    memset(&mr, 0, sizeof(mr));
447    int i = find_arg_with_value(ua, "job");
448    if (i <= 0) {
449       if ((job = select_job_resource(ua)) == NULL) {
450          return false;
451       }
452    } else {
453       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
454       if (!job) {
455          Jmsg(jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
456          if ((job = select_job_resource(ua)) == NULL) {
457             return false;
458          }
459       }
460    }
461    for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
462       pool = run->pool ? run->pool : NULL;
463       if (!complete_jcr_for_job(jcr, job, pool)) {
464          return false;
465       }
466       mr.PoolId = jcr->PoolId;
467       if (run->storage) {
468          jcr->store = run->storage;
469       }
470       memset(&pr, 0, sizeof(pr));
471       pr.PoolId = jcr->PoolId;
472       if (! db_get_pool_record(ua->jcr, ua->db, &pr)) {
473          strcpy(pr.Name, "*UnknownPool*");
474       }
475       if (!find_next_volume_for_append(jcr, &mr, 1, false/*no create*/)) {
476          bsendmsg(ua, _("Could not find next Volume for Job %s (%s, %s).\n"),
477             job->hdr.name, pr.Name, level_to_str(run->level));
478       } else {
479          bsendmsg(ua,
480             _("The next Volume to be used by Job \"%s\" (%s, %s) will be %s\n"),
481             job->hdr.name, pr.Name, level_to_str(run->level), mr.VolumeName);
482          found = true;
483       }
484       if (jcr->db && jcr->db != ua->db) {
485          db_close_database(jcr, jcr->db);
486          jcr->db = NULL;
487       }
488    }
489    if (!found) {
490       bsendmsg(ua, _("Could not find next Volume for Job %s.\n"),
491          job->hdr.name);
492       return false;
493    }
494    return true;
495 }
496
497
498 /*
499  * For a given job, we examine all his run records
500  *  to see if it is scheduled today or tomorrow.
501  */
502 RUN *find_next_run(RUN *run, JOB *job, time_t &runtime, int ndays)
503 {
504    time_t now, future, endtime;
505    SCHED *sched;
506    struct tm tm, runtm;
507    int mday, wday, month, wom, i;
508    int woy;
509    int day;
510    int is_scheduled;
511
512    sched = job->schedule;
513    if (sched == NULL) {            /* scheduled? */
514       return NULL;                 /* no nothing to report */
515    }
516
517    /* Break down the time into components */
518    now = time(NULL);
519    endtime = now + (ndays * 60 * 60 * 24);
520
521    if (run == NULL) {
522       run = sched->run;
523    } else {
524       run = run->next;
525    }
526    for ( ; run; run=run->next) {
527       /*
528        * Find runs in next 24 hours.  Day 0 is today, so if
529        *   ndays=1, look at today and tomorrow.
530        */
531       for (day = 0; day <= ndays; day++) {
532          future = now + (day * 60 * 60 * 24);
533
534          /* Break down the time into components */
535          localtime_r(&future, &tm);
536          mday = tm.tm_mday - 1;
537          wday = tm.tm_wday;
538          month = tm.tm_mon;
539          wom = mday / 7;
540          woy = tm_woy(future);
541
542          is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
543             bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
544             bit_is_set(woy, run->woy);
545  
546 #ifdef xxx
547          Dmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
548          Dmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
549          Dmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
550          Dmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
551          Dmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
552          Dmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
553 #endif
554
555          if (is_scheduled) { /* Jobs scheduled on that day */
556 #ifdef xxx
557             char buf[300], num[10];
558             bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
559             for (i=0; i<24; i++) {
560                if (bit_is_set(i, run->hour)) {
561                   bsnprintf(num, sizeof(num), "%d ", i);
562                   bstrncat(buf, num, sizeof(buf));
563                }
564             }
565             bstrncat(buf, "\n", sizeof(buf));
566             Dmsg1(000, "%s", buf);
567 #endif
568             /* find time (time_t) job is to be run */
569             localtime_r(&future, &runtm);
570             for (i= 0; i < 24; i++) {
571                if (bit_is_set(i, run->hour)) {
572                   runtm.tm_hour = i;
573                   runtm.tm_min = run->minute;
574                   runtm.tm_sec = 0;
575                   runtime = mktime(&runtm);
576                   Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
577                   if ((runtime > now) && (runtime < endtime)) {
578                      Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
579                      return run;         /* found it, return run resource */
580                   }
581                }
582             }
583          }
584       }
585    } /* end for loop over runs */
586    /* Nothing found */
587    return NULL;
588 }
589 /*
590  * Fill in the remaining fields of the jcr as if it
591  *  is going to run the job.
592  */
593 int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
594 {
595    POOL_DBR pr;
596
597    memset(&pr, 0, sizeof(POOL_DBR));
598    set_jcr_defaults(jcr, job);
599    if (pool) {
600       jcr->pool = pool;               /* override */
601    }
602    jcr->db = jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
603                       jcr->catalog->db_password, jcr->catalog->db_address,
604                       jcr->catalog->db_port, jcr->catalog->db_socket,
605                       jcr->catalog->mult_db_connections);
606    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
607       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
608                  jcr->catalog->db_name);
609       if (jcr->db) {
610          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
611       }
612       return 0;
613    }
614    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
615    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
616       /* Try to create the pool */
617       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
618          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
619             db_strerror(jcr->db));
620          if (jcr->db) {
621             db_close_database(jcr, jcr->db);
622             jcr->db = NULL;
623          }
624          return 0;
625       } else {
626          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
627       }
628    }
629    jcr->PoolId = pr.PoolId;
630    jcr->jr.PoolId = pr.PoolId;
631    return 1;
632 }
633
634
635 static void con_lock_release(void *arg)
636 {
637    Vw(con_lock);
638 }
639
640 void do_messages(UAContext *ua, const char *cmd)
641 {
642    char msg[2000];
643    int mlen;
644    bool do_truncate = false;
645
646    Pw(con_lock);
647    pthread_cleanup_push(con_lock_release, (void *)NULL);
648    rewind(con_fd);
649    while (fgets(msg, sizeof(msg), con_fd)) {
650       mlen = strlen(msg);
651       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
652       strcpy(ua->UA_sock->msg, msg);
653       ua->UA_sock->msglen = mlen;
654       bnet_send(ua->UA_sock);
655       do_truncate = true;
656    }
657    if (do_truncate) {
658       (void)ftruncate(fileno(con_fd), 0L);
659    }
660    console_msg_pending = FALSE;
661    ua->user_notified_msg_pending = FALSE;
662    pthread_cleanup_pop(0);
663    Vw(con_lock);
664 }
665
666
667 int qmessagescmd(UAContext *ua, const char *cmd)
668 {
669    if (console_msg_pending && ua->auto_display_messages) {
670       do_messages(ua, cmd);
671    }
672    return 1;
673 }
674
675 int messagescmd(UAContext *ua, const char *cmd)
676 {
677    if (console_msg_pending) {
678       do_messages(ua, cmd);
679    } else {
680       bnet_fsend(ua->UA_sock, _("You have no messages.\n"));
681    }
682    return 1;
683 }
684
685 /*
686  * Callback routine for "printing" database file listing
687  */
688 void prtit(void *ctx, const char *msg)
689 {
690    UAContext *ua = (UAContext *)ctx;
691
692    bnet_fsend(ua->UA_sock, "%s", msg);
693 }
694
695 /*
696  * Format message and send to other end.
697
698  * If the UA_sock is NULL, it means that there is no user
699  * agent, so we are being called from Bacula core. In
700  * that case direct the messages to the Job.
701  */
702 void bsendmsg(void *ctx, const char *fmt, ...)
703 {
704    va_list arg_ptr;
705    UAContext *ua = (UAContext *)ctx;
706    BSOCK *bs = ua->UA_sock;
707    int maxlen, len;
708    POOLMEM *msg;
709
710    if (bs) {
711       msg = bs->msg;
712    } else {
713       msg = get_pool_memory(PM_EMSG);
714    }
715
716 again:
717    maxlen = sizeof_pool_memory(msg) - 1;
718    va_start(arg_ptr, fmt);
719    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
720    va_end(arg_ptr);
721    if (len < 0 || len >= maxlen) {
722       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
723       goto again;
724    }
725
726    if (bs) {
727       bs->msg = msg;
728       bs->msglen = len;
729       bnet_send(bs);
730    } else {                           /* No UA, send to Job */
731       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
732       free_pool_memory(msg);
733    }
734
735 }