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";
55 int qstatus_cmd(UAContext *ua, const char *cmd)
61 Dmsg1(20, "status:%s:\n", cmd);
63 if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
64 bsendmsg(ua, "1900 Bad .status command, missing arguments.\n");
68 if (strcasecmp(ua->argk[2], "current") == 0) {
69 bsendmsg(ua, OKqstatus, ua->argk[2]);
71 if (njcr->JobId != 0 && acl_access_ok(ua, Job_ACL, njcr->job->name())) {
72 bsendmsg(ua, DotStatusJob, edit_int64(njcr->JobId, ed1),
73 njcr->JobStatus, njcr->JobErrors);
77 } else if (strcasecmp(ua->argk[2], "last") == 0) {
78 bsendmsg(ua, OKqstatus, ua->argk[2]);
79 if ((last_jobs) && (last_jobs->size() > 0)) {
80 job = (s_last_job*)last_jobs->last();
81 if (acl_access_ok(ua, Job_ACL, job->Job)) {
82 bsendmsg(ua, DotStatusJob, edit_int64(job->JobId, ed1),
83 job->JobStatus, job->Errors);
87 bsendmsg(ua, "1900 Bad .status command, wrong argument.\n");
97 int status_cmd(UAContext *ua, const char *cmd)
103 Dmsg1(20, "status:%s:\n", cmd);
105 for (i=1; i<ua->argc; i++) {
106 if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
109 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
110 strcasecmp(ua->argk[i], NT_("director")) == 0) {
111 do_director_status(ua);
113 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
114 client = get_client_resource(ua);
116 do_client_status(ua, client);
120 store = get_storage_resource(ua, false/*no default*/);
122 do_storage_status(ua, store);
127 /* If no args, ask for status type */
129 char prmt[MAX_NAME_LENGTH];
131 start_prompt(ua, _("Status available for:\n"));
132 add_prompt(ua, NT_("Director"));
133 add_prompt(ua, NT_("Storage"));
134 add_prompt(ua, NT_("Client"));
135 add_prompt(ua, NT_("All"));
136 Dmsg0(20, "do_prompt: select daemon\n");
137 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
140 Dmsg1(20, "item=%d\n", item);
142 case 0: /* Director */
143 do_director_status(ua);
146 store = select_storage_resource(ua);
148 do_storage_status(ua, store);
152 client = select_client_resource(ua);
154 do_client_status(ua, client);
167 static void do_all_status(UAContext *ua)
169 STORE *store, **unique_store;
170 CLIENT *client, **unique_client;
174 do_director_status(ua);
176 /* Count Storage items */
179 foreach_res(store, R_STORAGE) {
182 unique_store = (STORE **) malloc(i * sizeof(STORE));
183 /* Find Unique Storage address/port */
185 foreach_res(store, R_STORAGE) {
187 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
190 for (j=0; j<i; j++) {
191 if (strcmp(unique_store[j]->address, store->address) == 0 &&
192 unique_store[j]->SDport == store->SDport) {
198 unique_store[i++] = store;
199 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
204 /* Call each unique Storage daemon */
205 for (j=0; j<i; j++) {
206 do_storage_status(ua, unique_store[j]);
210 /* Count Client items */
213 foreach_res(client, R_CLIENT) {
216 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
217 /* Find Unique Client address/port */
219 foreach_res(client, R_CLIENT) {
221 if (!acl_access_ok(ua, Client_ACL, client->name())) {
224 for (j=0; j<i; j++) {
225 if (strcmp(unique_client[j]->address, client->address) == 0 &&
226 unique_client[j]->FDport == client->FDport) {
232 unique_client[i++] = client;
233 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
238 /* Call each unique File daemon */
239 for (j=0; j<i; j++) {
240 do_client_status(ua, unique_client[j]);
246 static void do_director_status(UAContext *ua)
248 char dt[MAX_TIME_LENGTH];
249 char b1[35], b2[35], b3[35], b4[35];
251 bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
252 HOST_OS, DISTNAME, DISTVER);
253 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
254 if (num_jobs_run == 1) {
255 bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
258 bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
261 bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
262 edit_uint64_with_commas(sm_bytes, b1),
263 edit_uint64_with_commas(sm_max_bytes, b2),
264 edit_uint64_with_commas(sm_buffers, b3),
265 edit_uint64_with_commas(sm_max_buffers, b4));
268 * List scheduled Jobs
270 list_scheduled_jobs(ua);
275 list_running_jobs(ua);
278 * List terminated jobs
280 list_terminated_jobs(ua);
281 bsendmsg(ua, _("====\n"));
284 static void do_storage_status(UAContext *ua, STORE *store)
289 lstore.store = store;
290 pm_strcpy(lstore.store_source, _("unknown source"));
291 set_wstorage(ua->jcr, &lstore);
292 /* Try connecting for up to 15 seconds */
293 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
294 store->name(), store->address, store->SDport);
295 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
296 bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
298 if (ua->jcr->store_bsock) {
299 bnet_close(ua->jcr->store_bsock);
300 ua->jcr->store_bsock = NULL;
304 Dmsg0(20, _("Connected to storage daemon\n"));
305 sd = ua->jcr->store_bsock;
306 bnet_fsend(sd, "status");
307 while (bnet_recv(sd) >= 0) {
308 bsendmsg(ua, "%s", sd->msg);
310 bnet_sig(sd, BNET_TERMINATE);
312 ua->jcr->store_bsock = NULL;
316 static void do_client_status(UAContext *ua, CLIENT *client)
320 /* Connect to File daemon */
322 ua->jcr->client = client;
323 /* Release any old dummy key */
324 if (ua->jcr->sd_auth_key) {
325 free(ua->jcr->sd_auth_key);
327 /* Create a new dummy SD auth key */
328 ua->jcr->sd_auth_key = bstrdup("dummy");
330 /* Try to connect for 15 seconds */
331 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
332 client->name(), client->address, client->FDport);
333 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
334 bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
336 if (ua->jcr->file_bsock) {
337 bnet_close(ua->jcr->file_bsock);
338 ua->jcr->file_bsock = NULL;
342 Dmsg0(20, _("Connected to file daemon\n"));
343 fd = ua->jcr->file_bsock;
344 bnet_fsend(fd, "status");
345 while (bnet_recv(fd) >= 0) {
346 bsendmsg(ua, "%s", fd->msg);
348 bnet_sig(fd, BNET_TERMINATE);
350 ua->jcr->file_bsock = NULL;
355 static void prt_runhdr(UAContext *ua)
357 bsendmsg(ua, _("\nScheduled Jobs:\n"));
358 bsendmsg(ua, _("Level Type Pri Scheduled Name Volume\n"));
359 bsendmsg(ua, _("===================================================================================\n"));
362 /* Scheduling packet */
364 dlink link; /* keep this as first item!!! */
373 static void prt_runtime(UAContext *ua, sched_pkt *sp)
375 char dt[MAX_TIME_LENGTH];
376 const char *level_ptr;
378 bool close_db = false;
382 memset(&mr, 0, sizeof(mr));
383 if (sp->job->JobType == JT_BACKUP) {
385 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
387 close_db = true; /* new db opened, remember to close it */
390 mr.PoolId = jcr->jr.PoolId;
391 mr.StorageId = sp->store->StorageId;
392 jcr->wstore = sp->store;
393 ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
396 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
399 bstrftime_nc(dt, sizeof(dt), sp->runtime);
400 switch (sp->job->JobType) {
406 level_ptr = level_to_str(sp->level);
409 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
410 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
411 sp->job->name(), mr.VolumeName);
413 db_close_database(jcr, jcr->db);
415 jcr->db = ua->db; /* restore ua db to jcr */
419 * Sort items by runtime, priority
421 static int my_compare(void *item1, void *item2)
423 sched_pkt *p1 = (sched_pkt *)item1;
424 sched_pkt *p2 = (sched_pkt *)item2;
425 if (p1->runtime < p2->runtime) {
427 } else if (p1->runtime > p2->runtime) {
430 if (p1->priority < p2->priority) {
432 } else if (p1->priority > p2->priority) {
439 * Find all jobs to be run in roughly the
442 static void list_scheduled_jobs(UAContext *ua)
447 int level, num_jobs = 0;
449 bool hdr_printed = false;
454 Dmsg0(200, "enter list_sched_jobs()\n");
457 i = find_arg_with_value(ua, NT_("days"));
459 days = atoi(ua->argv[i]);
460 if ((days < 0) || (days > 50)) {
461 bsendmsg(ua, _("Ignoring invalid value for days. Max is 50.\n"));
466 /* Loop through all jobs */
468 foreach_res(job, R_JOB) {
469 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
472 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
474 level = job->JobLevel;
478 priority = job->Priority;
480 priority = run->Priority;
486 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
489 sp->priority = priority;
490 sp->runtime = runtime;
491 sp->pool = run->pool;
492 get_job_storage(&store, job, run);
493 sp->store = store.store;
494 sched.binary_insert_multiple(sp, my_compare);
497 } /* end for loop over resources */
499 foreach_dlist(sp, &sched) {
503 bsendmsg(ua, _("No Scheduled Jobs.\n"));
505 bsendmsg(ua, _("====\n"));
506 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
509 static void list_running_jobs(UAContext *ua)
514 char *emsg; /* edited message */
515 char dt[MAX_TIME_LENGTH];
517 bool pool_mem = false;
519 Dmsg0(200, "enter list_run_jobs()\n");
520 bsendmsg(ua, _("\nRunning Jobs:\n"));
522 if (jcr->JobId == 0) { /* this is us */
523 /* this is a console or other control job. We only show console
524 * jobs in the status output.
526 if (jcr->JobType == JT_CONSOLE) {
527 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
528 bsendmsg(ua, _("Console connected at %s\n"), dt);
537 /* Note the following message is used in regress -- don't change */
538 bsendmsg(ua, _("No Jobs running.\n====\n"));
539 Dmsg0(200, "leave list_run_jobs()\n");
543 bsendmsg(ua, _(" JobId Level Name Status\n"));
544 bsendmsg(ua, _("======================================================================\n"));
546 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
550 switch (jcr->JobStatus) {
552 msg = _("is waiting execution");
555 msg = _("is running");
558 msg = _("is blocked");
561 msg = _("has terminated");
563 case JS_ErrorTerminated:
564 msg = _("has erred");
567 msg = _("has errors");
570 msg = _("has a fatal error");
573 msg = _("has verify differences");
576 msg = _("has been canceled");
579 emsg = (char *) get_pool_memory(PM_FNAME);
580 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
585 emsg = (char *) get_pool_memory(PM_FNAME);
587 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
589 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
594 case JS_WaitStoreRes:
595 msg = _("is waiting on max Storage jobs");
597 case JS_WaitClientRes:
598 msg = _("is waiting on max Client jobs");
601 msg = _("is waiting on max Job jobs");
604 msg = _("is waiting on max total jobs");
606 case JS_WaitStartTime:
607 msg = _("is waiting for its start time");
609 case JS_WaitPriority:
610 msg = _("is waiting for higher priority jobs to finish");
614 emsg = (char *) get_pool_memory(PM_FNAME);
615 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
621 * Now report Storage daemon status code
623 switch (jcr->SDJobStatus) {
626 free_pool_memory(emsg);
629 msg = _("is waiting for a mount request");
633 free_pool_memory(emsg);
636 msg = _("is waiting for an appendable Volume");
640 emsg = (char *)get_pool_memory(PM_FNAME);
643 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
644 jcr->client->name(), jcr->wstore->name());
648 switch (jcr->JobType) {
651 bstrncpy(level, " ", sizeof(level));
654 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
659 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
666 free_pool_memory(emsg);
671 bsendmsg(ua, _("====\n"));
672 Dmsg0(200, "leave list_run_jobs()\n");
675 static void list_terminated_jobs(UAContext *ua)
677 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
680 if (last_jobs->empty()) {
681 bsendmsg(ua, _("No Terminated Jobs.\n"));
684 lock_last_jobs_list();
685 struct s_last_job *je;
686 bsendmsg(ua, _("\nTerminated Jobs:\n"));
687 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
688 bsendmsg(ua, _("====================================================================\n"));
689 foreach_dlist(je, last_jobs) {
690 char JobName[MAX_NAME_LENGTH];
691 const char *termstat;
693 bstrncpy(JobName, je->Job, sizeof(JobName));
694 /* There are three periods after the Job name */
696 for (int i=0; i<3; i++) {
697 if ((p=strrchr(JobName, '.')) != NULL) {
702 if (!acl_access_ok(ua, Job_ACL, JobName)) {
706 bstrftime_nc(dt, sizeof(dt), je->end_time);
707 switch (je->JobType) {
710 bstrncpy(level, " ", sizeof(level));
713 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
717 switch (je->JobStatus) {
719 termstat = _("Created");
722 case JS_ErrorTerminated:
723 termstat = _("Error");
726 termstat = _("Diffs");
729 termstat = _("Cancel");
735 termstat = _("Other");
738 bsendmsg(ua, _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
741 edit_uint64_with_commas(je->JobFiles, b1),
742 edit_uint64_with_suffix(je->JobBytes, b2),
746 bsendmsg(ua, _("\n"));
747 unlock_last_jobs_list();