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