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);
587 db_close_database(jcr, jcr->db);
592 ua->error_msg(_("Could not find next Volume for Job %s.\n"),
601 * For a given job, we examine all his run records
602 * to see if it is scheduled today or tomorrow.
604 RUN *find_next_run(RUN *run, JOB *job, utime_t &runtime, int ndays)
606 time_t now, future, endtime;
609 int mday, wday, month, wom, i;
614 sched = job->schedule;
615 if (sched == NULL) { /* scheduled? */
616 return NULL; /* no nothing to report */
619 /* Break down the time into components */
621 endtime = now + (ndays * 60 * 60 * 24);
628 for ( ; run; run=run->next) {
630 * Find runs in next 24 hours. Day 0 is today, so if
631 * ndays=1, look at today and tomorrow.
633 for (day = 0; day <= ndays; day++) {
634 future = now + (day * 60 * 60 * 24);
636 /* Break down the time into components */
637 (void)localtime_r(&future, &tm);
638 mday = tm.tm_mday - 1;
642 woy = tm_woy(future);
644 is_scheduled = bit_is_set(mday, run->mday) && bit_is_set(wday, run->wday) &&
645 bit_is_set(month, run->month) && bit_is_set(wom, run->wom) &&
646 bit_is_set(woy, run->woy);
649 Pmsg2(000, "day=%d is_scheduled=%d\n", day, is_scheduled);
650 Pmsg1(000, "bit_set_mday=%d\n", bit_is_set(mday, run->mday));
651 Pmsg1(000, "bit_set_wday=%d\n", bit_is_set(wday, run->wday));
652 Pmsg1(000, "bit_set_month=%d\n", bit_is_set(month, run->month));
653 Pmsg1(000, "bit_set_wom=%d\n", bit_is_set(wom, run->wom));
654 Pmsg1(000, "bit_set_woy=%d\n", bit_is_set(woy, run->woy));
657 if (is_scheduled) { /* Jobs scheduled on that day */
659 char buf[300], num[10];
660 bsnprintf(buf, sizeof(buf), "tm.hour=%d hour=", tm.tm_hour);
661 for (i=0; i<24; i++) {
662 if (bit_is_set(i, run->hour)) {
663 bsnprintf(num, sizeof(num), "%d ", i);
664 bstrncat(buf, num, sizeof(buf));
667 bstrncat(buf, "\n", sizeof(buf));
668 Pmsg1(000, "%s", buf);
670 /* find time (time_t) job is to be run */
671 (void)localtime_r(&future, &runtm);
672 for (i= 0; i < 24; i++) {
673 if (bit_is_set(i, run->hour)) {
675 runtm.tm_min = run->minute;
677 runtime = mktime(&runtm);
678 Dmsg2(200, "now=%d runtime=%lld\n", now, runtime);
679 if ((runtime > now) && (runtime < endtime)) {
680 Dmsg2(200, "Found it level=%d %c\n", run->level, run->level);
681 return run; /* found it, return run resource */
687 } /* end for loop over runs */
693 * Fill in the remaining fields of the jcr as if it
694 * is going to run the job.
696 bool complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool)
700 memset(&pr, 0, sizeof(POOL_DBR));
701 set_jcr_defaults(jcr, job);
703 jcr->pool = pool; /* override */
706 Dmsg0(100, "complete_jcr close db\n");
707 db_close_database(jcr, jcr->db);
711 Dmsg0(100, "complete_jcr open db\n");
712 jcr->db = db_init_database(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
713 jcr->catalog->db_user,
714 jcr->catalog->db_password, jcr->catalog->db_address,
715 jcr->catalog->db_port, jcr->catalog->db_socket,
716 jcr->catalog->mult_db_connections,
717 jcr->catalog->disable_batch_insert);
718 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
719 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
720 jcr->catalog->db_name);
722 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
723 db_close_database(jcr, jcr->db);
728 bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
729 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
730 /* Try to create the pool */
731 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
732 Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
733 db_strerror(jcr->db));
735 db_close_database(jcr, jcr->db);
740 Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
743 jcr->jr.PoolId = pr.PoolId;
748 static void con_lock_release(void *arg)
753 void do_messages(UAContext *ua, const char *cmd)
757 bool do_truncate = false;
760 pthread_cleanup_push(con_lock_release, (void *)NULL);
762 while (fgets(msg, sizeof(msg), con_fd)) {
764 ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1);
765 strcpy(ua->UA_sock->msg, msg);
766 ua->UA_sock->msglen = mlen;
771 (void)ftruncate(fileno(con_fd), 0L);
773 console_msg_pending = FALSE;
774 ua->user_notified_msg_pending = FALSE;
775 pthread_cleanup_pop(0);
780 int qmessagescmd(UAContext *ua, const char *cmd)
782 if (console_msg_pending && ua->auto_display_messages) {
783 do_messages(ua, cmd);
788 int messagescmd(UAContext *ua, const char *cmd)
790 if (console_msg_pending) {
791 do_messages(ua, cmd);
793 ua->UA_sock->fsend(_("You have no messages.\n"));
799 * Callback routine for "printing" database file listing
801 void prtit(void *ctx, const char *msg)
803 UAContext *ua = (UAContext *)ctx;
805 ua->UA_sock->fsend("%s", msg);
809 * Format message and send to other end.
811 * If the UA_sock is NULL, it means that there is no user
812 * agent, so we are being called from Bacula core. In
813 * that case direct the messages to the Job.
816 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
818 BSOCK *bs = ua->UA_sock;
827 msg = get_pool_memory(PM_EMSG);
831 maxlen = sizeof_pool_memory(msg) - 1;
832 va_copy(ap, arg_ptr);
833 len = bvsnprintf(msg, maxlen, fmt, ap);
835 if (len < 0 || len >= maxlen) {
836 msg = realloc_pool_memory(msg, maxlen + maxlen/2);
844 } else { /* No UA, send to Job */
845 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
846 free_pool_memory(msg);
851 #else /* no va_copy() -- brain damaged version of variable arguments */
853 void bmsg(UAContext *ua, const char *fmt, va_list arg_ptr)
855 BSOCK *bs = ua->UA_sock;
863 msg = get_memory(5000);
866 maxlen = sizeof_pool_memory(msg) - 1;
868 msg = realloc_pool_memory(msg, 5000);
871 len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
872 if (len < 0 || len >= maxlen) {
873 pm_strcpy(msg, _("Message too long to display.\n"));
881 } else { /* No UA, send to Job */
882 Jmsg(ua->jcr, M_INFO, 0, "%s", msg);
883 free_pool_memory(msg);
889 void bsendmsg(void *ctx, const char *fmt, ...)
892 va_start(arg_ptr, fmt);
893 bmsg((UAContext *)ctx, fmt, arg_ptr);
898 * The following UA methods are mainly intended for GUI
902 * This is a message that should be displayed on the user's
905 void UAContext::send_msg(const char *fmt, ...)
908 va_start(arg_ptr, fmt);
909 bmsg(this, fmt, arg_ptr);
915 * This is an error condition with a command. The gui should put
916 * up an error or critical dialog box. The command is aborted.
918 void UAContext::error_msg(const char *fmt, ...)
923 if (bs && api) bs->signal(BNET_ERROR_MSG);
924 va_start(arg_ptr, fmt);
925 bmsg(this, fmt, arg_ptr);
930 * This is a warning message, that should bring up a warning
931 * dialog box on the GUI. The command is not aborted, but something
934 void UAContext::warning_msg(const char *fmt, ...)
939 if (bs && api) bs->signal(BNET_WARNING_MSG);
940 va_start(arg_ptr, fmt);
941 bmsg(this, fmt, arg_ptr);
946 * This is an information message that should probably be put
947 * into the status line of a GUI program.
949 void UAContext::info_msg(const char *fmt, ...)
954 if (bs && api) bs->signal(BNET_INFO_MSG);
955 va_start(arg_ptr, fmt);
956 bmsg(this, fmt, arg_ptr);