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 ammended 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(njcr->JobId, ed1),
76 njcr->JobStatus, njcr->JobErrors);
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], _("all")) == 0) {
104 } else if (strcasecmp(ua->argk[i], _("dir")) == 0 ||
105 strcasecmp(ua->argk[i], _("director")) == 0) {
106 do_director_status(ua);
108 } else if (strcasecmp(ua->argk[i], _("client")) == 0) {
109 client = get_client_resource(ua);
111 do_client_status(ua, client);
115 store = get_storage_resource(ua, 0);
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, _("Director"));
128 add_prompt(ua, _("Storage"));
129 add_prompt(ua, _("Client"));
130 add_prompt(ua, _("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: " VERSION " (" BDATE ") %s %s %s\n", my_name,
246 HOST_OS, DISTNAME, DISTVER);
247 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
248 bsendmsg(ua, _("Daemon started %s, %d Job%s run since started.\n"),
249 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
250 if (debug_level > 0) {
251 char b1[35], b2[35], b3[35], b4[35];
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_storage(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->hdr.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->hdr.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!!! */
360 static void prt_runtime(UAContext *ua, sched_pkt *sp)
362 char dt[MAX_TIME_LENGTH];
363 const char *level_ptr;
365 bool close_db = false;
369 memset(&mr, 0, sizeof(mr));
370 if (sp->job->JobType == JT_BACKUP) {
372 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
374 close_db = true; /* new db opened, remember to close it */
377 mr.PoolId = jcr->PoolId;
378 ok = find_next_volume_for_append(jcr, &mr, 0);
381 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
384 bstrftime_nc(dt, sizeof(dt), sp->runtime);
385 switch (sp->job->JobType) {
391 level_ptr = level_to_str(sp->level);
394 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
395 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
396 sp->job->hdr.name, mr.VolumeName);
398 db_close_database(jcr, jcr->db);
400 jcr->db = ua->db; /* restore ua db to jcr */
405 * Sort items by runtime, priority
407 static int my_compare(void *item1, void *item2)
409 sched_pkt *p1 = (sched_pkt *)item1;
410 sched_pkt *p2 = (sched_pkt *)item2;
411 if (p1->runtime < p2->runtime) {
413 } else if (p1->runtime > p2->runtime) {
416 if (p1->priority < p2->priority) {
418 } else if (p1->priority > p2->priority) {
425 * Find all jobs to be run in roughly the
428 static void list_scheduled_jobs(UAContext *ua)
433 int level, num_jobs = 0;
435 bool hdr_printed = false;
439 Dmsg0(200, "enter list_sched_jobs()\n");
441 /* Loop through all jobs */
443 foreach_res(job, R_JOB) {
444 if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
447 for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
448 level = job->JobLevel;
452 priority = job->Priority;
454 priority = run->Priority;
460 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
463 sp->priority = priority;
464 sp->runtime = runtime;
465 sp->pool = run->pool;
466 sched.binary_insert_multiple(sp, my_compare);
469 } /* end for loop over resources */
471 foreach_dlist(sp, &sched) {
475 bsendmsg(ua, _("No Scheduled Jobs.\n"));
477 bsendmsg(ua, "====\n");
478 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
481 static void list_running_jobs(UAContext *ua)
486 char *emsg; /* edited message */
487 char dt[MAX_TIME_LENGTH];
489 bool pool_mem = false;
491 Dmsg0(200, "enter list_run_jobs()\n");
492 bsendmsg(ua, _("\nRunning Jobs:\n"));
495 if (jcr->JobId == 0) { /* this is us */
496 /* this is a console or other control job. We only show console
497 * jobs in the status output.
499 if (jcr->JobType == JT_CONSOLE) {
500 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
501 bsendmsg(ua, _("Console connected at %s\n"), dt);
508 /* Note the following message is used in regress -- don't change */
509 bsendmsg(ua, _("No Jobs running.\n====\n"));
510 Dmsg0(200, "leave list_run_jobs()\n");
514 bsendmsg(ua, _(" JobId Level Name Status\n"));
515 bsendmsg(ua, _("======================================================================\n"));
517 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
522 switch (jcr->JobStatus) {
524 msg = _("is waiting execution");
527 msg = _("is running");
530 msg = _("is blocked");
533 msg = _("has terminated");
535 case JS_ErrorTerminated:
536 msg = _("has erred");
539 msg = _("has errors");
542 msg = _("has a fatal error");
545 msg = _("has verify differences");
548 msg = _("has been canceled");
551 emsg = (char *) get_pool_memory(PM_FNAME);
552 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->hdr.name);
557 emsg = (char *) get_pool_memory(PM_FNAME);
558 Mmsg(emsg, _("is waiting on Storage %s"), jcr->store->hdr.name);
562 case JS_WaitStoreRes:
563 msg = _("is waiting on max Storage jobs");
565 case JS_WaitClientRes:
566 msg = _("is waiting on max Client jobs");
569 msg = _("is waiting on max Job jobs");
572 msg = _("is waiting on max total jobs");
574 case JS_WaitStartTime:
575 msg = _("is waiting for its start time");
577 case JS_WaitPriority:
578 msg = _("is waiting for higher priority jobs to finish");
582 emsg = (char *) get_pool_memory(PM_FNAME);
583 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
589 * Now report Storage daemon status code
591 switch (jcr->SDJobStatus) {
594 free_pool_memory(emsg);
597 msg = _("is waiting for a mount request");
601 free_pool_memory(emsg);
604 msg = _("is waiting for an appendable Volume");
608 emsg = (char *) get_pool_memory(PM_FNAME);
611 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
612 jcr->client->hdr.name, jcr->store->hdr.name);
616 switch (jcr->JobType) {
619 bstrncpy(level, " ", sizeof(level));
622 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
627 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
634 free_pool_memory(emsg);
639 bsendmsg(ua, "====\n");
640 Dmsg0(200, "leave list_run_jobs()\n");
643 static void list_terminated_jobs(UAContext *ua)
645 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
648 if (last_jobs->empty()) {
649 bsendmsg(ua, _("No Terminated Jobs.\n"));
652 lock_last_jobs_list();
653 struct s_last_job *je;
654 bsendmsg(ua, _("\nTerminated Jobs:\n"));
655 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
656 bsendmsg(ua, _("========================================================================\n"));
657 foreach_dlist(je, last_jobs) {
658 char JobName[MAX_NAME_LENGTH];
659 const char *termstat;
661 bstrncpy(JobName, je->Job, sizeof(JobName));
662 /* There are three periods after the Job name */
664 for (int i=0; i<3; i++) {
665 if ((p=strrchr(JobName, '.')) != NULL) {
670 if (!acl_access_ok(ua, Job_ACL, JobName)) {
674 bstrftime_nc(dt, sizeof(dt), je->end_time);
675 switch (je->JobType) {
678 bstrncpy(level, " ", sizeof(level));
681 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
685 switch (je->JobStatus) {
687 termstat = "Created";
690 case JS_ErrorTerminated:
706 bsendmsg(ua, _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
709 edit_uint64_with_commas(je->JobFiles, b1),
710 edit_uint64_with_commas(je->JobBytes, b2),
715 unlock_last_jobs_list();