2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2007 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 plus additions
11 that are listed in the file LICENSE.
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 static void list_scheduled_jobs(UAContext *ua);
42 static void list_running_jobs(UAContext *ua);
43 static void list_terminated_jobs(UAContext *ua);
44 static void do_storage_status(UAContext *ua, STORE *store);
45 static void do_client_status(UAContext *ua, CLIENT *client);
46 static void do_director_status(UAContext *ua);
47 static void do_all_status(UAContext *ua);
49 static char OKqstatus[] = "1000 OK .status\n";
50 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
56 bool dot_status_cmd(UAContext *ua, const char *cmd)
62 Dmsg1(20, "status:%s:\n", cmd);
64 if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
65 bsendmsg(ua, "1900 Bad .status command, missing arguments.\n");
69 if (strcasecmp(ua->argk[2], "current") == 0) {
70 bsendmsg(ua, OKqstatus, ua->argk[2]);
72 if (njcr->JobId != 0 && acl_access_ok(ua, Job_ACL, njcr->job->name())) {
73 bsendmsg(ua, DotStatusJob, edit_int64(njcr->JobId, ed1),
74 njcr->JobStatus, njcr->JobErrors);
78 } else if (strcasecmp(ua->argk[2], "last") == 0) {
79 bsendmsg(ua, OKqstatus, ua->argk[2]);
80 if ((last_jobs) && (last_jobs->size() > 0)) {
81 job = (s_last_job*)last_jobs->last();
82 if (acl_access_ok(ua, Job_ACL, job->Job)) {
83 bsendmsg(ua, DotStatusJob, edit_int64(job->JobId, ed1),
84 job->JobStatus, job->Errors);
88 bsendmsg(ua, "1900 Bad .status command, wrong argument.\n");
95 /* This is the *old* command handler, so we must return
96 * 1 or it closes the connection
98 int qstatus_cmd(UAContext *ua, const char *cmd)
100 dot_status_cmd(ua, cmd);
107 int status_cmd(UAContext *ua, const char *cmd)
113 Dmsg1(20, "status:%s:\n", cmd);
115 for (i=1; i<ua->argc; i++) {
116 if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
119 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
120 strcasecmp(ua->argk[i], NT_("director")) == 0) {
121 do_director_status(ua);
123 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
124 client = get_client_resource(ua);
126 do_client_status(ua, client);
130 store = get_storage_resource(ua, false/*no default*/);
132 do_storage_status(ua, store);
137 /* If no args, ask for status type */
139 char prmt[MAX_NAME_LENGTH];
141 start_prompt(ua, _("Status available for:\n"));
142 add_prompt(ua, NT_("Director"));
143 add_prompt(ua, NT_("Storage"));
144 add_prompt(ua, NT_("Client"));
145 add_prompt(ua, NT_("All"));
146 Dmsg0(20, "do_prompt: select daemon\n");
147 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
150 Dmsg1(20, "item=%d\n", item);
152 case 0: /* Director */
153 do_director_status(ua);
156 store = select_storage_resource(ua);
158 do_storage_status(ua, store);
162 client = select_client_resource(ua);
164 do_client_status(ua, client);
177 static void do_all_status(UAContext *ua)
179 STORE *store, **unique_store;
180 CLIENT *client, **unique_client;
184 do_director_status(ua);
186 /* Count Storage items */
189 foreach_res(store, R_STORAGE) {
192 unique_store = (STORE **) malloc(i * sizeof(STORE));
193 /* Find Unique Storage address/port */
195 foreach_res(store, R_STORAGE) {
197 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
200 for (j=0; j<i; j++) {
201 if (strcmp(unique_store[j]->address, store->address) == 0 &&
202 unique_store[j]->SDport == store->SDport) {
208 unique_store[i++] = store;
209 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
214 /* Call each unique Storage daemon */
215 for (j=0; j<i; j++) {
216 do_storage_status(ua, unique_store[j]);
220 /* Count Client items */
223 foreach_res(client, R_CLIENT) {
226 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
227 /* Find Unique Client address/port */
229 foreach_res(client, R_CLIENT) {
231 if (!acl_access_ok(ua, Client_ACL, client->name())) {
234 for (j=0; j<i; j++) {
235 if (strcmp(unique_client[j]->address, client->address) == 0 &&
236 unique_client[j]->FDport == client->FDport) {
242 unique_client[i++] = client;
243 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
248 /* Call each unique File daemon */
249 for (j=0; j<i; j++) {
250 do_client_status(ua, unique_client[j]);
256 static void do_director_status(UAContext *ua)
258 char dt[MAX_TIME_LENGTH];
259 char b1[35], b2[35], b3[35], b4[35];
261 bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
262 HOST_OS, DISTNAME, DISTVER);
263 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
264 if (num_jobs_run == 1) {
265 bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
268 bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
271 bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
272 edit_uint64_with_commas(sm_bytes, b1),
273 edit_uint64_with_commas(sm_max_bytes, b2),
274 edit_uint64_with_commas(sm_buffers, b3),
275 edit_uint64_with_commas(sm_max_buffers, b4));
278 * List scheduled Jobs
280 list_scheduled_jobs(ua);
285 list_running_jobs(ua);
288 * List terminated jobs
290 list_terminated_jobs(ua);
291 bsendmsg(ua, _("====\n"));
294 static void do_storage_status(UAContext *ua, STORE *store)
299 lstore.store = store;
300 pm_strcpy(lstore.store_source, _("unknown source"));
301 set_wstorage(ua->jcr, &lstore);
302 /* Try connecting for up to 15 seconds */
303 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
304 store->name(), store->address, store->SDport);
305 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
306 bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
308 if (ua->jcr->store_bsock) {
309 bnet_close(ua->jcr->store_bsock);
310 ua->jcr->store_bsock = NULL;
314 Dmsg0(20, _("Connected to storage daemon\n"));
315 sd = ua->jcr->store_bsock;
316 bnet_fsend(sd, "status");
317 while (bnet_recv(sd) >= 0) {
318 bsendmsg(ua, "%s", sd->msg);
320 bnet_sig(sd, BNET_TERMINATE);
322 ua->jcr->store_bsock = NULL;
326 static void do_client_status(UAContext *ua, CLIENT *client)
330 /* Connect to File daemon */
332 ua->jcr->client = client;
333 /* Release any old dummy key */
334 if (ua->jcr->sd_auth_key) {
335 free(ua->jcr->sd_auth_key);
337 /* Create a new dummy SD auth key */
338 ua->jcr->sd_auth_key = bstrdup("dummy");
340 /* Try to connect for 15 seconds */
341 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
342 client->name(), client->address, client->FDport);
343 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
344 bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
346 if (ua->jcr->file_bsock) {
347 bnet_close(ua->jcr->file_bsock);
348 ua->jcr->file_bsock = NULL;
352 Dmsg0(20, _("Connected to file daemon\n"));
353 fd = ua->jcr->file_bsock;
354 bnet_fsend(fd, "status");
355 while (bnet_recv(fd) >= 0) {
356 bsendmsg(ua, "%s", fd->msg);
358 bnet_sig(fd, BNET_TERMINATE);
360 ua->jcr->file_bsock = NULL;
365 static void prt_runhdr(UAContext *ua)
367 bsendmsg(ua, _("\nScheduled Jobs:\n"));
368 bsendmsg(ua, _("Level Type Pri Scheduled Name Volume\n"));
369 bsendmsg(ua, _("===================================================================================\n"));
372 /* Scheduling packet */
374 dlink link; /* keep this as first item!!! */
383 static void prt_runtime(UAContext *ua, sched_pkt *sp)
385 char dt[MAX_TIME_LENGTH];
386 const char *level_ptr;
388 bool close_db = false;
392 memset(&mr, 0, sizeof(mr));
393 if (sp->job->JobType == JT_BACKUP) {
395 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
396 Dmsg1(250, "Using pool=%s\n", jcr->pool->name());
398 close_db = true; /* new db opened, remember to close it */
401 mr.PoolId = jcr->jr.PoolId;
402 mr.StorageId = sp->store->StorageId;
403 jcr->wstore = sp->store;
404 Dmsg0(250, "call find_next_volume_for_append\n");
405 ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune);
408 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
411 bstrftime_nc(dt, sizeof(dt), sp->runtime);
412 switch (sp->job->JobType) {
418 level_ptr = level_to_str(sp->level);
421 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
422 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
423 sp->job->name(), mr.VolumeName);
425 db_close_database(jcr, jcr->db);
427 jcr->db = ua->db; /* restore ua db to jcr */
431 * Sort items by runtime, priority
433 static int my_compare(void *item1, void *item2)
435 sched_pkt *p1 = (sched_pkt *)item1;
436 sched_pkt *p2 = (sched_pkt *)item2;
437 if (p1->runtime < p2->runtime) {
439 } else if (p1->runtime > p2->runtime) {
442 if (p1->priority < p2->priority) {
444 } else if (p1->priority > p2->priority) {
451 * Find all jobs to be run in roughly the
454 static void list_scheduled_jobs(UAContext *ua)
459 int level, num_jobs = 0;
461 bool hdr_printed = false;
466 Dmsg0(200, "enter list_sched_jobs()\n");
469 i = find_arg_with_value(ua, NT_("days"));
471 days = atoi(ua->argv[i]);
472 if ((days < 0) || (days > 50)) {
473 bsendmsg(ua, _("Ignoring invalid value for days. Max is 50.\n"));
478 /* Loop through all jobs */
480 foreach_res(job, R_JOB) {
481 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
484 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
486 level = job->JobLevel;
490 priority = job->Priority;
492 priority = run->Priority;
498 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
501 sp->priority = priority;
502 sp->runtime = runtime;
503 sp->pool = run->pool;
504 get_job_storage(&store, job, run);
505 sp->store = store.store;
506 Dmsg3(250, "job=%s store=%s MediaType=%s\n", job->name(), sp->store->name(), sp->store->media_type);
507 sched.binary_insert_multiple(sp, my_compare);
510 } /* end for loop over resources */
512 foreach_dlist(sp, &sched) {
516 bsendmsg(ua, _("No Scheduled Jobs.\n"));
518 bsendmsg(ua, _("====\n"));
519 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
522 static void list_running_jobs(UAContext *ua)
527 char *emsg; /* edited message */
528 char dt[MAX_TIME_LENGTH];
530 bool pool_mem = false;
532 Dmsg0(200, "enter list_run_jobs()\n");
533 bsendmsg(ua, _("\nRunning Jobs:\n"));
535 if (jcr->JobId == 0) { /* this is us */
536 /* this is a console or other control job. We only show console
537 * jobs in the status output.
539 if (jcr->JobType == JT_CONSOLE) {
540 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
541 bsendmsg(ua, _("Console connected at %s\n"), dt);
550 /* Note the following message is used in regress -- don't change */
551 bsendmsg(ua, _("No Jobs running.\n====\n"));
552 Dmsg0(200, "leave list_run_jobs()\n");
556 bsendmsg(ua, _(" JobId Level Name Status\n"));
557 bsendmsg(ua, _("======================================================================\n"));
559 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
563 switch (jcr->JobStatus) {
565 msg = _("is waiting execution");
568 msg = _("is running");
571 msg = _("is blocked");
574 msg = _("has terminated");
576 case JS_ErrorTerminated:
577 msg = _("has erred");
580 msg = _("has errors");
583 msg = _("has a fatal error");
586 msg = _("has verify differences");
589 msg = _("has been canceled");
592 emsg = (char *) get_pool_memory(PM_FNAME);
593 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
598 emsg = (char *) get_pool_memory(PM_FNAME);
600 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
602 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
607 case JS_WaitStoreRes:
608 msg = _("is waiting on max Storage jobs");
610 case JS_WaitClientRes:
611 msg = _("is waiting on max Client jobs");
614 msg = _("is waiting on max Job jobs");
617 msg = _("is waiting on max total jobs");
619 case JS_WaitStartTime:
620 msg = _("is waiting for its start time");
622 case JS_WaitPriority:
623 msg = _("is waiting for higher priority jobs to finish");
627 emsg = (char *) get_pool_memory(PM_FNAME);
628 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
634 * Now report Storage daemon status code
636 switch (jcr->SDJobStatus) {
639 free_pool_memory(emsg);
642 msg = _("is waiting for a mount request");
646 free_pool_memory(emsg);
649 msg = _("is waiting for an appendable Volume");
653 emsg = (char *)get_pool_memory(PM_FNAME);
656 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
657 jcr->client->name(), jcr->wstore->name());
661 switch (jcr->JobType) {
664 bstrncpy(level, " ", sizeof(level));
667 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
672 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
679 free_pool_memory(emsg);
684 bsendmsg(ua, _("====\n"));
685 Dmsg0(200, "leave list_run_jobs()\n");
688 static void list_terminated_jobs(UAContext *ua)
690 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
693 if (last_jobs->empty()) {
694 bsendmsg(ua, _("No Terminated Jobs.\n"));
697 lock_last_jobs_list();
698 struct s_last_job *je;
699 bsendmsg(ua, _("\nTerminated Jobs:\n"));
700 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
701 bsendmsg(ua, _("====================================================================\n"));
702 foreach_dlist(je, last_jobs) {
703 char JobName[MAX_NAME_LENGTH];
704 const char *termstat;
706 bstrncpy(JobName, je->Job, sizeof(JobName));
707 /* There are three periods after the Job name */
709 for (int i=0; i<3; i++) {
710 if ((p=strrchr(JobName, '.')) != NULL) {
715 if (!acl_access_ok(ua, Job_ACL, JobName)) {
719 bstrftime_nc(dt, sizeof(dt), je->end_time);
720 switch (je->JobType) {
723 bstrncpy(level, " ", sizeof(level));
726 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
730 switch (je->JobStatus) {
732 termstat = _("Created");
735 case JS_ErrorTerminated:
736 termstat = _("Error");
739 termstat = _("Diffs");
742 termstat = _("Cancel");
748 termstat = _("Other");
751 bsendmsg(ua, _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
754 edit_uint64_with_commas(je->JobFiles, b1),
755 edit_uint64_with_suffix(je->JobBytes, b2),
759 bsendmsg(ua, _("\n"));
760 unlock_last_jobs_list();