2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Output Commands
31 * I.e. messages, listing database, showing resources, ...
33 * Kern Sibbald, September MM
41 /* Imported subroutines */
43 /* Imported variables */
46 extern RES_TABLE resources[];
47 extern RES **res_head;
49 /* Imported functions */
51 /* Forward referenced functions */
52 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist);
53 static bool list_nextvol(UAContext *ua, int ndays);
56 * Turn auto display of console messages on/off
58 int autodisplay_cmd(UAContext *ua, const char *cmd)
60 static const char *kw[] = {
65 switch (find_arg_keyword(ua, kw)) {
67 ua->auto_display_messages = true;
70 ua->auto_display_messages = false;
73 ua->error_msg(_("ON or OFF keyword missing.\n"));
80 * Turn GUI mode on/off
82 int gui_cmd(UAContext *ua, const char *cmd)
84 static const char *kw[] = {
89 switch (find_arg_keyword(ua, kw)) {
91 ua->jcr->gui = ua->gui = true;
94 ua->jcr->gui = ua->gui = false;
97 ua->error_msg(_("ON or OFF keyword missing.\n"));
105 struct showstruct {const char *res_name; int type;};
106 static struct showstruct reses[] = {
107 {NT_("directors"), R_DIRECTOR},
108 {NT_("clients"), R_CLIENT},
109 {NT_("counters"), R_COUNTER},
110 {NT_("devices"), R_DEVICE},
111 {NT_("jobs"), R_JOB},
112 {NT_("storages"), R_STORAGE},
113 {NT_("catalogs"), R_CATALOG},
114 {NT_("schedules"), R_SCHEDULE},
115 {NT_("filesets"), R_FILESET},
116 {NT_("pools"), R_POOL},
117 {NT_("messages"), R_MSGS},
128 * show <resource-keyword-name> e.g. show directors
129 * show <resource-keyword-name>=<name> e.g. show director=HeadMan
132 int show_cmd(UAContext *ua, const char *cmd)
139 Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
143 for (i=1; i<ua->argc; i++) {
145 res_name = ua->argk[i];
146 if (!ua->argv[i]) { /* was a name given? */
147 /* No name, dump all resources of specified type */
149 len = strlen(res_name);
150 for (j=0; reses[j].res_name; j++) {
151 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
152 type = reses[j].type;
154 res = res_head[type-r_first];
163 /* Dump a single resource with specified name */
165 len = strlen(res_name);
166 for (j=0; reses[j].res_name; j++) {
167 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
168 type = reses[j].type;
169 res = (RES *)GetResWithName(type, ua->argv[i]);
180 for (j=r_first; j<=r_last; j++) {
181 dump_resource(j, res_head[j-r_first], bsendmsg, ua);
185 ua->send_msg(_("Keywords for the show command are:\n"));
186 for (j=0; reses[j].res_name; j++) {
187 ua->error_msg("%s\n", _(reses[j].res_name));
191 ua->error_msg(_("%s resource %s not found.\n"), res_name, ua->argv[i]);
194 ua->error_msg(_("Resource %s not found\n"), res_name);
197 dump_resource(recurse?type:-type, res, bsendmsg, ua);
210 * List contents of database
212 * list jobs - lists all jobs run
213 * list jobid=nnn - list job data for jobid
214 * list ujobid=uname - list job data for unique jobid
215 * list job=name - list all jobs with "name"
216 * list jobname=name - same as above
217 * list jobmedia jobid=<nn>
218 * list jobmedia job=name
219 * list files jobid=<nn> - list files saved for job nn
220 * list files job=name
221 * list pools - list pool records
222 * list jobtotals - list totals for all jobs
223 * list media - list media for given pool (deprecated)
224 * list volumes - list Volumes
225 * list clients - list clients
226 * list nextvol job=xx - list the next vol to be used by job
227 * list nextvolume job=xx - same as above.
231 /* Do long or full listing */
232 int llist_cmd(UAContext *ua, const char *cmd)
234 return do_list_cmd(ua, cmd, VERT_LIST);
237 /* Do short or summary listing */
238 int list_cmd(UAContext *ua, const char *cmd)
240 return do_list_cmd(ua, cmd, HORZ_LIST);
243 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
252 if (!open_client_db(ua))
255 memset(&jr, 0, sizeof(jr));
256 memset(&pr, 0, sizeof(pr));
257 memset(&mr, 0, sizeof(mr));
259 Dmsg1(20, "list: %s\n", cmd);
262 ua->error_msg(_("Hey! DB is NULL\n"));
265 /* Scan arguments looking for things to do */
266 for (i=1; i<ua->argc; i++) {
268 if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
269 /* Apply any limit */
270 j = find_arg_with_value(ua, NT_("limit"));
272 jr.limit = atoi(ua->argv[j]);
274 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
277 } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
278 db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
281 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
283 jobid = str_to_int64(ua->argv[i]);
286 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
291 } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
292 strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
293 bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
295 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
297 /* List UJOBID=xxx */
298 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
299 bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
301 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
304 } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
306 for (j=i+1; j<ua->argc; j++) {
307 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
308 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
310 db_get_job_record(ua->jcr, ua->db, &jr);
312 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
313 jobid = str_to_int64(ua->argv[j]);
318 db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
323 } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
325 for (j=i+1; j<ua->argc; j++) {
326 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
327 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
329 db_get_job_record(ua->jcr, ua->db, &jr);
331 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
332 jobid = str_to_int64(ua->argv[j]);
336 db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
340 /* List for all jobs (jobid=0) */
341 db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
345 } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
346 strcasecmp(ua->argk[i], NT_("pools")) == 0) {
348 memset(&pr, 0, sizeof(pr));
350 bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
352 db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
354 } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
355 db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
358 /* List MEDIA or VOLUMES */
359 } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
360 strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
361 strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
363 for (j=i+1; j<ua->argc; j++) {
364 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
365 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
367 db_get_job_record(ua->jcr, ua->db, &jr);
369 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
370 jobid = str_to_int64(ua->argv[j]);
374 VolumeName = get_pool_memory(PM_FNAME);
375 n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
376 ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
377 free_pool_memory(VolumeName);
380 /* if no job or jobid keyword found, then we list all media */
384 /* List a specific volume? */
386 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
387 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
390 /* Is a specific pool wanted? */
391 for (i=1; i<ua->argc; i++) {
392 if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
393 if (!get_pool_dbr(ua, &pr)) {
394 ua->error_msg(_("No Pool specified.\n"));
397 mr.PoolId = pr.PoolId;
398 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
403 /* List Volumes in all pools */
404 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
405 ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"),
406 db_strerror(ua->db));
409 if (num_pools <= 0) {
412 for (i=0; i < num_pools; i++) {
414 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
415 ua->send_msg(_("Pool: %s\n"), pr.Name);
418 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
423 /* List next volume */
424 } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
425 strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
427 j = find_arg_with_value(ua, NT_("days"));
429 n = atoi(ua->argv[j]);
430 if ((n < 0) || (n > 50)) {
431 ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n"));
436 } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
437 || strcasecmp(ua->argk[i], NT_("days")) == 0) {
440 ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
446 static bool list_nextvol(UAContext *ua, int ndays)
457 memset(&mr, 0, sizeof(mr));
458 int i = find_arg_with_value(ua, "job");
460 if ((job = select_job_resource(ua)) == NULL) {
464 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
466 Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
467 if ((job = select_job_resource(ua)) == NULL) {
473 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
474 for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
475 if (!complete_jcr_for_job(jcr, job, run->pool)) {
479 if (!jcr->jr.PoolId) {
480 ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
483 memset(&pr, 0, sizeof(pr));
484 pr.PoolId = jcr->jr.PoolId;
485 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
486 bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
488 mr.PoolId = jcr->jr.PoolId;
489 get_job_storage(&store, job, run);
490 mr.StorageId = store.store->StorageId;
491 if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
492 ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
493 job->name(), pr.Name, level_to_str(run->level));
496 _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
497 job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
503 db_close_database(jcr, jcr->db);
507 ua->error_msg(_("Could not find next Volume for Job %s.\n"),
516 * For a given job, we examine all his run records
517 * to see if it is scheduled today or tomorrow.
519 RUN *find_next_run(RUN *run, JOB *job, time_t &runtime, int ndays)
521 time_t now, future, endtime;
524 int mday, wday, month, wom, i;
529 sched = job->schedule;
530 if (sched == NULL) { /* scheduled? */
531 return NULL; /* no nothing to report */
534 /* Break down the time into components */
536 endtime = now + (ndays * 60 * 60 * 24);
543 for ( ; run; run=run->next) {
545 * Find runs in next 24 hours. Day 0 is today, so if
546 * ndays=1, look at today and tomorrow.
548 for (day = 0; day <= ndays; day++) {
549 future = now + (day * 60 * 60 * 24);
551 /* Break down the time into components */
552 (void)localtime_r(&future, &tm);
553 mday = tm.tm_mday - 1;
557 woy = tm_woy(future);
559 is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
560 bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
561 bit_is_set(woy, run->woy);
564 Pmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
565 Pmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
566 Pmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
567 Pmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
568 Pmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
569 Pmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
572 if (is_scheduled) { /* Jobs scheduled on that day */
574 char buf[300], num[10];
575 bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
576 for (i=0; i<24; i++) {
577 if (bit_is_set(i, run->hour)) {
578 bsnprintf(num, sizeof(num), "%d ", i);
579 bstrncat(buf, num, sizeof(buf));
582 bstrncat(buf, "\n", sizeof(buf));
583 Pmsg1(000, "%s", buf);
585 /* find time (time_t) job is to be run */
586 (void)localtime_r(&future, &runtm);
587 for (i= 0; i < 24; i++) {
588 if (bit_is_set(i, run->hour)) {
590 runtm.tm_min = run->minute;
592 runtime = mktime(&runtm);
593 Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
594 if ((runtime > now) && (runtime < endtime)) {
595 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
596 return run; /* found it, return run resource */
602 } /* end for loop over runs */
608 * Fill in the remaining fields of the jcr as if it
609 * is going to run the job.
611 bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
615 memset(&pr, 0, sizeof(POOL_DBR));
616 set_jcr_defaults(jcr, job);
618 jcr->pool = pool; /* override */
621 Dmsg0(100, "complete_jcr close db\n");
622 db_close_database(jcr, jcr->db);
626 Dmsg0(100, "complete_jcr open db\n");
627 jcr->db = jcr->db=db_init(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
628 jcr->catalog->db_user,
629 jcr->catalog->db_password, jcr->catalog->db_address,
630 jcr->catalog->db_port, jcr->catalog->db_socket,
631 jcr->catalog->mult_db_connections);
632 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
633 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
634 jcr->catalog->db_name);
636 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
637 db_close_database(jcr, jcr->db);
642 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
643 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
644 /* Try to create the pool */
645 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
646 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
647 db_strerror(jcr->db));
649 db_close_database(jcr, jcr->db);
654 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
657 jcr->jr.PoolId = pr.PoolId;
662 static void con_lock_release(void *arg)
667 void do_messages(UAContext *ua, const char *cmd)
671 bool do_truncate = false;
674 pthread_cleanup_push(con_lock_release, (void *)NULL);
676 while (fgets(msg, sizeof(msg), con_fd)) {
678 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
679 strcpy(ua->UA_sock->msg, msg);
680 ua->UA_sock->msglen = mlen;
685 (void)ftruncate(fileno(con_fd), 0L);
687 console_msg_pending = FALSE;
688 ua->user_notified_msg_pending = FALSE;
689 pthread_cleanup_pop(0);
694 int qmessagescmd(UAContext *ua, const char *cmd)
696 if (console_msg_pending && ua->auto_display_messages) {
697 do_messages(ua, cmd);
702 int messagescmd(UAContext *ua, const char *cmd)
704 if (console_msg_pending) {
705 do_messages(ua, cmd);
707 ua->UA_sock->fsend(_("You have no messages.\n"));
713 * Callback routine for "printing" database file listing
715 void prtit(void *ctx, const char *msg)
717 UAContext *ua = (UAContext *)ctx;
719 ua->UA_sock->fsend("%s", msg);
723 * Format message and send to other end.
725 * If the UA_sock is NULL, it means that there is no user
726 * agent, so we are being called from Bacula core. In
727 * that case direct the messages to the Job.
730 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
732 BSOCK *bs = ua->UA_sock;
741 msg = get_pool_memory(PM_EMSG);
745 maxlen = sizeof_pool_memory(msg) - 1;
746 va_copy(ap, arg_ptr);
747 len = bvsnprintf(msg, maxlen, fmt, ap);
749 if (len < 0 || len >= maxlen) {
750 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
758 } else { /* No UA, send to Job */
759 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
760 free_pool_memory(msg);
765 #else /* no va_copy() -- brain damaged version of variable arguments */
767 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
769 BSOCK *bs = ua->UA_sock;
777 msg = get_memory(5000);
780 maxlen = sizeof_pool_memory(msg) - 1;
782 msg = realloc_pool_memory(msg, 5000);
785 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
786 if (len < 0 || len >= maxlen) {
787 pm_strcpy(msg, _("Message too long to display.\n"));
795 } else { /* No UA, send to Job */
796 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
797 free_pool_memory(msg);
803 void bsendmsg(void *ctx, const char *fmt, ...)
806 va_start(arg_ptr, fmt);
807 bmsg((UAContext *)ctx, fmt, arg_ptr);
812 * The following UA methods are mainly intended for GUI
816 * This is a message that should be displayed on the user's
819 void UAContext::send_msg(const char *fmt, ...)
822 va_start(arg_ptr, fmt);
823 bmsg(this, fmt, arg_ptr);
829 * This is an error condition with a command. The gui should put
830 * up an error or critical dialog box. The command is aborted.
832 void UAContext::error_msg(const char *fmt, ...)
837 if (bs && api) bs->signal(BNET_ERROR_MSG);
838 va_start(arg_ptr, fmt);
839 bmsg(this, fmt, arg_ptr);
844 * This is a warning message, that should bring up a warning
845 * dialog box on the GUI. The command is not aborted, but something
848 void UAContext::warning_msg(const char *fmt, ...)
853 if (bs && api) bs->signal(BNET_WARNING_MSG);
854 va_start(arg_ptr, fmt);
855 bmsg(this, fmt, arg_ptr);
860 * This is an information message that should probably be put
861 * into the status line of a GUI program.
863 void UAContext::info_msg(const char *fmt, ...)
868 if (bs && api) bs->signal(BNET_INFO_MSG);
869 va_start(arg_ptr, fmt);
870 bmsg(this, fmt, arg_ptr);