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);
397 close_db = true; /* new db opened, remember to close it */
400 mr.PoolId = jcr->jr.PoolId;
401 mr.StorageId = sp->store->StorageId;
402 jcr->wstore = sp->store;
403 ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
406 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
409 bstrftime_nc(dt, sizeof(dt), sp->runtime);
410 switch (sp->job->JobType) {
416 level_ptr = level_to_str(sp->level);
419 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
420 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
421 sp->job->name(), mr.VolumeName);
423 db_close_database(jcr, jcr->db);
425 jcr->db = ua->db; /* restore ua db to jcr */
429 * Sort items by runtime, priority
431 static int my_compare(void *item1, void *item2)
433 sched_pkt *p1 = (sched_pkt *)item1;
434 sched_pkt *p2 = (sched_pkt *)item2;
435 if (p1->runtime < p2->runtime) {
437 } else if (p1->runtime > p2->runtime) {
440 if (p1->priority < p2->priority) {
442 } else if (p1->priority > p2->priority) {
449 * Find all jobs to be run in roughly the
452 static void list_scheduled_jobs(UAContext *ua)
457 int level, num_jobs = 0;
459 bool hdr_printed = false;
464 Dmsg0(200, "enter list_sched_jobs()\n");
467 i = find_arg_with_value(ua, NT_("days"));
469 days = atoi(ua->argv[i]);
470 if ((days < 0) || (days > 50)) {
471 bsendmsg(ua, _("Ignoring invalid value for days. Max is 50.\n"));
476 /* Loop through all jobs */
478 foreach_res(job, R_JOB) {
479 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
482 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
484 level = job->JobLevel;
488 priority = job->Priority;
490 priority = run->Priority;
496 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
499 sp->priority = priority;
500 sp->runtime = runtime;
501 sp->pool = run->pool;
502 get_job_storage(&store, job, run);
503 sp->store = store.store;
504 sched.binary_insert_multiple(sp, my_compare);
507 } /* end for loop over resources */
509 foreach_dlist(sp, &sched) {
513 bsendmsg(ua, _("No Scheduled Jobs.\n"));
515 bsendmsg(ua, _("====\n"));
516 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
519 static void list_running_jobs(UAContext *ua)
524 char *emsg; /* edited message */
525 char dt[MAX_TIME_LENGTH];
527 bool pool_mem = false;
529 Dmsg0(200, "enter list_run_jobs()\n");
530 bsendmsg(ua, _("\nRunning Jobs:\n"));
532 if (jcr->JobId == 0) { /* this is us */
533 /* this is a console or other control job. We only show console
534 * jobs in the status output.
536 if (jcr->JobType == JT_CONSOLE) {
537 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
538 bsendmsg(ua, _("Console connected at %s\n"), dt);
547 /* Note the following message is used in regress -- don't change */
548 bsendmsg(ua, _("No Jobs running.\n====\n"));
549 Dmsg0(200, "leave list_run_jobs()\n");
553 bsendmsg(ua, _(" JobId Level Name Status\n"));
554 bsendmsg(ua, _("======================================================================\n"));
556 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
560 switch (jcr->JobStatus) {
562 msg = _("is waiting execution");
565 msg = _("is running");
568 msg = _("is blocked");
571 msg = _("has terminated");
573 case JS_ErrorTerminated:
574 msg = _("has erred");
577 msg = _("has errors");
580 msg = _("has a fatal error");
583 msg = _("has verify differences");
586 msg = _("has been canceled");
589 emsg = (char *) get_pool_memory(PM_FNAME);
590 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
595 emsg = (char *) get_pool_memory(PM_FNAME);
597 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
599 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
604 case JS_WaitStoreRes:
605 msg = _("is waiting on max Storage jobs");
607 case JS_WaitClientRes:
608 msg = _("is waiting on max Client jobs");
611 msg = _("is waiting on max Job jobs");
614 msg = _("is waiting on max total jobs");
616 case JS_WaitStartTime:
617 msg = _("is waiting for its start time");
619 case JS_WaitPriority:
620 msg = _("is waiting for higher priority jobs to finish");
624 emsg = (char *) get_pool_memory(PM_FNAME);
625 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
631 * Now report Storage daemon status code
633 switch (jcr->SDJobStatus) {
636 free_pool_memory(emsg);
639 msg = _("is waiting for a mount request");
643 free_pool_memory(emsg);
646 msg = _("is waiting for an appendable Volume");
650 emsg = (char *)get_pool_memory(PM_FNAME);
653 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
654 jcr->client->name(), jcr->wstore->name());
658 switch (jcr->JobType) {
661 bstrncpy(level, " ", sizeof(level));
664 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
669 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
676 free_pool_memory(emsg);
681 bsendmsg(ua, _("====\n"));
682 Dmsg0(200, "leave list_run_jobs()\n");
685 static void list_terminated_jobs(UAContext *ua)
687 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
690 if (last_jobs->empty()) {
691 bsendmsg(ua, _("No Terminated Jobs.\n"));
694 lock_last_jobs_list();
695 struct s_last_job *je;
696 bsendmsg(ua, _("\nTerminated Jobs:\n"));
697 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
698 bsendmsg(ua, _("====================================================================\n"));
699 foreach_dlist(je, last_jobs) {
700 char JobName[MAX_NAME_LENGTH];
701 const char *termstat;
703 bstrncpy(JobName, je->Job, sizeof(JobName));
704 /* There are three periods after the Job name */
706 for (int i=0; i<3; i++) {
707 if ((p=strrchr(JobName, '.')) != NULL) {
712 if (!acl_access_ok(ua, Job_ACL, JobName)) {
716 bstrftime_nc(dt, sizeof(dt), je->end_time);
717 switch (je->JobType) {
720 bstrncpy(level, " ", sizeof(level));
723 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
727 switch (je->JobStatus) {
729 termstat = _("Created");
732 case JS_ErrorTerminated:
733 termstat = _("Error");
736 termstat = _("Diffs");
739 termstat = _("Cancel");
745 termstat = _("Other");
748 bsendmsg(ua, _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
751 edit_uint64_with_commas(je->JobFiles, b1),
752 edit_uint64_with_suffix(je->JobBytes, b2),
756 bsendmsg(ua, _("\n"));
757 unlock_last_jobs_list();