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