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 Kern Sibbald.
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 */
45 /* Imported functions */
47 /* Forward referenced functions */
48 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist);
49 static bool list_nextvol(UAContext *ua, int ndays);
52 * Turn auto display of console messages on/off
54 int autodisplay_cmd(UAContext *ua, const char *cmd)
56 static const char *kw[] = {
61 switch (find_arg_keyword(ua, kw)) {
63 ua->auto_display_messages = true;
66 ua->auto_display_messages = false;
69 ua->error_msg(_("ON or OFF keyword missing.\n"));
76 * Turn GUI mode on/off
78 int gui_cmd(UAContext *ua, const char *cmd)
80 static const char *kw[] = {
85 switch (find_arg_keyword(ua, kw)) {
87 ua->jcr->gui = ua->gui = true;
90 ua->jcr->gui = ua->gui = false;
93 ua->error_msg(_("ON or OFF keyword missing.\n"));
101 struct showstruct {const char *res_name; int type;};
102 static struct showstruct reses[] = {
103 {NT_("directors"), R_DIRECTOR},
104 {NT_("clients"), R_CLIENT},
105 {NT_("counters"), R_COUNTER},
106 {NT_("devices"), R_DEVICE},
107 {NT_("jobs"), R_JOB},
108 {NT_("storages"), R_STORAGE},
109 {NT_("catalogs"), R_CATALOG},
110 {NT_("schedules"), R_SCHEDULE},
111 {NT_("filesets"), R_FILESET},
112 {NT_("pools"), R_POOL},
113 {NT_("messages"), R_MSGS},
124 * show <resource-keyword-name> e.g. show directors
125 * show <resource-keyword-name>=<name> e.g. show director=HeadMan
128 int show_cmd(UAContext *ua, const char *cmd)
135 Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
139 for (i=1; i<ua->argc; i++) {
141 res_name = ua->argk[i];
142 if (!ua->argv[i]) { /* was a name given? */
143 /* No name, dump all resources of specified type */
145 len = strlen(res_name);
146 for (j=0; reses[j].res_name; j++) {
147 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
148 type = reses[j].type;
150 res = res_head[type-r_first];
159 /* Dump a single resource with specified name */
161 len = strlen(res_name);
162 for (j=0; reses[j].res_name; j++) {
163 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
164 type = reses[j].type;
165 res = (RES *)GetResWithName(type, ua->argv[i]);
176 for (j=r_first; j<=r_last; j++) {
177 dump_resource(j, res_head[j-r_first], bsendmsg, ua);
181 ua->send_msg(_("Keywords for the show command are:\n"));
182 for (j=0; reses[j].res_name; j++) {
183 ua->error_msg("%s\n", _(reses[j].res_name));
187 ua->error_msg(_("%s resource %s not found.\n"), res_name, ua->argv[i]);
190 ua->error_msg(_("Resource %s not found\n"), res_name);
193 dump_resource(recurse?type:-type, res, bsendmsg, ua);
206 * List contents of database
208 * list jobs - lists all jobs run
209 * list jobid=nnn - list job data for jobid
210 * list ujobid=uname - list job data for unique jobid
211 * list job=name - list all jobs with "name"
212 * list jobname=name - same as above
213 * list jobmedia jobid=<nn>
214 * list jobmedia job=name
215 * list files jobid=<nn> - list files saved for job nn
216 * list files job=name
217 * list pools - list pool records
218 * list jobtotals - list totals for all jobs
219 * list media - list media for given pool (deprecated)
220 * list volumes - list Volumes
221 * list clients - list clients
222 * list nextvol job=xx - list the next vol to be used by job
223 * list nextvolume job=xx - same as above.
227 /* Do long or full listing */
228 int llist_cmd(UAContext *ua, const char *cmd)
230 return do_list_cmd(ua, cmd, VERT_LIST);
233 /* Do short or summary listing */
234 int list_cmd(UAContext *ua, const char *cmd)
236 return do_list_cmd(ua, cmd, HORZ_LIST);
239 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
248 if (!open_client_db(ua))
251 memset(&jr, 0, sizeof(jr));
252 memset(&pr, 0, sizeof(pr));
253 memset(&mr, 0, sizeof(mr));
255 Dmsg1(20, "list: %s\n", cmd);
258 ua->error_msg(_("Hey! DB is NULL\n"));
261 /* Scan arguments looking for things to do */
262 for (i=1; i<ua->argc; i++) {
264 if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
265 /* Apply any limit */
266 j = find_arg_with_value(ua, NT_("limit"));
268 jr.limit = atoi(ua->argv[j]);
270 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
273 } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
274 db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
277 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
279 jobid = str_to_int64(ua->argv[i]);
282 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
287 } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
288 strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
289 bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
291 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
293 /* List UJOBID=xxx */
294 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
295 bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
297 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
300 } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
302 for (j=i+1; j<ua->argc; j++) {
303 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
304 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
306 db_get_job_record(ua->jcr, ua->db, &jr);
308 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
309 jobid = str_to_int64(ua->argv[j]);
314 db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
319 } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
321 for (j=i+1; j<ua->argc; j++) {
322 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
323 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
325 db_get_job_record(ua->jcr, ua->db, &jr);
327 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
328 jobid = str_to_int64(ua->argv[j]);
332 db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
336 /* List for all jobs (jobid=0) */
337 db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
341 } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
342 strcasecmp(ua->argk[i], NT_("pools")) == 0) {
344 memset(&pr, 0, sizeof(pr));
346 bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
348 db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
350 } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
351 db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
354 /* List MEDIA or VOLUMES */
355 } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
356 strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
357 strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
359 for (j=i+1; j<ua->argc; j++) {
360 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
361 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
363 db_get_job_record(ua->jcr, ua->db, &jr);
365 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
366 jobid = str_to_int64(ua->argv[j]);
370 VolumeName = get_pool_memory(PM_FNAME);
371 n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
372 ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
373 free_pool_memory(VolumeName);
376 /* if no job or jobid keyword found, then we list all media */
380 /* List a specific volume? */
382 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
383 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
386 /* Is a specific pool wanted? */
387 for (i=1; i<ua->argc; i++) {
388 if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
389 if (!get_pool_dbr(ua, &pr)) {
390 ua->error_msg(_("No Pool specified.\n"));
393 mr.PoolId = pr.PoolId;
394 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
399 /* List Volumes in all pools */
400 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
401 ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"),
402 db_strerror(ua->db));
405 if (num_pools <= 0) {
408 for (i=0; i < num_pools; i++) {
410 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
411 ua->send_msg(_("Pool: %s\n"), pr.Name);
414 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
419 /* List next volume */
420 } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
421 strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
423 j = find_arg_with_value(ua, NT_("days"));
425 n = atoi(ua->argv[j]);
426 if ((n < 0) || (n > 50)) {
427 ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n"));
432 } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
433 || strcasecmp(ua->argk[i], NT_("days")) == 0) {
436 ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
442 static bool list_nextvol(UAContext *ua, int ndays)
453 memset(&mr, 0, sizeof(mr));
454 int i = find_arg_with_value(ua, "job");
456 if ((job = select_job_resource(ua)) == NULL) {
460 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
462 Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
463 if ((job = select_job_resource(ua)) == NULL) {
469 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
470 for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
471 if (!complete_jcr_for_job(jcr, job, run->pool)) {
475 if (!jcr->jr.PoolId) {
476 ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
479 memset(&pr, 0, sizeof(pr));
480 pr.PoolId = jcr->jr.PoolId;
481 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
482 bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
484 mr.PoolId = jcr->jr.PoolId;
485 get_job_storage(&store, job, run);
486 mr.StorageId = store.store->StorageId;
487 if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
488 ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
489 job->name(), pr.Name, level_to_str(run->level));
492 _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
493 job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
499 db_close_database(jcr, jcr->db);
503 ua->error_msg(_("Could not find next Volume for Job %s.\n"),
512 * For a given job, we examine all his run records
513 * to see if it is scheduled today or tomorrow.
515 RUN *find_next_run(RUN *run, JOB *job, time_t &runtime, int ndays)
517 time_t now, future, endtime;
520 int mday, wday, month, wom, i;
525 sched = job->schedule;
526 if (sched == NULL) { /* scheduled? */
527 return NULL; /* no nothing to report */
530 /* Break down the time into components */
532 endtime = now + (ndays * 60 * 60 * 24);
539 for ( ; run; run=run->next) {
541 * Find runs in next 24 hours. Day 0 is today, so if
542 * ndays=1, look at today and tomorrow.
544 for (day = 0; day <= ndays; day++) {
545 future = now + (day * 60 * 60 * 24);
547 /* Break down the time into components */
548 (void)localtime_r(&future, &tm);
549 mday = tm.tm_mday - 1;
553 woy = tm_woy(future);
555 is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
556 bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
557 bit_is_set(woy, run->woy);
560 Pmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
561 Pmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
562 Pmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
563 Pmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
564 Pmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
565 Pmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
568 if (is_scheduled) { /* Jobs scheduled on that day */
570 char buf[300], num[10];
571 bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
572 for (i=0; i<24; i++) {
573 if (bit_is_set(i, run->hour)) {
574 bsnprintf(num, sizeof(num), "%d ", i);
575 bstrncat(buf, num, sizeof(buf));
578 bstrncat(buf, "\n", sizeof(buf));
579 Pmsg1(000, "%s", buf);
581 /* find time (time_t) job is to be run */
582 (void)localtime_r(&future, &runtm);
583 for (i= 0; i < 24; i++) {
584 if (bit_is_set(i, run->hour)) {
586 runtm.tm_min = run->minute;
588 runtime = mktime(&runtm);
589 Dmsg2(200, "now=%d runtime=%d\n", now, runtime);
590 if ((runtime > now) && (runtime < endtime)) {
591 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
592 return run; /* found it, return run resource */
598 } /* end for loop over runs */
604 * Fill in the remaining fields of the jcr as if it
605 * is going to run the job.
607 bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
611 memset(&pr, 0, sizeof(POOL_DBR));
612 set_jcr_defaults(jcr, job);
614 jcr->pool = pool; /* override */
617 Dmsg0(100, "complete_jcr close db\n");
618 db_close_database(jcr, jcr->db);
622 Dmsg0(100, "complete_jcr open db\n");
623 jcr->db = jcr->db=db_init(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
624 jcr->catalog->db_user,
625 jcr->catalog->db_password, jcr->catalog->db_address,
626 jcr->catalog->db_port, jcr->catalog->db_socket,
627 jcr->catalog->mult_db_connections);
628 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
629 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
630 jcr->catalog->db_name);
632 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
633 db_close_database(jcr, jcr->db);
638 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
639 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
640 /* Try to create the pool */
641 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
642 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
643 db_strerror(jcr->db));
645 db_close_database(jcr, jcr->db);
650 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
653 jcr->jr.PoolId = pr.PoolId;
658 static void con_lock_release(void *arg)
663 void do_messages(UAContext *ua, const char *cmd)
667 bool do_truncate = false;
670 pthread_cleanup_push(con_lock_release, (void *)NULL);
672 while (fgets(msg, sizeof(msg), con_fd)) {
674 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
675 strcpy(ua->UA_sock->msg, msg);
676 ua->UA_sock->msglen = mlen;
681 (void)ftruncate(fileno(con_fd), 0L);
683 console_msg_pending = FALSE;
684 ua->user_notified_msg_pending = FALSE;
685 pthread_cleanup_pop(0);
690 int qmessagescmd(UAContext *ua, const char *cmd)
692 if (console_msg_pending && ua->auto_display_messages) {
693 do_messages(ua, cmd);
698 int messagescmd(UAContext *ua, const char *cmd)
700 if (console_msg_pending) {
701 do_messages(ua, cmd);
703 ua->UA_sock->fsend(_("You have no messages.\n"));
709 * Callback routine for "printing" database file listing
711 void prtit(void *ctx, const char *msg)
713 UAContext *ua = (UAContext *)ctx;
715 ua->UA_sock->fsend("%s", msg);
719 * Format message and send to other end.
721 * If the UA_sock is NULL, it means that there is no user
722 * agent, so we are being called from Bacula core. In
723 * that case direct the messages to the Job.
726 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
728 BSOCK *bs = ua->UA_sock;
737 msg = get_pool_memory(PM_EMSG);
741 maxlen = sizeof_pool_memory(msg) - 1;
742 va_copy(ap, arg_ptr);
743 len = bvsnprintf(msg, maxlen, fmt, ap);
745 if (len < 0 || len >= maxlen) {
746 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
754 } else { /* No UA, send to Job */
755 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
756 free_pool_memory(msg);
761 #else /* no va_copy() -- brain damaged version of variable arguments */
763 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
765 BSOCK *bs = ua->UA_sock;
773 msg = get_memory(5000);
776 maxlen = sizeof_pool_memory(msg) - 1;
778 msg = realloc_pool_memory(msg, 5000);
781 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
782 if (len < 0 || len >= maxlen) {
783 pm_strcpy(msg, _("Message too long to display.\n"));
791 } else { /* No UA, send to Job */
792 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
793 free_pool_memory(msg);
799 void bsendmsg(void *ctx, const char *fmt, ...)
802 va_start(arg_ptr, fmt);
803 bmsg((UAContext *)ctx, fmt, arg_ptr);
808 * The following UA methods are mainly intended for GUI
812 * This is a message that should be displayed on the user's
815 void UAContext::send_msg(const char *fmt, ...)
818 va_start(arg_ptr, fmt);
819 bmsg(this, fmt, arg_ptr);
825 * This is an error condition with a command. The gui should put
826 * up an error or critical dialog box. The command is aborted.
828 void UAContext::error_msg(const char *fmt, ...)
833 if (bs && api) bs->signal(BNET_ERROR_MSG);
834 va_start(arg_ptr, fmt);
835 bmsg(this, fmt, arg_ptr);
840 * This is a warning message, that should bring up a warning
841 * dialog box on the GUI. The command is not aborted, but something
844 void UAContext::warning_msg(const char *fmt, ...)
849 if (bs && api) bs->signal(BNET_WARNING_MSG);
850 va_start(arg_ptr, fmt);
851 bmsg(this, fmt, arg_ptr);
856 * This is an information message that should probably be put
857 * into the status line of a GUI program.
859 void UAContext::info_msg(const char *fmt, ...)
864 if (bs && api) bs->signal(BNET_INFO_MSG);
865 va_start(arg_ptr, fmt);
866 bmsg(this, fmt, arg_ptr);