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