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 static void list_scheduled_jobs(UAContext *ua);
29 static void list_running_jobs(UAContext *ua);
30 static void list_terminated_jobs(UAContext *ua);
31 static void do_storage_status(UAContext *ua, STORE *store);
32 static void do_client_status(UAContext *ua, CLIENT *client);
33 static void do_director_status(UAContext *ua);
34 static void do_all_status(UAContext *ua);
36 static char OKqstatus[] = "1000 OK .status\n";
37 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
42 int qstatus_cmd(UAContext *ua, const char *cmd)
51 Dmsg1(20, "status:%s:\n", cmd);
53 if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
54 bsendmsg(ua, "1900 Bad .status command, missing arguments.\n");
58 if (strcasecmp(ua->argk[2], "current") == 0) {
59 bsendmsg(ua, OKqstatus, ua->argk[2]);
61 if (njcr->JobId != 0) {
62 bsendmsg(ua, DotStatusJob, edit_int64(njcr->JobId, ed1),
63 njcr->JobStatus, njcr->JobErrors);
67 } else if (strcasecmp(ua->argk[2], "last") == 0) {
68 bsendmsg(ua, OKqstatus, ua->argk[2]);
69 if ((last_jobs) && (last_jobs->size() > 0)) {
70 job = (s_last_job*)last_jobs->last();
71 bsendmsg(ua, DotStatusJob, edit_int64(job->JobId, ed1),
72 job->JobStatus, job->Errors);
75 bsendmsg(ua, "1900 Bad .status command, wrong argument.\n");
85 int status_cmd(UAContext *ua, const char *cmd)
94 Dmsg1(20, "status:%s:\n", cmd);
96 for (i=1; i<ua->argc; i++) {
97 if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
100 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
101 strcasecmp(ua->argk[i], NT_("director")) == 0) {
102 do_director_status(ua);
104 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
105 client = get_client_resource(ua);
107 do_client_status(ua, client);
111 store = get_storage_resource(ua, false/*no default*/);
113 do_storage_status(ua, store);
118 /* If no args, ask for status type */
120 char prmt[MAX_NAME_LENGTH];
122 start_prompt(ua, _("Status available for:\n"));
123 add_prompt(ua, NT_("Director"));
124 add_prompt(ua, NT_("Storage"));
125 add_prompt(ua, NT_("Client"));
126 add_prompt(ua, NT_("All"));
127 Dmsg0(20, "do_prompt: select daemon\n");
128 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
131 Dmsg1(20, "item=%d\n", item);
133 case 0: /* Director */
134 do_director_status(ua);
137 store = select_storage_resource(ua);
139 do_storage_status(ua, store);
143 client = select_client_resource(ua);
145 do_client_status(ua, client);
158 static void do_all_status(UAContext *ua)
160 STORE *store, **unique_store;
161 CLIENT *client, **unique_client;
165 do_director_status(ua);
167 /* Count Storage items */
170 foreach_res(store, R_STORAGE) {
173 unique_store = (STORE **) malloc(i * sizeof(STORE));
174 /* Find Unique Storage address/port */
176 foreach_res(store, R_STORAGE) {
178 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
181 for (j=0; j<i; j++) {
182 if (strcmp(unique_store[j]->address, store->address) == 0 &&
183 unique_store[j]->SDport == store->SDport) {
189 unique_store[i++] = store;
190 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
195 /* Call each unique Storage daemon */
196 for (j=0; j<i; j++) {
197 do_storage_status(ua, unique_store[j]);
201 /* Count Client items */
204 foreach_res(client, R_CLIENT) {
207 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
208 /* Find Unique Client address/port */
210 foreach_res(client, R_CLIENT) {
212 if (!acl_access_ok(ua, Client_ACL, client->name())) {
215 for (j=0; j<i; j++) {
216 if (strcmp(unique_client[j]->address, client->address) == 0 &&
217 unique_client[j]->FDport == client->FDport) {
223 unique_client[i++] = client;
224 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
229 /* Call each unique File daemon */
230 for (j=0; j<i; j++) {
231 do_client_status(ua, unique_client[j]);
237 static void do_director_status(UAContext *ua)
239 char dt[MAX_TIME_LENGTH];
241 bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
242 HOST_OS, DISTNAME, DISTVER);
243 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
244 if (num_jobs_run == 1) {
245 bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
248 bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
251 if (debug_level > 0) {
252 char b1[35], b2[35], b3[35], b4[35];
253 bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
254 edit_uint64_with_commas(sm_bytes, b1),
255 edit_uint64_with_commas(sm_max_bytes, b2),
256 edit_uint64_with_commas(sm_buffers, b3),
257 edit_uint64_with_commas(sm_max_buffers, b4));
260 * List scheduled Jobs
262 list_scheduled_jobs(ua);
267 list_running_jobs(ua);
270 * List terminated jobs
272 list_terminated_jobs(ua);
273 bsendmsg(ua, _("====\n"));
276 static void do_storage_status(UAContext *ua, STORE *store)
280 set_wstorage(ua->jcr, store);
281 /* Try connecting for up to 15 seconds */
282 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
283 store->name(), store->address, store->SDport);
284 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
285 bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
287 if (ua->jcr->store_bsock) {
288 bnet_close(ua->jcr->store_bsock);
289 ua->jcr->store_bsock = NULL;
293 Dmsg0(20, _("Connected to storage daemon\n"));
294 sd = ua->jcr->store_bsock;
295 bnet_fsend(sd, "status");
296 while (bnet_recv(sd) >= 0) {
297 bsendmsg(ua, "%s", sd->msg);
299 bnet_sig(sd, BNET_TERMINATE);
301 ua->jcr->store_bsock = NULL;
305 static void do_client_status(UAContext *ua, CLIENT *client)
309 /* Connect to File daemon */
311 ua->jcr->client = client;
312 /* Release any old dummy key */
313 if (ua->jcr->sd_auth_key) {
314 free(ua->jcr->sd_auth_key);
316 /* Create a new dummy SD auth key */
317 ua->jcr->sd_auth_key = bstrdup("dummy");
319 /* Try to connect for 15 seconds */
320 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
321 client->name(), client->address, client->FDport);
322 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
323 bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
325 if (ua->jcr->file_bsock) {
326 bnet_close(ua->jcr->file_bsock);
327 ua->jcr->file_bsock = NULL;
331 Dmsg0(20, _("Connected to file daemon\n"));
332 fd = ua->jcr->file_bsock;
333 bnet_fsend(fd, "status");
334 while (bnet_recv(fd) >= 0) {
335 bsendmsg(ua, "%s", fd->msg);
337 bnet_sig(fd, BNET_TERMINATE);
339 ua->jcr->file_bsock = NULL;
344 static void prt_runhdr(UAContext *ua)
346 bsendmsg(ua, _("\nScheduled Jobs:\n"));
347 bsendmsg(ua, _("Level Type Pri Scheduled Name Volume\n"));
348 bsendmsg(ua, _("===================================================================================\n"));
351 /* Scheduling packet */
353 dlink link; /* keep this as first item!!! */
362 static void prt_runtime(UAContext *ua, sched_pkt *sp)
364 char dt[MAX_TIME_LENGTH];
365 const char *level_ptr;
367 bool close_db = false;
371 memset(&mr, 0, sizeof(mr));
372 if (sp->job->JobType == JT_BACKUP) {
374 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
376 close_db = true; /* new db opened, remember to close it */
379 mr.PoolId = jcr->jr.PoolId;
380 mr.StorageId = sp->store->StorageId;
381 ok = find_next_volume_for_append(jcr, &mr, 1, false/*no create*/);
384 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
387 bstrftime_nc(dt, sizeof(dt), sp->runtime);
388 switch (sp->job->JobType) {
394 level_ptr = level_to_str(sp->level);
397 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
398 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
399 sp->job->name(), mr.VolumeName);
401 db_close_database(jcr, jcr->db);
403 jcr->db = ua->db; /* restore ua db to jcr */
408 * Sort items by runtime, priority
410 static int my_compare(void *item1, void *item2)
412 sched_pkt *p1 = (sched_pkt *)item1;
413 sched_pkt *p2 = (sched_pkt *)item2;
414 if (p1->runtime < p2->runtime) {
416 } else if (p1->runtime > p2->runtime) {
419 if (p1->priority < p2->priority) {
421 } else if (p1->priority > p2->priority) {
428 * Find all jobs to be run in roughly the
431 static void list_scheduled_jobs(UAContext *ua)
437 int level, num_jobs = 0;
439 bool hdr_printed = false;
444 Dmsg0(200, "enter list_sched_jobs()\n");
447 i = find_arg_with_value(ua, NT_("days"));
449 days = atoi(ua->argv[i]);
450 if ((days < 0) || (days > 50)) {
451 bsendmsg(ua, _("Ignoring illegal value for days.\n"));
456 /* Loop through all jobs */
458 foreach_res(job, R_JOB) {
459 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
462 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
463 level = job->JobLevel;
467 priority = job->Priority;
469 priority = run->Priority;
472 store = run->storage;
474 store = (STORE *)job->storage->first();
480 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
483 sp->priority = priority;
484 sp->runtime = runtime;
485 sp->pool = run->pool;
487 sched.binary_insert_multiple(sp, my_compare);
490 } /* end for loop over resources */
492 foreach_dlist(sp, &sched) {
496 bsendmsg(ua, _("No Scheduled Jobs.\n"));
498 bsendmsg(ua, _("====\n"));
499 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
502 static void list_running_jobs(UAContext *ua)
507 char *emsg; /* edited message */
508 char dt[MAX_TIME_LENGTH];
510 bool pool_mem = false;
512 Dmsg0(200, "enter list_run_jobs()\n");
513 bsendmsg(ua, _("\nRunning Jobs:\n"));
515 if (jcr->JobId == 0) { /* this is us */
516 /* this is a console or other control job. We only show console
517 * jobs in the status output.
519 if (jcr->JobType == JT_CONSOLE) {
520 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
521 bsendmsg(ua, _("Console connected at %s\n"), dt);
530 /* Note the following message is used in regress -- don't change */
531 bsendmsg(ua, _("No Jobs running.\n====\n"));
532 Dmsg0(200, "leave list_run_jobs()\n");
536 bsendmsg(ua, _(" JobId Level Name Status\n"));
537 bsendmsg(ua, _("======================================================================\n"));
539 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
543 switch (jcr->JobStatus) {
545 msg = _("is waiting execution");
548 msg = _("is running");
551 msg = _("is blocked");
554 msg = _("has terminated");
556 case JS_ErrorTerminated:
557 msg = _("has erred");
560 msg = _("has errors");
563 msg = _("has a fatal error");
566 msg = _("has verify differences");
569 msg = _("has been canceled");
572 emsg = (char *) get_pool_memory(PM_FNAME);
573 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
578 emsg = (char *) get_pool_memory(PM_FNAME);
580 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
582 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->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->name(), jcr->wstore->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();