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