2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2012 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 three of the GNU Affero 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 Affero 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"));
100 * Enter with Resources locked
102 static void show_disabled_jobs(UAContext *ua)
106 foreach_res(job, R_JOB) {
107 if (!acl_access_ok(ua, Job_ACL, job->name())) {
113 ua->send_msg(_("Disabled Jobs:\n"));
115 ua->send_msg(" %s\n", job->name());
119 ua->send_msg(_("No disabled Jobs.\n"));
123 struct showstruct {const char *res_name; int type;};
124 static struct showstruct reses[] = {
125 {NT_("directors"), R_DIRECTOR},
126 {NT_("clients"), R_CLIENT},
127 {NT_("counters"), R_COUNTER},
128 {NT_("devices"), R_DEVICE},
129 {NT_("jobs"), R_JOB},
130 {NT_("storages"), R_STORAGE},
131 {NT_("catalogs"), R_CATALOG},
132 {NT_("schedules"), R_SCHEDULE},
133 {NT_("filesets"), R_FILESET},
134 {NT_("pools"), R_POOL},
135 {NT_("messages"), R_MSGS},
146 * show <resource-keyword-name> e.g. show directors
147 * show <resource-keyword-name>=<name> e.g. show director=HeadMan
148 * show disabled shows disabled jobs
151 int show_cmd(UAContext *ua, const char *cmd)
158 Dmsg1(20, "show: %s\n", ua->UA_sock->msg);
162 for (i=1; i<ua->argc; i++) {
163 if (strcasecmp(ua->argk[i], _("disabled")) == 0) {
164 show_disabled_jobs(ua);
168 res_name = ua->argk[i];
169 if (!ua->argv[i]) { /* was a name given? */
170 /* No name, dump all resources of specified type */
172 len = strlen(res_name);
173 for (j=0; reses[j].res_name; j++) {
174 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
175 type = reses[j].type;
177 res = res_head[type-r_first];
186 /* Dump a single resource with specified name */
188 len = strlen(res_name);
189 for (j=0; reses[j].res_name; j++) {
190 if (strncasecmp(res_name, _(reses[j].res_name), len) == 0) {
191 type = reses[j].type;
192 res = (RES *)GetResWithName(type, ua->argv[i]);
203 for (j=r_first; j<=r_last; j++) {
204 dump_resource(j, res_head[j-r_first], bsendmsg, ua);
208 ua->send_msg(_("Keywords for the show command are:\n"));
209 for (j=0; reses[j].res_name; j++) {
210 ua->error_msg("%s\n", _(reses[j].res_name));
214 ua->error_msg(_("%s resource %s not found.\n"), res_name, ua->argv[i]);
217 ua->error_msg(_("Resource %s not found\n"), res_name);
220 dump_resource(recurse?type:-type, res, bsendmsg, ua);
233 * List contents of database
235 * list jobs - lists all jobs run
236 * list jobid=nnn - list job data for jobid
237 * list ujobid=uname - list job data for unique jobid
238 * list job=name - list all jobs with "name"
239 * list jobname=name - same as above
240 * list jobmedia jobid=<nn>
241 * list jobmedia job=name
242 * list joblog jobid=<nn>
243 * list joblog job=name
244 * list files jobid=<nn> - list files saved for job nn
245 * list files job=name
246 * list pools - list pool records
247 * list jobtotals - list totals for all jobs
248 * list media - list media for given pool (deprecated)
249 * list volumes - list Volumes
250 * list clients - list clients
251 * list nextvol job=xx - list the next vol to be used by job
252 * list nextvolume job=xx - same as above.
253 * list copies jobid=x,y,z
257 /* Do long or full listing */
258 int llist_cmd(UAContext *ua, const char *cmd)
260 return do_list_cmd(ua, cmd, VERT_LIST);
263 /* Do short or summary listing */
264 int list_cmd(UAContext *ua, const char *cmd)
266 return do_list_cmd(ua, cmd, HORZ_LIST);
269 static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
278 if (!open_client_db(ua))
281 memset(&jr, 0, sizeof(jr));
282 memset(&pr, 0, sizeof(pr));
284 Dmsg1(20, "list: %s\n", cmd);
287 ua->error_msg(_("Hey! DB is NULL\n"));
290 /* Apply any limit */
291 j = find_arg_with_value(ua, NT_("limit"));
293 jr.limit = atoi(ua->argv[j]);
296 /* Scan arguments looking for things to do */
297 for (i=1; i<ua->argc; i++) {
299 if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
300 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
303 } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
304 db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
307 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
309 jobid = str_to_int64(ua->argv[i]);
312 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
317 } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
318 strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
319 bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
321 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
323 /* List UJOBID=xxx */
324 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
325 bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
327 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
329 /* List Base files */
330 } else if (strcasecmp(ua->argk[i], NT_("basefiles")) == 0) {
331 /* TODO: cleanup this block */
332 for (j=i+1; j<ua->argc; j++) {
333 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
334 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
336 db_get_job_record(ua->jcr, ua->db, &jr);
338 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
339 jobid = str_to_int64(ua->argv[j]);
344 db_list_base_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
349 } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
351 for (j=i+1; j<ua->argc; j++) {
352 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
353 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
355 db_get_job_record(ua->jcr, ua->db, &jr);
357 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
358 jobid = str_to_int64(ua->argv[j]);
363 db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
368 } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
370 for (j=i+1; j<ua->argc; j++) {
371 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
372 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
374 db_get_job_record(ua->jcr, ua->db, &jr);
376 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
377 jobid = str_to_int64(ua->argv[j]);
381 db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
385 /* List for all jobs (jobid=0) */
386 db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
390 } else if (strcasecmp(ua->argk[i], NT_("joblog")) == 0) {
392 for (j=i+1; j<ua->argc; j++) {
393 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
394 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
396 db_get_job_record(ua->jcr, ua->db, &jr);
398 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
399 jobid = str_to_int64(ua->argv[j]);
403 db_list_joblog_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
407 /* List for all jobs (jobid=0) */
408 db_list_joblog_records(ua->jcr, ua->db, 0, prtit, ua, llist);
413 } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
414 strcasecmp(ua->argk[i], NT_("pools")) == 0) {
416 memset(&pr, 0, sizeof(pr));
418 bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
420 db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
422 } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
423 db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
426 /* List MEDIA or VOLUMES */
427 } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
428 strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
429 strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
431 for (j=i+1; j<ua->argc; j++) {
432 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
433 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
435 db_get_job_record(ua->jcr, ua->db, &jr);
437 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
438 jobid = str_to_int64(ua->argv[j]);
442 VolumeName = get_pool_memory(PM_FNAME);
443 n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
444 ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
445 free_pool_memory(VolumeName);
448 /* if no job or jobid keyword found, then we list all media */
452 /* List a specific volume? */
454 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
455 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
458 /* Is a specific pool wanted? */
459 for (i=1; i<ua->argc; i++) {
460 if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
461 if (!get_pool_dbr(ua, &pr)) {
462 ua->error_msg(_("No Pool specified.\n"));
465 mr.PoolId = pr.PoolId;
466 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
471 /* List Volumes in all pools */
472 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
473 ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"),
474 db_strerror(ua->db));
477 if (num_pools <= 0) {
480 for (i=0; i < num_pools; i++) {
482 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
483 ua->send_msg(_("Pool: %s\n"), pr.Name);
486 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
491 /* List next volume */
492 } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
493 strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
495 j = find_arg_with_value(ua, NT_("days"));
497 n = atoi(ua->argv[j]);
498 if ((n < 0) || (n > 50)) {
499 ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n"));
504 } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
507 for (j=i+1; j<ua->argc; j++) {
508 if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
509 if (is_a_number_list(ua->argv[j])) {
510 jobids = ua->argv[j];
512 } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
513 limit = atoi(ua->argv[j]);
516 db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
517 } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
518 || strcasecmp(ua->argk[i], NT_("days")) == 0) {
521 ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
527 static bool list_nextvol(UAContext *ua, int ndays)
538 int i = find_arg_with_value(ua, "job");
540 if ((job = select_job_resource(ua)) == NULL) {
544 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
546 Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
547 if ((job = select_job_resource(ua)) == NULL) {
553 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
554 for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
555 if (!complete_jcr_for_job(jcr, job, run->pool)) {
559 if (!jcr->jr.PoolId) {
560 ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
563 memset(&pr, 0, sizeof(pr));
564 pr.PoolId = jcr->jr.PoolId;
565 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
566 bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
568 mr.PoolId = jcr->jr.PoolId;
569 get_job_storage(&store, job, run);
570 set_storageid_in_mr(store.store, &mr);
571 /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
572 if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
573 ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
574 job->name(), pr.Name, level_to_str(run->level));
577 _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
578 job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
585 db_close_database(jcr, jcr->db);
590 ua->error_msg(_("Could not find next Volume for Job %s.\n"),
599 * For a given job, we examine all his run records
600 * to see if it is scheduled today or tomorrow.
602 RUN *find_next_run(RUN *run, JOB *job, utime_t &runtime, int ndays)
604 time_t now, future, endtime;
607 int mday, wday, month, wom, i;
612 sched = job->schedule;
613 if (sched == NULL) { /* scheduled? */
614 return NULL; /* no nothing to report */
617 /* Break down the time into components */
619 endtime = now + (ndays * 60 * 60 * 24);
626 for ( ; run; run=run->next) {
628 * Find runs in next 24 hours. Day 0 is today, so if
629 * ndays=1, look at today and tomorrow.
631 for (day = 0; day <= ndays; day++) {
632 future = now + (day * 60 * 60 * 24);
634 /* Break down the time into components */
635 (void)localtime_r(&future, &tm);
636 mday = tm.tm_mday - 1;
640 woy = tm_woy(future);
642 is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
643 bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
644 bit_is_set(woy, run->woy);
647 Pmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
648 Pmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
649 Pmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
650 Pmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
651 Pmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
652 Pmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
655 if (is_scheduled) { /* Jobs scheduled on that day */
657 char buf[300], num[10];
658 bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
659 for (i=0; i<24; i++) {
660 if (bit_is_set(i, run->hour)) {
661 bsnprintf(num, sizeof(num), "%d ", i);
662 bstrncat(buf, num, sizeof(buf));
665 bstrncat(buf, "\n", sizeof(buf));
666 Pmsg1(000, "%s", buf);
668 /* find time (time_t) job is to be run */
669 (void)localtime_r(&future, &runtm);
670 for (i= 0; i < 24; i++) {
671 if (bit_is_set(i, run->hour)) {
673 runtm.tm_min = run->minute;
675 runtime = mktime(&runtm);
676 Dmsg2(200, "now=%d runtime=%lld\n", now, runtime);
677 if ((runtime > now) && (runtime < endtime)) {
678 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
679 return run; /* found it, return run resource */
685 } /* end for loop over runs */
691 * Fill in the remaining fields of the jcr as if it
692 * is going to run the job.
694 bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
698 memset(&pr, 0, sizeof(POOL_DBR));
699 set_jcr_defaults(jcr, job);
701 jcr->pool = pool; /* override */
704 Dmsg0(100, "complete_jcr close db\n");
705 db_close_database(jcr, jcr->db);
709 Dmsg0(100, "complete_jcr open db\n");
710 jcr->db = db_init_database(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
711 jcr->catalog->db_user,
712 jcr->catalog->db_password, jcr->catalog->db_address,
713 jcr->catalog->db_port, jcr->catalog->db_socket,
714 jcr->catalog->mult_db_connections,
715 jcr->catalog->disable_batch_insert);
716 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
717 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
718 jcr->catalog->db_name);
720 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
721 db_close_database(jcr, jcr->db);
726 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
727 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
728 /* Try to create the pool */
729 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
730 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
731 db_strerror(jcr->db));
733 db_close_database(jcr, jcr->db);
738 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
741 jcr->jr.PoolId = pr.PoolId;
746 static void con_lock_release(void *arg)
751 void do_messages(UAContext *ua, const char *cmd)
755 bool do_truncate = false;
758 pthread_cleanup_push(con_lock_release, (void *)NULL);
760 while (fgets(msg, sizeof(msg), con_fd)) {
762 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
763 strcpy(ua->UA_sock->msg, msg);
764 ua->UA_sock->msglen = mlen;
769 (void)ftruncate(fileno(con_fd), 0L);
771 console_msg_pending = FALSE;
772 ua->user_notified_msg_pending = FALSE;
773 pthread_cleanup_pop(0);
778 int qmessagescmd(UAContext *ua, const char *cmd)
780 if (console_msg_pending && ua->auto_display_messages) {
781 do_messages(ua, cmd);
786 int messagescmd(UAContext *ua, const char *cmd)
788 if (console_msg_pending) {
789 do_messages(ua, cmd);
791 ua->UA_sock->fsend(_("You have no messages.\n"));
797 * Callback routine for "printing" database file listing
799 void prtit(void *ctx, const char *msg)
801 UAContext *ua = (UAContext *)ctx;
803 ua->UA_sock->fsend("%s", msg);
807 * Format message and send to other end.
809 * If the UA_sock is NULL, it means that there is no user
810 * agent, so we are being called from Bacula core. In
811 * that case direct the messages to the Job.
814 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
816 BSOCK *bs = ua->UA_sock;
825 msg = get_pool_memory(PM_EMSG);
829 maxlen = sizeof_pool_memory(msg) - 1;
830 va_copy(ap, arg_ptr);
831 len = bvsnprintf(msg, maxlen, fmt, ap);
833 if (len < 0 || len >= maxlen) {
834 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
842 } else { /* No UA, send to Job */
843 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
844 free_pool_memory(msg);
849 #else /* no va_copy() -- brain damaged version of variable arguments */
851 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
853 BSOCK *bs = ua->UA_sock;
861 msg = get_memory(5000);
864 maxlen = sizeof_pool_memory(msg) - 1;
866 msg = realloc_pool_memory(msg, 5000);
869 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
870 if (len < 0 || len >= maxlen) {
871 pm_strcpy(msg, _("Message too long to display.\n"));
879 } else { /* No UA, send to Job */
880 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
881 free_pool_memory(msg);
887 void bsendmsg(void *ctx, const char *fmt, ...)
890 va_start(arg_ptr, fmt);
891 bmsg((UAContext *)ctx, fmt, arg_ptr);
896 * The following UA methods are mainly intended for GUI
900 * This is a message that should be displayed on the user's
903 void UAContext::send_msg(const char *fmt, ...)
906 va_start(arg_ptr, fmt);
907 bmsg(this, fmt, arg_ptr);
913 * This is an error condition with a command. The gui should put
914 * up an error or critical dialog box. The command is aborted.
916 void UAContext::error_msg(const char *fmt, ...)
921 if (bs && api) bs->signal(BNET_ERROR_MSG);
922 va_start(arg_ptr, fmt);
923 bmsg(this, fmt, arg_ptr);
928 * This is a warning message, that should bring up a warning
929 * dialog box on the GUI. The command is not aborted, but something
932 void UAContext::warning_msg(const char *fmt, ...)
937 if (bs && api) bs->signal(BNET_WARNING_MSG);
938 va_start(arg_ptr, fmt);
939 bmsg(this, fmt, arg_ptr);
944 * This is an information message that should probably be put
945 * into the status line of a GUI program.
947 void UAContext::info_msg(const char *fmt, ...)
952 if (bs && api) bs->signal(BNET_INFO_MSG);
953 va_start(arg_ptr, fmt);
954 bmsg(this, fmt, arg_ptr);