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 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));
283 memset(&mr, 0, sizeof(mr));
285 Dmsg1(20, "list: %s\n", cmd);
288 ua->error_msg(_("Hey! DB is NULL\n"));
291 /* Apply any limit */
292 j = find_arg_with_value(ua, NT_("limit"));
294 jr.limit = atoi(ua->argv[j]);
297 /* Scan arguments looking for things to do */
298 for (i=1; i<ua->argc; i++) {
300 if (strcasecmp(ua->argk[i], NT_("jobs")) == 0) {
301 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
304 } else if (strcasecmp(ua->argk[i], NT_("jobtotals")) == 0) {
305 db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua);
308 } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
310 jobid = str_to_int64(ua->argv[i]);
313 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
318 } else if ((strcasecmp(ua->argk[i], NT_("job")) == 0 ||
319 strcasecmp(ua->argk[i], NT_("jobname")) == 0) && ua->argv[i]) {
320 bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH);
322 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
324 /* List UJOBID=xxx */
325 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0 && ua->argv[i]) {
326 bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH);
328 db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
330 /* List Base files */
331 } else if (strcasecmp(ua->argk[i], NT_("basefiles")) == 0) {
332 /* TODO: cleanup this block */
333 for (j=i+1; j<ua->argc; j++) {
334 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
335 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
337 db_get_job_record(ua->jcr, ua->db, &jr);
339 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
340 jobid = str_to_int64(ua->argv[j]);
345 db_list_base_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
350 } else if (strcasecmp(ua->argk[i], NT_("files")) == 0) {
352 for (j=i+1; j<ua->argc; j++) {
353 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
354 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
356 db_get_job_record(ua->jcr, ua->db, &jr);
358 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
359 jobid = str_to_int64(ua->argv[j]);
364 db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua);
369 } else if (strcasecmp(ua->argk[i], NT_("jobmedia")) == 0) {
371 for (j=i+1; j<ua->argc; j++) {
372 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
373 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
375 db_get_job_record(ua->jcr, ua->db, &jr);
377 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
378 jobid = str_to_int64(ua->argv[j]);
382 db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
386 /* List for all jobs (jobid=0) */
387 db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist);
391 } else if (strcasecmp(ua->argk[i], NT_("joblog")) == 0) {
393 for (j=i+1; j<ua->argc; j++) {
394 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
395 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
397 db_get_job_record(ua->jcr, ua->db, &jr);
399 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
400 jobid = str_to_int64(ua->argv[j]);
404 db_list_joblog_records(ua->jcr, ua->db, jobid, prtit, ua, llist);
408 /* List for all jobs (jobid=0) */
409 db_list_joblog_records(ua->jcr, ua->db, 0, prtit, ua, llist);
414 } else if (strcasecmp(ua->argk[i], NT_("pool")) == 0 ||
415 strcasecmp(ua->argk[i], NT_("pools")) == 0) {
417 memset(&pr, 0, sizeof(pr));
419 bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name));
421 db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist);
423 } else if (strcasecmp(ua->argk[i], NT_("clients")) == 0) {
424 db_list_client_records(ua->jcr, ua->db, prtit, ua, llist);
427 /* List MEDIA or VOLUMES */
428 } else if (strcasecmp(ua->argk[i], NT_("media")) == 0 ||
429 strcasecmp(ua->argk[i], NT_("volume")) == 0 ||
430 strcasecmp(ua->argk[i], NT_("volumes")) == 0) {
432 for (j=i+1; j<ua->argc; j++) {
433 if (strcasecmp(ua->argk[j], NT_("ujobid")) == 0 && ua->argv[j]) {
434 bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH);
436 db_get_job_record(ua->jcr, ua->db, &jr);
438 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
439 jobid = str_to_int64(ua->argv[j]);
443 VolumeName = get_pool_memory(PM_FNAME);
444 n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName);
445 ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName);
446 free_pool_memory(VolumeName);
449 /* if no job or jobid keyword found, then we list all media */
453 /* List a specific volume? */
455 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
456 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
459 /* Is a specific pool wanted? */
460 for (i=1; i<ua->argc; i++) {
461 if (strcasecmp(ua->argk[i], NT_("pool")) == 0) {
462 if (!get_pool_dbr(ua, &pr)) {
463 ua->error_msg(_("No Pool specified.\n"));
466 mr.PoolId = pr.PoolId;
467 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
472 /* List Volumes in all pools */
473 if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) {
474 ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"),
475 db_strerror(ua->db));
478 if (num_pools <= 0) {
481 for (i=0; i < num_pools; i++) {
483 if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
484 ua->send_msg(_("Pool: %s\n"), pr.Name);
487 db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist);
492 /* List next volume */
493 } else if (strcasecmp(ua->argk[i], NT_("nextvol")) == 0 ||
494 strcasecmp(ua->argk[i], NT_("nextvolume")) == 0) {
496 j = find_arg_with_value(ua, NT_("days"));
498 n = atoi(ua->argv[j]);
499 if ((n < 0) || (n > 50)) {
500 ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n"));
505 } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
508 for (j=i+1; j<ua->argc; j++) {
509 if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
510 if (is_a_number_list(ua->argv[j])) {
511 jobids = ua->argv[j];
513 } else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
514 limit = atoi(ua->argv[j]);
517 db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist);
518 } else if (strcasecmp(ua->argk[i], NT_("limit")) == 0
519 || strcasecmp(ua->argk[i], NT_("days")) == 0) {
522 ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i]));
528 static bool list_nextvol(UAContext *ua, int ndays)
539 memset(&mr, 0, sizeof(mr));
540 int i = find_arg_with_value(ua, "job");
542 if ((job = select_job_resource(ua)) == NULL) {
546 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
548 Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]);
549 if ((job = select_job_resource(ua)) == NULL) {
555 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
556 for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) {
557 if (!complete_jcr_for_job(jcr, job, run->pool)) {
561 if (!jcr->jr.PoolId) {
562 ua->error_msg(_("Could not find Pool for Job %s\n"), job->name());
565 memset(&pr, 0, sizeof(pr));
566 pr.PoolId = jcr->jr.PoolId;
567 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
568 bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name));
570 mr.PoolId = jcr->jr.PoolId;
571 get_job_storage(&store, job, run);
572 mr.StorageId = store.store->StorageId;
573 /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
574 if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) {
575 ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"),
576 job->name(), pr.Name, level_to_str(run->level));
579 _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"),
580 job->name(), pr.Name, level_to_str(run->level), mr.VolumeName);
586 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(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 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
716 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
717 jcr->catalog->db_name);
719 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
720 db_close_database(jcr, jcr->db);
725 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
726 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
727 /* Try to create the pool */
728 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
729 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
730 db_strerror(jcr->db));
732 db_close_database(jcr, jcr->db);
737 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
740 jcr->jr.PoolId = pr.PoolId;
745 static void con_lock_release(void *arg)
750 void do_messages(UAContext *ua, const char *cmd)
754 bool do_truncate = false;
757 pthread_cleanup_push(con_lock_release, (void *)NULL);
759 while (fgets(msg, sizeof(msg), con_fd)) {
761 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
762 strcpy(ua->UA_sock->msg, msg);
763 ua->UA_sock->msglen = mlen;
768 (void)ftruncate(fileno(con_fd), 0L);
770 console_msg_pending = FALSE;
771 ua->user_notified_msg_pending = FALSE;
772 pthread_cleanup_pop(0);
777 int qmessagescmd(UAContext *ua, const char *cmd)
779 if (console_msg_pending && ua->auto_display_messages) {
780 do_messages(ua, cmd);
785 int messagescmd(UAContext *ua, const char *cmd)
787 if (console_msg_pending) {
788 do_messages(ua, cmd);
790 ua->UA_sock->fsend(_("You have no messages.\n"));
796 * Callback routine for "printing" database file listing
798 void prtit(void *ctx, const char *msg)
800 UAContext *ua = (UAContext *)ctx;
802 ua->UA_sock->fsend("%s", msg);
806 * Format message and send to other end.
808 * If the UA_sock is NULL, it means that there is no user
809 * agent, so we are being called from Bacula core. In
810 * that case direct the messages to the Job.
813 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
815 BSOCK *bs = ua->UA_sock;
824 msg = get_pool_memory(PM_EMSG);
828 maxlen = sizeof_pool_memory(msg) - 1;
829 va_copy(ap, arg_ptr);
830 len = bvsnprintf(msg, maxlen, fmt, ap);
832 if (len < 0 || len >= maxlen) {
833 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
841 } else { /* No UA, send to Job */
842 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
843 free_pool_memory(msg);
848 #else /* no va_copy() -- brain damaged version of variable arguments */
850 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
852 BSOCK *bs = ua->UA_sock;
860 msg = get_memory(5000);
863 maxlen = sizeof_pool_memory(msg) - 1;
865 msg = realloc_pool_memory(msg, 5000);
868 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
869 if (len < 0 || len >= maxlen) {
870 pm_strcpy(msg, _("Message too long to display.\n"));
878 } else { /* No UA, send to Job */
879 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
880 free_pool_memory(msg);
886 void bsendmsg(void *ctx, const char *fmt, ...)
889 va_start(arg_ptr, fmt);
890 bmsg((UAContext *)ctx, fmt, arg_ptr);
895 * The following UA methods are mainly intended for GUI
899 * This is a message that should be displayed on the user's
902 void UAContext::send_msg(const char *fmt, ...)
905 va_start(arg_ptr, fmt);
906 bmsg(this, fmt, arg_ptr);
912 * This is an error condition with a command. The gui should put
913 * up an error or critical dialog box. The command is aborted.
915 void UAContext::error_msg(const char *fmt, ...)
920 if (bs && api) bs->signal(BNET_ERROR_MSG);
921 va_start(arg_ptr, fmt);
922 bmsg(this, fmt, arg_ptr);
927 * This is a warning message, that should bring up a warning
928 * dialog box on the GUI. The command is not aborted, but something
931 void UAContext::warning_msg(const char *fmt, ...)
936 if (bs && api) bs->signal(BNET_WARNING_MSG);
937 va_start(arg_ptr, fmt);
938 bmsg(this, fmt, arg_ptr);
943 * This is an information message that should probably be put
944 * into the status line of a GUI program.
946 void UAContext::info_msg(const char *fmt, ...)
951 if (bs && api) bs->signal(BNET_INFO_MSG);
952 va_start(arg_ptr, fmt);
953 bmsg(this, fmt, arg_ptr);