]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_output.c
kes Enable disk seeking on 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    Bacula® - The Network Backup Solution
12
13    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
14
15    The main author of Bacula is Kern Sibbald, with contributions from
16    many others, a complete list can be found in the file AUTHORS.
17    This program is Free Software; you can redistribute it and/or
18    modify it under the terms of version two of the GNU General Public
19    License as published by the Free Software Foundation plus additions
20    that are listed in the file LICENSE.
21
22    This program is distributed in the hope that it will be useful, but
23    WITHOUT ANY WARRANTY; without even the implied warranty of
24    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25    General Public License for more details.
26
27    You should have received a copy of the GNU General Public License
28    along with this program; if not, write to the Free Software
29    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
30    02110-1301, USA.
31
32    Bacula® is a registered trademark of John Walker.
33    The licensor of Bacula is the Free Software Foundation Europe
34    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
35    Switzerland, email:ftf@fsfeurope.org.
36 */
37
38 #include "bacula.h"
39 #include "dird.h"
40
41 /* Imported subroutines */
42
43 /* Imported variables */
44 extern int r_first;
45 extern int r_last;
46 extern RES_TABLE resources[];
47 extern RES **res_head;
48
49 /* Imported functions */
50
51 /* Forward referenced functions */
52 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist);
53 static bool list_nextvol(UAContext *ua, int ndays);
54
55 /*
56  * Turn auto display of console messages on/off
57  */
58 int autodisplay_cmd(UAContext *ua, const char *cmd)
59 {
60    static const char *kw[] = {
61       NT_("on"),
62       NT_("off"),
63       NULL};
64
65    switch (find_arg_keyword(ua, kw)) {
66    case 0:
67       ua->auto_display_messages = true;
68       break;
69    case 1:
70       ua->auto_display_messages = false;
71       break;
72    default:
73       bsendmsg(ua, _("ON or OFF keyword missing.\n"));
74       break;
75    }
76    return 1;
77 }
78
79 /*
80  * Turn GUI mode on/off
81  */
82 int gui_cmd(UAContext *ua, const char *cmd)
83 {
84    static const char *kw[] = {
85       NT_("on"),
86       NT_("off"),
87       NULL};
88
89    switch (find_arg_keyword(ua, kw)) {
90    case 0:
91       ua->jcr->gui = ua->gui = true;
92       break;
93    case 1:
94       ua->jcr->gui = ua->gui = false;
95       break;
96    default:
97       bsendmsg(ua, _("ON or OFF keyword missing.\n"));
98       break;
99    }
100    return 1;
101 }
102
103
104
105 struct showstruct {const char *res_name; int type;};
106 static struct showstruct reses[] = {
107    {NT_("directors"),  R_DIRECTOR},
108    {NT_("clients"),    R_CLIENT},
109    {NT_("counters"),   R_COUNTER},
110    {NT_("devices"),    R_DEVICE},
111    {NT_("jobs"),       R_JOB},
112    {NT_("storages"),   R_STORAGE},
113    {NT_("catalogs"),   R_CATALOG},
114    {NT_("schedules"),  R_SCHEDULE},
115    {NT_("filesets"),   R_FILESET},
116    {NT_("pools"),      R_POOL},
117    {NT_("messages"),   R_MSGS},
118    {NT_("all"),        -1},
119    {NT_("help"),       -2},
120    {NULL,           0}
121 };
122
123
124 /*
125  *  Displays Resources
126  *
127  *  show all
128  *  show <resource-keyword-name>  e.g. show directors
129  *  show <resource-keyword-name>=<name> e.g. show director=HeadMan
130  *
131  */
132 int show_cmd(UAContext *ua, const char *cmd)
133 {
134    int i, j, type, len;
135    int recurse;
136    char *res_name;
137    RES *res = NULL;
138
139    Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
140
141
142    LockRes();
143    for (i=1; i<ua->argc; i++) {
144       type = 0;
145       res_name = ua->argk[i];
146       if (!ua->argv[i]) {             /* was a name given? */
147          /* No name, dump all resources of specified type */
148          recurse = 1;
149          len = strlen(res_name);
150          for (j=0; reses[j].res_name; j++) {
151             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
152                type = reses[j].type;
153                if (type > 0) {
154                   res = res_head[type-r_first];
155                } else {
156                   res = NULL;
157                }
158                break;
159             }
160          }
161
162       } else {
163          /* Dump a single resource with specified name */
164          recurse = 0;
165          len = strlen(res_name);
166          for (j=0; reses[j].res_name; j++) {
167             if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
168                type = reses[j].type;
169                res = (RES *)GetResWithName(type, ua->argv[i]);
170                if (!res) {
171                   type = -3;
172                }
173                break;
174             }
175          }
176       }
177
178       switch (type) {
179       case -1:                           /* all */
180          for (j=r_first; j<=r_last; j++) {
181             dump_resource(j, res_head[j-r_first], bsendmsg, ua);
182          }
183          break;
184       case -2:
185          bsendmsg(ua, _("Keywords for the show command are:\n"));
186          for (j=0; reses[j].res_name; j++) {
187             bsendmsg(ua, "%s\n", _(reses[j].res_name));
188          }
189          goto bail_out;
190       case -3:
191          bsendmsg(ua, _("%s resource %s not found.\n"), res_name, ua->argv[i]);
192          goto bail_out;
193       case 0:
194          bsendmsg(ua, _("Resource %s not found\n"), res_name);
195          goto bail_out;
196       default:
197          dump_resource(recurse?type:-type, res, bsendmsg, ua);
198          break;
199       }
200    }
201 bail_out:
202    UnlockRes();
203    return 1;
204 }
205
206
207
208
209 /*
210  *  List contents of database
211  *
212  *  list jobs           - lists all jobs run
213  *  list jobid=nnn      - list job data for jobid
214  *  list ujobid=uname   - list job data for unique jobid
215  *  list job=name       - list all jobs with "name"   
216  *  list jobname=name   - same as above 
217  *  list jobmedia jobid=<nn>
218  *  list jobmedia job=name
219  *  list files jobid=<nn> - list files saved for job nn
220  *  list files job=name
221  *  list pools          - list pool records
222  *  list jobtotals      - list totals for all jobs
223  *  list media          - list media for given pool (deprecated)
224  *  list volumes        - list Volumes
225  *  list clients        - list clients
226  *  list nextvol job=xx  - list the next vol to be used by job
227  *  list nextvolume job=xx - same as above.
228  *
229  */
230
231 /* Do long or full listing */
232 int llist_cmd(UAContext *ua, const char *cmd)
233 {
234    return do_list_cmd(ua, cmd, VERT_LIST);
235 }
236
237 /* Do short or summary listing */
238 int list_cmd(UAContext *ua, const char *cmd)
239 {
240    return do_list_cmd(ua, cmd, HORZ_LIST);
241 }
242
243 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
244 {
245    POOLMEM *VolumeName;
246    int jobid, n;
247    int i, j;
248    JOB_DBR jr;
249    POOL_DBR pr;
250    MEDIA_DBR mr;
251
252    if (!open_db(ua))
253       return 1;
254
255    memset(&jr, 0, sizeof(jr));
256    memset(&pr, 0, sizeof(pr));
257    memset(&mr, 0, sizeof(mr));
258
259    Dmsg1(20, "list: %s\n", cmd);
260
261    if (!ua->db) {
262       bsendmsg(ua, _("Hey! DB is NULL\n"));
263    }
264
265    /* Scan arguments looking for things to do */
266    for (i=1; i<ua->argc; i++) {
267       /* List JOBS */
268       if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
269          /* Apply any limit */
270          j = find_arg_with_value(ua, NT_("limit"));
271          if (j >= 0) {
272             jr.limit = atoi(ua->argv[j]);
273          }
274          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
275
276          /* List JOBTOTALS */
277       } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
278          db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
279
280       /* List JOBID=nn */
281       } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
282          if (ua->argv[i]) {
283             jobid = str_to_int64(ua->argv[i]);
284             if (jobid > 0) {
285                jr.JobId = jobid;
286                db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
287             }
288          }
289
290       /* List JOB=xxx */
291       } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
292                   strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
293          bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
294          jr.JobId = 0;
295          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
296
297       /* List UJOBID=xxx */
298       } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
299          bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
300          jr.JobId = 0;
301          db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
302
303       /* List FILES */
304       } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
305
306          for (j=i+1; j<ua->argc; j++) {
307             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
308                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
309                jr.JobId = 0;
310                db_get_job_record(ua->jcr, ua->db, &jr);
311                jobid = jr.JobId;
312             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
313                jobid = str_to_int64(ua->argv[j]);
314             } else {
315                continue;
316             }
317             if (jobid > 0) {
318                db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
319             }
320          }
321
322       /* List JOBMEDIA */
323       } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
324          int done = FALSE;
325          for (j=i+1; j<ua->argc; j++) {
326             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
327                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
328                jr.JobId = 0;
329                db_get_job_record(ua->jcr, ua->db, &jr);
330                jobid = jr.JobId;
331             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
332                jobid = str_to_int64(ua->argv[j]);
333             } else {
334                continue;
335             }
336             db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
337             done = TRUE;
338          }
339          if (!done) {
340             /* List for all jobs (jobid=0) */
341             db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
342          }
343
344       /* List POOLS */
345       } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
346                  strcasecmp(ua->argk[i], NT_("pools")) == 0) {
347          POOL_DBR pr;
348          memset(&pr, 0, sizeof(pr));
349          if (ua->argv[i]) {
350             bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
351          }
352          db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
353
354       } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
355          db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
356
357
358       /* List MEDIA or VOLUMES */
359       } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
360                  strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
361                  strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
362          bool done = false;
363          for (j=i+1; j<ua->argc; j++) {
364             if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
365                bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
366                jr.JobId = 0;
367                db_get_job_record(ua->jcr, ua->db, &jr);
368                jobid = jr.JobId;
369             } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
370                jobid = str_to_int64(ua->argv[j]);
371             } else {
372                continue;
373             }
374             VolumeName = get_pool_memory(PM_FNAME);
375             n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
376             bsendmsg(ua, _("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
377             free_pool_memory(VolumeName);
378             done = true;
379          }
380          /* if no job or jobid keyword found, then we list all media */
381          if (!done) {
382             int num_pools;
383             uint32_t *ids;
384             /* List a specific volume? */
385             if (ua->argv[i]) {
386                bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
387                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
388                return 1;
389             }
390             /* Is a specific pool wanted? */
391             for (i=1; i<ua->argc; i++) {
392                if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
393                   if (!get_pool_dbr(ua, &pr)) {
394                      bsendmsg(ua, _("No Pool specified.\n"));
395                      return 1;
396                   }
397                   mr.PoolId = pr.PoolId;
398                   db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
399                   return 1;
400                }
401             }
402
403             /* List Volumes in all pools */
404             if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
405                bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"),
406                         db_strerror(ua->db));
407                return 1;
408             }
409             if (num_pools <= 0) {
410                return 1;
411             }
412             for (i=0; i < num_pools; i++) {
413                pr.PoolId = ids[i];
414                if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
415                   bsendmsg(ua, _("Pool: %s\n"), pr.Name);
416                }
417                mr.PoolId = ids[i];
418                db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
419             }
420             free(ids);
421             return 1;
422          }
423       /* List next volume */
424       } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
425                  strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
426          n = 1;
427          j = find_arg_with_value(ua, NT_("days"));
428          if (j >= 0) {
429             n = atoi(ua->argv[j]);
430             if ((n < 0) || (n > 50)) {
431               bsendmsg(ua, _("Ignoring invalid value for days. Max is 50.\n"));
432               n = 1;
433             }
434          }
435          list_nextvol(ua, n);
436       } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
437                  || strcasecmp(ua->argk[i], NT_("days")) == 0) {
438          /* Ignore it */
439       } else {
440          bsendmsg(ua, _("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
441       }
442    }
443    return 1;
444 }
445
446 static bool list_nextvol(UAContext *ua, int ndays)
447 {
448    JOB *job;
449    JCR *jcr = ua->jcr;
450    POOL *pool;
451    USTORE store;
452    RUN *run;
453    time_t runtime;
454    bool found = false;
455    MEDIA_DBR mr;
456    POOL_DBR pr;
457
458    memset(&mr, 0, sizeof(mr));
459    int i = find_arg_with_value(ua, "job");
460    if (i <= 0) {
461       if ((job = select_job_resource(ua)) == NULL) {
462          return false;
463       }
464    } else {
465       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
466       if (!job) {
467          Jmsg(jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
468          if ((job = select_job_resource(ua)) == NULL) {
469             return false;
470          }
471       }
472    }
473    for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
474       pool = run->pool ? run->pool : NULL;
475       if (!complete_jcr_for_job(jcr, job, pool)) {
476          return false;
477       }
478       memset(&pr, 0, sizeof(pr));
479       pr.PoolId = jcr->jr.PoolId;
480       if (! db_get_pool_record(ua->jcr, ua->db, &pr)) {
481          bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
482       }
483       mr.PoolId = jcr->jr.PoolId;
484       get_job_storage(&store, job, run);
485       mr.StorageId = store.store->StorageId;
486       if (!find_next_volume_for_append(jcr, &mr, 1, false/*no create*/)) {
487          bsendmsg(ua, _("Could not find next Volume for Job %s (%s, %s).\n"),
488             job->hdr.name, pr.Name, level_to_str(run->level));
489       } else {
490          bsendmsg(ua,
491             _("The next Volume to be used by Job \"%s\" (%s, %s) will be %s\n"),
492             job->hdr.name, pr.Name, level_to_str(run->level), mr.VolumeName);
493          found = true;
494       }
495       if (jcr->db && jcr->db != ua->db) {
496          db_close_database(jcr, jcr->db);
497          jcr->db = NULL;
498       }
499    }
500    if (!found) {
501       bsendmsg(ua, _("Could not find next Volume for Job %s.\n"),
502          job->hdr.name);
503       return false;
504    }
505    return true;
506 }
507
508
509 /*
510  * For a given job, we examine all his run records
511  *  to see if it is scheduled today or tomorrow.
512  */
513 RUN *find_next_run(RUN *run, JOB *job, time_t &runtime, int ndays)
514 {
515    time_t now, future, endtime;
516    SCHED *sched;
517    struct tm tm, runtm;
518    int mday, wday, month, wom, i;
519    int woy;
520    int day;
521    int is_scheduled;
522
523    sched = job->schedule;
524    if (sched == NULL) {            /* scheduled? */
525       return NULL;                 /* no nothing to report */
526    }
527
528    /* Break down the time into components */
529    now = time(NULL);
530    endtime = now + (ndays * 60 * 60 * 24);
531
532    if (run == NULL) {
533       run = sched->run;
534    } else {
535       run = run->next;
536    }
537    for ( ; run; run=run->next) {
538       /*
539        * Find runs in next 24 hours.  Day 0 is today, so if
540        *   ndays=1, look at today and tomorrow.
541        */
542       for (day = 0; day <= ndays; day++) {
543          future = now + (day * 60 * 60 * 24);
544
545          /* Break down the time into components */
546          (void)localtime_r(&future, &tm);
547          mday = tm.tm_mday - 1;
548          wday = tm.tm_wday;
549          month = tm.tm_mon;
550          wom = mday / 7;
551          woy = tm_woy(future);
552
553          is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
554             bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
555             bit_is_set(woy, run->woy);
556  
557 #ifdef xxx
558          Dmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
559          Dmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
560          Dmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
561          Dmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
562          Dmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
563          Dmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
564 #endif
565
566          if (is_scheduled) { /* Jobs scheduled on that day */
567 #ifdef xxx
568             char buf[300], num[10];
569             bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
570             for (i=0; i<24; i++) {
571                if (bit_is_set(i, run->hour)) {
572                   bsnprintf(num, sizeof(num), "%d ", i);
573                   bstrncat(buf, num, sizeof(buf));
574                }
575             }
576             bstrncat(buf, "\n", sizeof(buf));
577             Dmsg1(000, "%s", buf);
578 #endif
579             /* find time (time_t) job is to be run */
580             (void)localtime_r(&future, &runtm);
581             for (i= 0; i < 24; i++) {
582                if (bit_is_set(i, run->hour)) {
583                   runtm.tm_hour = i;
584                   runtm.tm_min = run->minute;
585                   runtm.tm_sec = 0;
586                   runtime = mktime(&runtm);
587                   Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
588                   if ((runtime > now) && (runtime < endtime)) {
589                      Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
590                      return run;         /* found it, return run resource */
591                   }
592                }
593             }
594          }
595       }
596    } /* end for loop over runs */
597    /* Nothing found */
598    return NULL;
599 }
600
601 /*
602  * Fill in the remaining fields of the jcr as if it
603  *  is going to run the job.
604  */
605 int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
606 {
607    POOL_DBR pr;
608
609    memset(&pr, 0, sizeof(POOL_DBR));
610    set_jcr_defaults(jcr, job);
611    if (pool) {
612       jcr->pool = pool;               /* override */
613    }
614    jcr->db = jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
615                       jcr->catalog->db_password, jcr->catalog->db_address,
616                       jcr->catalog->db_port, jcr->catalog->db_socket,
617                       jcr->catalog->mult_db_connections);
618    if (!jcr->db || !db_open_database(jcr, jcr->db)) {
619       Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
620                  jcr->catalog->db_name);
621       if (jcr->db) {
622          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
623       }
624       return 0;
625    }
626    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
627    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
628       /* Try to create the pool */
629       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
630          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
631             db_strerror(jcr->db));
632          if (jcr->db) {
633             db_close_database(jcr, jcr->db);
634             jcr->db = NULL;
635          }
636          return 0;
637       } else {
638          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
639       }
640    }
641    jcr->jr.PoolId = pr.PoolId;
642    return 1;
643 }
644
645
646 static void con_lock_release(void *arg)
647 {
648    Vw(con_lock);
649 }
650
651 void do_messages(UAContext *ua, const char *cmd)
652 {
653    char msg[2000];
654    int mlen;
655    bool do_truncate = false;
656
657    Pw(con_lock);
658    pthread_cleanup_push(con_lock_release, (void *)NULL);
659    rewind(con_fd);
660    while (fgets(msg, sizeof(msg), con_fd)) {
661       mlen = strlen(msg);
662       ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
663       strcpy(ua->UA_sock->msg, msg);
664       ua->UA_sock->msglen = mlen;
665       bnet_send(ua->UA_sock);
666       do_truncate = true;
667    }
668    if (do_truncate) {
669       (void)ftruncate(fileno(con_fd), 0L);
670    }
671    console_msg_pending = FALSE;
672    ua->user_notified_msg_pending = FALSE;
673    pthread_cleanup_pop(0);
674    Vw(con_lock);
675 }
676
677
678 int qmessagescmd(UAContext *ua, const char *cmd)
679 {
680    if (console_msg_pending && ua->auto_display_messages) {
681       do_messages(ua, cmd);
682    }
683    return 1;
684 }
685
686 int messagescmd(UAContext *ua, const char *cmd)
687 {
688    if (console_msg_pending) {
689       do_messages(ua, cmd);
690    } else {
691       bnet_fsend(ua->UA_sock, _("You have no messages.\n"));
692    }
693    return 1;
694 }
695
696 /*
697  * Callback routine for "printing" database file listing
698  */
699 void prtit(void *ctx, const char *msg)
700 {
701    UAContext *ua = (UAContext *)ctx;
702
703    bnet_fsend(ua->UA_sock, "%s", msg);
704 }
705
706 /*
707  * Format message and send to other end.
708
709  * If the UA_sock is NULL, it means that there is no user
710  * agent, so we are being called from Bacula core. In
711  * that case direct the messages to the Job.
712  */
713 void bsendmsg(void *ctx, const char *fmt, ...)
714 {
715    va_list arg_ptr;
716    UAContext *ua = (UAContext *)ctx;
717    BSOCK *bs = ua->UA_sock;
718    int maxlen, len;
719    POOLMEM *msg;
720
721    if (bs) {
722       msg = bs->msg;
723    } else {
724       msg = get_pool_memory(PM_EMSG);
725    }
726
727 again:
728    maxlen = sizeof_pool_memory(msg) - 1;
729    va_start(arg_ptr, fmt);
730    len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
731    va_end(arg_ptr);
732    if (len < 0 || len >= maxlen) {
733       msg = realloc_pool_memory(msg, maxlen + maxlen/2);
734       goto again;
735    }
736
737    if (bs) {
738       bs->msg = msg;
739       bs->msglen = len;
740       bnet_send(bs);
741    } else {                           /* No UA, send to Job */
742       Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
743       free_pool_memory(msg);
744    }
745
746 }