3 * Bacula Director -- User Agent Status Command
5 * Kern Sibbald, August MMI
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2001-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
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)
64 Dmsg1(20, "status:%s:\n", cmd);
66 if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
67 bsendmsg(ua, "1900 Bad .status command, missing arguments.\n");
71 if (strcasecmp(ua->argk[2], "current") == 0) {
72 bsendmsg(ua, OKqstatus, ua->argk[2]);
74 if (njcr->JobId != 0) {
75 bsendmsg(ua, DotStatusJob, edit_int64(njcr->JobId, ed1),
76 njcr->JobStatus, njcr->JobErrors);
80 } else if (strcasecmp(ua->argk[2], "last") == 0) {
81 bsendmsg(ua, OKqstatus, ua->argk[2]);
82 if ((last_jobs) && (last_jobs->size() > 0)) {
83 job = (s_last_job*)last_jobs->last();
84 bsendmsg(ua, DotStatusJob, edit_int64(job->JobId, ed1),
85 job->JobStatus, job->Errors);
88 bsendmsg(ua, "1900 Bad .status command, wrong argument.\n");
98 int status_cmd(UAContext *ua, const char *cmd)
107 Dmsg1(20, "status:%s:\n", cmd);
109 for (i=1; i<ua->argc; i++) {
110 if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
113 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
114 strcasecmp(ua->argk[i], NT_("director")) == 0) {
115 do_director_status(ua);
117 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
118 client = get_client_resource(ua);
120 do_client_status(ua, client);
124 store = get_storage_resource(ua, false/*no default*/);
126 do_storage_status(ua, store);
131 /* If no args, ask for status type */
133 char prmt[MAX_NAME_LENGTH];
135 start_prompt(ua, _("Status available for:\n"));
136 add_prompt(ua, NT_("Director"));
137 add_prompt(ua, NT_("Storage"));
138 add_prompt(ua, NT_("Client"));
139 add_prompt(ua, NT_("All"));
140 Dmsg0(20, "do_prompt: select daemon\n");
141 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
144 Dmsg1(20, "item=%d\n", item);
146 case 0: /* Director */
147 do_director_status(ua);
150 store = select_storage_resource(ua);
152 do_storage_status(ua, store);
156 client = select_client_resource(ua);
158 do_client_status(ua, client);
171 static void do_all_status(UAContext *ua)
173 STORE *store, **unique_store;
174 CLIENT *client, **unique_client;
178 do_director_status(ua);
180 /* Count Storage items */
183 foreach_res(store, R_STORAGE) {
186 unique_store = (STORE **) malloc(i * sizeof(STORE));
187 /* Find Unique Storage address/port */
189 foreach_res(store, R_STORAGE) {
191 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
194 for (j=0; j<i; j++) {
195 if (strcmp(unique_store[j]->address, store->address) == 0 &&
196 unique_store[j]->SDport == store->SDport) {
202 unique_store[i++] = store;
203 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
208 /* Call each unique Storage daemon */
209 for (j=0; j<i; j++) {
210 do_storage_status(ua, unique_store[j]);
214 /* Count Client items */
217 foreach_res(client, R_CLIENT) {
220 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
221 /* Find Unique Client address/port */
223 foreach_res(client, R_CLIENT) {
225 if (!acl_access_ok(ua, Client_ACL, client->name())) {
228 for (j=0; j<i; j++) {
229 if (strcmp(unique_client[j]->address, client->address) == 0 &&
230 unique_client[j]->FDport == client->FDport) {
236 unique_client[i++] = client;
237 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
242 /* Call each unique File daemon */
243 for (j=0; j<i; j++) {
244 do_client_status(ua, unique_client[j]);
250 static void do_director_status(UAContext *ua)
252 char dt[MAX_TIME_LENGTH];
253 char b1[35], b2[35], b3[35], b4[35];
255 bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
256 HOST_OS, DISTNAME, DISTVER);
257 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
258 if (num_jobs_run == 1) {
259 bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
262 bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
265 bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
266 edit_uint64_with_commas(sm_bytes, b1),
267 edit_uint64_with_commas(sm_max_bytes, b2),
268 edit_uint64_with_commas(sm_buffers, b3),
269 edit_uint64_with_commas(sm_max_buffers, b4));
272 * List scheduled Jobs
274 list_scheduled_jobs(ua);
279 list_running_jobs(ua);
282 * List terminated jobs
284 list_terminated_jobs(ua);
285 bsendmsg(ua, _("====\n"));
288 static void do_storage_status(UAContext *ua, STORE *store)
293 lstore.store = store;
294 pm_strcpy(lstore.store_source, _("unknown source"));
295 set_wstorage(ua->jcr, &lstore);
296 /* Try connecting for up to 15 seconds */
297 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
298 store->name(), store->address, store->SDport);
299 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
300 bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
302 if (ua->jcr->store_bsock) {
303 bnet_close(ua->jcr->store_bsock);
304 ua->jcr->store_bsock = NULL;
308 Dmsg0(20, _("Connected to storage daemon\n"));
309 sd = ua->jcr->store_bsock;
310 bnet_fsend(sd, "status");
311 while (bnet_recv(sd) >= 0) {
312 bsendmsg(ua, "%s", sd->msg);
314 bnet_sig(sd, BNET_TERMINATE);
316 ua->jcr->store_bsock = NULL;
320 static void do_client_status(UAContext *ua, CLIENT *client)
324 /* Connect to File daemon */
326 ua->jcr->client = client;
327 /* Release any old dummy key */
328 if (ua->jcr->sd_auth_key) {
329 free(ua->jcr->sd_auth_key);
331 /* Create a new dummy SD auth key */
332 ua->jcr->sd_auth_key = bstrdup("dummy");
334 /* Try to connect for 15 seconds */
335 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
336 client->name(), client->address, client->FDport);
337 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
338 bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
340 if (ua->jcr->file_bsock) {
341 bnet_close(ua->jcr->file_bsock);
342 ua->jcr->file_bsock = NULL;
346 Dmsg0(20, _("Connected to file daemon\n"));
347 fd = ua->jcr->file_bsock;
348 bnet_fsend(fd, "status");
349 while (bnet_recv(fd) >= 0) {
350 bsendmsg(ua, "%s", fd->msg);
352 bnet_sig(fd, BNET_TERMINATE);
354 ua->jcr->file_bsock = NULL;
359 static void prt_runhdr(UAContext *ua)
361 bsendmsg(ua, _("\nScheduled Jobs:\n"));
362 bsendmsg(ua, _("Level Type Pri Scheduled Name Volume\n"));
363 bsendmsg(ua, _("===================================================================================\n"));
366 /* Scheduling packet */
368 dlink link; /* keep this as first item!!! */
377 static void prt_runtime(UAContext *ua, sched_pkt *sp)
379 char dt[MAX_TIME_LENGTH];
380 const char *level_ptr;
382 bool close_db = false;
386 memset(&mr, 0, sizeof(mr));
387 if (sp->job->JobType == JT_BACKUP) {
389 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
391 close_db = true; /* new db opened, remember to close it */
394 mr.PoolId = jcr->jr.PoolId;
395 mr.StorageId = sp->store->StorageId;
396 jcr->wstore = sp->store;
397 ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
400 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
403 bstrftime_nc(dt, sizeof(dt), sp->runtime);
404 switch (sp->job->JobType) {
410 level_ptr = level_to_str(sp->level);
413 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
414 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
415 sp->job->name(), mr.VolumeName);
417 db_close_database(jcr, jcr->db);
419 jcr->db = ua->db; /* restore ua db to jcr */
423 * Sort items by runtime, priority
425 static int my_compare(void *item1, void *item2)
427 sched_pkt *p1 = (sched_pkt *)item1;
428 sched_pkt *p2 = (sched_pkt *)item2;
429 if (p1->runtime < p2->runtime) {
431 } else if (p1->runtime > p2->runtime) {
434 if (p1->priority < p2->priority) {
436 } else if (p1->priority > p2->priority) {
443 * Find all jobs to be run in roughly the
446 static void list_scheduled_jobs(UAContext *ua)
451 int level, num_jobs = 0;
453 bool hdr_printed = false;
458 Dmsg0(200, "enter list_sched_jobs()\n");
461 i = find_arg_with_value(ua, NT_("days"));
463 days = atoi(ua->argv[i]);
464 if ((days < 0) || (days > 50)) {
465 bsendmsg(ua, _("Ignoring invalid value for days. Max is 50.\n"));
470 /* Loop through all jobs */
472 foreach_res(job, R_JOB) {
473 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
476 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
478 level = job->JobLevel;
482 priority = job->Priority;
484 priority = run->Priority;
490 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
493 sp->priority = priority;
494 sp->runtime = runtime;
495 sp->pool = run->pool;
496 get_job_storage(&store, job, run);
497 sp->store = store.store;
498 sched.binary_insert_multiple(sp, my_compare);
501 } /* end for loop over resources */
503 foreach_dlist(sp, &sched) {
507 bsendmsg(ua, _("No Scheduled Jobs.\n"));
509 bsendmsg(ua, _("====\n"));
510 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
513 static void list_running_jobs(UAContext *ua)
518 char *emsg; /* edited message */
519 char dt[MAX_TIME_LENGTH];
521 bool pool_mem = false;
523 Dmsg0(200, "enter list_run_jobs()\n");
524 bsendmsg(ua, _("\nRunning Jobs:\n"));
526 if (jcr->JobId == 0) { /* this is us */
527 /* this is a console or other control job. We only show console
528 * jobs in the status output.
530 if (jcr->JobType == JT_CONSOLE) {
531 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
532 bsendmsg(ua, _("Console connected at %s\n"), dt);
541 /* Note the following message is used in regress -- don't change */
542 bsendmsg(ua, _("No Jobs running.\n====\n"));
543 Dmsg0(200, "leave list_run_jobs()\n");
547 bsendmsg(ua, _(" JobId Level Name Status\n"));
548 bsendmsg(ua, _("======================================================================\n"));
550 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
554 switch (jcr->JobStatus) {
556 msg = _("is waiting execution");
559 msg = _("is running");
562 msg = _("is blocked");
565 msg = _("has terminated");
567 case JS_ErrorTerminated:
568 msg = _("has erred");
571 msg = _("has errors");
574 msg = _("has a fatal error");
577 msg = _("has verify differences");
580 msg = _("has been canceled");
583 emsg = (char *) get_pool_memory(PM_FNAME);
584 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
589 emsg = (char *) get_pool_memory(PM_FNAME);
591 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
593 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
598 case JS_WaitStoreRes:
599 msg = _("is waiting on max Storage jobs");
601 case JS_WaitClientRes:
602 msg = _("is waiting on max Client jobs");
605 msg = _("is waiting on max Job jobs");
608 msg = _("is waiting on max total jobs");
610 case JS_WaitStartTime:
611 msg = _("is waiting for its start time");
613 case JS_WaitPriority:
614 msg = _("is waiting for higher priority jobs to finish");
618 emsg = (char *) get_pool_memory(PM_FNAME);
619 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
625 * Now report Storage daemon status code
627 switch (jcr->SDJobStatus) {
630 free_pool_memory(emsg);
633 msg = _("is waiting for a mount request");
637 free_pool_memory(emsg);
640 msg = _("is waiting for an appendable Volume");
644 emsg = (char *) get_pool_memory(PM_FNAME);
647 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
648 jcr->client->name(), jcr->wstore->name());
652 switch (jcr->JobType) {
655 bstrncpy(level, " ", sizeof(level));
658 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
663 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
670 free_pool_memory(emsg);
675 bsendmsg(ua, _("====\n"));
676 Dmsg0(200, "leave list_run_jobs()\n");
679 static void list_terminated_jobs(UAContext *ua)
681 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
684 if (last_jobs->empty()) {
685 bsendmsg(ua, _("No Terminated Jobs.\n"));
688 lock_last_jobs_list();
689 struct s_last_job *je;
690 bsendmsg(ua, _("\nTerminated Jobs:\n"));
691 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
692 bsendmsg(ua, _("====================================================================\n"));
693 foreach_dlist(je, last_jobs) {
694 char JobName[MAX_NAME_LENGTH];
695 const char *termstat;
697 bstrncpy(JobName, je->Job, sizeof(JobName));
698 /* There are three periods after the Job name */
700 for (int i=0; i<3; i++) {
701 if ((p=strrchr(JobName, '.')) != NULL) {
706 if (!acl_access_ok(ua, Job_ACL, JobName)) {
710 bstrftime_nc(dt, sizeof(dt), je->end_time);
711 switch (je->JobType) {
714 bstrncpy(level, " ", sizeof(level));
717 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
721 switch (je->JobStatus) {
723 termstat = _("Created");
726 case JS_ErrorTerminated:
727 termstat = _("Error");
730 termstat = _("Diffs");
733 termstat = _("Cancel");
739 termstat = _("Other");
742 bsendmsg(ua, _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
745 edit_uint64_with_commas(je->JobFiles, b1),
746 edit_uint64_with_suffix(je->JobBytes, b2),
750 bsendmsg(ua, _("\n"));
751 unlock_last_jobs_list();