2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2008 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 John Walker.
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 Status Command
32 * Kern Sibbald, August MMI
41 extern void *start_heap;
43 static void list_scheduled_jobs(UAContext *ua);
44 static void list_running_jobs(UAContext *ua);
45 static void list_terminated_jobs(UAContext *ua);
46 static void do_storage_status(UAContext *ua, STORE *store);
47 static void do_client_status(UAContext *ua, CLIENT *client);
48 static void do_director_status(UAContext *ua);
49 static void do_all_status(UAContext *ua);
51 static char OKqstatus[] = "1000 OK .status\n";
52 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
58 bool dot_status_cmd(UAContext *ua, const char *cmd)
64 Dmsg1(20, "status:%s:\n", cmd);
66 if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
67 ua->send_msg("1900 Bad .status command, missing arguments.\n");
71 if (strcasecmp(ua->argk[2], "current") == 0) {
72 ua->send_msg(OKqstatus, ua->argk[2]);
74 if (njcr->JobId != 0 && acl_access_ok(ua, Job_ACL, njcr->job->name())) {
75 ua->send_msg(DotStatusJob, edit_int64(njcr->JobId, ed1),
76 njcr->JobStatus, njcr->JobErrors);
80 } else if (strcasecmp(ua->argk[2], "last") == 0) {
81 ua->send_msg(OKqstatus, ua->argk[2]);
82 if ((last_jobs) && (last_jobs->size() > 0)) {
83 job = (s_last_job*)last_jobs->last();
84 if (acl_access_ok(ua, Job_ACL, job->Job)) {
85 ua->send_msg(DotStatusJob, edit_int64(job->JobId, ed1),
86 job->JobStatus, job->Errors);
89 } else if (strcasecmp(ua->argk[2], "header") == 0) {
90 list_dir_status_header(ua);
91 } else if (strcasecmp(ua->argk[2], "scheduled") == 0) {
92 list_scheduled_jobs(ua);
93 } else if (strcasecmp(ua->argk[2], "running") == 0) {
94 list_running_jobs(ua);
95 } else if (strcasecmp(ua->argk[2], "terminated") == 0) {
96 list_terminated_jobs(ua);
98 ua->send_msg("1900 Bad .status command, wrong argument.\n");
105 /* This is the *old* command handler, so we must return
106 * 1 or it closes the connection
108 int qstatus_cmd(UAContext *ua, const char *cmd)
110 dot_status_cmd(ua, cmd);
117 int status_cmd(UAContext *ua, const char *cmd)
123 Dmsg1(20, "status:%s:\n", cmd);
125 for (i=1; i<ua->argc; i++) {
126 if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
129 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
130 strcasecmp(ua->argk[i], NT_("director")) == 0) {
131 do_director_status(ua);
133 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
134 client = get_client_resource(ua);
136 do_client_status(ua, client);
140 store = get_storage_resource(ua, false/*no default*/);
142 do_storage_status(ua, store);
147 /* If no args, ask for status type */
149 char prmt[MAX_NAME_LENGTH];
151 start_prompt(ua, _("Status available for:\n"));
152 add_prompt(ua, NT_("Director"));
153 add_prompt(ua, NT_("Storage"));
154 add_prompt(ua, NT_("Client"));
155 add_prompt(ua, NT_("All"));
156 Dmsg0(20, "do_prompt: select daemon\n");
157 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
160 Dmsg1(20, "item=%d\n", item);
162 case 0: /* Director */
163 do_director_status(ua);
166 store = select_storage_resource(ua);
168 do_storage_status(ua, store);
172 client = select_client_resource(ua);
174 do_client_status(ua, client);
187 static void do_all_status(UAContext *ua)
189 STORE *store, **unique_store;
190 CLIENT *client, **unique_client;
194 do_director_status(ua);
196 /* Count Storage items */
199 foreach_res(store, R_STORAGE) {
202 unique_store = (STORE **) malloc(i * sizeof(STORE));
203 /* Find Unique Storage address/port */
205 foreach_res(store, R_STORAGE) {
207 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
210 for (j=0; j<i; j++) {
211 if (strcmp(unique_store[j]->address, store->address) == 0 &&
212 unique_store[j]->SDport == store->SDport) {
218 unique_store[i++] = store;
219 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
224 /* Call each unique Storage daemon */
225 for (j=0; j<i; j++) {
226 do_storage_status(ua, unique_store[j]);
230 /* Count Client items */
233 foreach_res(client, R_CLIENT) {
236 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
237 /* Find Unique Client address/port */
239 foreach_res(client, R_CLIENT) {
241 if (!acl_access_ok(ua, Client_ACL, client->name())) {
244 for (j=0; j<i; j++) {
245 if (strcmp(unique_client[j]->address, client->address) == 0 &&
246 unique_client[j]->FDport == client->FDport) {
252 unique_client[i++] = client;
253 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
258 /* Call each unique File daemon */
259 for (j=0; j<i; j++) {
260 do_client_status(ua, unique_client[j]);
266 void list_dir_status_header(UAContext *ua)
268 char dt[MAX_TIME_LENGTH];
269 char b1[35], b2[35], b3[35], b4[35], b5[35];
271 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
272 HOST_OS, DISTNAME, DISTVER);
273 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
274 if (num_jobs_run == 1) {
275 ua->send_msg(_("Daemon started %s, 1 Job run since started.\n"), dt);
278 ua->send_msg(_("Daemon started %s, %d Jobs run since started.\n"),
281 ua->send_msg(_(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
282 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
283 edit_uint64_with_commas(sm_bytes, b2),
284 edit_uint64_with_commas(sm_max_bytes, b3),
285 edit_uint64_with_commas(sm_buffers, b4),
286 edit_uint64_with_commas(sm_max_buffers, b5));
289 static void do_director_status(UAContext *ua)
291 list_dir_status_header(ua);
294 * List scheduled Jobs
296 list_scheduled_jobs(ua);
301 list_running_jobs(ua);
304 * List terminated jobs
306 list_terminated_jobs(ua);
307 ua->send_msg("====\n");
310 static void do_storage_status(UAContext *ua, STORE *store)
315 lstore.store = store;
316 pm_strcpy(lstore.store_source, _("unknown source"));
317 set_wstorage(ua->jcr, &lstore);
318 /* Try connecting for up to 15 seconds */
319 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
320 store->name(), store->address, store->SDport);
321 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
322 ua->send_msg(_("\nFailed to connect to Storage daemon %s.\n====\n"),
324 if (ua->jcr->store_bsock) {
325 bnet_close(ua->jcr->store_bsock);
326 ua->jcr->store_bsock = NULL;
330 Dmsg0(20, _("Connected to storage daemon\n"));
331 sd = ua->jcr->store_bsock;
332 bnet_fsend(sd, "status");
333 while (bnet_recv(sd) >= 0) {
334 ua->send_msg("%s", sd->msg);
336 bnet_sig(sd, BNET_TERMINATE);
338 ua->jcr->store_bsock = NULL;
342 static void do_client_status(UAContext *ua, CLIENT *client)
346 /* Connect to File daemon */
348 ua->jcr->client = client;
349 /* Release any old dummy key */
350 if (ua->jcr->sd_auth_key) {
351 free(ua->jcr->sd_auth_key);
353 /* Create a new dummy SD auth key */
354 ua->jcr->sd_auth_key = bstrdup("dummy");
356 /* Try to connect for 15 seconds */
357 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
358 client->name(), client->address, client->FDport);
359 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
360 ua->send_msg(_("Failed to connect to Client %s.\n====\n"),
362 if (ua->jcr->file_bsock) {
363 bnet_close(ua->jcr->file_bsock);
364 ua->jcr->file_bsock = NULL;
368 Dmsg0(20, _("Connected to file daemon\n"));
369 fd = ua->jcr->file_bsock;
371 while (fd->recv() >= 0) {
372 ua->send_msg("%s", fd->msg);
374 fd->signal(BNET_TERMINATE);
376 ua->jcr->file_bsock = NULL;
381 static void prt_runhdr(UAContext *ua)
384 ua->send_msg(_("\nScheduled Jobs:\n"));
385 ua->send_msg(_("Level Type Pri Scheduled Name Volume\n"));
386 ua->send_msg(_("===================================================================================\n"));
390 /* Scheduling packet */
392 dlink link; /* keep this as first item!!! */
401 static void prt_runtime(UAContext *ua, sched_pkt *sp)
403 char dt[MAX_TIME_LENGTH];
404 const char *level_ptr;
406 bool close_db = false;
410 memset(&mr, 0, sizeof(mr));
411 if (sp->job->JobType == JT_BACKUP) {
413 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
414 Dmsg1(250, "Using pool=%s\n", jcr->pool->name());
416 close_db = true; /* new db opened, remember to close it */
419 mr.PoolId = jcr->jr.PoolId;
420 mr.StorageId = sp->store->StorageId;
421 jcr->wstore = sp->store;
422 Dmsg0(250, "call find_next_volume_for_append\n");
423 ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune);
426 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
429 bstrftime_nc(dt, sizeof(dt), sp->runtime);
430 switch (sp->job->JobType) {
436 level_ptr = level_to_str(sp->level);
440 ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
441 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
442 sp->job->name(), mr.VolumeName);
444 ua->send_msg(_("%-14s %-8s %3d %-18s %-18s %s\n"),
445 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
446 sp->job->name(), mr.VolumeName);
449 db_close_database(jcr, jcr->db);
451 jcr->db = ua->db; /* restore ua db to jcr */
455 * Sort items by runtime, priority
457 static int my_compare(void *item1, void *item2)
459 sched_pkt *p1 = (sched_pkt *)item1;
460 sched_pkt *p2 = (sched_pkt *)item2;
461 if (p1->runtime < p2->runtime) {
463 } else if (p1->runtime > p2->runtime) {
466 if (p1->priority < p2->priority) {
468 } else if (p1->priority > p2->priority) {
475 * Find all jobs to be run in roughly the
478 static void list_scheduled_jobs(UAContext *ua)
483 int level, num_jobs = 0;
485 bool hdr_printed = false;
490 Dmsg0(200, "enter list_sched_jobs()\n");
493 i = find_arg_with_value(ua, NT_("days"));
495 days = atoi(ua->argv[i]);
496 if ((days < 0) || (days > 500) && !ua->api) {
497 ua->send_msg(_("Ignoring invalid value for days. Max is 500.\n"));
502 /* Loop through all jobs */
504 foreach_res(job, R_JOB) {
505 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
508 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
510 level = job->JobLevel;
514 priority = job->Priority;
516 priority = run->Priority;
522 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
525 sp->priority = priority;
526 sp->runtime = runtime;
527 sp->pool = run->pool;
528 get_job_storage(&store, job, run);
529 sp->store = store.store;
530 Dmsg3(250, "job=%s store=%s MediaType=%s\n", job->name(), sp->store->name(), sp->store->media_type);
531 sched.binary_insert_multiple(sp, my_compare);
534 } /* end for loop over resources */
536 foreach_dlist(sp, &sched) {
539 if (num_jobs == 0 && !ua->api) {
540 ua->send_msg(_("No Scheduled Jobs.\n"));
542 if (!ua->api) ua->send_msg("====\n");
543 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
546 static void list_running_jobs(UAContext *ua)
551 char *emsg; /* edited message */
552 char dt[MAX_TIME_LENGTH];
554 bool pool_mem = false;
556 Dmsg0(200, "enter list_run_jobs()\n");
557 if (!ua->api) ua->send_msg(_("\nRunning Jobs:\n"));
559 if (jcr->JobId == 0) { /* this is us */
560 /* this is a console or other control job. We only show console
561 * jobs in the status output.
563 if (jcr->JobType == JT_CONSOLE && !ua->api) {
564 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
565 ua->send_msg(_("Console connected at %s\n"), dt);
574 /* Note the following message is used in regress -- don't change */
575 if (!ua->api) ua->send_msg(_("No Jobs running.\n====\n"));
576 Dmsg0(200, "leave list_run_jobs()\n");
581 ua->send_msg(_(" JobId Level Name Status\n"));
582 ua->send_msg(_("======================================================================\n"));
585 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
589 switch (jcr->JobStatus) {
591 msg = _("is waiting execution");
594 msg = _("is running");
597 msg = _("is blocked");
600 msg = _("has terminated");
602 case JS_ErrorTerminated:
603 msg = _("has erred");
606 msg = _("has errors");
609 msg = _("has a fatal error");
612 msg = _("has verify differences");
615 msg = _("has been canceled");
618 emsg = (char *) get_pool_memory(PM_FNAME);
620 Mmsg(emsg, _("is waiting on Client"));
622 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
628 emsg = (char *) get_pool_memory(PM_FNAME);
630 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
631 } else if (jcr->rstore) {
632 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
634 Mmsg(emsg, _("is waiting on Storage"));
639 case JS_WaitStoreRes:
640 msg = _("is waiting on max Storage jobs");
642 case JS_WaitClientRes:
643 msg = _("is waiting on max Client jobs");
646 msg = _("is waiting on max Job jobs");
649 msg = _("is waiting on max total jobs");
651 case JS_WaitStartTime:
652 msg = _("is waiting for its start time");
654 case JS_WaitPriority:
655 msg = _("is waiting for higher priority jobs to finish");
657 case JS_DataCommitting:
658 msg = _("SD committing Data");
660 case JS_DataDespooling:
661 msg = _("SD despooling Data");
663 case JS_AttrDespooling:
664 msg = _("SD despooling Attributes");
666 case JS_AttrInserting:
667 msg = _("Dir inserting Attributes");
671 emsg = (char *)get_pool_memory(PM_FNAME);
672 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
678 * Now report Storage daemon status code
680 switch (jcr->SDJobStatus) {
683 free_pool_memory(emsg);
686 msg = _("is waiting for a mount request");
690 free_pool_memory(emsg);
693 msg = _("is waiting for an appendable Volume");
697 emsg = (char *)get_pool_memory(PM_FNAME);
700 if (!jcr->client || !jcr->wstore) {
701 Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
703 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
704 jcr->client->name(), jcr->wstore->name());
708 case JS_DataCommitting:
709 msg = _("SD committing Data");
711 case JS_DataDespooling:
712 msg = _("SD despooling Data");
714 case JS_AttrDespooling:
715 msg = _("SD despooling Attributes");
717 case JS_AttrInserting:
718 msg = _("Dir inserting Attributes");
721 switch (jcr->JobType) {
724 bstrncpy(level, " ", sizeof(level));
727 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
733 ua->send_msg(_("%6d\t%-6s\t%-20s\t%s\n"),
734 jcr->JobId, level, jcr->Job, msg);
736 ua->send_msg(_("%6d %-6s %-20s %s\n"),
737 jcr->JobId, level, jcr->Job, msg);
741 free_pool_memory(emsg);
746 if (!ua->api) ua->send_msg("====\n");
747 Dmsg0(200, "leave list_run_jobs()\n");
750 static void list_terminated_jobs(UAContext *ua)
752 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
755 if (last_jobs->empty()) {
756 if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
759 lock_last_jobs_list();
760 struct s_last_job *je;
762 ua->send_msg(_("\nTerminated Jobs:\n"));
763 ua->send_msg(_(" JobId Level Files Bytes Status Finished Name \n"));
764 ua->send_msg(_("====================================================================\n"));
766 foreach_dlist(je, last_jobs) {
767 char JobName[MAX_NAME_LENGTH];
768 const char *termstat;
770 bstrncpy(JobName, je->Job, sizeof(JobName));
771 /* There are three periods after the Job name */
773 for (int i=0; i<3; i++) {
774 if ((p=strrchr(JobName, '.')) != NULL) {
779 if (!acl_access_ok(ua, Job_ACL, JobName)) {
783 bstrftime_nc(dt, sizeof(dt), je->end_time);
784 switch (je->JobType) {
787 bstrncpy(level, " ", sizeof(level));
790 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
794 switch (je->JobStatus) {
796 termstat = _("Created");
799 case JS_ErrorTerminated:
800 termstat = _("Error");
803 termstat = _("Diffs");
806 termstat = _("Cancel");
812 termstat = _("Other");
816 ua->send_msg(_("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
819 edit_uint64_with_commas(je->JobFiles, b1),
820 edit_uint64_with_suffix(je->JobBytes, b2),
824 ua->send_msg(_("%6d %-6s %8s %10s %-7s %-8s %s\n"),
827 edit_uint64_with_commas(je->JobFiles, b1),
828 edit_uint64_with_suffix(je->JobBytes, b2),
833 if (!ua->api) ua->send_msg(_("\n"));
834 unlock_last_jobs_list();