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