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)
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 if (!open_client_db(ua)) {
106 Dmsg1(20, "status:%s:\n", cmd);
108 for (i=1; i<ua->argc; i++) {
109 if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
112 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
113 strcasecmp(ua->argk[i], NT_("director")) == 0) {
114 do_director_status(ua);
116 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
117 client = get_client_resource(ua);
119 do_client_status(ua, client);
123 store = get_storage_resource(ua, false/*no default*/);
125 do_storage_status(ua, store);
130 /* If no args, ask for status type */
132 char prmt[MAX_NAME_LENGTH];
134 start_prompt(ua, _("Status available for:\n"));
135 add_prompt(ua, NT_("Director"));
136 add_prompt(ua, NT_("Storage"));
137 add_prompt(ua, NT_("Client"));
138 add_prompt(ua, NT_("All"));
139 Dmsg0(20, "do_prompt: select daemon\n");
140 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
143 Dmsg1(20, "item=%d\n", item);
145 case 0: /* Director */
146 do_director_status(ua);
149 store = select_storage_resource(ua);
151 do_storage_status(ua, store);
155 client = select_client_resource(ua);
157 do_client_status(ua, client);
170 static void do_all_status(UAContext *ua)
172 STORE *store, **unique_store;
173 CLIENT *client, **unique_client;
177 do_director_status(ua);
179 /* Count Storage items */
182 foreach_res(store, R_STORAGE) {
185 unique_store = (STORE **) malloc(i * sizeof(STORE));
186 /* Find Unique Storage address/port */
188 foreach_res(store, R_STORAGE) {
190 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
193 for (j=0; j<i; j++) {
194 if (strcmp(unique_store[j]->address, store->address) == 0 &&
195 unique_store[j]->SDport == store->SDport) {
201 unique_store[i++] = store;
202 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
207 /* Call each unique Storage daemon */
208 for (j=0; j<i; j++) {
209 do_storage_status(ua, unique_store[j]);
213 /* Count Client items */
216 foreach_res(client, R_CLIENT) {
219 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
220 /* Find Unique Client address/port */
222 foreach_res(client, R_CLIENT) {
224 if (!acl_access_ok(ua, Client_ACL, client->name())) {
227 for (j=0; j<i; j++) {
228 if (strcmp(unique_client[j]->address, client->address) == 0 &&
229 unique_client[j]->FDport == client->FDport) {
235 unique_client[i++] = client;
236 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
241 /* Call each unique File daemon */
242 for (j=0; j<i; j++) {
243 do_client_status(ua, unique_client[j]);
249 static void do_director_status(UAContext *ua)
251 char dt[MAX_TIME_LENGTH];
252 char b1[35], b2[35], b3[35], b4[35];
254 bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
255 HOST_OS, DISTNAME, DISTVER);
256 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
257 if (num_jobs_run == 1) {
258 bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
261 bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
264 bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
265 edit_uint64_with_commas(sm_bytes, b1),
266 edit_uint64_with_commas(sm_max_bytes, b2),
267 edit_uint64_with_commas(sm_buffers, b3),
268 edit_uint64_with_commas(sm_max_buffers, b4));
271 * List scheduled Jobs
273 list_scheduled_jobs(ua);
278 list_running_jobs(ua);
281 * List terminated jobs
283 list_terminated_jobs(ua);
284 bsendmsg(ua, _("====\n"));
287 static void do_storage_status(UAContext *ua, STORE *store)
292 lstore.store = store;
293 pm_strcpy(lstore.store_source, _("unknown source"));
294 set_wstorage(ua->jcr, &lstore);
295 /* Try connecting for up to 15 seconds */
296 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
297 store->name(), store->address, store->SDport);
298 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
299 bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
301 if (ua->jcr->store_bsock) {
302 bnet_close(ua->jcr->store_bsock);
303 ua->jcr->store_bsock = NULL;
307 Dmsg0(20, _("Connected to storage daemon\n"));
308 sd = ua->jcr->store_bsock;
309 bnet_fsend(sd, "status");
310 while (bnet_recv(sd) >= 0) {
311 bsendmsg(ua, "%s", sd->msg);
313 bnet_sig(sd, BNET_TERMINATE);
315 ua->jcr->store_bsock = NULL;
319 static void do_client_status(UAContext *ua, CLIENT *client)
323 /* Connect to File daemon */
325 ua->jcr->client = client;
326 /* Release any old dummy key */
327 if (ua->jcr->sd_auth_key) {
328 free(ua->jcr->sd_auth_key);
330 /* Create a new dummy SD auth key */
331 ua->jcr->sd_auth_key = bstrdup("dummy");
333 /* Try to connect for 15 seconds */
334 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
335 client->name(), client->address, client->FDport);
336 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
337 bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
339 if (ua->jcr->file_bsock) {
340 bnet_close(ua->jcr->file_bsock);
341 ua->jcr->file_bsock = NULL;
345 Dmsg0(20, _("Connected to file daemon\n"));
346 fd = ua->jcr->file_bsock;
347 bnet_fsend(fd, "status");
348 while (bnet_recv(fd) >= 0) {
349 bsendmsg(ua, "%s", fd->msg);
351 bnet_sig(fd, BNET_TERMINATE);
353 ua->jcr->file_bsock = NULL;
358 static void prt_runhdr(UAContext *ua)
360 bsendmsg(ua, _("\nScheduled Jobs:\n"));
361 bsendmsg(ua, _("Level Type Pri Scheduled Name Volume\n"));
362 bsendmsg(ua, _("===================================================================================\n"));
365 /* Scheduling packet */
367 dlink link; /* keep this as first item!!! */
376 static void prt_runtime(UAContext *ua, sched_pkt *sp)
378 char dt[MAX_TIME_LENGTH];
379 const char *level_ptr;
381 bool close_db = false;
385 memset(&mr, 0, sizeof(mr));
386 if (sp->job->JobType == JT_BACKUP) {
388 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
390 close_db = true; /* new db opened, remember to close it */
393 mr.PoolId = jcr->jr.PoolId;
394 mr.StorageId = sp->store->StorageId;
395 jcr->wstore = sp->store;
396 ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
399 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
402 bstrftime_nc(dt, sizeof(dt), sp->runtime);
403 switch (sp->job->JobType) {
409 level_ptr = level_to_str(sp->level);
412 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
413 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
414 sp->job->name(), mr.VolumeName);
416 db_close_database(jcr, jcr->db);
418 jcr->db = ua->db; /* restore ua db to jcr */
422 * Sort items by runtime, priority
424 static int my_compare(void *item1, void *item2)
426 sched_pkt *p1 = (sched_pkt *)item1;
427 sched_pkt *p2 = (sched_pkt *)item2;
428 if (p1->runtime < p2->runtime) {
430 } else if (p1->runtime > p2->runtime) {
433 if (p1->priority < p2->priority) {
435 } else if (p1->priority > p2->priority) {
442 * Find all jobs to be run in roughly the
445 static void list_scheduled_jobs(UAContext *ua)
450 int level, num_jobs = 0;
452 bool hdr_printed = false;
457 Dmsg0(200, "enter list_sched_jobs()\n");
460 i = find_arg_with_value(ua, NT_("days"));
462 days = atoi(ua->argv[i]);
463 if ((days < 0) || (days > 50)) {
464 bsendmsg(ua, _("Ignoring invalid value for days. Max is 50.\n"));
469 /* Loop through all jobs */
471 foreach_res(job, R_JOB) {
472 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
475 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
477 level = job->JobLevel;
481 priority = job->Priority;
483 priority = run->Priority;
489 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
492 sp->priority = priority;
493 sp->runtime = runtime;
494 sp->pool = run->pool;
495 get_job_storage(&store, job, run);
496 sp->store = store.store;
497 sched.binary_insert_multiple(sp, my_compare);
500 } /* end for loop over resources */
502 foreach_dlist(sp, &sched) {
506 bsendmsg(ua, _("No Scheduled Jobs.\n"));
508 bsendmsg(ua, _("====\n"));
509 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
512 static void list_running_jobs(UAContext *ua)
517 char *emsg; /* edited message */
518 char dt[MAX_TIME_LENGTH];
520 bool pool_mem = false;
522 Dmsg0(200, "enter list_run_jobs()\n");
523 bsendmsg(ua, _("\nRunning Jobs:\n"));
525 if (jcr->JobId == 0) { /* this is us */
526 /* this is a console or other control job. We only show console
527 * jobs in the status output.
529 if (jcr->JobType == JT_CONSOLE) {
530 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
531 bsendmsg(ua, _("Console connected at %s\n"), dt);
540 /* Note the following message is used in regress -- don't change */
541 bsendmsg(ua, _("No Jobs running.\n====\n"));
542 Dmsg0(200, "leave list_run_jobs()\n");
546 bsendmsg(ua, _(" JobId Level Name Status\n"));
547 bsendmsg(ua, _("======================================================================\n"));
549 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
553 switch (jcr->JobStatus) {
555 msg = _("is waiting execution");
558 msg = _("is running");
561 msg = _("is blocked");
564 msg = _("has terminated");
566 case JS_ErrorTerminated:
567 msg = _("has erred");
570 msg = _("has errors");
573 msg = _("has a fatal error");
576 msg = _("has verify differences");
579 msg = _("has been canceled");
582 emsg = (char *) get_pool_memory(PM_FNAME);
583 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
588 emsg = (char *) get_pool_memory(PM_FNAME);
590 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
592 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
597 case JS_WaitStoreRes:
598 msg = _("is waiting on max Storage jobs");
600 case JS_WaitClientRes:
601 msg = _("is waiting on max Client jobs");
604 msg = _("is waiting on max Job jobs");
607 msg = _("is waiting on max total jobs");
609 case JS_WaitStartTime:
610 msg = _("is waiting for its start time");
612 case JS_WaitPriority:
613 msg = _("is waiting for higher priority jobs to finish");
617 emsg = (char *) get_pool_memory(PM_FNAME);
618 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
624 * Now report Storage daemon status code
626 switch (jcr->SDJobStatus) {
629 free_pool_memory(emsg);
632 msg = _("is waiting for a mount request");
636 free_pool_memory(emsg);
639 msg = _("is waiting for an appendable Volume");
643 emsg = (char *)get_pool_memory(PM_FNAME);
646 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
647 jcr->client->name(), jcr->wstore->name());
651 switch (jcr->JobType) {
654 bstrncpy(level, " ", sizeof(level));
657 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
662 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
669 free_pool_memory(emsg);
674 bsendmsg(ua, _("====\n"));
675 Dmsg0(200, "leave list_run_jobs()\n");
678 static void list_terminated_jobs(UAContext *ua)
680 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
683 if (last_jobs->empty()) {
684 bsendmsg(ua, _("No Terminated Jobs.\n"));
687 lock_last_jobs_list();
688 struct s_last_job *je;
689 bsendmsg(ua, _("\nTerminated Jobs:\n"));
690 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
691 bsendmsg(ua, _("====================================================================\n"));
692 foreach_dlist(je, last_jobs) {
693 char JobName[MAX_NAME_LENGTH];
694 const char *termstat;
696 bstrncpy(JobName, je->Job, sizeof(JobName));
697 /* There are three periods after the Job name */
699 for (int i=0; i<3; i++) {
700 if ((p=strrchr(JobName, '.')) != NULL) {
705 if (!acl_access_ok(ua, Job_ACL, JobName)) {
709 bstrftime_nc(dt, sizeof(dt), je->end_time);
710 switch (je->JobType) {
713 bstrncpy(level, " ", sizeof(level));
716 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
720 switch (je->JobStatus) {
722 termstat = _("Created");
725 case JS_ErrorTerminated:
726 termstat = _("Error");
729 termstat = _("Diffs");
732 termstat = _("Cancel");
738 termstat = _("Other");
741 bsendmsg(ua, _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
744 edit_uint64_with_commas(je->JobFiles, b1),
745 edit_uint64_with_suffix(je->JobBytes, b2),
749 bsendmsg(ua, _("\n"));
750 unlock_last_jobs_list();