]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
7cbff318a874b9de5b0cee3f0d1c2b15ff521e01
[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], 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          POOL_DBR pr;
331          memset(&pr, 0, sizeof(pr));
332          if (ua->argv[i]) {
333             bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
334          }
335          db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
336
337       } else if (strcasecmp(ua->argk[i], N_("clients")) == 0) {
338          db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
339
340
341       /* List MEDIA or VOLUMES */
342       } else if (strcasecmp(ua->argk[i], N_("media")) == 0 ||
343                  strncasecmp(ua->argk[i], N_("volume"), 7) == 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          list_nextvol(ua);
409       } else if (strcasecmp(ua->argk[i], N_("limit")) == 0) {
410          /* Ignore it */
411       } else {
412          bsendmsg(ua, _("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
413       }
414    }
415    return 1;
416 }
417
418 static bool list_nextvol(UAContext *ua)
419 {
420    JOB *job;
421    JCR *jcr = ua->jcr;
422    POOL *pool;
423    RUN *run;
424    time_t runtime;
425    bool found = false;
426    MEDIA_DBR mr;
427
428    memset(&mr, 0, sizeof(mr));
429    int i = find_arg_with_value(ua, "job");
430    if (i <= 0) {
431       if ((job = select_job_resource(ua)) == NULL) {
432          return false;
433       }
434    } else {
435       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
436       if (!job) {
437          Jmsg(jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
438          if ((job = select_job_resource(ua)) == NULL) {
439             return false;
440          }
441       }
442    }
443    for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
444       pool = run->pool ? run->pool : NULL;
445       if (!complete_jcr_for_job(jcr, job, pool)) {
446          return false;
447       }
448       mr.PoolId = jcr->PoolId;
449       if (run->storage) {
450          jcr->store = run->storage;
451       }
452       if (!find_next_volume_for_append(jcr, &mr, 0)) {
453          bsendmsg(ua, _("Could not find next Volume.\n"));
454       } else {
455          bsendmsg(ua, _("The next Volume to be used by Job \"%s\" will be %s\n"),
456             job->hdr.name, mr.VolumeName);
457          found = true;
458       }
459       if (jcr->db && jcr->db != ua->db) {
460          db_close_database(jcr, jcr->db);
461          jcr->db = NULL;
462       }
463    }
464    if (!found) {
465       bsendmsg(ua, _("Could not find next Volume.\n"));
466       return false;
467    }
468    return true;
469 }
470
471
472 /*
473  * For a given job, we examine all his run records
474  *  to see if it is scheduled today or tomorrow.
475  */
476 RUN *find_next_run(RUN *run, JOB *job, time_t &runtime)
477 {
478    time_t now, tomorrow;
479    SCHED *sched;
480    struct tm tm;
481    int mday, wday, month, wom, tmday, twday, tmonth, twom, i;
482    int woy, twoy;
483    int tod, tom;
484
485    sched = job->schedule;
486    if (sched == NULL) {            /* scheduled? */
487       return NULL;                 /* no nothing to report */
488    }
489    /* Break down current time into components */
490    now = time(NULL);
491    localtime_r(&now, &tm);
492    mday = tm.tm_mday - 1;
493    wday = tm.tm_wday;
494    month = tm.tm_mon;
495    wom = mday / 7;
496    woy = tm_woy(now);
497
498    /* Break down tomorrow into components */
499    tomorrow = now + 60 * 60 * 24;
500    localtime_r(&tomorrow, &tm);
501    tmday = tm.tm_mday - 1;
502    twday = tm.tm_wday;
503    tmonth = tm.tm_mon;
504    twom = tmday / 7;
505    twoy  = tm_woy(tomorrow);
506
507    if (run == NULL) {
508       run = sched->run;
509    } else {
510       run = run->next;
511    }
512    for ( ; run; run=run->next) {
513       /*
514        * Find runs in next 24 hours
515        */
516       tod = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
517             bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
518             bit_is_set(woy, run->woy);
519
520       tom = bit_is_set(tmday, run->mday) && bit_is_set(twday, run->wday) &&
521             bit_is_set(tmonth, run->month) && bit_is_set(twom, run->wom) &&
522             bit_is_set(twoy, run->woy);
523
524 #ifdef xxx
525       Dmsg2(000, "tod=%d tom=%d\n", tod, tom);
526       Dmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
527       Dmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
528       Dmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
529       Dmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
530       Dmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
531 #endif
532       if (tod) {                   /* Jobs scheduled today (next 24 hours) */
533 #ifdef xxx
534          char buf[300], num[10];
535          bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
536          for (i=0; i<24; i++) {
537             if (bit_is_set(i, run->hour)) {
538                bsnprintf(num, sizeof(num), "%d ", i);
539                bstrncat(buf, num, sizeof(buf));
540             }
541          }
542          bstrncat(buf, "\n", sizeof(buf));
543          Dmsg1(000, "%s", buf);
544 #endif
545          /* find time (time_t) job is to be run */
546          localtime_r(&now, &tm);
547          for (i=tm.tm_hour; i < 24; i++) {
548             if (bit_is_set(i, run->hour)) {
549                tm.tm_hour = i;
550                tm.tm_min = run->minute;
551                tm.tm_sec = 0;
552                runtime = mktime(&tm);
553                Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
554                if (runtime > now) {
555                   Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
556                   return run;         /* found it, return run resource */
557                }
558             }
559          }
560       }
561
562 //    Dmsg2(200, "runtime=%d now=%d\n", runtime, now);
563       if (tom) {                /* look at jobs scheduled tomorrow */
564          localtime_r(&tomorrow, &tm);
565          for (i=0; i < 24; i++) {
566             if (bit_is_set(i, run->hour)) {
567                tm.tm_hour = i;
568                tm.tm_min = run->minute;
569                tm.tm_sec = 0;
570                runtime = mktime(&tm);
571                Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
572                if (runtime < tomorrow) {
573                   Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
574                   return run;         /* found it, return run resource */
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    int 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 }