3 * Bacula Director -- User Agent Output Commands
4 * I.e. messages, listing database, showing resources, ...
6 * Kern Sibbald, September MM
12 Copyright (C) 2000-2006 Kern Sibbald
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License
16 version 2 as amended with additional clauses defined in the
17 file LICENSE in the main source directory.
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
22 the file LICENSE for additional details.
29 /* Imported subroutines */
31 /* Imported variables */
34 extern RES_TABLE resources[];
35 extern RES **res_head;
36 extern int console_msg_pending;
38 extern brwlock_t con_lock;
40 /* Imported functions */
42 /* Forward referenced functions */
43 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist);
44 static bool list_nextvol(UAContext *ua, int ndays);
47 * Turn auto display of console messages on/off
49 int autodisplay_cmd(UAContext *ua, const char *cmd)
51 static const char *kw[] = {
56 switch (find_arg_keyword(ua, kw)) {
58 ua->auto_display_messages = true;
61 ua->auto_display_messages = false;
64 bsendmsg(ua, _("ON or OFF keyword missing.\n"));
71 * Turn GUI mode on/off
73 int gui_cmd(UAContext *ua, const char *cmd)
75 static const char *kw[] = {
80 switch (find_arg_keyword(ua, kw)) {
82 ua->jcr->gui = ua->gui = true;
85 ua->jcr->gui = ua->gui = false;
88 bsendmsg(ua, _("ON or OFF keyword missing.\n"));
96 struct showstruct {const char *res_name; int type;};
97 static struct showstruct reses[] = {
98 {N_("directors"), R_DIRECTOR},
99 {N_("clients"), R_CLIENT},
100 {N_("counters"), R_COUNTER},
101 {N_("devices"), R_DEVICE},
103 {N_("storages"), R_STORAGE},
104 {N_("catalogs"), R_CATALOG},
105 {N_("schedules"), R_SCHEDULE},
106 {N_("filesets"), R_FILESET},
107 {N_("pools"), R_POOL},
108 {N_("messages"), R_MSGS},
119 * show <resource-keyword-name> e.g. show directors
120 * show <resource-keyword-name>=<name> e.g. show director=HeadMan
123 int show_cmd(UAContext *ua, const char *cmd)
130 Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
134 for (i=1; i<ua->argc; i++) {
136 res_name = ua->argk[i];
137 if (!ua->argv[i]) { /* was a name given? */
138 /* No name, dump all resources of specified type */
140 len = strlen(res_name);
141 for (j=0; reses[j].res_name; j++) {
142 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
143 type = reses[j].type;
145 res = res_head[type-r_first];
154 /* Dump a single resource with specified name */
156 len = strlen(res_name);
157 for (j=0; reses[j].res_name; j++) {
158 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
159 type = reses[j].type;
160 res = (RES *)GetResWithName(type, ua->argv[i]);
171 for (j=r_first; j<=r_last; j++) {
172 dump_resource(j, res_head[j-r_first], bsendmsg, ua);
176 bsendmsg(ua, _("Keywords for the show command are:\n"));
177 for (j=0; reses[j].res_name; j++) {
178 bsendmsg(ua, "%s\n", _(reses[j].res_name));
182 bsendmsg(ua, _("%s resource %s not found.\n"), res_name, ua->argv[i]);
185 bsendmsg(ua, _("Resource %s not found\n"), res_name);
188 dump_resource(recurse?type:-type, res, bsendmsg, ua);
201 * List contents of database
203 * list jobs - lists all jobs run
204 * list jobid=nnn - list job data for jobid
205 * list jobuid=uname - list job data for unique jobid
206 * list job=name - list all jobs with "name"
207 * list jobname=name - same as above
208 * list jobmedia jobid=<nn>
209 * list jobmedia job=name
210 * list files jobid=<nn> - list files saved for job nn
211 * list files job=name
212 * list pools - list pool records
213 * list jobtotals - list totals for all jobs
214 * list media - list media for given pool (deprecated)
215 * list volumes - list Volumes
216 * list clients - list clients
217 * list nextvol job=xx - list the next vol to be used by job
218 * list nextvolume job=xx - same as above.
222 /* Do long or full listing */
223 int llist_cmd(UAContext *ua, const char *cmd)
225 return do_list_cmd(ua, cmd, VERT_LIST);
228 /* Do short or summary listing */
229 int list_cmd(UAContext *ua, const char *cmd)
231 return do_list_cmd(ua, cmd, HORZ_LIST);
234 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
246 memset(&jr, 0, sizeof(jr));
247 memset(&pr, 0, sizeof(pr));
248 memset(&mr, 0, sizeof(mr));
250 Dmsg1(20, "list: %s\n", cmd);
253 bsendmsg(ua, _("Hey! DB is NULL\n"));
256 /* Scan arguments looking for things to do */
257 for (i=1; i<ua->argc; i++) {
259 if (strcasecmp(ua->argk[i], N_("jobs")) == 0) {
260 /* Apply any limit */
261 j = find_arg_with_value(ua, N_("limit"));
263 jr.limit = atoi(ua->argv[j]);
265 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
268 } else if (strcasecmp(ua->argk[i], N_("jobtotals")) == 0) {
269 db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
272 } else if (strcasecmp(ua->argk[i], N_("jobid")) == 0) {
274 jobid = str_to_int64(ua->argv[i]);
277 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
282 } else if ((strcasecmp(ua->argk[i], N_("job")) == 0 ||
283 strcasecmp(ua->argk[i], N_("jobname")) == 0) && ua->argv[i]) {
284 bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
286 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
288 /* List JOBUID=xxx */
289 } else if (strcasecmp(ua->argk[i], N_("jobuid")) == 0 && ua->argv[i]) {
290 bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
292 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
295 } else if (strcasecmp(ua->argk[i], N_("files")) == 0) {
297 for (j=i+1; j<ua->argc; j++) {
298 if (strcasecmp(ua->argk[j], N_("jobuid")) == 0 && ua->argv[j]) {
299 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
301 db_get_job_record(ua->jcr, ua->db, &jr);
303 } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
304 jobid = str_to_int64(ua->argv[j]);
309 db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
314 } else if (strcasecmp(ua->argk[i], N_("jobmedia")) == 0) {
316 for (j=i+1; j<ua->argc; j++) {
317 if (strcasecmp(ua->argk[j], N_("jobuid")) == 0 && ua->argv[j]) {
318 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
320 db_get_job_record(ua->jcr, ua->db, &jr);
322 } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
323 jobid = str_to_int64(ua->argv[j]);
327 db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
331 /* List for all jobs (jobid=0) */
332 db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
336 } else if (strcasecmp(ua->argk[i], N_("pool")) == 0 ||
337 strcasecmp(ua->argk[i], N_("pools")) == 0) {
339 memset(&pr, 0, sizeof(pr));
341 bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
343 db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
345 } else if (strcasecmp(ua->argk[i], N_("clients")) == 0) {
346 db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
349 /* List MEDIA or VOLUMES */
350 } else if (strcasecmp(ua->argk[i], N_("media")) == 0 ||
351 strcasecmp(ua->argk[i], N_("volume")) == 0 ||
352 strcasecmp(ua->argk[i], N_("volumes")) == 0) {
354 for (j=i+1; j<ua->argc; j++) {
355 if (strcasecmp(ua->argk[j], N_("jobuid")) == 0 && ua->argv[j]) {
356 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
358 db_get_job_record(ua->jcr, ua->db, &jr);
360 } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
361 jobid = str_to_int64(ua->argv[j]);
365 VolumeName = get_pool_memory(PM_FNAME);
366 n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
367 bsendmsg(ua, _("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
368 free_pool_memory(VolumeName);
371 /* if no job or jobid keyword found, then we list all media */
375 /* List a specific volume? */
377 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
378 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
381 /* Is a specific pool wanted? */
382 for (i=1; i<ua->argc; i++) {
383 if (strcasecmp(ua->argk[i], N_("pool")) == 0) {
384 if (!get_pool_dbr(ua, &pr)) {
385 bsendmsg(ua, _("No Pool specified.\n"));
388 mr.PoolId = pr.PoolId;
389 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
394 /* List Volumes in all pools */
395 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
396 bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"),
397 db_strerror(ua->db));
400 if (num_pools <= 0) {
403 for (i=0; i < num_pools; i++) {
405 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
406 bsendmsg(ua, _("Pool: %s\n"), pr.Name);
409 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
414 /* List next volume */
415 } else if (strcasecmp(ua->argk[i], N_("nextvol")) == 0 ||
416 strcasecmp(ua->argk[i], N_("nextvolume")) == 0) {
418 j = find_arg_with_value(ua, N_("days"));
420 n = atoi(ua->argv[j]);
421 if ((n < 0) || (n > 50)) {
422 bsendmsg(ua, _("Ignoring illegal value for days.\n"));
427 } else if (strcasecmp(ua->argk[i], N_("limit")) == 0
428 || strcasecmp(ua->argk[i], N_("days")) == 0) {
431 bsendmsg(ua, _("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
437 static bool list_nextvol(UAContext *ua, int ndays)
448 memset(&mr, 0, sizeof(mr));
449 int i = find_arg_with_value(ua, "job");
451 if ((job = select_job_resource(ua)) == NULL) {
455 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
457 Jmsg(jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
458 if ((job = select_job_resource(ua)) == NULL) {
463 for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
464 pool = run->pool ? run->pool : NULL;
465 if (!complete_jcr_for_job(jcr, job, pool)) {
468 mr.PoolId = jcr->jr.PoolId;
470 jcr->store = run->storage;
472 memset(&pr, 0, sizeof(pr));
473 pr.PoolId = jcr->jr.PoolId;
474 if (! db_get_pool_record(ua->jcr, ua->db, &pr)) {
475 strcpy(pr.Name, "*UnknownPool*");
477 if (!find_next_volume_for_append(jcr, &mr, 1, false/*no create*/)) {
478 bsendmsg(ua, _("Could not find next Volume for Job %s (%s, %s).\n"),
479 job->hdr.name, pr.Name, level_to_str(run->level));
482 _("The next Volume to be used by Job \"%s\" (%s, %s) will be %s\n"),
483 job->hdr.name, pr.Name, level_to_str(run->level), mr.VolumeName);
486 if (jcr->db && jcr->db != ua->db) {
487 db_close_database(jcr, jcr->db);
492 bsendmsg(ua, _("Could not find next Volume for Job %s.\n"),
501 * For a given job, we examine all his run records
502 * to see if it is scheduled today or tomorrow.
504 RUN *find_next_run(RUN *run, JOB *job, time_t &runtime, int ndays)
506 time_t now, future, endtime;
509 int mday, wday, month, wom, i;
514 sched = job->schedule;
515 if (sched == NULL) { /* scheduled? */
516 return NULL; /* no nothing to report */
519 /* Break down the time into components */
521 endtime = now + (ndays * 60 * 60 * 24);
528 for ( ; run; run=run->next) {
530 * Find runs in next 24 hours. Day 0 is today, so if
531 * ndays=1, look at today and tomorrow.
533 for (day = 0; day <= ndays; day++) {
534 future = now + (day * 60 * 60 * 24);
536 /* Break down the time into components */
537 localtime_r(&future, &tm);
538 mday = tm.tm_mday - 1;
542 woy = tm_woy(future);
544 is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
545 bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
546 bit_is_set(woy, run->woy);
549 Dmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
550 Dmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
551 Dmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
552 Dmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
553 Dmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
554 Dmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
557 if (is_scheduled) { /* Jobs scheduled on that day */
559 char buf[300], num[10];
560 bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
561 for (i=0; i<24; i++) {
562 if (bit_is_set(i, run->hour)) {
563 bsnprintf(num, sizeof(num), "%d ", i);
564 bstrncat(buf, num, sizeof(buf));
567 bstrncat(buf, "\n", sizeof(buf));
568 Dmsg1(000, "%s", buf);
570 /* find time (time_t) job is to be run */
571 localtime_r(&future, &runtm);
572 for (i= 0; i < 24; i++) {
573 if (bit_is_set(i, run->hour)) {
575 runtm.tm_min = run->minute;
577 runtime = mktime(&runtm);
578 Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
579 if ((runtime > now) && (runtime < endtime)) {
580 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
581 return run; /* found it, return run resource */
587 } /* end for loop over runs */
592 * Fill in the remaining fields of the jcr as if it
593 * is going to run the job.
595 int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
599 memset(&pr, 0, sizeof(POOL_DBR));
600 set_jcr_defaults(jcr, job);
602 jcr->pool = pool; /* override */
604 jcr->db = jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
605 jcr->catalog->db_password, jcr->catalog->db_address,
606 jcr->catalog->db_port, jcr->catalog->db_socket,
607 jcr->catalog->mult_db_connections);
608 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
609 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
610 jcr->catalog->db_name);
612 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
616 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
617 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
618 /* Try to create the pool */
619 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
620 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
621 db_strerror(jcr->db));
623 db_close_database(jcr, jcr->db);
628 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
631 jcr->jr.PoolId = pr.PoolId;
636 static void con_lock_release(void *arg)
641 void do_messages(UAContext *ua, const char *cmd)
645 bool do_truncate = false;
648 pthread_cleanup_push(con_lock_release, (void *)NULL);
650 while (fgets(msg, sizeof(msg), con_fd)) {
652 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
653 strcpy(ua->UA_sock->msg, msg);
654 ua->UA_sock->msglen = mlen;
655 bnet_send(ua->UA_sock);
659 (void)ftruncate(fileno(con_fd), 0L);
661 console_msg_pending = FALSE;
662 ua->user_notified_msg_pending = FALSE;
663 pthread_cleanup_pop(0);
668 int qmessagescmd(UAContext *ua, const char *cmd)
670 if (console_msg_pending && ua->auto_display_messages) {
671 do_messages(ua, cmd);
676 int messagescmd(UAContext *ua, const char *cmd)
678 if (console_msg_pending) {
679 do_messages(ua, cmd);
681 bnet_fsend(ua->UA_sock, _("You have no messages.\n"));
687 * Callback routine for "printing" database file listing
689 void prtit(void *ctx, const char *msg)
691 UAContext *ua = (UAContext *)ctx;
693 bnet_fsend(ua->UA_sock, "%s", msg);
697 * Format message and send to other end.
699 * If the UA_sock is NULL, it means that there is no user
700 * agent, so we are being called from Bacula core. In
701 * that case direct the messages to the Job.
703 void bsendmsg(void *ctx, const char *fmt, ...)
706 UAContext *ua = (UAContext *)ctx;
707 BSOCK *bs = ua->UA_sock;
714 msg = get_pool_memory(PM_EMSG);
718 maxlen = sizeof_pool_memory(msg) - 1;
719 va_start(arg_ptr, fmt);
720 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
722 if (len < 0 || len >= maxlen) {
723 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
731 } else { /* No UA, send to Job */
732 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
733 free_pool_memory(msg);