]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
913adb9f1b1949ee80d86f37037854c1e7bd3e2d
[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.  Day 0 is today, so if
522        *   ndays=1, look at today and tomorrow.
523        */
524       for (day = 0; day <= ndays; day++) {
525          future = now + (day * 60 * 60 * 24);
526
527          /* Break down the time into components */
528          localtime_r(&future, &tm);
529          mday = tm.tm_mday - 1;
530          wday = tm.tm_wday;
531          month = tm.tm_mon;
532          wom = mday / 7;
533          woy = tm_woy(future);
534
535          is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
536             bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
537             bit_is_set(woy, run->woy);
538  
539 #ifdef xxx
540          Dmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
541          Dmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
542          Dmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
543          Dmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
544          Dmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
545          Dmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
546 #endif
547
548          if (is_scheduled) { /* Jobs scheduled on that day */
549 #ifdef xxx
550             char buf[300], num[10];
551             bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
552             for (i=0; i<24; i++) {
553                if (bit_is_set(i, run->hour)) {
554                   bsnprintf(num, sizeof(num), "%d ", i);
555                   bstrncat(buf, num, sizeof(buf));
556                }
557             }
558             bstrncat(buf, "\n", sizeof(buf));
559             Dmsg1(000, "%s", buf);
560 #endif
561             /* find time (time_t) job is to be run */
562             localtime_r(&future, &runtm);
563             for (i= 0; i < 24; i++) {
564                if (bit_is_set(i, run->hour)) {
565                   runtm.tm_hour = i;
566                   runtm.tm_min = run->minute;
567                   runtm.tm_sec = 0;
568                   runtime = mktime(&runtm);
569                   Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
570                   if ((runtime > now) && (runtime < endtime)) {
571                      Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
572                      return run;         /* found it, return run resource */
573                   }
574                }
575             }
576          }
577       }
578    } /* end for loop over runs */
579    /* Nothing found */
580    return NULL;
581 }
582 /*
583  * Fill in the remaining fields of the jcr as if it
584  *  is going to run the job.
585  */
586 int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
587 {
588    POOL_DBR pr;
589
590    memset(&pr, 0, sizeof(POOL_DBR));
591    set_jcr_defaults(jcr, job);
592    if (pool) {
593       jcr->pool = pool;               /* override */
594    }
595    jcr->db = jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
596                       jcr->catalog->db_password, jcr->catalog->db_address,
597                       jcr->catalog->db_port, jcr->catalog->db_socket,
598                       jcr->catalog->mult_db_connections);
599    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
600       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
601                  jcr->catalog->db_name);
602       if (jcr->db) {
603          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
604       }
605       return 0;
606    }
607    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
608    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
609       /* Try to create the pool */
610       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
611          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
612             db_strerror(jcr->db));
613          if (jcr->db) {
614             db_close_database(jcr, jcr->db);
615             jcr->db = NULL;
616          }
617          return 0;
618       } else {
619          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
620       }
621    }
622    jcr->PoolId = pr.PoolId;
623    jcr->jr.PoolId = pr.PoolId;
624    return 1;
625 }
626
627
628 static void con_lock_release(void *arg)
629 {
630    Vw(con_lock);
631 }
632
633 void do_messages(UAContext *ua, const char *cmd)
634 {
635    char msg[2000];
636    int mlen;
637    bool do_truncate = false;
638
639    Pw(con_lock);
640    pthread_cleanup_push(con_lock_release, (void *)NULL);
641    rewind(con_fd);
642    while (fgets(msg, sizeof(msg), con_fd)) {
643       mlen = strlen(msg);
644       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
645       strcpy(ua->UA_sock->msg, msg);
646       ua->UA_sock->msglen = mlen;
647       bnet_send(ua->UA_sock);
648       do_truncate = true;
649    }
650    if (do_truncate) {
651       (void)ftruncate(fileno(con_fd), 0L);
652    }
653    console_msg_pending = FALSE;
654    ua->user_notified_msg_pending = FALSE;
655    pthread_cleanup_pop(0);
656    Vw(con_lock);
657 }
658
659
660 int qmessagescmd(UAContext *ua, const char *cmd)
661 {
662    if (console_msg_pending && ua->auto_display_messages) {
663       do_messages(ua, cmd);
664    }
665    return 1;
666 }
667
668 int messagescmd(UAContext *ua, const char *cmd)
669 {
670    if (console_msg_pending) {
671       do_messages(ua, cmd);
672    } else {
673       bnet_fsend(ua->UA_sock, _("You have no messages.\n"));
674    }
675    return 1;
676 }
677
678 /*
679  * Callback routine for "printing" database file listing
680  */
681 void prtit(void *ctx, const char *msg)
682 {
683    UAContext *ua = (UAContext *)ctx;
684
685    bnet_fsend(ua->UA_sock, "%s", msg);
686 }
687
688 /*
689  * Format message and send to other end.
690
691  * If the UA_sock is NULL, it means that there is no user
692  * agent, so we are being called from Bacula core. In
693  * that case direct the messages to the Job.
694  */
695 void bsendmsg(void *ctx, const char *fmt, ...)
696 {
697    va_list arg_ptr;
698    UAContext *ua = (UAContext *)ctx;
699    BSOCK *bs = ua->UA_sock;
700    int maxlen, len;
701    POOLMEM *msg;
702
703    if (bs) {
704       msg = bs->msg;
705    } else {
706       msg = get_pool_memory(PM_EMSG);
707    }
708
709 again:
710    maxlen = sizeof_pool_memory(msg) - 1;
711    va_start(arg_ptr, fmt);
712    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
713    va_end(arg_ptr);
714    if (len < 0 || len >= maxlen) {
715       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
716       goto again;
717    }
718
719    if (bs) {
720       bs->msg = msg;
721       bs->msglen = len;
722       bnet_send(bs);
723    } else {                           /* No UA, send to Job */
724       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
725       free_pool_memory(msg);
726    }
727
728 }