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=%d JobStatus=%c JobErrors=%d\n";
46 int qstatus_cmd(UAContext *ua, const char *cmd)
54 Dmsg1(20, "status:%s:\n", cmd);
56 if ((ua->argc != 3) || (strcasecmp(ua->argk[1], "dir"))) {
57 bsendmsg(ua, "1900 Bad .status command, missing arguments.\n");
61 if (strcasecmp(ua->argk[2], "current") == 0) {
62 bsendmsg(ua, OKqstatus, ua->argk[2]);
64 if (njcr->JobId != 0) {
65 bsendmsg(ua, DotStatusJob, njcr->JobId, njcr->JobStatus, njcr->JobErrors);
70 else if (strcasecmp(ua->argk[2], "last") == 0) {
71 bsendmsg(ua, OKqstatus, ua->argk[2]);
72 if ((last_jobs) && (last_jobs->size() > 0)) {
73 job = (s_last_job*)last_jobs->last();
74 bsendmsg(ua, DotStatusJob, job->JobId, job->JobStatus, job->Errors);
78 bsendmsg(ua, "1900 Bad .status command, wrong argument.\n");
88 int status_cmd(UAContext *ua, const char *cmd)
97 Dmsg1(20, "status:%s:\n", cmd);
99 for (i=1; i<ua->argc; i++) {
100 if (strcasecmp(ua->argk[i], _("all")) == 0) {
103 } else if (strcasecmp(ua->argk[i], _("dir")) == 0 ||
104 strcasecmp(ua->argk[i], _("director")) == 0) {
105 do_director_status(ua);
107 } else if (strcasecmp(ua->argk[i], _("client")) == 0) {
108 client = get_client_resource(ua);
110 do_client_status(ua, client);
114 store = get_storage_resource(ua, 0);
116 do_storage_status(ua, store);
121 /* If no args, ask for status type */
123 char prmt[MAX_NAME_LENGTH];
125 start_prompt(ua, _("Status available for:\n"));
126 add_prompt(ua, _("Director"));
127 add_prompt(ua, _("Storage"));
128 add_prompt(ua, _("Client"));
129 add_prompt(ua, _("All"));
130 Dmsg0(20, "do_prompt: select daemon\n");
131 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
134 Dmsg1(20, "item=%d\n", item);
136 case 0: /* Director */
137 do_director_status(ua);
140 store = select_storage_resource(ua);
142 do_storage_status(ua, store);
146 client = select_client_resource(ua);
148 do_client_status(ua, client);
161 static void do_all_status(UAContext *ua)
163 STORE *store, **unique_store;
164 CLIENT *client, **unique_client;
168 do_director_status(ua);
170 /* Count Storage items */
173 foreach_res(store, R_STORAGE) {
176 unique_store = (STORE **) malloc(i * sizeof(STORE));
177 /* Find Unique Storage address/port */
179 foreach_res(store, R_STORAGE) {
181 if (!acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
184 for (j=0; j<i; j++) {
185 if (strcmp(unique_store[j]->address, store->address) == 0 &&
186 unique_store[j]->SDport == store->SDport) {
192 unique_store[i++] = store;
193 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
198 /* Call each unique Storage daemon */
199 for (j=0; j<i; j++) {
200 do_storage_status(ua, unique_store[j]);
204 /* Count Client items */
207 foreach_res(client, R_CLIENT) {
210 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
211 /* Find Unique Client address/port */
213 foreach_res(client, R_CLIENT) {
215 if (!acl_access_ok(ua, Client_ACL, client->hdr.name)) {
218 for (j=0; j<i; j++) {
219 if (strcmp(unique_client[j]->address, client->address) == 0 &&
220 unique_client[j]->FDport == client->FDport) {
226 unique_client[i++] = client;
227 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
232 /* Call each unique File daemon */
233 for (j=0; j<i; j++) {
234 do_client_status(ua, unique_client[j]);
240 static void do_director_status(UAContext *ua)
242 char dt[MAX_TIME_LENGTH];
244 bsendmsg(ua, "%s Version: " VERSION " (" BDATE ") %s %s %s\n", my_name,
245 HOST_OS, DISTNAME, DISTVER);
246 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
247 bsendmsg(ua, _("Daemon started %s, %d Job%s run since started.\n"),
248 dt, num_jobs_run, num_jobs_run == 1 ? "" : "s");
249 if (debug_level > 0) {
250 char b1[35], b2[35], b3[35], b4[35];
251 bsendmsg(ua, _(" Heap: bytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
252 edit_uint64_with_commas(sm_bytes, b1),
253 edit_uint64_with_commas(sm_max_bytes, b2),
254 edit_uint64_with_commas(sm_buffers, b3),
255 edit_uint64_with_commas(sm_max_buffers, b4));
258 * List scheduled Jobs
260 list_scheduled_jobs(ua);
265 list_running_jobs(ua);
268 * List terminated jobs
270 list_terminated_jobs(ua);
271 bsendmsg(ua, "====\n");
274 static void do_storage_status(UAContext *ua, STORE *store)
278 set_storage(ua->jcr, store);
279 /* Try connecting for up to 15 seconds */
280 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
281 store->hdr.name, store->address, store->SDport);
282 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
283 bsendmsg(ua, _("\nFailed to connect to Storage daemon %s.\n====\n"),
285 if (ua->jcr->store_bsock) {
286 bnet_close(ua->jcr->store_bsock);
287 ua->jcr->store_bsock = NULL;
291 Dmsg0(20, _("Connected to storage daemon\n"));
292 sd = ua->jcr->store_bsock;
293 bnet_fsend(sd, "status");
294 while (bnet_recv(sd) >= 0) {
295 bsendmsg(ua, "%s", sd->msg);
297 bnet_sig(sd, BNET_TERMINATE);
299 ua->jcr->store_bsock = NULL;
303 static void do_client_status(UAContext *ua, CLIENT *client)
307 /* Connect to File daemon */
309 ua->jcr->client = client;
310 /* Release any old dummy key */
311 if (ua->jcr->sd_auth_key) {
312 free(ua->jcr->sd_auth_key);
314 /* Create a new dummy SD auth key */
315 ua->jcr->sd_auth_key = bstrdup("dummy");
317 /* Try to connect for 15 seconds */
318 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
319 client->hdr.name, client->address, client->FDport);
320 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
321 bsendmsg(ua, _("Failed to connect to Client %s.\n====\n"),
323 if (ua->jcr->file_bsock) {
324 bnet_close(ua->jcr->file_bsock);
325 ua->jcr->file_bsock = NULL;
329 Dmsg0(20, _("Connected to file daemon\n"));
330 fd = ua->jcr->file_bsock;
331 bnet_fsend(fd, "status");
332 while (bnet_recv(fd) >= 0) {
333 bsendmsg(ua, "%s", fd->msg);
335 bnet_sig(fd, BNET_TERMINATE);
337 ua->jcr->file_bsock = NULL;
342 static void prt_runhdr(UAContext *ua)
344 bsendmsg(ua, _("\nScheduled Jobs:\n"));
345 bsendmsg(ua, _("Level Type Pri Scheduled Name Volume\n"));
346 bsendmsg(ua, _("===================================================================================\n"));
349 /* Scheduling packet */
351 dlink link; /* keep this as first item!!! */
359 static void prt_runtime(UAContext *ua, sched_pkt *sp)
361 char dt[MAX_TIME_LENGTH];
362 const char *level_ptr;
364 bool close_db = false;
368 memset(&mr, 0, sizeof(mr));
369 if (sp->job->JobType == JT_BACKUP) {
371 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
373 close_db = true; /* new db opened, remember to close it */
376 mr.PoolId = jcr->PoolId;
377 ok = find_next_volume_for_append(jcr, &mr, 0);
380 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
383 bstrftime_nc(dt, sizeof(dt), sp->runtime);
384 switch (sp->job->JobType) {
390 level_ptr = level_to_str(sp->level);
393 bsendmsg(ua, _("%-14s %-8s %3d %-18s %-18s %s\n"),
394 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
395 sp->job->hdr.name, mr.VolumeName);
397 db_close_database(jcr, jcr->db);
399 jcr->db = ua->db; /* restore ua db to jcr */
404 * Sort items by runtime, priority
406 static int my_compare(void *item1, void *item2)
408 sched_pkt *p1 = (sched_pkt *)item1;
409 sched_pkt *p2 = (sched_pkt *)item2;
410 if (p1->runtime < p2->runtime) {
412 } else if (p1->runtime > p2->runtime) {
415 if (p1->priority < p2->priority) {
417 } else if (p1->priority > p2->priority) {
424 * Find all jobs to be run in roughly the
427 static void list_scheduled_jobs(UAContext *ua)
432 int level, num_jobs = 0;
434 bool hdr_printed = false;
438 Dmsg0(200, "enter list_sched_jobs()\n");
440 /* Loop through all jobs */
442 foreach_res(job, R_JOB) {
443 if (!acl_access_ok(ua, Job_ACL, job->hdr.name)) {
446 for (run=NULL; (run = find_next_run(run, job, runtime)); ) {
447 level = job->JobLevel;
451 priority = job->Priority;
453 priority = run->Priority;
459 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
462 sp->priority = priority;
463 sp->runtime = runtime;
464 sp->pool = run->pool;
465 sched.binary_insert_multiple(sp, my_compare);
468 } /* end for loop over resources */
470 foreach_dlist(sp, &sched) {
474 bsendmsg(ua, _("No Scheduled Jobs.\n"));
476 bsendmsg(ua, "====\n");
477 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
480 static void list_running_jobs(UAContext *ua)
485 char *emsg; /* edited message */
486 char dt[MAX_TIME_LENGTH];
488 bool pool_mem = false;
490 Dmsg0(200, "enter list_run_jobs()\n");
491 bsendmsg(ua, _("\nRunning Jobs:\n"));
494 if (jcr->JobId == 0) { /* this is us */
495 /* this is a console or other control job. We only show console
496 * jobs in the status output.
498 if (jcr->JobType == JT_CONSOLE) {
499 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
500 bsendmsg(ua, _("Console connected at %s\n"), dt);
507 /* Note the following message is used in regress -- don't change */
508 bsendmsg(ua, _("No Jobs running.\n====\n"));
509 Dmsg0(200, "leave list_run_jobs()\n");
513 bsendmsg(ua, _(" JobId Level Name Status\n"));
514 bsendmsg(ua, _("======================================================================\n"));
516 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->hdr.name)) {
521 switch (jcr->JobStatus) {
523 msg = _("is waiting execution");
526 msg = _("is running");
529 msg = _("is blocked");
532 msg = _("has terminated");
534 case JS_ErrorTerminated:
535 msg = _("has erred");
538 msg = _("has errors");
541 msg = _("has a fatal error");
544 msg = _("has verify differences");
547 msg = _("has been canceled");
550 emsg = (char *) get_pool_memory(PM_FNAME);
551 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->hdr.name);
556 emsg = (char *) get_pool_memory(PM_FNAME);
557 Mmsg(emsg, _("is waiting on Storage %s"), jcr->store->hdr.name);
561 case JS_WaitStoreRes:
562 msg = _("is waiting on max Storage jobs");
564 case JS_WaitClientRes:
565 msg = _("is waiting on max Client jobs");
568 msg = _("is waiting on max Job jobs");
571 msg = _("is waiting on max total jobs");
573 case JS_WaitStartTime:
574 msg = _("is waiting for its start time");
576 case JS_WaitPriority:
577 msg = _("is waiting for higher priority jobs to finish");
581 emsg = (char *) get_pool_memory(PM_FNAME);
582 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
588 * Now report Storage daemon status code
590 switch (jcr->SDJobStatus) {
593 free_pool_memory(emsg);
596 msg = _("is waiting for a mount request");
600 free_pool_memory(emsg);
603 msg = _("is waiting for an appendable Volume");
607 emsg = (char *) get_pool_memory(PM_FNAME);
610 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
611 jcr->client->hdr.name, jcr->store->hdr.name);
615 switch (jcr->JobType) {
618 bstrncpy(level, " ", sizeof(level));
621 bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level));
626 bsendmsg(ua, _("%6d %-6s %-20s %s\n"),
633 free_pool_memory(emsg);
638 bsendmsg(ua, "====\n");
639 Dmsg0(200, "leave list_run_jobs()\n");
642 static void list_terminated_jobs(UAContext *ua)
644 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
647 if (last_jobs->empty()) {
648 bsendmsg(ua, _("No Terminated Jobs.\n"));
651 lock_last_jobs_list();
652 struct s_last_job *je;
653 bsendmsg(ua, _("\nTerminated Jobs:\n"));
654 bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n"));
655 bsendmsg(ua, _("========================================================================\n"));
656 foreach_dlist(je, last_jobs) {
657 char JobName[MAX_NAME_LENGTH];
658 const char *termstat;
660 bstrncpy(JobName, je->Job, sizeof(JobName));
661 /* There are three periods after the Job name */
663 for (int i=0; i<3; i++) {
664 if ((p=strrchr(JobName, '.')) != NULL) {
669 if (!acl_access_ok(ua, Job_ACL, JobName)) {
673 bstrftime_nc(dt, sizeof(dt), je->end_time);
674 switch (je->JobType) {
677 bstrncpy(level, " ", sizeof(level));
680 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
684 switch (je->JobStatus) {
686 termstat = "Created";
689 case JS_ErrorTerminated:
705 bsendmsg(ua, _("%6d %-6s %8s %14s %-7s %-8s %s\n"),
708 edit_uint64_with_commas(je->JobFiles, b1),
709 edit_uint64_with_commas(je->JobBytes, b2),
714 unlock_last_jobs_list();