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