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, char *cmd);
47 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd);
48 static void do_director_status(UAContext *ua);
49 static void do_all_status(UAContext *ua);
50 void status_slots(UAContext *ua, STORE *store);
52 static char OKqstatus[] = "1000 OK .status\n";
53 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
59 bool dot_status_cmd(UAContext *ua, const char *cmd)
67 Dmsg2(20, "status=\"%s\" argc=%d\n", cmd, ua->argc);
70 ua->send_msg("1900 Bad .status command, missing arguments.\n");
74 if (strcasecmp(ua->argk[1], "dir") == 0) {
75 if (strcasecmp(ua->argk[2], "current") == 0) {
76 ua->send_msg(OKqstatus, ua->argk[2]);
78 if (njcr->JobId != 0 && acl_access_ok(ua, Job_ACL, njcr->job->name())) {
79 ua->send_msg(DotStatusJob, edit_int64(njcr->JobId, ed1),
80 njcr->JobStatus, njcr->JobErrors);
84 } else if (strcasecmp(ua->argk[2], "last") == 0) {
85 ua->send_msg(OKqstatus, ua->argk[2]);
86 if ((last_jobs) && (last_jobs->size() > 0)) {
87 job = (s_last_job*)last_jobs->last();
88 if (acl_access_ok(ua, Job_ACL, job->Job)) {
89 ua->send_msg(DotStatusJob, edit_int64(job->JobId, ed1),
90 job->JobStatus, job->Errors);
93 } else if (strcasecmp(ua->argk[2], "header") == 0) {
94 list_dir_status_header(ua);
95 } else if (strcasecmp(ua->argk[2], "scheduled") == 0) {
96 list_scheduled_jobs(ua);
97 } else if (strcasecmp(ua->argk[2], "running") == 0) {
98 list_running_jobs(ua);
99 } else if (strcasecmp(ua->argk[2], "terminated") == 0) {
100 list_terminated_jobs(ua);
102 ua->send_msg("1900 Bad .status command, wrong argument.\n");
105 } else if (strcasecmp(ua->argk[1], "client") == 0) {
106 client = get_client_resource(ua);
108 Dmsg2(200, "Client=%s arg=%s\n", client->name(), NPRT(ua->argk[2]));
109 do_client_status(ua, client, ua->argk[2]);
111 } else if (strcasecmp(ua->argk[1], "storage") == 0) {
112 store = get_storage_resource(ua, false /*no default*/);
114 do_storage_status(ua, store, ua->argk[2]);
117 ua->send_msg("1900 Bad .status command, wrong argument.\n");
124 /* This is the *old* command handler, so we must return
125 * 1 or it closes the connection
127 int qstatus_cmd(UAContext *ua, const char *cmd)
129 dot_status_cmd(ua, cmd);
136 int status_cmd(UAContext *ua, const char *cmd)
142 Dmsg1(20, "status:%s:\n", cmd);
144 for (i=1; i<ua->argc; i++) {
145 if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
148 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
149 strcasecmp(ua->argk[i], NT_("director")) == 0) {
150 do_director_status(ua);
152 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
153 client = get_client_resource(ua);
155 do_client_status(ua, client, NULL);
159 store = get_storage_resource(ua, false/*no default*/);
161 if (find_arg(ua, NT_("slots")) > 0) {
162 status_slots(ua, store);
164 do_storage_status(ua, store, NULL);
170 /* If no args, ask for status type */
172 char prmt[MAX_NAME_LENGTH];
174 start_prompt(ua, _("Status available for:\n"));
175 add_prompt(ua, NT_("Director"));
176 add_prompt(ua, NT_("Storage"));
177 add_prompt(ua, NT_("Client"));
178 add_prompt(ua, NT_("All"));
179 Dmsg0(20, "do_prompt: select daemon\n");
180 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
183 Dmsg1(20, "item=%d\n", item);
185 case 0: /* Director */
186 do_director_status(ua);
189 store = select_storage_resource(ua);
191 do_storage_status(ua, store, NULL);
195 client = select_client_resource(ua);
197 do_client_status(ua, client, NULL);
210 static void do_all_status(UAContext *ua)
212 STORE *store, **unique_store;
213 CLIENT *client, **unique_client;
217 do_director_status(ua);
219 /* Count Storage items */
222 foreach_res(store, R_STORAGE) {
225 unique_store = (STORE **) malloc(i * sizeof(STORE));
226 /* Find Unique Storage address/port */
228 foreach_res(store, R_STORAGE) {
230 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
233 for (j=0; j<i; j++) {
234 if (strcmp(unique_store[j]->address, store->address) == 0 &&
235 unique_store[j]->SDport == store->SDport) {
241 unique_store[i++] = store;
242 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
247 /* Call each unique Storage daemon */
248 for (j=0; j<i; j++) {
249 do_storage_status(ua, unique_store[j], NULL);
253 /* Count Client items */
256 foreach_res(client, R_CLIENT) {
259 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
260 /* Find Unique Client address/port */
262 foreach_res(client, R_CLIENT) {
264 if (!acl_access_ok(ua, Client_ACL, client->name())) {
267 for (j=0; j<i; j++) {
268 if (strcmp(unique_client[j]->address, client->address) == 0 &&
269 unique_client[j]->FDport == client->FDport) {
275 unique_client[i++] = client;
276 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
281 /* Call each unique File daemon */
282 for (j=0; j<i; j++) {
283 do_client_status(ua, unique_client[j], NULL);
289 void list_dir_status_header(UAContext *ua)
291 char dt[MAX_TIME_LENGTH];
292 char b1[35], b2[35], b3[35], b4[35], b5[35];
294 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
295 HOST_OS, DISTNAME, DISTVER);
296 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
297 if (num_jobs_run == 1) {
298 ua->send_msg(_("Daemon started %s, 1 Job run since started.\n"), dt);
301 ua->send_msg(_("Daemon started %s, %d Jobs run since started.\n"),
304 ua->send_msg(_(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
305 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
306 edit_uint64_with_commas(sm_bytes, b2),
307 edit_uint64_with_commas(sm_max_bytes, b3),
308 edit_uint64_with_commas(sm_buffers, b4),
309 edit_uint64_with_commas(sm_max_buffers, b5));
312 static void do_director_status(UAContext *ua)
314 list_dir_status_header(ua);
317 * List scheduled Jobs
319 list_scheduled_jobs(ua);
324 list_running_jobs(ua);
327 * List terminated jobs
329 list_terminated_jobs(ua);
330 ua->send_msg("====\n");
333 static void do_storage_status(UAContext *ua, STORE *store, char *cmd)
338 lstore.store = store;
339 pm_strcpy(lstore.store_source, _("unknown source"));
340 set_wstorage(ua->jcr, &lstore);
341 /* Try connecting for up to 15 seconds */
342 if (!ua->api) ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
343 store->name(), store->address, store->SDport);
344 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
345 ua->send_msg(_("\nFailed to connect to Storage daemon %s.\n====\n"),
347 if (ua->jcr->store_bsock) {
348 bnet_close(ua->jcr->store_bsock);
349 ua->jcr->store_bsock = NULL;
353 Dmsg0(20, _("Connected to storage daemon\n"));
354 sd = ua->jcr->store_bsock;
356 sd->fsend(".status %s", cmd);
360 while (sd->recv() >= 0) {
361 ua->send_msg("%s", sd->msg);
363 sd->signal( BNET_TERMINATE);
365 ua->jcr->store_bsock = NULL;
369 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd)
373 /* Connect to File daemon */
375 ua->jcr->client = client;
376 /* Release any old dummy key */
377 if (ua->jcr->sd_auth_key) {
378 free(ua->jcr->sd_auth_key);
380 /* Create a new dummy SD auth key */
381 ua->jcr->sd_auth_key = bstrdup("dummy");
383 /* Try to connect for 15 seconds */
384 if (!ua->api) ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
385 client->name(), client->address, client->FDport);
386 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
387 ua->send_msg(_("Failed to connect to Client %s.\n====\n"),
389 if (ua->jcr->file_bsock) {
390 bnet_close(ua->jcr->file_bsock);
391 ua->jcr->file_bsock = NULL;
395 Dmsg0(20, _("Connected to file daemon\n"));
396 fd = ua->jcr->file_bsock;
398 fd->fsend(".status %s", cmd);
402 while (fd->recv() >= 0) {
403 ua->send_msg("%s", fd->msg);
405 fd->signal(BNET_TERMINATE);
407 ua->jcr->file_bsock = NULL;
412 static void prt_runhdr(UAContext *ua)
415 ua->send_msg(_("\nScheduled Jobs:\n"));
416 ua->send_msg(_("Level Type Pri Scheduled Name Volume\n"));
417 ua->send_msg(_("===================================================================================\n"));
421 /* Scheduling packet */
423 dlink link; /* keep this as first item!!! */
432 static void prt_runtime(UAContext *ua, sched_pkt *sp)
434 char dt[MAX_TIME_LENGTH];
435 const char *level_ptr;
437 bool close_db = false;
442 orig_jobtype = jcr->JobType;
443 memset(&mr, 0, sizeof(mr));
444 if (sp->job->JobType == JT_BACKUP) {
446 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
447 Dmsg1(250, "Using pool=%s\n", jcr->pool->name());
449 close_db = true; /* new db opened, remember to close it */
452 mr.PoolId = jcr->jr.PoolId;
453 mr.StorageId = sp->store->StorageId;
454 jcr->wstore = sp->store;
455 Dmsg0(250, "call find_next_volume_for_append\n");
456 ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune);
459 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
462 bstrftime_nc(dt, sizeof(dt), sp->runtime);
463 switch (sp->job->JobType) {
469 level_ptr = level_to_str(sp->level);
473 ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
474 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
475 sp->job->name(), mr.VolumeName);
477 ua->send_msg(_("%-14s %-8s %3d %-18s %-18s %s\n"),
478 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
479 sp->job->name(), mr.VolumeName);
482 db_close_database(jcr, jcr->db);
484 jcr->db = ua->db; /* restore ua db to jcr */
485 jcr->JobType = orig_jobtype;
489 * Sort items by runtime, priority
491 static int my_compare(void *item1, void *item2)
493 sched_pkt *p1 = (sched_pkt *)item1;
494 sched_pkt *p2 = (sched_pkt *)item2;
495 if (p1->runtime < p2->runtime) {
497 } else if (p1->runtime > p2->runtime) {
500 if (p1->priority < p2->priority) {
502 } else if (p1->priority > p2->priority) {
509 * Find all jobs to be run in roughly the
512 static void list_scheduled_jobs(UAContext *ua)
517 int level, num_jobs = 0;
519 bool hdr_printed = false;
524 Dmsg0(200, "enter list_sched_jobs()\n");
527 i = find_arg_with_value(ua, NT_("days"));
529 days = atoi(ua->argv[i]);
530 if ((days < 0) || (days > 500) && !ua->api) {
531 ua->send_msg(_("Ignoring invalid value for days. Max is 500.\n"));
536 /* Loop through all jobs */
538 foreach_res(job, R_JOB) {
539 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
542 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
544 level = job->JobLevel;
548 priority = job->Priority;
550 priority = run->Priority;
556 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
559 sp->priority = priority;
560 sp->runtime = runtime;
561 sp->pool = run->pool;
562 get_job_storage(&store, job, run);
563 sp->store = store.store;
564 Dmsg3(250, "job=%s store=%s MediaType=%s\n", job->name(), sp->store->name(), sp->store->media_type);
565 sched.binary_insert_multiple(sp, my_compare);
568 } /* end for loop over resources */
570 foreach_dlist(sp, &sched) {
573 if (num_jobs == 0 && !ua->api) {
574 ua->send_msg(_("No Scheduled Jobs.\n"));
576 if (!ua->api) ua->send_msg("====\n");
577 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
580 static void list_running_jobs(UAContext *ua)
585 char *emsg; /* edited message */
586 char dt[MAX_TIME_LENGTH];
588 bool pool_mem = false;
590 Dmsg0(200, "enter list_run_jobs()\n");
591 if (!ua->api) ua->send_msg(_("\nRunning Jobs:\n"));
593 if (jcr->JobId == 0) { /* this is us */
594 /* this is a console or other control job. We only show console
595 * jobs in the status output.
597 if (jcr->JobType == JT_CONSOLE && !ua->api) {
598 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
599 ua->send_msg(_("Console connected at %s\n"), dt);
608 /* Note the following message is used in regress -- don't change */
609 if (!ua->api) ua->send_msg(_("No Jobs running.\n====\n"));
610 Dmsg0(200, "leave list_run_jobs()\n");
615 ua->send_msg(_(" JobId Level Name Status\n"));
616 ua->send_msg(_("======================================================================\n"));
619 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
623 switch (jcr->JobStatus) {
625 msg = _("is waiting execution");
628 msg = _("is running");
631 msg = _("is blocked");
634 msg = _("has terminated");
636 case JS_ErrorTerminated:
637 msg = _("has erred");
640 msg = _("has errors");
643 msg = _("has a fatal error");
646 msg = _("has verify differences");
649 msg = _("has been canceled");
652 emsg = (char *) get_pool_memory(PM_FNAME);
654 Mmsg(emsg, _("is waiting on Client"));
656 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
662 emsg = (char *) get_pool_memory(PM_FNAME);
664 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
665 } else if (jcr->rstore) {
666 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
668 Mmsg(emsg, _("is waiting on Storage"));
673 case JS_WaitStoreRes:
674 msg = _("is waiting on max Storage jobs");
676 case JS_WaitClientRes:
677 msg = _("is waiting on max Client jobs");
680 msg = _("is waiting on max Job jobs");
683 msg = _("is waiting on max total jobs");
685 case JS_WaitStartTime:
686 msg = _("is waiting for its start time");
688 case JS_WaitPriority:
689 msg = _("is waiting for higher priority jobs to finish");
691 case JS_DataCommitting:
692 msg = _("SD committing Data");
694 case JS_DataDespooling:
695 msg = _("SD despooling Data");
697 case JS_AttrDespooling:
698 msg = _("SD despooling Attributes");
700 case JS_AttrInserting:
701 msg = _("Dir inserting Attributes");
705 emsg = (char *)get_pool_memory(PM_FNAME);
706 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
712 * Now report Storage daemon status code
714 switch (jcr->SDJobStatus) {
717 free_pool_memory(emsg);
720 msg = _("is waiting for a mount request");
724 free_pool_memory(emsg);
727 msg = _("is waiting for an appendable Volume");
731 emsg = (char *)get_pool_memory(PM_FNAME);
734 if (!jcr->client || !jcr->wstore) {
735 Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
737 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
738 jcr->client->name(), jcr->wstore->name());
742 case JS_DataCommitting:
743 msg = _("SD committing Data");
745 case JS_DataDespooling:
746 msg = _("SD despooling Data");
748 case JS_AttrDespooling:
749 msg = _("SD despooling Attributes");
751 case JS_AttrInserting:
752 msg = _("Dir inserting Attributes");
755 switch (jcr->JobType) {
758 bstrncpy(level, " ", sizeof(level));
761 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
767 ua->send_msg(_("%6d\t%-6s\t%-20s\t%s\n"),
768 jcr->JobId, level, jcr->Job, msg);
770 ua->send_msg(_("%6d %-6s %-20s %s\n"),
771 jcr->JobId, level, jcr->Job, msg);
775 free_pool_memory(emsg);
780 if (!ua->api) ua->send_msg("====\n");
781 Dmsg0(200, "leave list_run_jobs()\n");
784 static void list_terminated_jobs(UAContext *ua)
786 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
789 if (last_jobs->empty()) {
790 if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
793 lock_last_jobs_list();
794 struct s_last_job *je;
796 ua->send_msg(_("\nTerminated Jobs:\n"));
797 ua->send_msg(_(" JobId Level Files Bytes Status Finished Name \n"));
798 ua->send_msg(_("====================================================================\n"));
800 foreach_dlist(je, last_jobs) {
801 char JobName[MAX_NAME_LENGTH];
802 const char *termstat;
804 bstrncpy(JobName, je->Job, sizeof(JobName));
805 /* There are three periods after the Job name */
807 for (int i=0; i<3; i++) {
808 if ((p=strrchr(JobName, '.')) != NULL) {
813 if (!acl_access_ok(ua, Job_ACL, JobName)) {
817 bstrftime_nc(dt, sizeof(dt), je->end_time);
818 switch (je->JobType) {
821 bstrncpy(level, " ", sizeof(level));
824 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
828 switch (je->JobStatus) {
830 termstat = _("Created");
833 case JS_ErrorTerminated:
834 termstat = _("Error");
837 termstat = _("Diffs");
840 termstat = _("Cancel");
846 termstat = _("Other");
850 ua->send_msg(_("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
853 edit_uint64_with_commas(je->JobFiles, b1),
854 edit_uint64_with_suffix(je->JobBytes, b2),
858 ua->send_msg(_("%6d %-6s %8s %10s %-7s %-8s %s\n"),
861 edit_uint64_with_commas(je->JobFiles, b1),
862 edit_uint64_with_suffix(je->JobBytes, b2),
867 if (!ua->api) ua->send_msg(_("\n"));
868 unlock_last_jobs_list();