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;
444 Dmsg0(200, "enter list_sched_jobs()\n");
446 /* Loop through all jobs */
448 foreach_res(job, R_JOB) {
449 if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
452 for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
453 level = job->JobLevel;
457 priority = job->Priority;
459 priority = run->Priority;
465 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
468 sp->priority = priority;
469 sp->runtime = runtime;
470 sp->pool = run->pool;
471 sched.binary_insert_multiple(sp, my_compare);
474 } /* end for loop over resources */
476 foreach_dlist(sp, &sched) {
480 bsendmsg(ua, _("No Scheduled Jobs.\n"));
482 bsendmsg(ua, _("====\n"));
483 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
486 static void list_running_jobs(UAContext *ua)
491 char *emsg; /* edited message */
492 char dt[MAX_TIME_LENGTH];
494 bool pool_mem = false;
496 Dmsg0(200, "enter list_run_jobs()\n");
497 bsendmsg(ua, _("\nRunning Jobs:\n"));
500 if (jcr->JobId == 0) { /* this is us */
501 /* this is a console or other control job. We only show console
502 * jobs in the status output.
504 if (jcr->JobType == JT_CONSOLE) {
505 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
506 bsendmsg(ua, _("Console connected at %s\n"), dt);
513 /* Note the following message is used in regress -- don't change */
514 bsendmsg(ua, _("No Jobs running.\n====\n"));
515 Dmsg0(200, "leave list_run_jobs()\n");
519 bsendmsg(ua, _(" JobId Level Name Status\n"));
520 bsendmsg(ua, _("======================================================================\n"));
522 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
527 switch (jcr->JobStatus) {
529 msg = _("is waiting execution");
532 msg = _("is running");
535 msg = _("is blocked");
538 msg = _("has terminated");
540 case JS_ErrorTerminated:
541 msg = _("has erred");
544 msg = _("has errors");
547 msg = _("has a fatal error");
550 msg = _("has verify differences");
553 msg = _("has been canceled");
556 emsg = (char *) get_pool_memory(PM_FNAME);
557 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->hdr.name);
562 emsg = (char *) get_pool_memory(PM_FNAME);
563 Mmsg(emsg, _("is waiting on Storage %s"), jcr->store->hdr.name);
567 case JS_WaitStoreRes:
568 msg = _("is waiting on max Storage jobs");
570 case JS_WaitClientRes:
571 msg = _("is waiting on max Client jobs");
574 msg = _("is waiting on max Job jobs");
577 msg = _("is waiting on max total jobs");
579 case JS_WaitStartTime:
580 msg = _("is waiting for its start time");
582 case JS_WaitPriority:
583 msg = _("is waiting for higher priority jobs to finish");
587 emsg = (char *) get_pool_memory(PM_FNAME);
588 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
594 * Now report Storage daemon status code
596 switch (jcr->SDJobStatus) {
599 free_pool_memory(emsg);
602 msg = _("is waiting for a mount request");
606 free_pool_memory(emsg);
609 msg = _("is waiting for an appendable Volume");
613 emsg = (char *) get_pool_memory(PM_FNAME);
616 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
617 jcr->client->hdr.name, jcr->store->hdr.name);
621 switch (jcr->JobType) {
624 bstrncpy(level, " ", sizeof(level));
627 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
632 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
639 free_pool_memory(emsg);
644 bsendmsg(ua, _("====\n"));
645 Dmsg0(200, "leave list_run_jobs()\n");
648 static void list_terminated_jobs(UAContext *ua)
650 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
653 if (last_jobs->empty()) {
654 bsendmsg(ua, _("No Terminated Jobs.\n"));
657 lock_last_jobs_list();
658 struct s_last_job *je;
659 bsendmsg(ua, _("\nTerminated Jobs:\n"));
660 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
661 bsendmsg(ua, _("========================================================================\n"));
662 foreach_dlist(je, last_jobs) {
663 char JobName[MAX_NAME_LENGTH];
664 const char *termstat;
666 bstrncpy(JobName, je->Job, sizeof(JobName));
667 /* There are three periods after the Job name */
669 for (int i=0; i<3; i++) {
670 if ((p=strrchr(JobName, '.')) != NULL) {
675 if (!acl_access_ok(ua, Job_ACL, JobName)) {
679 bstrftime_nc(dt, sizeof(dt), je->end_time);
680 switch (je->JobType) {
683 bstrncpy(level, " ", sizeof(level));
686 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
690 switch (je->JobStatus) {
692 termstat = _("Created");
695 case JS_ErrorTerminated:
696 termstat = _("Error");
699 termstat = _("Diffs");
702 termstat = _("Cancel");
708 termstat = _("Other");
711 bsendmsg(ua, _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
714 edit_uint64_with_commas(je->JobFiles, b1),
715 edit_uint64_with_commas(je->JobBytes, b2),
719 bsendmsg(ua, _("\n"));
720 unlock_last_jobs_list();