2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Bacula Director -- User Agent Output Commands
23 * I.e. messages, listing database, showing resources, ...
25 * Kern Sibbald, September MM
31 /* Imported subroutines */
33 /* Imported variables */
35 /* Imported functions */
37 /* Forward referenced functions */
38 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist);
39 static bool list_nextvol(UAContext *ua, int ndays);
42 * Turn auto display of console messages on/off
44 int autodisplay_cmd(UAContext *ua, const char *cmd)
46 static const char *kw[] = {
51 switch (find_arg_keyword(ua, kw)) {
53 ua->auto_display_messages = true;
56 ua->auto_display_messages = false;
59 ua->error_msg(_("ON or OFF keyword missing.\n"));
66 * Turn GUI mode on/off
68 int gui_cmd(UAContext *ua, const char *cmd)
70 static const char *kw[] = {
75 switch (find_arg_keyword(ua, kw)) {
77 ua->jcr->gui = ua->gui = true;
80 ua->jcr->gui = ua->gui = false;
83 ua->error_msg(_("ON or OFF keyword missing.\n"));
90 * Enter with Resources locked
92 static void show_disabled_jobs(UAContext *ua)
96 foreach_res(job, R_JOB) {
97 if (!acl_access_ok(ua, Job_ACL, job->name())) {
103 ua->send_msg(_("Disabled Jobs:\n"));
105 ua->send_msg(" %s\n", job->name());
109 ua->send_msg(_("No disabled Jobs.\n"));
113 struct showstruct {const char *res_name; int type;};
114 static struct showstruct reses[] = {
115 {NT_("directors"), R_DIRECTOR},
116 {NT_("clients"), R_CLIENT},
117 {NT_("counters"), R_COUNTER},
118 {NT_("devices"), R_DEVICE},
119 {NT_("jobs"), R_JOB},
120 {NT_("storages"), R_STORAGE},
121 {NT_("catalogs"), R_CATALOG},
122 {NT_("schedules"), R_SCHEDULE},
123 {NT_("filesets"), R_FILESET},
124 {NT_("pools"), R_POOL},
125 {NT_("messages"), R_MSGS},
136 * show <resource-keyword-name> e.g. show directors
137 * show <resource-keyword-name>=<name> e.g. show director=HeadMan
138 * show disabled shows disabled jobs
141 int show_cmd(UAContext *ua, const char *cmd)
148 Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
152 for (i=1; i<ua->argc; i++) {
153 if (strcasecmp(ua->argk[i], NT_("disabled")) == 0) {
154 show_disabled_jobs(ua);
160 res_name = ua->argk[i];
161 if (!ua->argv[i]) { /* was a name given? */
162 /* No name, dump all resources of specified type */
164 len = strlen(res_name);
165 for (j=0; reses[j].res_name; j++) {
166 if (strncasecmp(res_name, reses[j].res_name, len) == 0) {
167 type = reses[j].type;
169 res = res_head[type-r_first];
178 /* Dump a single resource with specified name */
180 len = strlen(res_name);
181 for (j=0; reses[j].res_name; j++) {
182 if (strncasecmp(res_name, reses[j].res_name, len) == 0) {
183 type = reses[j].type;
184 res = (RES *)GetResWithName(type, ua->argv[i]);
196 for (j=r_first; j<=r_last; j++) {
197 /* Skip R_DEVICE since it is really not used or updated */
199 dump_resource(j, res_head[j-r_first], bsendmsg, ua);
205 ua->send_msg(_("Keywords for the show command are:\n"));
206 for (j=0; reses[j].res_name; j++) {
207 ua->error_msg("%s\n", reses[j].res_name);
210 /* Resource not found */
212 ua->error_msg(_("%s resource %s not found.\n"), res_name, ua->argv[i]);
214 /* Resource not found */
216 ua->error_msg(_("Resource %s not found\n"), res_name);
218 /* Dump a specific type */
220 dump_resource(recurse?type:-type, res, bsendmsg, ua);
230 * Check if the access is permitted for a list of jobids
232 * Not in ua_acl.c because it's using db access, and tools such
233 * as bdirjson are not linked with cats.
235 bool acl_access_jobid_ok(UAContext *ua, const char *jobids)
246 if (!is_a_number_list(jobids)) {
250 /* If no console resource => default console and all is permitted */
251 if (!ua || !ua->cons) {
252 Dmsg0(1400, "Root cons access OK.\n");
253 return true; /* No cons resource -> root console OK for everything */
256 alist *list = ua->cons->ACL_lists[Job_ACL];
257 if (!list) { /* empty list */
258 return false; /* List empty, reject everything */
261 /* Special case *all* gives full access */
262 if (list->size() == 1 && strcasecmp("*all*", (char *)list->get(0)) == 0) {
266 /* If we can't open the database, just say no */
267 if (!open_new_client_db(ua)) {
271 p = tmp = bstrdup(jobids);
273 while (get_next_jobid_from_list(&p, &jid) > 0) {
274 memset(&jr, 0, sizeof(jr));
277 if (db_get_job_record(ua->jcr, ua->db, &jr)) {
278 for (int i=0; i<list->size(); i++) {
279 if (strcasecmp(jr.Name, (char *)list->get(i)) == 0) {
280 Dmsg3(1400, "ACL found %s in %d %s\n", jr.Name,
281 Job_ACL, (char *)list->get(i));
297 * List contents of database
299 * list jobs - lists all jobs run
300 * list jobid=nnn - list job data for jobid
301 * list ujobid=uname - list job data for unique jobid
302 * list job=name - list all jobs with "name"
303 * list jobname=name - same as above
304 * list jobmedia jobid=<nn>
305 * list jobmedia job=name
306 * list joblog jobid=<nn>
307 * list joblog job=name
308 * list files jobid=<nn> - list files saved for job nn
309 * list files job=name
310 * list pools - list pool records
311 * list jobtotals - list totals for all jobs
312 * list media - list media for given pool (deprecated)
313 * list volumes - list Volumes
314 * list clients - list clients
315 * list nextvol job=xx - list the next vol to be used by job
316 * list nextvolume job=xx - same as above.
317 * list copies jobid=x,y,z
321 /* Do long or full listing */
322 int llist_cmd(UAContext *ua, const char *cmd)
324 return do_list_cmd(ua, cmd, VERT_LIST);
327 /* Do short or summary listing */
328 int list_cmd(UAContext *ua, const char *cmd)
330 return do_list_cmd(ua, cmd, HORZ_LIST);
333 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
342 if (!open_new_client_db(ua))
345 memset(&jr, 0, sizeof(jr));
346 memset(&pr, 0, sizeof(pr));
348 Dmsg1(20, "list: %s\n", cmd);
351 ua->error_msg(_("Hey! DB is NULL\n"));
354 /* Apply any limit */
355 j = find_arg_with_value(ua, NT_("limit"));
357 jr.limit = atoi(ua->argv[j]);
360 /* Scan arguments looking for things to do */
361 for (i=1; i<ua->argc; i++) {
363 if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
364 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
367 } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
368 db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
371 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
373 jobid = str_to_int64(ua->argv[i]);
376 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
381 } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
382 strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
383 bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
385 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
387 /* List UJOBID=xxx */
388 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
389 bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
391 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
393 /* List Base files */
394 } else if (strcasecmp(ua->argk[i], NT_("basefiles")) == 0) {
395 /* TODO: cleanup this block */
396 for (j=i+1; j<ua->argc; j++) {
397 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
398 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
400 db_get_job_record(ua->jcr, ua->db, &jr);
402 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
403 jobid = str_to_int64(ua->argv[j]);
408 db_list_base_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
413 } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
415 for (j=i+1; j<ua->argc; j++) {
416 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
417 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
419 db_get_job_record(ua->jcr, ua->db, &jr);
421 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
422 jobid = str_to_int64(ua->argv[j]);
427 db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
432 } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
434 for (j=i+1; j<ua->argc; j++) {
435 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
436 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
438 db_get_job_record(ua->jcr, ua->db, &jr);
440 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
441 jobid = str_to_int64(ua->argv[j]);
445 db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
449 /* List for all jobs (jobid=0) */
450 db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
454 } else if (strcasecmp(ua->argk[i], NT_("joblog")) == 0) {
456 for (j=i+1; j<ua->argc; j++) {
457 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
458 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
460 db_get_job_record(ua->jcr, ua->db, &jr);
462 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
463 jobid = str_to_int64(ua->argv[j]);
467 db_list_joblog_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
471 /* List for all jobs (jobid=0) */
472 db_list_joblog_records(ua->jcr, ua->db, 0, prtit, ua, llist);
477 } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
478 strcasecmp(ua->argk[i], NT_("pools")) == 0) {
480 memset(&pr, 0, sizeof(pr));
482 bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
484 db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
486 } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
487 db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
489 /* List MEDIA or VOLUMES */
490 } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
491 strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
492 strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
494 for (j=i+1; j<ua->argc; j++) {
495 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
496 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
498 db_get_job_record(ua->jcr, ua->db, &jr);
500 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
501 jobid = str_to_int64(ua->argv[j]);
505 VolumeName = get_pool_memory(PM_FNAME);
506 n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
507 ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
508 free_pool_memory(VolumeName);
512 /* if no job or jobid keyword found, then we list all media */
516 /* List a specific volume? */
517 if (ua->argv[i] && *ua->argv[i]) {
518 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
519 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
522 /* Is a specific pool wanted? */
523 for (i=1; i<ua->argc; i++) {
524 if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
525 if (!get_pool_dbr(ua, &pr)) {
526 ua->error_msg(_("No Pool specified.\n"));
529 mr.PoolId = pr.PoolId;
530 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
535 /* List Volumes in all pools */
536 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
537 ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"),
538 db_strerror(ua->db));
541 if (num_pools <= 0) {
544 for (i=0; i < num_pools; i++) {
546 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
547 ua->send_msg(_("Pool: %s\n"), pr.Name);
550 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
555 /* List next volume */
556 } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
557 strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
559 j = find_arg_with_value(ua, NT_("days"));
561 n = atoi(ua->argv[j]);
562 if ((n < 0) || (n > 50)) {
563 ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n"));
568 } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
571 for (j=i+1; j<ua->argc; j++) {
572 if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
573 if (is_a_number_list(ua->argv[j])) {
574 jobids = ua->argv[j];
576 } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
577 limit = atoi(ua->argv[j]);
580 db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
581 } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
582 || strcasecmp(ua->argk[i], NT_("days")) == 0) {
584 } else if (strcasecmp(ua->argk[i], NT_("snapshot")) == 0 ||
585 strcasecmp(ua->argk[i], NT_("snapshots")) == 0)
587 snapshot_list(ua, i, prtit, llist);
591 ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
597 static bool list_nextvol(UAContext *ua, int ndays)
608 int i = find_arg_with_value(ua, "job");
610 if ((job = select_job_resource(ua)) == NULL) {
614 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
616 Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
617 if ((job = select_job_resource(ua)) == NULL) {
623 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
624 for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
625 if (!complete_jcr_for_job(jcr, job, run->pool)) {
629 if (!jcr->jr.PoolId) {
630 ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
633 memset(&pr, 0, sizeof(pr));
634 pr.PoolId = jcr->jr.PoolId;
635 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
636 bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
638 mr.PoolId = jcr->jr.PoolId;
639 get_job_storage(&store, job, run);
640 set_storageid_in_mr(store.store, &mr);
641 /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
642 if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
643 ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
644 job->name(), pr.Name, level_to_str(run->level));
647 _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
648 job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
654 if (jcr->db) db_close_database(jcr, jcr->db);
658 ua->error_msg(_("Could not find next Volume for Job %s.\n"),
667 * For a given job, we examine all his run records
668 * to see if it is scheduled today or tomorrow.
670 RUN *find_next_run(RUN *run, JOB *job, utime_t &runtime, int ndays)
672 time_t now, future, endtime;
675 int mday, wday, month, wom, i;
680 sched = job->schedule;
681 if (!sched || !job->enabled || (sched && !sched->enabled) ||
682 (job->client && !job->client->enabled)) {
683 return NULL; /* no nothing to report */
686 /* Break down the time into components */
688 endtime = now + (ndays * 60 * 60 * 24);
695 for ( ; run; run=run->next) {
697 * Find runs in next 24 hours. Day 0 is today, so if
698 * ndays=1, look at today and tomorrow.
700 for (day = 0; day <= ndays; day++) {
701 future = now + (day * 60 * 60 * 24);
703 /* Break down the time into components */
704 (void)localtime_r(&future, &tm);
705 mday = tm.tm_mday - 1;
709 woy = tm_woy(future);
710 ldom = tm_ldom(month, tm.tm_year + 1900);
712 is_scheduled = (bit_is_set(mday, run->mday) &&
713 bit_is_set(wday, run->wday) &&
714 bit_is_set(month, run->month) &&
715 bit_is_set(wom, run->wom) &&
716 bit_is_set(woy, run->woy)) ||
717 (bit_is_set(month, run->month) &&
718 bit_is_set(31, run->mday) && mday == ldom);
721 Pmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
722 Pmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
723 Pmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
724 Pmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
725 Pmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
726 Pmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
729 if (is_scheduled) { /* Jobs scheduled on that day */
731 char buf[300], num[10];
732 bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
733 for (i=0; i<24; i++) {
734 if (bit_is_set(i, run->hour)) {
735 bsnprintf(num, sizeof(num), "%d ", i);
736 bstrncat(buf, num, sizeof(buf));
739 bstrncat(buf, "\n", sizeof(buf));
740 Pmsg1(000, "%s", buf);
742 /* find time (time_t) job is to be run */
743 (void)localtime_r(&future, &runtm);
744 for (i= 0; i < 24; i++) {
745 if (bit_is_set(i, run->hour)) {
747 runtm.tm_min = run->minute;
749 runtime = mktime(&runtm);
750 Dmsg2(200, "now=%d runtime=%lld\n", now, runtime);
751 if ((runtime > now) && (runtime < endtime)) {
752 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
753 return run; /* found it, return run resource */
759 } /* end for loop over runs */
765 * Fill in the remaining fields of the jcr as if it
766 * is going to run the job.
768 bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
772 memset(&pr, 0, sizeof(POOL_DBR));
773 set_jcr_defaults(jcr, job);
775 jcr->pool = pool; /* override */
778 Dmsg0(100, "complete_jcr close db\n");
779 db_close_database(jcr, jcr->db);
783 Dmsg0(100, "complete_jcr open db\n");
784 jcr->db = db_init_database(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
785 jcr->catalog->db_user,
786 jcr->catalog->db_password, jcr->catalog->db_address,
787 jcr->catalog->db_port, jcr->catalog->db_socket,
788 jcr->catalog->mult_db_connections,
789 jcr->catalog->disable_batch_insert);
790 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
791 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
792 jcr->catalog->db_name);
794 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
795 db_close_database(jcr, jcr->db);
800 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
801 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
802 /* Try to create the pool */
803 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
804 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
805 db_strerror(jcr->db));
807 db_close_database(jcr, jcr->db);
812 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
815 jcr->jr.PoolId = pr.PoolId;
820 static void con_lock_release(void *arg)
825 void do_messages(UAContext *ua, const char *cmd)
829 bool do_truncate = false;
832 dequeue_messages(ua->jcr);
835 pthread_cleanup_push(con_lock_release, (void *)NULL);
837 while (fgets(msg, sizeof(msg), con_fd)) {
839 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
840 strcpy(ua->UA_sock->msg, msg);
841 ua->UA_sock->msglen = mlen;
846 (void)ftruncate(fileno(con_fd), 0L);
848 console_msg_pending = FALSE;
849 ua->user_notified_msg_pending = FALSE;
850 pthread_cleanup_pop(0);
855 int qmessagescmd(UAContext *ua, const char *cmd)
857 if (console_msg_pending && ua->auto_display_messages) {
858 do_messages(ua, cmd);
863 int messagescmd(UAContext *ua, const char *cmd)
865 if (console_msg_pending) {
866 do_messages(ua, cmd);
868 ua->UA_sock->fsend(_("You have no messages.\n"));
874 * Callback routine for "printing" database file listing
876 void prtit(void *ctx, const char *msg)
878 UAContext *ua = (UAContext *)ctx;
880 if (ua) ua->send_msg("%s", msg);
884 * Format message and send to other end.
886 * If the UA_sock is NULL, it means that there is no user
887 * agent, so we are being called from Bacula core. In
888 * that case direct the messages to the Job.
891 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
893 BSOCK *bs = ua->UA_sock;
902 msg = get_pool_memory(PM_EMSG);
906 maxlen = sizeof_pool_memory(msg) - 1;
907 va_copy(ap, arg_ptr);
908 len = bvsnprintf(msg, maxlen, fmt, ap);
910 if (len < 0 || len >= maxlen) {
911 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
919 } else { /* No UA, send to Job */
920 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
921 free_pool_memory(msg);
926 #else /* no va_copy() -- brain damaged version of variable arguments */
928 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
930 BSOCK *bs = ua->UA_sock;
938 msg = get_memory(5000);
941 maxlen = sizeof_pool_memory(msg) - 1;
943 msg = realloc_pool_memory(msg, 5000);
946 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
947 if (len < 0 || len >= maxlen) {
948 pm_strcpy(msg, _("Message too long to display.\n"));
956 } else { /* No UA, send to Job */
957 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
958 free_pool_memory(msg);
964 void bsendmsg(void *ctx, const char *fmt, ...)
967 va_start(arg_ptr, fmt);
968 bmsg((UAContext *)ctx, fmt, arg_ptr);
973 * The following UA methods are mainly intended for GUI
977 * This is a message that should be displayed on the user's
980 void UAContext::send_msg(const char *fmt, ...)
983 va_start(arg_ptr, fmt);
984 bmsg(this, fmt, arg_ptr);
990 * This is an error condition with a command. The gui should put
991 * up an error or critical dialog box. The command is aborted.
993 void UAContext::error_msg(const char *fmt, ...)
998 if (bs && api) bs->signal(BNET_ERROR_MSG);
999 va_start(arg_ptr, fmt);
1000 bmsg(this, fmt, arg_ptr);
1005 * This is a warning message, that should bring up a warning
1006 * dialog box on the GUI. The command is not aborted, but something
1009 void UAContext::warning_msg(const char *fmt, ...)
1011 BSOCK *bs = UA_sock;
1014 if (bs && api) bs->signal(BNET_WARNING_MSG);
1015 va_start(arg_ptr, fmt);
1016 bmsg(this, fmt, arg_ptr);
1021 * This is an information message that should probably be put
1022 * into the status line of a GUI program.
1024 void UAContext::info_msg(const char *fmt, ...)
1026 BSOCK *bs = UA_sock;
1029 if (bs && api) bs->signal(BNET_INFO_MSG);
1030 va_start(arg_ptr, fmt);
1031 bmsg(this, fmt, arg_ptr);