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