3 * Bacula Director -- User Agent Status Command
5 * Kern Sibbald, August MMI
10 Copyright (C) 2001-2005 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
28 extern char my_name[];
29 extern time_t daemon_start_time;
30 extern int num_jobs_run;
32 static void list_scheduled_jobs(UAContext *ua);
33 static void list_running_jobs(UAContext *ua);
34 static void list_terminated_jobs(UAContext *ua);
35 static void do_storage_status(UAContext *ua, STORE *store);
36 static void do_client_status(UAContext *ua, CLIENT *client);
37 static void do_director_status(UAContext *ua);
38 static void do_all_status(UAContext *ua);
40 static char OKqstatus[] = "1000 OK .status\n";
41 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
46 int qstatus_cmd(UAContext *ua, const char *cmd)
55 Dmsg1(20, "status:%s:\n", cmd);
57 if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
58 bsendmsg(ua, "1900 Bad .status command, missing arguments.\n");
62 if (strcasecmp(ua->argk[2], "current") == 0) {
63 bsendmsg(ua, OKqstatus, ua->argk[2]);
65 if (njcr->JobId != 0) {
66 bsendmsg(ua, DotStatusJob, edit_int64(njcr->JobId, ed1),
67 njcr->JobStatus, njcr->JobErrors);
71 } else if (strcasecmp(ua->argk[2], "last") == 0) {
72 bsendmsg(ua, OKqstatus, ua->argk[2]);
73 if ((last_jobs) && (last_jobs->size() > 0)) {
74 job = (s_last_job*)last_jobs->last();
75 bsendmsg(ua, DotStatusJob, edit_int64(job->JobId, ed1),
76 job->JobStatus, job->Errors);
79 bsendmsg(ua, "1900 Bad .status command, wrong argument.\n");
89 int status_cmd(UAContext *ua, const char *cmd)
98 Dmsg1(20, "status:%s:\n", cmd);
100 for (i=1; i<ua->argc; i++) {
101 if (strcasecmp(ua->argk[i], N_("all")) == 0) {
104 } else if (strcasecmp(ua->argk[i], N_("dir")) == 0 ||
105 strcasecmp(ua->argk[i], N_("director")) == 0) {
106 do_director_status(ua);
108 } else if (strcasecmp(ua->argk[i], N_("client")) == 0) {
109 client = get_client_resource(ua);
111 do_client_status(ua, client);
115 store = get_storage_resource(ua, false/*no default*/);
117 do_storage_status(ua, store);
122 /* If no args, ask for status type */
124 char prmt[MAX_NAME_LENGTH];
126 start_prompt(ua, _("Status available for:\n"));
127 add_prompt(ua, N_("Director"));
128 add_prompt(ua, N_("Storage"));
129 add_prompt(ua, N_("Client"));
130 add_prompt(ua, N_("All"));
131 Dmsg0(20, "do_prompt: select daemon\n");
132 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
135 Dmsg1(20, "item=%d\n", item);
137 case 0: /* Director */
138 do_director_status(ua);
141 store = select_storage_resource(ua);
143 do_storage_status(ua, store);
147 client = select_client_resource(ua);
149 do_client_status(ua, client);
162 static void do_all_status(UAContext *ua)
164 STORE *store, **unique_store;
165 CLIENT *client, **unique_client;
169 do_director_status(ua);
171 /* Count Storage items */
174 foreach_res(store, R_STORAGE) {
177 unique_store = (STORE **) malloc(i * sizeof(STORE));
178 /* Find Unique Storage address/port */
180 foreach_res(store, R_STORAGE) {
182 if (!acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
185 for (j=0; j<i; j++) {
186 if (strcmp(unique_store[j]->address, store->address) == 0 &&
187 unique_store[j]->SDport == store->SDport) {
193 unique_store[i++] = store;
194 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
199 /* Call each unique Storage daemon */
200 for (j=0; j<i; j++) {
201 do_storage_status(ua, unique_store[j]);
205 /* Count Client items */
208 foreach_res(client, R_CLIENT) {
211 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
212 /* Find Unique Client address/port */
214 foreach_res(client, R_CLIENT) {
216 if (!acl_access_ok(ua, Client_ACL, client->hdr.name)) {
219 for (j=0; j<i; j++) {
220 if (strcmp(unique_client[j]->address, client->address) == 0 &&
221 unique_client[j]->FDport == client->FDport) {
227 unique_client[i++] = client;
228 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
233 /* Call each unique File daemon */
234 for (j=0; j<i; j++) {
235 do_client_status(ua, unique_client[j]);
241 static void do_director_status(UAContext *ua)
243 char dt[MAX_TIME_LENGTH];
245 bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
246 HOST_OS, DISTNAME, DISTVER);
247 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
248 if (num_jobs_run == 1) {
249 bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
252 bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
255 if (debug_level > 0) {
256 char b1[35], b2[35], b3[35], b4[35];
257 bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
258 edit_uint64_with_commas(sm_bytes, b1),
259 edit_uint64_with_commas(sm_max_bytes, b2),
260 edit_uint64_with_commas(sm_buffers, b3),
261 edit_uint64_with_commas(sm_max_buffers, b4));
264 * List scheduled Jobs
266 list_scheduled_jobs(ua);
271 list_running_jobs(ua);
274 * List terminated jobs
276 list_terminated_jobs(ua);
277 bsendmsg(ua, _("====\n"));
280 static void do_storage_status(UAContext *ua, STORE *store)
284 set_storage(ua->jcr, store);
285 /* Try connecting for up to 15 seconds */
286 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
287 store->hdr.name, store->address, store->SDport);
288 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
289 bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
291 if (ua->jcr->store_bsock) {
292 bnet_close(ua->jcr->store_bsock);
293 ua->jcr->store_bsock = NULL;
297 Dmsg0(20, _("Connected to storage daemon\n"));
298 sd = ua->jcr->store_bsock;
299 bnet_fsend(sd, "status");
300 while (bnet_recv(sd) >= 0) {
301 bsendmsg(ua, "%s", sd->msg);
303 bnet_sig(sd, BNET_TERMINATE);
305 ua->jcr->store_bsock = NULL;
309 static void do_client_status(UAContext *ua, CLIENT *client)
313 /* Connect to File daemon */
315 ua->jcr->client = client;
316 /* Release any old dummy key */
317 if (ua->jcr->sd_auth_key) {
318 free(ua->jcr->sd_auth_key);
320 /* Create a new dummy SD auth key */
321 ua->jcr->sd_auth_key = bstrdup("dummy");
323 /* Try to connect for 15 seconds */
324 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
325 client->hdr.name, client->address, client->FDport);
326 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
327 bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
329 if (ua->jcr->file_bsock) {
330 bnet_close(ua->jcr->file_bsock);
331 ua->jcr->file_bsock = NULL;
335 Dmsg0(20, _("Connected to file daemon\n"));
336 fd = ua->jcr->file_bsock;
337 bnet_fsend(fd, "status");
338 while (bnet_recv(fd) >= 0) {
339 bsendmsg(ua, "%s", fd->msg);
341 bnet_sig(fd, BNET_TERMINATE);
343 ua->jcr->file_bsock = NULL;
348 static void prt_runhdr(UAContext *ua)
350 bsendmsg(ua, _("\nScheduled Jobs:\n"));
351 bsendmsg(ua, _("Level Type Pri Scheduled Name Volume\n"));
352 bsendmsg(ua, _("===================================================================================\n"));
355 /* Scheduling packet */
357 dlink link; /* keep this as first item!!! */
365 static void prt_runtime(UAContext *ua, sched_pkt *sp)
367 char dt[MAX_TIME_LENGTH];
368 const char *level_ptr;
370 bool close_db = false;
374 memset(&mr, 0, sizeof(mr));
375 if (sp->job->JobType == JT_BACKUP) {
377 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
379 close_db = true; /* new db opened, remember to close it */
382 mr.PoolId = jcr->PoolId;
383 ok = find_next_volume_for_append(jcr, &mr, 0);
386 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
389 bstrftime_nc(dt, sizeof(dt), sp->runtime);
390 switch (sp->job->JobType) {
396 level_ptr = level_to_str(sp->level);
399 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
400 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
401 sp->job->hdr.name, mr.VolumeName);
403 db_close_database(jcr, jcr->db);
405 jcr->db = ua->db; /* restore ua db to jcr */
410 * Sort items by runtime, priority
412 static int my_compare(void *item1, void *item2)
414 sched_pkt *p1 = (sched_pkt *)item1;
415 sched_pkt *p2 = (sched_pkt *)item2;
416 if (p1->runtime < p2->runtime) {
418 } else if (p1->runtime > p2->runtime) {
421 if (p1->priority < p2->priority) {
423 } else if (p1->priority > p2->priority) {
430 * Find all jobs to be run in roughly the
433 static void list_scheduled_jobs(UAContext *ua)
438 int level, num_jobs = 0;
440 bool hdr_printed = false;
445 Dmsg0(200, "enter list_sched_jobs()\n");
448 i = find_arg_with_value(ua, N_("days"));
450 days = atoi(ua->argv[i]);
451 if ((days < 0) || (days > 50)) {
452 bsendmsg(ua, _("Ignoring illegal value for days.\n"));
457 /* Loop through all jobs */
459 foreach_res(job, R_JOB) {
460 if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
463 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
464 level = job->JobLevel;
468 priority = job->Priority;
470 priority = run->Priority;
476 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
479 sp->priority = priority;
480 sp->runtime = runtime;
481 sp->pool = run->pool;
482 sched.binary_insert_multiple(sp, my_compare);
485 } /* end for loop over resources */
487 foreach_dlist(sp, &sched) {
491 bsendmsg(ua, _("No Scheduled Jobs.\n"));
493 bsendmsg(ua, _("====\n"));
494 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
497 static void list_running_jobs(UAContext *ua)
502 char *emsg; /* edited message */
503 char dt[MAX_TIME_LENGTH];
505 bool pool_mem = false;
507 Dmsg0(200, "enter list_run_jobs()\n");
508 bsendmsg(ua, _("\nRunning Jobs:\n"));
511 if (jcr->JobId == 0) { /* this is us */
512 /* this is a console or other control job. We only show console
513 * jobs in the status output.
515 if (jcr->JobType == JT_CONSOLE) {
516 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
517 bsendmsg(ua, _("Console connected at %s\n"), dt);
524 /* Note the following message is used in regress -- don't change */
525 bsendmsg(ua, _("No Jobs running.\n====\n"));
526 Dmsg0(200, "leave list_run_jobs()\n");
530 bsendmsg(ua, _(" JobId Level Name Status\n"));
531 bsendmsg(ua, _("======================================================================\n"));
533 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
538 switch (jcr->JobStatus) {
540 msg = _("is waiting execution");
543 msg = _("is running");
546 msg = _("is blocked");
549 msg = _("has terminated");
551 case JS_ErrorTerminated:
552 msg = _("has erred");
555 msg = _("has errors");
558 msg = _("has a fatal error");
561 msg = _("has verify differences");
564 msg = _("has been canceled");
567 emsg = (char *) get_pool_memory(PM_FNAME);
568 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->hdr.name);
573 emsg = (char *) get_pool_memory(PM_FNAME);
574 Mmsg(emsg, _("is waiting on Storage %s"), jcr->store->hdr.name);
578 case JS_WaitStoreRes:
579 msg = _("is waiting on max Storage jobs");
581 case JS_WaitClientRes:
582 msg = _("is waiting on max Client jobs");
585 msg = _("is waiting on max Job jobs");
588 msg = _("is waiting on max total jobs");
590 case JS_WaitStartTime:
591 msg = _("is waiting for its start time");
593 case JS_WaitPriority:
594 msg = _("is waiting for higher priority jobs to finish");
598 emsg = (char *) get_pool_memory(PM_FNAME);
599 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
605 * Now report Storage daemon status code
607 switch (jcr->SDJobStatus) {
610 free_pool_memory(emsg);
613 msg = _("is waiting for a mount request");
617 free_pool_memory(emsg);
620 msg = _("is waiting for an appendable Volume");
624 emsg = (char *) get_pool_memory(PM_FNAME);
627 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
628 jcr->client->hdr.name, jcr->store->hdr.name);
632 switch (jcr->JobType) {
635 bstrncpy(level, " ", sizeof(level));
638 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
643 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
650 free_pool_memory(emsg);
655 bsendmsg(ua, _("====\n"));
656 Dmsg0(200, "leave list_run_jobs()\n");
659 static void list_terminated_jobs(UAContext *ua)
661 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
664 if (last_jobs->empty()) {
665 bsendmsg(ua, _("No Terminated Jobs.\n"));
668 lock_last_jobs_list();
669 struct s_last_job *je;
670 bsendmsg(ua, _("\nTerminated Jobs:\n"));
671 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
672 bsendmsg(ua, _("========================================================================\n"));
673 foreach_dlist(je, last_jobs) {
674 char JobName[MAX_NAME_LENGTH];
675 const char *termstat;
677 bstrncpy(JobName, je->Job, sizeof(JobName));
678 /* There are three periods after the Job name */
680 for (int i=0; i<3; i++) {
681 if ((p=strrchr(JobName, '.')) != NULL) {
686 if (!acl_access_ok(ua, Job_ACL, JobName)) {
690 bstrftime_nc(dt, sizeof(dt), je->end_time);
691 switch (je->JobType) {
694 bstrncpy(level, " ", sizeof(level));
697 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
701 switch (je->JobStatus) {
703 termstat = _("Created");
706 case JS_ErrorTerminated:
707 termstat = _("Error");
710 termstat = _("Diffs");
713 termstat = _("Cancel");
719 termstat = _("Other");
722 bsendmsg(ua, _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
725 edit_uint64_with_commas(je->JobFiles, b1),
726 edit_uint64_with_commas(je->JobBytes, b2),
730 bsendmsg(ua, _("\n"));
731 unlock_last_jobs_list();