]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
- Correct typo in Copyright
[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-2005 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);
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 = 1;
59       break;
60    case 1:
61       ua->auto_display_messages = 0;
62       break;
63    default:
64       bsendmsg(ua, _("ON or OFF keyword missing.\n"));
65       break;
66    }
67    return 1;
68 }
69
70 /*
71  * Turn batch processing 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->batch = true;
83       ua->jcr->gui = true;
84       break;
85    case 1:
86       ua->batch = false;
87       ua->jcr->gui = false;
88       break;
89    default:
90       bsendmsg(ua, _("ON or OFF keyword missing.\n"));
91       break;
92    }
93    return 1;
94 }
95
96
97
98 struct showstruct {const char *res_name; int type;};
99 static struct showstruct reses[] = {
100    {N_("directors"),  R_DIRECTOR},
101    {N_("clients"),    R_CLIENT},
102    {N_("counters"),   R_COUNTER},
103    {N_("devices"),    R_DEVICE},
104    {N_("jobs"),       R_JOB},
105    {N_("storages"),   R_STORAGE},
106    {N_("catalogs"),   R_CATALOG},
107    {N_("schedules"),  R_SCHEDULE},
108    {N_("filesets"),   R_FILESET},
109    {N_("pools"),      R_POOL},
110    {N_("messages"),   R_MSGS},
111    {N_("all"),        -1},
112    {N_("help"),       -2},
113    {NULL,           0}
114 };
115
116
117 /*
118  *  Displays Resources
119  *
120  *  show all
121  *  show <resource-keyword-name>  e.g. show directors
122  *  show <resource-keyword-name>=<name> e.g. show director=HeadMan
123  *
124  */
125 int show_cmd(UAContext *ua, const char *cmd)
126 {
127    int i, j, type, len;
128    int recurse;
129    char *res_name;
130    RES *res = NULL;
131
132    Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
133
134
135    LockRes();
136    for (i=1; i<ua->argc; i++) {
137       type = 0;
138       res_name = ua->argk[i];
139       if (!ua->argv[i]) {             /* was a name given? */
140          /* No name, dump all resources of specified type */
141          recurse = 1;
142          len = strlen(res_name);
143          for (j=0; reses[j].res_name; j++) {
144             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
145                type = reses[j].type;
146                if (type > 0) {
147                   res = res_head[type-r_first];
148                } else {
149                   res = NULL;
150                }
151                break;
152             }
153          }
154
155       } else {
156          /* Dump a single resource with specified name */
157          recurse = 0;
158          len = strlen(res_name);
159          for (j=0; reses[j].res_name; j++) {
160             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
161                type = reses[j].type;
162                res = (RES *)GetResWithName(type, ua->argv[i]);
163                if (!res) {
164                   type = -3;
165                }
166                break;
167             }
168          }
169       }
170
171       switch (type) {
172       case -1:                           /* all */
173          for (j=r_first; j<=r_last; j++) {
174             dump_resource(j, res_head[j-r_first], bsendmsg, ua);
175          }
176          break;
177       case -2:
178          bsendmsg(ua, _("Keywords for the show command are:\n"));
179          for (j=0; reses[j].res_name; j++) {
180             bsendmsg(ua, "%s\n", _(reses[j].res_name));
181          }
182          goto bail_out;
183       case -3:
184          bsendmsg(ua, _("%s resource %s not found.\n"), res_name, ua->argv[i]);
185          goto bail_out;
186       case 0:
187          bsendmsg(ua, _("Resource %s not found\n"), res_name);
188          goto bail_out;
189       default:
190          dump_resource(recurse?type:-type, res, bsendmsg, ua);
191          break;
192       }
193    }
194 bail_out:
195    UnlockRes();
196    return 1;
197 }
198
199
200
201
202 /*
203  *  List contents of database
204  *
205  *  list jobs           - lists all jobs run
206  *  list jobid=nnn      - list job data for jobid
207  *  list job=name       - list job data for job
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], _("jobs")) == 0) {
260          /* Apply any limit */
261          j = find_arg_with_value(ua, _("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], _("jobtotals")) == 0) {
269          db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
270
271       /* List JOBID */
272       } else if (strcasecmp(ua->argk[i], _("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 */
282       } else if (strcasecmp(ua->argk[i], _("job")) == 0 && ua->argv[i]) {
283          bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
284          jr.JobId = 0;
285          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
286
287       /* List FILES */
288       } else if (strcasecmp(ua->argk[i], _("files")) == 0) {
289
290          for (j=i+1; j<ua->argc; j++) {
291             if (strcasecmp(ua->argk[j], _("job")) == 0 && ua->argv[j]) {
292                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
293                jr.JobId = 0;
294                db_get_job_record(ua->jcr, ua->db, &jr);
295                jobid = jr.JobId;
296             } else if (strcasecmp(ua->argk[j], _("jobid")) == 0 && ua->argv[j]) {
297                jobid = str_to_int64(ua->argv[j]);
298             } else {
299                continue;
300             }
301             if (jobid > 0) {
302                db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
303             }
304          }
305
306       /* List JOBMEDIA */
307       } else if (strcasecmp(ua->argk[i], _("jobmedia")) == 0) {
308          int done = FALSE;
309          for (j=i+1; j<ua->argc; j++) {
310             if (strcasecmp(ua->argk[j], _("job")) == 0 && ua->argv[j]) {
311                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
312                jr.JobId = 0;
313                db_get_job_record(ua->jcr, ua->db, &jr);
314                jobid = jr.JobId;
315             } else if (strcasecmp(ua->argk[j], _("jobid")) == 0 && ua->argv[j]) {
316                jobid = str_to_int64(ua->argv[j]);
317             } else {
318                continue;
319             }
320             db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
321             done = TRUE;
322          }
323          if (!done) {
324             /* List for all jobs (jobid=0) */
325             db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
326          }
327
328       /* List POOLS */
329       } else if (strcasecmp(ua->argk[i], _("pools")) == 0) {
330          db_list_pool_records(ua->jcr, ua->db, prtit, ua, llist);
331
332       } else if (strcasecmp(ua->argk[i], _("clients")) == 0) {
333          db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
334
335
336       /* List MEDIA or VOLUMES */
337       } else if (strcasecmp(ua->argk[i], _("media")) == 0 ||
338                  strcasecmp(ua->argk[i], _("volumes")) == 0) {
339          bool done = false;
340          for (j=i+1; j<ua->argc; j++) {
341             if (strcasecmp(ua->argk[j], _("job")) == 0 && ua->argv[j]) {
342                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
343                jr.JobId = 0;
344                db_get_job_record(ua->jcr, ua->db, &jr);
345                jobid = jr.JobId;
346             } else if (strcasecmp(ua->argk[j], _("jobid")) == 0 && ua->argv[j]) {
347                jobid = str_to_int64(ua->argv[j]);
348             } else {
349                continue;
350             }
351             VolumeName = get_pool_memory(PM_FNAME);
352             n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
353             bsendmsg(ua, _("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
354             free_pool_memory(VolumeName);
355             done = true;
356          }
357          /* if no job or jobid keyword found, then we list all media */
358          if (!done) {
359             int num_pools;
360             uint32_t *ids;
361             /* Is a specific pool wanted? */
362             for (i=1; i<ua->argc; i++) {
363                if (strcasecmp(ua->argk[i], _("pool")) == 0) {
364                   if (!get_pool_dbr(ua, &pr)) {
365                      bsendmsg(ua, _("No Pool specified.\n"));
366                      return 1;
367                   }
368                   mr.PoolId = pr.PoolId;
369                   db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
370                   return 1;
371                }
372             }
373             /* List Volumes in all pools */
374             if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
375                bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"),
376                         db_strerror(ua->db));
377                return 1;
378             }
379             if (num_pools <= 0) {
380                return 1;
381             }
382             for (i=0; i < num_pools; i++) {
383                pr.PoolId = ids[i];
384                if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
385                   bsendmsg(ua, _("Pool: %s\n"), pr.Name);
386                }
387                mr.PoolId = ids[i];
388                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
389             }
390             free(ids);
391             return 1;
392          }
393       /* List a specific volume */
394       } else if (strcasecmp(ua->argk[i], _("volume")) == 0) {
395          if (!ua->argv[i]) {
396             bsendmsg(ua, _("No Volume Name specified.\n"));
397             return 1;
398          }
399          bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
400          db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
401          return 1;
402       /* List next volume */
403       } else if (strcasecmp(ua->argk[i], _("nextvol")) == 0 ||
404                  strcasecmp(ua->argk[i], _("nextvolume")) == 0) {
405          list_nextvol(ua);
406       } else if (strcasecmp(ua->argk[i], _("limit")) == 0) {
407          /* Ignore it */
408       } else {
409          bsendmsg(ua, _("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
410       }
411    }
412    return 1;
413 }
414
415 static bool list_nextvol(UAContext *ua)
416 {
417    JOB *job;
418    JCR *jcr = ua->jcr;
419    POOL *pool;
420    RUN *run;
421    time_t runtime;
422    bool found = false;
423    MEDIA_DBR mr;
424
425    memset(&mr, 0, sizeof(mr));
426    int i = find_arg_with_value(ua, "job");
427    if (i <= 0) {
428       if ((job = select_job_resource(ua)) == NULL) {
429          return false;
430       }
431    } else {
432       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
433       if (!job) {
434          Jmsg(jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
435          if ((job = select_job_resource(ua)) == NULL) {
436             return false;
437          }
438       }
439    }
440    for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
441       pool = run->pool ? run->pool : NULL;
442       if (!complete_jcr_for_job(jcr, job, pool)) {
443          return false;
444       }
445       mr.PoolId = jcr->PoolId;
446       if (run->storage) {
447          jcr->store = run->storage;
448       }
449       if (!find_next_volume_for_append(jcr, &mr, 0)) {
450          bsendmsg(ua, _("Could not find next Volume.\n"));
451       } else {
452          bsendmsg(ua, _("The next Volume to be used by Job \"%s\" will be %s\n"),
453             job->hdr.name, mr.VolumeName);
454          found = true;
455       }
456       if (jcr->db && jcr->db != ua->db) {
457          db_close_database(jcr, jcr->db);
458          jcr->db = NULL;
459       }
460    }
461    if (!found) {
462       bsendmsg(ua, _("Could not find next Volume.\n"));
463       return false;
464    }
465    return true;
466 }
467
468
469 /*
470  * For a given job, we examine all his run records
471  *  to see if it is scheduled today or tomorrow.
472  */
473 RUN *find_next_run(RUN *run, JOB *job, time_t &runtime)
474 {
475    time_t now, tomorrow;
476    SCHED *sched;
477    struct tm tm;
478    int mday, wday, month, wom, tmday, twday, tmonth, twom, i;
479    int woy, twoy;
480    int tod, tom;
481
482    sched = job->schedule;
483    if (sched == NULL) {            /* scheduled? */
484       return NULL;                 /* no nothing to report */
485    }
486    /* Break down current time into components */
487    now = time(NULL);
488    localtime_r(&now, &tm);
489    mday = tm.tm_mday - 1;
490    wday = tm.tm_wday;
491    month = tm.tm_mon;
492    wom = mday / 7;
493    woy = tm_woy(now);
494
495    /* Break down tomorrow into components */
496    tomorrow = now + 60 * 60 * 24;
497    localtime_r(&tomorrow, &tm);
498    tmday = tm.tm_mday - 1;
499    twday = tm.tm_wday;
500    tmonth = tm.tm_mon;
501    twom = tmday / 7;
502    twoy  = tm_woy(tomorrow);
503
504    if (run == NULL) {
505       run = sched->run;
506    } else {
507       run = run->next;
508    }
509    for ( ; run; run=run->next) {
510       /*
511        * Find runs in next 24 hours
512        */
513       tod = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
514             bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
515             bit_is_set(woy, run->woy);
516
517       tom = bit_is_set(tmday, run->mday) && bit_is_set(twday, run->wday) &&
518             bit_is_set(tmonth, run->month) && bit_is_set(twom, run->wom) &&
519             bit_is_set(twoy, run->woy);
520
521 #ifdef xxx
522       Dmsg2(000, "tod=%d tom=%d\n", tod, tom);
523       Dmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
524       Dmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
525       Dmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
526       Dmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
527       Dmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
528 #endif
529       if (tod) {                   /* Jobs scheduled today (next 24 hours) */
530 #ifdef xxx
531          char buf[300], num[10];
532          bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
533          for (i=0; i<24; i++) {
534             if (bit_is_set(i, run->hour)) {
535                bsnprintf(num, sizeof(num), "%d ", i);
536                bstrncat(buf, num, sizeof(buf));
537             }
538          }
539          bstrncat(buf, "\n", sizeof(buf));
540          Dmsg1(000, "%s", buf);
541 #endif
542          /* find time (time_t) job is to be run */
543          localtime_r(&now, &tm);
544          for (i=tm.tm_hour; i < 24; i++) {
545             if (bit_is_set(i, run->hour)) {
546                tm.tm_hour = i;
547                tm.tm_min = run->minute;
548                tm.tm_sec = 0;
549                runtime = mktime(&tm);
550                Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
551                if (runtime > now) {
552                   Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
553                   return run;         /* found it, return run resource */
554                }
555             }
556          }
557       }
558
559 //    Dmsg2(200, "runtime=%d now=%d\n", runtime, now);
560       if (tom) {                /* look at jobs scheduled tomorrow */
561          localtime_r(&tomorrow, &tm);
562          for (i=0; i < 24; i++) {
563             if (bit_is_set(i, run->hour)) {
564                tm.tm_hour = i;
565                tm.tm_min = run->minute;
566                tm.tm_sec = 0;
567                runtime = mktime(&tm);
568                Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
569                if (runtime < tomorrow) {
570                   Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
571                   return run;         /* found it, return run resource */
572                }
573             }
574          }
575       }
576    } /* end for loop over runs */
577    /* Nothing found */
578    return NULL;
579 }
580 /*
581  * Fill in the remaining fields of the jcr as if it
582  *  is going to run the job.
583  */
584 int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
585 {
586    POOL_DBR pr;
587
588    memset(&pr, 0, sizeof(POOL_DBR));
589    set_jcr_defaults(jcr, job);
590    if (pool) {
591       jcr->pool = pool;               /* override */
592    }
593    jcr->db = jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
594                       jcr->catalog->db_password, jcr->catalog->db_address,
595                       jcr->catalog->db_port, jcr->catalog->db_socket,
596                       jcr->catalog->mult_db_connections);
597    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
598       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
599                  jcr->catalog->db_name);
600       if (jcr->db) {
601          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
602       }
603       return 0;
604    }
605    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
606    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
607       /* Try to create the pool */
608       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
609          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
610             db_strerror(jcr->db));
611          if (jcr->db) {
612             db_close_database(jcr, jcr->db);
613             jcr->db = NULL;
614          }
615          return 0;
616       } else {
617          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
618       }
619    }
620    jcr->PoolId = pr.PoolId;
621    jcr->jr.PoolId = pr.PoolId;
622    return 1;
623 }
624
625
626 static void con_lock_release(void *arg)
627 {
628    Vw(con_lock);
629 }
630
631 void do_messages(UAContext *ua, const char *cmd)
632 {
633    char msg[2000];
634    int mlen;
635    int do_truncate = FALSE;
636
637    Pw(con_lock);
638    pthread_cleanup_push(con_lock_release, (void *)NULL);
639    rewind(con_fd);
640    while (fgets(msg, sizeof(msg), con_fd)) {
641       mlen = strlen(msg);
642       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
643       strcpy(ua->UA_sock->msg, msg);
644       ua->UA_sock->msglen = mlen;
645       bnet_send(ua->UA_sock);
646       do_truncate = TRUE;
647    }
648    if (do_truncate) {
649       ftruncate(fileno(con_fd), 0L);
650    }
651    console_msg_pending = FALSE;
652    ua->user_notified_msg_pending = FALSE;
653    pthread_cleanup_pop(0);
654    Vw(con_lock);
655 }
656
657
658 int qmessagescmd(UAContext *ua, const char *cmd)
659 {
660    if (console_msg_pending && ua->auto_display_messages) {
661       do_messages(ua, cmd);
662    }
663    return 1;
664 }
665
666 int messagescmd(UAContext *ua, const char *cmd)
667 {
668    if (console_msg_pending) {
669       do_messages(ua, cmd);
670    } else {
671       bnet_fsend(ua->UA_sock, _("You have no messages.\n"));
672    }
673    return 1;
674 }
675
676 /*
677  * Callback routine for "printing" database file listing
678  */
679 void prtit(void *ctx, const char *msg)
680 {
681    UAContext *ua = (UAContext *)ctx;
682
683    bnet_fsend(ua->UA_sock, "%s", msg);
684 }
685
686 /*
687  * Format message and send to other end.
688
689  * If the UA_sock is NULL, it means that there is no user
690  * agent, so we are being called from Bacula core. In
691  * that case direct the messages to the Job.
692  */
693 void bsendmsg(void *ctx, const char *fmt, ...)
694 {
695    va_list arg_ptr;
696    UAContext *ua = (UAContext *)ctx;
697    BSOCK *bs = ua->UA_sock;
698    int maxlen, len;
699    POOLMEM *msg;
700
701    if (bs) {
702       msg = bs->msg;
703    } else {
704       msg = get_pool_memory(PM_EMSG);
705    }
706
707 again:
708    maxlen = sizeof_pool_memory(msg) - 1;
709    va_start(arg_ptr, fmt);
710    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
711    va_end(arg_ptr);
712    if (len < 0 || len >= maxlen) {
713       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
714       goto again;
715    }
716
717    if (bs) {
718       bs->msg = msg;
719       bs->msglen = len;
720       bnet_send(bs);
721    } else {                           /* No UA, send to Job */
722       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
723       free_pool_memory(msg);
724    }
725
726 }