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];
240 char b1[35], b2[35], b3[35], b4[35];
242 bsendmsg(ua, _("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
243 HOST_OS, DISTNAME, DISTVER);
244 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
245 if (num_jobs_run == 1) {
246 bsendmsg(ua, _("Daemon started %s, 1 Job run since started.\n"), dt);
249 bsendmsg(ua, _("Daemon started %s, %d Jobs run since started.\n"),
252 bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
253 edit_uint64_with_commas(sm_bytes, b1),
254 edit_uint64_with_commas(sm_max_bytes, b2),
255 edit_uint64_with_commas(sm_buffers, b3),
256 edit_uint64_with_commas(sm_max_buffers, b4));
259 * List scheduled Jobs
261 list_scheduled_jobs(ua);
266 list_running_jobs(ua);
269 * List terminated jobs
271 list_terminated_jobs(ua);
272 bsendmsg(ua, _("====\n"));
275 static void do_storage_status(UAContext *ua, STORE *store)
279 set_wstorage(ua->jcr, store);
280 /* Try connecting for up to 15 seconds */
281 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
282 store->name(), store->address, store->SDport);
283 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
284 bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
286 if (ua->jcr->store_bsock) {
287 bnet_close(ua->jcr->store_bsock);
288 ua->jcr->store_bsock = NULL;
292 Dmsg0(20, _("Connected to storage daemon\n"));
293 sd = ua->jcr->store_bsock;
294 bnet_fsend(sd, "status");
295 while (bnet_recv(sd) >= 0) {
296 bsendmsg(ua, "%s", sd->msg);
298 bnet_sig(sd, BNET_TERMINATE);
300 ua->jcr->store_bsock = NULL;
304 static void do_client_status(UAContext *ua, CLIENT *client)
308 /* Connect to File daemon */
310 ua->jcr->client = client;
311 /* Release any old dummy key */
312 if (ua->jcr->sd_auth_key) {
313 free(ua->jcr->sd_auth_key);
315 /* Create a new dummy SD auth key */
316 ua->jcr->sd_auth_key = bstrdup("dummy");
318 /* Try to connect for 15 seconds */
319 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
320 client->name(), client->address, client->FDport);
321 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
322 bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
324 if (ua->jcr->file_bsock) {
325 bnet_close(ua->jcr->file_bsock);
326 ua->jcr->file_bsock = NULL;
330 Dmsg0(20, _("Connected to file daemon\n"));
331 fd = ua->jcr->file_bsock;
332 bnet_fsend(fd, "status");
333 while (bnet_recv(fd) >= 0) {
334 bsendmsg(ua, "%s", fd->msg);
336 bnet_sig(fd, BNET_TERMINATE);
338 ua->jcr->file_bsock = NULL;
343 static void prt_runhdr(UAContext *ua)
345 bsendmsg(ua, _("\nScheduled Jobs:\n"));
346 bsendmsg(ua, _("Level Type Pri Scheduled Name Volume\n"));
347 bsendmsg(ua, _("===================================================================================\n"));
350 /* Scheduling packet */
352 dlink link; /* keep this as first item!!! */
361 static void prt_runtime(UAContext *ua, sched_pkt *sp)
363 char dt[MAX_TIME_LENGTH];
364 const char *level_ptr;
366 bool close_db = false;
370 memset(&mr, 0, sizeof(mr));
371 if (sp->job->JobType == JT_BACKUP) {
373 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
375 close_db = true; /* new db opened, remember to close it */
378 mr.PoolId = jcr->jr.PoolId;
379 mr.StorageId = sp->store->StorageId;
380 jcr->wstore = sp->store;
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 */
407 * Sort items by runtime, priority
409 static int my_compare(void *item1, void *item2)
411 sched_pkt *p1 = (sched_pkt *)item1;
412 sched_pkt *p2 = (sched_pkt *)item2;
413 if (p1->runtime < p2->runtime) {
415 } else if (p1->runtime > p2->runtime) {
418 if (p1->priority < p2->priority) {
420 } else if (p1->priority > p2->priority) {
427 * Find all jobs to be run in roughly the
430 static void list_scheduled_jobs(UAContext *ua)
436 int level, num_jobs = 0;
438 bool hdr_printed = false;
443 Dmsg0(200, "enter list_sched_jobs()\n");
446 i = find_arg_with_value(ua, NT_("days"));
448 days = atoi(ua->argv[i]);
449 if ((days < 0) || (days > 50)) {
450 bsendmsg(ua, _("Ignoring illegal value for days.\n"));
455 /* Loop through all jobs */
457 foreach_res(job, R_JOB) {
458 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
461 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
462 level = job->JobLevel;
466 priority = job->Priority;
468 priority = run->Priority;
471 store = run->storage;
473 store = (STORE *)job->storage->first();
479 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
482 sp->priority = priority;
483 sp->runtime = runtime;
484 sp->pool = run->pool;
486 sched.binary_insert_multiple(sp, my_compare);
489 } /* end for loop over resources */
491 foreach_dlist(sp, &sched) {
495 bsendmsg(ua, _("No Scheduled Jobs.\n"));
497 bsendmsg(ua, _("====\n"));
498 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
501 static void list_running_jobs(UAContext *ua)
506 char *emsg; /* edited message */
507 char dt[MAX_TIME_LENGTH];
509 bool pool_mem = false;
511 Dmsg0(200, "enter list_run_jobs()\n");
512 bsendmsg(ua, _("\nRunning Jobs:\n"));
514 if (jcr->JobId == 0) { /* this is us */
515 /* this is a console or other control job. We only show console
516 * jobs in the status output.
518 if (jcr->JobType == JT_CONSOLE) {
519 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
520 bsendmsg(ua, _("Console connected at %s\n"), dt);
529 /* Note the following message is used in regress -- don't change */
530 bsendmsg(ua, _("No Jobs running.\n====\n"));
531 Dmsg0(200, "leave list_run_jobs()\n");
535 bsendmsg(ua, _(" JobId Level Name Status\n"));
536 bsendmsg(ua, _("======================================================================\n"));
538 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
542 switch (jcr->JobStatus) {
544 msg = _("is waiting execution");
547 msg = _("is running");
550 msg = _("is blocked");
553 msg = _("has terminated");
555 case JS_ErrorTerminated:
556 msg = _("has erred");
559 msg = _("has errors");
562 msg = _("has a fatal error");
565 msg = _("has verify differences");
568 msg = _("has been canceled");
571 emsg = (char *) get_pool_memory(PM_FNAME);
572 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
577 emsg = (char *) get_pool_memory(PM_FNAME);
579 Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
581 Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
586 case JS_WaitStoreRes:
587 msg = _("is waiting on max Storage jobs");
589 case JS_WaitClientRes:
590 msg = _("is waiting on max Client jobs");
593 msg = _("is waiting on max Job jobs");
596 msg = _("is waiting on max total jobs");
598 case JS_WaitStartTime:
599 msg = _("is waiting for its start time");
601 case JS_WaitPriority:
602 msg = _("is waiting for higher priority jobs to finish");
606 emsg = (char *) get_pool_memory(PM_FNAME);
607 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
613 * Now report Storage daemon status code
615 switch (jcr->SDJobStatus) {
618 free_pool_memory(emsg);
621 msg = _("is waiting for a mount request");
625 free_pool_memory(emsg);
628 msg = _("is waiting for an appendable Volume");
632 emsg = (char *) get_pool_memory(PM_FNAME);
635 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
636 jcr->client->name(), jcr->wstore->name());
640 switch (jcr->JobType) {
643 bstrncpy(level, " ", sizeof(level));
646 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
651 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
658 free_pool_memory(emsg);
663 bsendmsg(ua, _("====\n"));
664 Dmsg0(200, "leave list_run_jobs()\n");
667 static void list_terminated_jobs(UAContext *ua)
669 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
672 if (last_jobs->empty()) {
673 bsendmsg(ua, _("No Terminated Jobs.\n"));
676 lock_last_jobs_list();
677 struct s_last_job *je;
678 bsendmsg(ua, _("\nTerminated Jobs:\n"));
679 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
680 bsendmsg(ua, _("====================================================================\n"));
681 foreach_dlist(je, last_jobs) {
682 char JobName[MAX_NAME_LENGTH];
683 const char *termstat;
685 bstrncpy(JobName, je->Job, sizeof(JobName));
686 /* There are three periods after the Job name */
688 for (int i=0; i<3; i++) {
689 if ((p=strrchr(JobName, '.')) != NULL) {
694 if (!acl_access_ok(ua, Job_ACL, JobName)) {
698 bstrftime_nc(dt, sizeof(dt), je->end_time);
699 switch (je->JobType) {
702 bstrncpy(level, " ", sizeof(level));
705 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
709 switch (je->JobStatus) {
711 termstat = _("Created");
714 case JS_ErrorTerminated:
715 termstat = _("Error");
718 termstat = _("Diffs");
721 termstat = _("Cancel");
727 termstat = _("Other");
730 bsendmsg(ua, _("%6d %-6s %8s %10s %-7s %-8s %s\n"),
733 edit_uint64_with_commas(je->JobFiles, b1),
734 edit_uint64_with_suffix(je->JobBytes, b2),
738 bsendmsg(ua, _("\n"));
739 unlock_last_jobs_list();