2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 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"));
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));
283 memset(&mr, 0, sizeof(mr));
285 Dmsg1(20, "list: %s\n", cmd);
288 ua->error_msg(_("Hey! DB is NULL\n"));
291 /* Scan arguments looking for things to do */
292 for (i=1; i<ua->argc; i++) {
294 if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
295 /* Apply any limit */
296 j = find_arg_with_value(ua, NT_("limit"));
298 jr.limit = atoi(ua->argv[j]);
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 memset(&mr, 0, sizeof(mr));
539 int i = find_arg_with_value(ua, "job");
541 if ((job = select_job_resource(ua)) == NULL) {
545 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
547 Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
548 if ((job = select_job_resource(ua)) == NULL) {
554 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
555 for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
556 if (!complete_jcr_for_job(jcr, job, run->pool)) {
560 if (!jcr->jr.PoolId) {
561 ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
564 memset(&pr, 0, sizeof(pr));
565 pr.PoolId = jcr->jr.PoolId;
566 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
567 bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
569 mr.PoolId = jcr->jr.PoolId;
570 get_job_storage(&store, job, run);
571 mr.StorageId = store.store->StorageId;
572 /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
573 if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
574 ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
575 job->name(), pr.Name, level_to_str(run->level));
578 _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
579 job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
585 db_close_database(jcr, jcr->db);
589 ua->error_msg(_("Could not find next Volume for Job %s.\n"),
598 * For a given job, we examine all his run records
599 * to see if it is scheduled today or tomorrow.
601 RUN *find_next_run(RUN *run, JOB *job, utime_t &runtime, int ndays)
603 time_t now, future, endtime;
606 int mday, wday, month, wom, i;
611 sched = job->schedule;
612 if (sched == NULL) { /* scheduled? */
613 return NULL; /* no nothing to report */
616 /* Break down the time into components */
618 endtime = now + (ndays * 60 * 60 * 24);
625 for ( ; run; run=run->next) {
627 * Find runs in next 24 hours. Day 0 is today, so if
628 * ndays=1, look at today and tomorrow.
630 for (day = 0; day <= ndays; day++) {
631 future = now + (day * 60 * 60 * 24);
633 /* Break down the time into components */
634 (void)localtime_r(&future, &tm);
635 mday = tm.tm_mday - 1;
639 woy = tm_woy(future);
641 is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
642 bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
643 bit_is_set(woy, run->woy);
646 Pmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
647 Pmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
648 Pmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
649 Pmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
650 Pmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
651 Pmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
654 if (is_scheduled) { /* Jobs scheduled on that day */
656 char buf[300], num[10];
657 bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
658 for (i=0; i<24; i++) {
659 if (bit_is_set(i, run->hour)) {
660 bsnprintf(num, sizeof(num), "%d ", i);
661 bstrncat(buf, num, sizeof(buf));
664 bstrncat(buf, "\n", sizeof(buf));
665 Pmsg1(000, "%s", buf);
667 /* find time (time_t) job is to be run */
668 (void)localtime_r(&future, &runtm);
669 for (i= 0; i < 24; i++) {
670 if (bit_is_set(i, run->hour)) {
672 runtm.tm_min = run->minute;
674 runtime = mktime(&runtm);
675 Dmsg2(200, "now=%d runtime=%lld\n", now, runtime);
676 if ((runtime > now) && (runtime < endtime)) {
677 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
678 return run; /* found it, return run resource */
684 } /* end for loop over runs */
690 * Fill in the remaining fields of the jcr as if it
691 * is going to run the job.
693 bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
697 memset(&pr, 0, sizeof(POOL_DBR));
698 set_jcr_defaults(jcr, job);
700 jcr->pool = pool; /* override */
703 Dmsg0(100, "complete_jcr close db\n");
704 db_close_database(jcr, jcr->db);
708 Dmsg0(100, "complete_jcr open db\n");
709 jcr->db = jcr->db=db_init(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
710 jcr->catalog->db_user,
711 jcr->catalog->db_password, jcr->catalog->db_address,
712 jcr->catalog->db_port, jcr->catalog->db_socket,
713 jcr->catalog->mult_db_connections);
714 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
715 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
716 jcr->catalog->db_name);
718 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
719 db_close_database(jcr, jcr->db);
724 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
725 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
726 /* Try to create the pool */
727 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
728 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
729 db_strerror(jcr->db));
731 db_close_database(jcr, jcr->db);
736 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
739 jcr->jr.PoolId = pr.PoolId;
744 static void con_lock_release(void *arg)
749 void do_messages(UAContext *ua, const char *cmd)
753 bool do_truncate = false;
756 pthread_cleanup_push(con_lock_release, (void *)NULL);
758 while (fgets(msg, sizeof(msg), con_fd)) {
760 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
761 strcpy(ua->UA_sock->msg, msg);
762 ua->UA_sock->msglen = mlen;
767 (void)ftruncate(fileno(con_fd), 0L);
769 console_msg_pending = FALSE;
770 ua->user_notified_msg_pending = FALSE;
771 pthread_cleanup_pop(0);
776 int qmessagescmd(UAContext *ua, const char *cmd)
778 if (console_msg_pending && ua->auto_display_messages) {
779 do_messages(ua, cmd);
784 int messagescmd(UAContext *ua, const char *cmd)
786 if (console_msg_pending) {
787 do_messages(ua, cmd);
789 ua->UA_sock->fsend(_("You have no messages.\n"));
795 * Callback routine for "printing" database file listing
797 void prtit(void *ctx, const char *msg)
799 UAContext *ua = (UAContext *)ctx;
801 ua->UA_sock->fsend("%s", msg);
805 * Format message and send to other end.
807 * If the UA_sock is NULL, it means that there is no user
808 * agent, so we are being called from Bacula core. In
809 * that case direct the messages to the Job.
812 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
814 BSOCK *bs = ua->UA_sock;
823 msg = get_pool_memory(PM_EMSG);
827 maxlen = sizeof_pool_memory(msg) - 1;
828 va_copy(ap, arg_ptr);
829 len = bvsnprintf(msg, maxlen, fmt, ap);
831 if (len < 0 || len >= maxlen) {
832 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
840 } else { /* No UA, send to Job */
841 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
842 free_pool_memory(msg);
847 #else /* no va_copy() -- brain damaged version of variable arguments */
849 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
851 BSOCK *bs = ua->UA_sock;
859 msg = get_memory(5000);
862 maxlen = sizeof_pool_memory(msg) - 1;
864 msg = realloc_pool_memory(msg, 5000);
867 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
868 if (len < 0 || len >= maxlen) {
869 pm_strcpy(msg, _("Message too long to display.\n"));
877 } else { /* No UA, send to Job */
878 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
879 free_pool_memory(msg);
885 void bsendmsg(void *ctx, const char *fmt, ...)
888 va_start(arg_ptr, fmt);
889 bmsg((UAContext *)ctx, fmt, arg_ptr);
894 * The following UA methods are mainly intended for GUI
898 * This is a message that should be displayed on the user's
901 void UAContext::send_msg(const char *fmt, ...)
904 va_start(arg_ptr, fmt);
905 bmsg(this, fmt, arg_ptr);
911 * This is an error condition with a command. The gui should put
912 * up an error or critical dialog box. The command is aborted.
914 void UAContext::error_msg(const char *fmt, ...)
919 if (bs && api) bs->signal(BNET_ERROR_MSG);
920 va_start(arg_ptr, fmt);
921 bmsg(this, fmt, arg_ptr);
926 * This is a warning message, that should bring up a warning
927 * dialog box on the GUI. The command is not aborted, but something
930 void UAContext::warning_msg(const char *fmt, ...)
935 if (bs && api) bs->signal(BNET_WARNING_MSG);
936 va_start(arg_ptr, fmt);
937 bmsg(this, fmt, arg_ptr);
942 * This is an information message that should probably be put
943 * into the status line of a GUI program.
945 void UAContext::info_msg(const char *fmt, ...)
950 if (bs && api) bs->signal(BNET_INFO_MSG);
951 va_start(arg_ptr, fmt);
952 bmsg(this, fmt, arg_ptr);