3 * Bacula Director -- User Agent Output Commands
4 * I.e. messages, listing database, showing resources, ...
6 * Kern Sibbald, September MM
12 Copyright (C) 2000-2005 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);
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 = 1;
61 ua->auto_display_messages = 0;
64 bsendmsg(ua, _("ON or OFF keyword missing.\n"));
71 * Turn batch processing on/off
73 int gui_cmd(UAContext *ua, const char *cmd)
75 static const char *kw[] = {
80 switch (find_arg_keyword(ua, kw)) {
90 bsendmsg(ua, _("ON or OFF keyword missing.\n"));
98 struct showstruct {const char *res_name; int type;};
99 static struct showstruct reses[] = {
100 {N_("directors"), R_DIRECTOR},
101 {N_("clients"), R_CLIENT},
102 {N_("counters"), R_COUNTER},
103 {N_("devices"), R_DEVICE},
105 {N_("storages"), R_STORAGE},
106 {N_("catalogs"), R_CATALOG},
107 {N_("schedules"), R_SCHEDULE},
108 {N_("filesets"), R_FILESET},
109 {N_("pools"), R_POOL},
110 {N_("messages"), R_MSGS},
121 * show <resource-keyword-name> e.g. show directors
122 * show <resource-keyword-name>=<name> e.g. show director=HeadMan
125 int show_cmd(UAContext *ua, const char *cmd)
132 Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
136 for (i=1; i<ua->argc; i++) {
138 res_name = ua->argk[i];
139 if (!ua->argv[i]) { /* was a name given? */
140 /* No name, dump all resources of specified type */
142 len = strlen(res_name);
143 for (j=0; reses[j].res_name; j++) {
144 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
145 type = reses[j].type;
147 res = res_head[type-r_first];
156 /* Dump a single resource with specified name */
158 len = strlen(res_name);
159 for (j=0; reses[j].res_name; j++) {
160 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
161 type = reses[j].type;
162 res = (RES *)GetResWithName(type, ua->argv[i]);
173 for (j=r_first; j<=r_last; j++) {
174 dump_resource(j, res_head[j-r_first], bsendmsg, ua);
178 bsendmsg(ua, _("Keywords for the show command are:\n"));
179 for (j=0; reses[j].res_name; j++) {
180 bsendmsg(ua, "%s\n", _(reses[j].res_name));
184 bsendmsg(ua, _("%s resource %s not found.\n"), res_name, ua->argv[i]);
187 bsendmsg(ua, _("Resource %s not found\n"), res_name);
190 dump_resource(recurse?type:-type, res, bsendmsg, ua);
203 * List contents of database
205 * list jobs - lists all jobs run
206 * list jobid=nnn - list job data for jobid
207 * list job=name - list job data for job
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 && ua->argv[i]) {
283 bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
285 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
288 } else if (strcasecmp(ua->argk[i], N_("files")) == 0) {
290 for (j=i+1; j<ua->argc; j++) {
291 if (strcasecmp(ua->argk[j], N_("job")) == 0 && ua->argv[j]) {
292 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
294 db_get_job_record(ua->jcr, ua->db, &jr);
296 } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
297 jobid = str_to_int64(ua->argv[j]);
302 db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
307 } else if (strcasecmp(ua->argk[i], N_("jobmedia")) == 0) {
309 for (j=i+1; j<ua->argc; j++) {
310 if (strcasecmp(ua->argk[j], N_("job")) == 0 && ua->argv[j]) {
311 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
313 db_get_job_record(ua->jcr, ua->db, &jr);
315 } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
316 jobid = str_to_int64(ua->argv[j]);
320 db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
324 /* List for all jobs (jobid=0) */
325 db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
329 } else if (strcasecmp(ua->argk[i], N_("pool")) == 0 ||
330 strcasecmp(ua->argk[i], N_("pools")) == 0) {
332 memset(&pr, 0, sizeof(pr));
334 bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
336 db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
338 } else if (strcasecmp(ua->argk[i], N_("clients")) == 0) {
339 db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
342 /* List MEDIA or VOLUMES */
343 } else if (strcasecmp(ua->argk[i], N_("media")) == 0 ||
344 strcasecmp(ua->argk[i], N_("volume")) == 0 ||
345 strcasecmp(ua->argk[i], N_("volumes")) == 0) {
347 for (j=i+1; j<ua->argc; j++) {
348 if (strcasecmp(ua->argk[j], N_("job")) == 0 && ua->argv[j]) {
349 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
351 db_get_job_record(ua->jcr, ua->db, &jr);
353 } else if (strcasecmp(ua->argk[j], N_("jobid")) == 0 && ua->argv[j]) {
354 jobid = str_to_int64(ua->argv[j]);
358 VolumeName = get_pool_memory(PM_FNAME);
359 n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
360 bsendmsg(ua, _("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
361 free_pool_memory(VolumeName);
364 /* if no job or jobid keyword found, then we list all media */
368 /* List a specific volume? */
370 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
371 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
374 /* Is a specific pool wanted? */
375 for (i=1; i<ua->argc; i++) {
376 if (strcasecmp(ua->argk[i], N_("pool")) == 0) {
377 if (!get_pool_dbr(ua, &pr)) {
378 bsendmsg(ua, _("No Pool specified.\n"));
381 mr.PoolId = pr.PoolId;
382 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
387 /* List Volumes in all pools */
388 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
389 bsendmsg(ua, _("Error obtaining pool ids. ERR=%s\n"),
390 db_strerror(ua->db));
393 if (num_pools <= 0) {
396 for (i=0; i < num_pools; i++) {
398 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
399 bsendmsg(ua, _("Pool: %s\n"), pr.Name);
402 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
407 /* List next volume */
408 } else if (strcasecmp(ua->argk[i], N_("nextvol")) == 0 ||
409 strcasecmp(ua->argk[i], N_("nextvolume")) == 0) {
411 } else if (strcasecmp(ua->argk[i], N_("limit")) == 0) {
414 bsendmsg(ua, _("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
420 static bool list_nextvol(UAContext *ua)
430 memset(&mr, 0, sizeof(mr));
431 int i = find_arg_with_value(ua, "job");
433 if ((job = select_job_resource(ua)) == NULL) {
437 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
439 Jmsg(jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
440 if ((job = select_job_resource(ua)) == NULL) {
445 for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
446 pool = run->pool ? run->pool : NULL;
447 if (!complete_jcr_for_job(jcr, job, pool)) {
450 mr.PoolId = jcr->PoolId;
452 jcr->store = run->storage;
454 if (!find_next_volume_for_append(jcr, &mr, 0)) {
455 bsendmsg(ua, _("Could not find next Volume.\n"));
457 bsendmsg(ua, _("The next Volume to be used by Job \"%s\" will be %s\n"),
458 job->hdr.name, mr.VolumeName);
461 if (jcr->db && jcr->db != ua->db) {
462 db_close_database(jcr, jcr->db);
467 bsendmsg(ua, _("Could not find next Volume.\n"));
475 * For a given job, we examine all his run records
476 * to see if it is scheduled today or tomorrow.
478 RUN *find_next_run(RUN *run, JOB *job, time_t &runtime)
480 time_t now, tomorrow;
483 int mday, wday, month, wom, tmday, twday, tmonth, twom, i;
487 sched = job->schedule;
488 if (sched == NULL) { /* scheduled? */
489 return NULL; /* no nothing to report */
491 /* Break down current time into components */
493 localtime_r(&now, &tm);
494 mday = tm.tm_mday - 1;
500 /* Break down tomorrow into components */
501 tomorrow = now + 60 * 60 * 24;
502 localtime_r(&tomorrow, &tm);
503 tmday = tm.tm_mday - 1;
507 twoy = tm_woy(tomorrow);
514 for ( ; run; run=run->next) {
516 * Find runs in next 24 hours
518 tod = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
519 bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
520 bit_is_set(woy, run->woy);
522 tom = bit_is_set(tmday, run->mday) && bit_is_set(twday, run->wday) &&
523 bit_is_set(tmonth, run->month) && bit_is_set(twom, run->wom) &&
524 bit_is_set(twoy, run->woy);
527 Dmsg2(000, "tod=%d tom=%d\n", tod, tom);
528 Dmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
529 Dmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
530 Dmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
531 Dmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
532 Dmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
534 if (tod) { /* Jobs scheduled today (next 24 hours) */
536 char buf[300], num[10];
537 bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
538 for (i=0; i<24; i++) {
539 if (bit_is_set(i, run->hour)) {
540 bsnprintf(num, sizeof(num), "%d ", i);
541 bstrncat(buf, num, sizeof(buf));
544 bstrncat(buf, "\n", sizeof(buf));
545 Dmsg1(000, "%s", buf);
547 /* find time (time_t) job is to be run */
548 localtime_r(&now, &tm);
549 for (i=tm.tm_hour; i < 24; i++) {
550 if (bit_is_set(i, run->hour)) {
552 tm.tm_min = run->minute;
554 runtime = mktime(&tm);
555 Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
557 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
558 return run; /* found it, return run resource */
564 // Dmsg2(200, "runtime=%d now=%d\n", runtime, now);
565 if (tom) { /* look at jobs scheduled tomorrow */
566 localtime_r(&tomorrow, &tm);
567 for (i=0; i < 24; i++) {
568 if (bit_is_set(i, run->hour)) {
570 tm.tm_min = run->minute;
572 runtime = mktime(&tm);
573 Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
574 if (runtime < tomorrow) {
575 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
576 return run; /* found it, return run resource */
581 } /* end for loop over runs */
586 * Fill in the remaining fields of the jcr as if it
587 * is going to run the job.
589 int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
593 memset(&pr, 0, sizeof(POOL_DBR));
594 set_jcr_defaults(jcr, job);
596 jcr->pool = pool; /* override */
598 jcr->db = jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
599 jcr->catalog->db_password, jcr->catalog->db_address,
600 jcr->catalog->db_port, jcr->catalog->db_socket,
601 jcr->catalog->mult_db_connections);
602 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
603 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
604 jcr->catalog->db_name);
606 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
610 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
611 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
612 /* Try to create the pool */
613 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
614 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
615 db_strerror(jcr->db));
617 db_close_database(jcr, jcr->db);
622 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
625 jcr->PoolId = pr.PoolId;
626 jcr->jr.PoolId = pr.PoolId;
631 static void con_lock_release(void *arg)
636 void do_messages(UAContext *ua, const char *cmd)
640 int do_truncate = FALSE;
643 pthread_cleanup_push(con_lock_release, (void *)NULL);
645 while (fgets(msg, sizeof(msg), con_fd)) {
647 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
648 strcpy(ua->UA_sock->msg, msg);
649 ua->UA_sock->msglen = mlen;
650 bnet_send(ua->UA_sock);
654 ftruncate(fileno(con_fd), 0L);
656 console_msg_pending = FALSE;
657 ua->user_notified_msg_pending = FALSE;
658 pthread_cleanup_pop(0);
663 int qmessagescmd(UAContext *ua, const char *cmd)
665 if (console_msg_pending && ua->auto_display_messages) {
666 do_messages(ua, cmd);
671 int messagescmd(UAContext *ua, const char *cmd)
673 if (console_msg_pending) {
674 do_messages(ua, cmd);
676 bnet_fsend(ua->UA_sock, _("You have no messages.\n"));
682 * Callback routine for "printing" database file listing
684 void prtit(void *ctx, const char *msg)
686 UAContext *ua = (UAContext *)ctx;
688 bnet_fsend(ua->UA_sock, "%s", msg);
692 * Format message and send to other end.
694 * If the UA_sock is NULL, it means that there is no user
695 * agent, so we are being called from Bacula core. In
696 * that case direct the messages to the Job.
698 void bsendmsg(void *ctx, const char *fmt, ...)
701 UAContext *ua = (UAContext *)ctx;
702 BSOCK *bs = ua->UA_sock;
709 msg = get_pool_memory(PM_EMSG);
713 maxlen = sizeof_pool_memory(msg) - 1;
714 va_start(arg_ptr, fmt);
715 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
717 if (len < 0 || len >= maxlen) {
718 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
726 } else { /* No UA, send to Job */
727 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
728 free_pool_memory(msg);