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