3 * Bacula Director -- User Agent Status Command
5 * Kern Sibbald, August MMI
10 Copyright (C) 2001-2006 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!!! */
366 static void prt_runtime(UAContext *ua, sched_pkt *sp)
368 char dt[MAX_TIME_LENGTH];
369 const char *level_ptr;
371 bool close_db = false;
375 memset(&mr, 0, sizeof(mr));
376 if (sp->job->JobType == JT_BACKUP) {
378 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
380 close_db = true; /* new db opened, remember to close it */
383 mr.PoolId = jcr->PoolId;
384 mr.StorageId = sp->store->StorageId;
385 ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
388 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
391 bstrftime_nc(dt, sizeof(dt), sp->runtime);
392 switch (sp->job->JobType) {
398 level_ptr = level_to_str(sp->level);
401 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
402 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
403 sp->job->hdr.name, mr.VolumeName);
405 db_close_database(jcr, jcr->db);
407 jcr->db = ua->db; /* restore ua db to jcr */
412 * Sort items by runtime, priority
414 static int my_compare(void *item1, void *item2)
416 sched_pkt *p1 = (sched_pkt *)item1;
417 sched_pkt *p2 = (sched_pkt *)item2;
418 if (p1->runtime < p2->runtime) {
420 } else if (p1->runtime > p2->runtime) {
423 if (p1->priority < p2->priority) {
425 } else if (p1->priority > p2->priority) {
432 * Find all jobs to be run in roughly the
435 static void list_scheduled_jobs(UAContext *ua)
441 int level, num_jobs = 0;
443 bool hdr_printed = false;
448 Dmsg0(200, "enter list_sched_jobs()\n");
451 i = find_arg_with_value(ua, N_("days"));
453 days = atoi(ua->argv[i]);
454 if ((days < 0) || (days > 50)) {
455 bsendmsg(ua, _("Ignoring illegal value for days.\n"));
460 /* Loop through all jobs */
462 foreach_res(job, R_JOB) {
463 if (!acl_access_ok(ua, Job_ACL, job->hdr.name) || !job->enabled) {
466 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
467 level = job->JobLevel;
471 priority = job->Priority;
473 priority = run->Priority;
476 store = run->storage;
478 store = (STORE *)job->storage->first();
484 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
487 sp->priority = priority;
488 sp->runtime = runtime;
489 sp->pool = run->pool;
491 sched.binary_insert_multiple(sp, my_compare);
494 } /* end for loop over resources */
496 foreach_dlist(sp, &sched) {
500 bsendmsg(ua, _("No Scheduled Jobs.\n"));
502 bsendmsg(ua, _("====\n"));
503 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
506 static void list_running_jobs(UAContext *ua)
511 char *emsg; /* edited message */
512 char dt[MAX_TIME_LENGTH];
514 bool pool_mem = false;
516 Dmsg0(200, "enter list_run_jobs()\n");
517 bsendmsg(ua, _("\nRunning Jobs:\n"));
519 if (jcr->JobId == 0) { /* this is us */
520 /* this is a console or other control job. We only show console
521 * jobs in the status output.
523 if (jcr->JobType == JT_CONSOLE) {
524 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
525 bsendmsg(ua, _("Console connected at %s\n"), dt);
534 /* Note the following message is used in regress -- don't change */
535 bsendmsg(ua, _("No Jobs running.\n====\n"));
536 Dmsg0(200, "leave list_run_jobs()\n");
540 bsendmsg(ua, _(" JobId Level Name Status\n"));
541 bsendmsg(ua, _("======================================================================\n"));
543 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
547 switch (jcr->JobStatus) {
549 msg = _("is waiting execution");
552 msg = _("is running");
555 msg = _("is blocked");
558 msg = _("has terminated");
560 case JS_ErrorTerminated:
561 msg = _("has erred");
564 msg = _("has errors");
567 msg = _("has a fatal error");
570 msg = _("has verify differences");
573 msg = _("has been canceled");
576 emsg = (char *) get_pool_memory(PM_FNAME);
577 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->hdr.name);
582 emsg = (char *) get_pool_memory(PM_FNAME);
583 Mmsg(emsg, _("is waiting on Storage %s"), jcr->store->hdr.name);
587 case JS_WaitStoreRes:
588 msg = _("is waiting on max Storage jobs");
590 case JS_WaitClientRes:
591 msg = _("is waiting on max Client jobs");
594 msg = _("is waiting on max Job jobs");
597 msg = _("is waiting on max total jobs");
599 case JS_WaitStartTime:
600 msg = _("is waiting for its start time");
602 case JS_WaitPriority:
603 msg = _("is waiting for higher priority jobs to finish");
607 emsg = (char *) get_pool_memory(PM_FNAME);
608 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
614 * Now report Storage daemon status code
616 switch (jcr->SDJobStatus) {
619 free_pool_memory(emsg);
622 msg = _("is waiting for a mount request");
626 free_pool_memory(emsg);
629 msg = _("is waiting for an appendable Volume");
633 emsg = (char *) get_pool_memory(PM_FNAME);
636 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
637 jcr->client->hdr.name, jcr->store->hdr.name);
641 switch (jcr->JobType) {
644 bstrncpy(level, " ", sizeof(level));
647 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
652 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
659 free_pool_memory(emsg);
664 bsendmsg(ua, _("====\n"));
665 Dmsg0(200, "leave list_run_jobs()\n");
668 static void list_terminated_jobs(UAContext *ua)
670 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
673 if (last_jobs->empty()) {
674 bsendmsg(ua, _("No Terminated Jobs.\n"));
677 lock_last_jobs_list();
678 struct s_last_job *je;
679 bsendmsg(ua, _("\nTerminated Jobs:\n"));
680 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
681 bsendmsg(ua, _("========================================================================\n"));
682 foreach_dlist(je, last_jobs) {
683 char JobName[MAX_NAME_LENGTH];
684 const char *termstat;
686 bstrncpy(JobName, je->Job, sizeof(JobName));
687 /* There are three periods after the Job name */
689 for (int i=0; i<3; i++) {
690 if ((p=strrchr(JobName, '.')) != NULL) {
695 if (!acl_access_ok(ua, Job_ACL, JobName)) {
699 bstrftime_nc(dt, sizeof(dt), je->end_time);
700 switch (je->JobType) {
703 bstrncpy(level, " ", sizeof(level));
706 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
710 switch (je->JobStatus) {
712 termstat = _("Created");
715 case JS_ErrorTerminated:
716 termstat = _("Error");
719 termstat = _("Diffs");
722 termstat = _("Cancel");
728 termstat = _("Other");
731 bsendmsg(ua, _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
734 edit_uint64_with_commas(je->JobFiles, b1),
735 edit_uint64_with_commas(je->JobBytes, b2),
739 bsendmsg(ua, _("\n"));
740 unlock_last_jobs_list();