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