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