2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Bacula Director -- User Agent Status Command
23 * Kern Sibbald, August MMI
31 extern void *start_heap;
32 extern utime_t last_reload_time;
34 static void list_scheduled_jobs(UAContext *ua);
35 static void llist_scheduled_jobs(UAContext *ua);
36 static void list_running_jobs(UAContext *ua);
37 static void list_terminated_jobs(UAContext *ua);
38 static void do_storage_status(UAContext *ua, STORE *store, char *cmd);
39 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd);
40 static void do_director_status(UAContext *ua);
41 static void do_all_status(UAContext *ua);
42 void status_slots(UAContext *ua, STORE *store);
43 void status_content(UAContext *ua, STORE *store);
45 static char OKqstatus[] = "1000 OK .status\n";
46 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
52 bool dot_status_cmd(UAContext *ua, const char *cmd)
60 Dmsg2(20, "status=\"%s\" argc=%d\n", cmd, ua->argc);
63 ua->send_msg("1900 Bad .status command, missing arguments.\n");
67 if (strcasecmp(ua->argk[1], "dir") == 0) {
68 if (strcasecmp(ua->argk[2], "current") == 0) {
69 ua->send_msg(OKqstatus, ua->argk[2]);
71 if (njcr->JobId != 0 && acl_access_ok(ua, Job_ACL, njcr->job->name())) {
72 ua->send_msg(DotStatusJob, edit_int64(njcr->JobId, ed1),
73 njcr->JobStatus, njcr->JobErrors);
77 } else if (strcasecmp(ua->argk[2], "last") == 0) {
78 ua->send_msg(OKqstatus, ua->argk[2]);
79 if ((last_jobs) && (last_jobs->size() > 0)) {
80 job = (s_last_job*)last_jobs->last();
81 if (acl_access_ok(ua, Job_ACL, job->Job)) {
82 ua->send_msg(DotStatusJob, edit_int64(job->JobId, ed1),
83 job->JobStatus, job->Errors);
86 } else if (strcasecmp(ua->argk[2], "header") == 0) {
87 list_dir_status_header(ua);
88 } else if (strcasecmp(ua->argk[2], "scheduled") == 0) {
89 list_scheduled_jobs(ua);
90 } else if (strcasecmp(ua->argk[2], "running") == 0) {
91 list_running_jobs(ua);
92 } else if (strcasecmp(ua->argk[2], "terminated") == 0) {
93 list_terminated_jobs(ua);
95 ua->send_msg("1900 Bad .status command, wrong argument.\n");
98 } else if (strcasecmp(ua->argk[1], "client") == 0) {
99 client = get_client_resource(ua);
101 Dmsg2(200, "Client=%s arg=%s\n", client->name(), NPRT(ua->argk[2]));
102 do_client_status(ua, client, ua->argk[2]);
104 } else if (strcasecmp(ua->argk[1], "storage") == 0) {
105 store = get_storage_resource(ua, false /*no default*/, true/*unique*/);
107 ua->send_msg("1900 Bad .status command, wrong argument.\n");
110 do_storage_status(ua, store, ua->argk[2]);
112 ua->send_msg("1900 Bad .status command, wrong argument.\n");
119 /* This is the *old* command handler, so we must return
120 * 1 or it closes the connection
122 int qstatus_cmd(UAContext *ua, const char *cmd)
124 dot_status_cmd(ua, cmd);
131 int status_cmd(UAContext *ua, const char *cmd)
137 Dmsg1(20, "status:%s:\n", cmd);
139 for (i=1; i<ua->argc; i++) {
140 if (strcasecmp(ua->argk[i], NT_("schedule")) == 0 ||
141 strcasecmp(ua->argk[i], NT_("scheduled")) == 0) {
142 llist_scheduled_jobs(ua);
144 } else if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
147 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
148 strcasecmp(ua->argk[i], NT_("director")) == 0) {
149 do_director_status(ua);
151 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
152 client = get_client_resource(ua);
154 do_client_status(ua, client, NULL);
158 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
160 if (find_arg(ua, NT_("slots")) > 0) {
161 status_slots(ua, store);
163 do_storage_status(ua, store, NULL);
169 /* If no args, ask for status type */
171 char prmt[MAX_NAME_LENGTH];
173 start_prompt(ua, _("Status available for:\n"));
174 add_prompt(ua, NT_("Director"));
175 add_prompt(ua, NT_("Storage"));
176 add_prompt(ua, NT_("Client"));
177 add_prompt(ua, NT_("Scheduled"));
178 add_prompt(ua, NT_("All"));
179 Dmsg0(20, "do_prompt: select daemon\n");
180 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
183 Dmsg1(20, "item=%d\n", item);
185 case 0: /* Director */
186 do_director_status(ua);
189 store = select_storage_resource(ua, true/*unique*/);
191 do_storage_status(ua, store, NULL);
195 client = select_client_resource(ua);
197 do_client_status(ua, client, NULL);
201 llist_scheduled_jobs(ua);
213 static void do_all_status(UAContext *ua)
215 STORE *store, **unique_store;
216 CLIENT *client, **unique_client;
220 do_director_status(ua);
222 /* Count Storage items */
225 foreach_res(store, R_STORAGE) {
228 unique_store = (STORE **) malloc(i * sizeof(STORE));
229 /* Find Unique Storage address/port */
231 foreach_res(store, R_STORAGE) {
233 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
236 for (j=0; j<i; j++) {
237 if (strcmp(unique_store[j]->address, store->address) == 0 &&
238 unique_store[j]->SDport == store->SDport) {
244 unique_store[i++] = store;
245 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
250 /* Call each unique Storage daemon */
251 for (j=0; j<i; j++) {
252 do_storage_status(ua, unique_store[j], NULL);
256 /* Count Client items */
259 foreach_res(client, R_CLIENT) {
262 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
263 /* Find Unique Client address/port */
265 foreach_res(client, R_CLIENT) {
267 if (!acl_access_ok(ua, Client_ACL, client->name())) {
270 for (j=0; j<i; j++) {
271 if (strcmp(unique_client[j]->address, client->address) == 0 &&
272 unique_client[j]->FDport == client->FDport) {
278 unique_client[i++] = client;
279 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
284 /* Call each unique File daemon */
285 for (j=0; j<i; j++) {
286 do_client_status(ua, unique_client[j], NULL);
292 void list_dir_status_header(UAContext *ua)
294 char dt[MAX_TIME_LENGTH], dt1[MAX_TIME_LENGTH];
295 char b1[35], b2[35], b3[35], b4[35], b5[35];
297 ua->send_msg(_("%s %sVersion: %s (%s) %s %s %s\n"), my_name,
298 "", VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
299 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
300 bstrftimes(dt1, sizeof(dt1), last_reload_time);
301 ua->send_msg(_("Daemon started %s, conf reloaded %s\n"), dt, dt1);
302 ua->send_msg(_(" Jobs: run=%d, running=%d mode=%d\n"),
303 num_jobs_run, job_count(), (int)DEVELOPER_MODE);
304 ua->send_msg(_(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
305 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
306 edit_uint64_with_commas(sm_bytes, b2),
307 edit_uint64_with_commas(sm_max_bytes, b3),
308 edit_uint64_with_commas(sm_buffers, b4),
309 edit_uint64_with_commas(sm_max_buffers, b5));
311 /* TODO: use this function once for all daemons */
312 if (b_plugin_list->size() > 0) {
315 POOL_MEM msg(PM_FNAME);
316 pm_strcpy(msg, " Plugin: ");
317 foreach_alist(plugin, b_plugin_list) {
318 len = pm_strcat(msg, plugin->file);
320 pm_strcat(msg, "\n ");
325 ua->send_msg("%s\n", msg.c_str());
329 static void do_director_status(UAContext *ua)
331 list_dir_status_header(ua);
334 * List scheduled Jobs
336 list_scheduled_jobs(ua);
341 list_running_jobs(ua);
344 * List terminated jobs
346 list_terminated_jobs(ua);
347 ua->send_msg("====\n");
350 static void do_storage_status(UAContext *ua, STORE *store, char *cmd)
356 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
357 ua->error_msg(_("No authorization for Storage \"%s\"\n"), store->name());
361 * The Storage daemon is problematic because it shows information
362 * related to multiple Job, so if there is a Client or Job
363 * ACL restriction, we forbid all access to the Storage.
365 if (have_restricted_acl(ua, Client_ACL) ||
366 have_restricted_acl(ua, Job_ACL)) {
367 ua->error_msg(_("Restricted Client or Job does not permit access to Storage daemons\n"));
370 lstore.store = store;
371 pm_strcpy(lstore.store_source, _("unknown source"));
372 set_wstorage(ua->jcr, &lstore);
373 /* Try connecting for up to 15 seconds */
374 if (!ua->api) ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
375 store->name(), store->address, store->SDport);
376 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
377 ua->send_msg(_("\nFailed to connect to Storage daemon %s.\n====\n"),
379 free_bsock(ua->jcr->store_bsock);
382 Dmsg0(20, "Connected to storage daemon\n");
383 sd = ua->jcr->store_bsock;
387 * For .status storage=xxx shstore list
388 * send .status shstore list xxx-device
390 if (strcasecmp(cmd, "shstore") == 0) {
392 ua->send_msg(_("Must have three aguments\n"));
395 pm_strcpy(devname, store->dev_name());
396 bash_spaces(devname.c_str());
397 sd->fsend(".status %s %s %s api=%d api_opts=%s",
398 cmd, ua->argk[3], devname.c_str(),
399 ua->api, ua->api_opts);
401 int i = find_arg_with_value(ua, "device");
403 Mmsg(devname, "device=%s", ua->argv[i]);
404 bash_spaces(devname.c_str());
406 sd->fsend(".status %s api=%d api_opts=%s %s",
407 cmd, ua->api, ua->api_opts, devname.c_str());
412 while (sd->recv() >= 0) {
413 ua->send_msg("%s", sd->msg);
415 sd->signal(BNET_TERMINATE);
416 free_bsock(ua->jcr->store_bsock);
420 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd)
424 if (!acl_access_ok(ua, Client_ACL, client->name())) {
425 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
428 /* Connect to File daemon */
429 ua->jcr->client = client;
430 /* Release any old dummy key */
431 if (ua->jcr->sd_auth_key) {
432 free(ua->jcr->sd_auth_key);
434 /* Create a new dummy SD auth key */
435 ua->jcr->sd_auth_key = bstrdup("dummy");
437 /* Try to connect for 15 seconds */
438 if (!ua->api) ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
439 client->name(), client->address, client->FDport);
440 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
441 ua->send_msg(_("Failed to connect to Client %s.\n====\n"),
443 free_bsock(ua->jcr->file_bsock);
446 Dmsg0(20, _("Connected to file daemon\n"));
447 fd = ua->jcr->file_bsock;
449 fd->fsend(".status %s api=%d api_opts=%s", cmd, ua->api, ua->api_opts);
453 while (fd->recv() >= 0) {
454 ua->send_msg("%s", fd->msg);
456 fd->signal(BNET_TERMINATE);
457 free_bsock(ua->jcr->file_bsock);
462 static void prt_runhdr(UAContext *ua)
465 ua->send_msg(_("\nScheduled Jobs:\n"));
466 ua->send_msg(_("Level Type Pri Scheduled Job Name Volume\n"));
467 ua->send_msg(_("===================================================================================\n"));
471 static void prt_lrunhdr(UAContext *ua)
474 ua->send_msg(_("\nScheduled Jobs:\n"));
475 ua->send_msg(_("Level Type Pri Scheduled Job Name Schedule\n"));
476 ua->send_msg(_("=====================================================================================\n"));
481 /* Scheduling packet */
483 dlink link; /* keep this as first item!!! */
492 static void prt_runtime(UAContext *ua, sched_pkt *sp)
494 char dt[MAX_TIME_LENGTH];
495 const char *level_ptr;
497 bool close_db = false;
502 orig_jobtype = jcr->getJobType();
503 if (sp->job->JobType == JT_BACKUP) {
505 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
506 Dmsg1(250, "Using pool=%s\n", jcr->pool->name());
508 close_db = true; /* new db opened, remember to close it */
511 mr.PoolId = jcr->jr.PoolId;
512 jcr->wstore = sp->store;
513 set_storageid_in_mr(jcr->wstore, &mr);
514 Dmsg0(250, "call find_next_volume_for_append\n");
515 /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
516 ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune);
519 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
522 bstrftime_nc(dt, sizeof(dt), sp->runtime);
523 switch (sp->job->JobType) {
529 level_ptr = level_to_str(sp->level);
533 ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
534 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
535 sp->job->name(), mr.VolumeName);
537 ua->send_msg(_("%-14s %-8s %3d %-18s %-18s %s\n"),
538 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
539 sp->job->name(), mr.VolumeName);
542 db_close_database(jcr, jcr->db);
544 jcr->db = ua->db; /* restore ua db to jcr */
545 jcr->setJobType(orig_jobtype);
549 * Detailed listing of all scheduler jobs
551 static void llist_scheduled_jobs(UAContext *ua)
556 int level, num_jobs = 0;
558 bool hdr_printed = false;
559 char sched_name[MAX_NAME_LENGTH];
560 char job_name[MAX_NAME_LENGTH];
563 time_t now = time(NULL);
565 const char *level_ptr;
567 Dmsg0(200, "enter list_sched_jobs()\n");
569 i = find_arg_with_value(ua, NT_("days"));
571 days = atoi(ua->argv[i]);
572 if (((days < 0) || (days > 3000)) && !ua->api) {
573 ua->send_msg(_("Ignoring invalid value for days. Max is 3000.\n"));
580 i = find_arg_with_value(ua, NT_("limit"));
582 limit = atoi(ua->argv[i]);
583 if (((limit < 0) || (limit > 2000)) && !ua->api) {
584 ua->send_msg(_("Ignoring invalid value for limit. Max is 2000.\n"));
591 i = find_arg_with_value(ua, NT_("time"));
593 now = str_to_utime(ua->argv[i]);
595 ua->send_msg(_("Ignoring invalid time.\n"));
600 i = find_arg_with_value(ua, NT_("schedule"));
602 bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
607 i = find_arg_with_value(ua, NT_("job"));
609 bstrncpy(job_name, ua->argv[i], sizeof(job_name));
614 /* Loop through all jobs */
616 foreach_res(job, R_JOB) {
617 sched = job->schedule;
618 if (!sched || !job->enabled || (sched && !sched->enabled) ||
619 (job->client && !job->client->enabled)) {
620 continue; /* no, skip this job */
622 if (job_name[0] && strcmp(job_name, job->name()) != 0) {
625 for (run=sched->run; run; run=run->next) {
627 for (i=0; i<days; i++) {
629 int mday, wday, month, wom, woy, ldom;
630 char dt[MAX_TIME_LENGTH];
633 /* compute values for next time */
634 (void)localtime_r(&next, &tm);
635 mday = tm.tm_mday - 1;
639 woy = tm_woy(next); /* get week of year */
640 ldom = tm_ldom(month, tm.tm_year + 1900);
644 Dmsg6(000, "m=%d md=%d wd=%d wom=%d woy=%d ldom=%d\n",
645 month, mday, wday, wom, woy, ldom);
646 Dmsg6(000, "bitset bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d bsldom=%d\n",
647 bit_is_set(month, run->month),
648 bit_is_set(mday, run->mday),
649 bit_is_set(wday, run->wday),
650 bit_is_set(wom, run->wom),
651 bit_is_set(woy, run->woy),
652 bit_is_set(31, run->mday));
655 ok = (bit_is_set(mday, run->mday) &&
656 bit_is_set(wday, run->wday) &&
657 bit_is_set(month, run->month) &&
658 bit_is_set(wom, run->wom) &&
659 bit_is_set(woy, run->woy)) ||
660 (bit_is_set(month, run->month) &&
661 bit_is_set(31, run->mday) && mday == ldom);
663 next += 24 * 60 * 60; /* Add one day */
666 for (int j=0; j < 24; j++) {
667 if (bit_is_set(j, run->hour)) {
669 tm.tm_min = run->minute;
671 runtime = mktime(&tm);
672 bstrftime_dn(dt, sizeof(dt), runtime);
677 level = job->JobLevel;
681 switch (job->JobType) {
687 level_ptr = level_to_str(level);
690 priority = job->Priority;
692 priority = run->Priority;
699 ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
700 level_ptr, job_type_to_str(job->JobType), priority, dt,
701 job->name(), sched->name());
703 ua->send_msg(_("%-14s %-8s %3d %-18s %-18s %s\n"),
704 level_ptr, job_type_to_str(job->JobType), priority, dt,
705 job->name(), sched->name());
707 next += 24 * 60 * 60; /* Add one day */
709 if (num_jobs >= limit) {
713 } /* end loop over run pkts */
714 } /* end for loop over resources */
717 if (num_jobs == 0 && !ua->api) {
718 ua->send_msg(_("No Scheduled Jobs.\n"));
720 if (!ua->api) ua->send_msg("====\n");
721 Dmsg0(200, "Leave ;list_sched_jobs_runs()\n");
726 * Sort items by runtime, priority
728 static int my_compare(void *item1, void *item2)
730 sched_pkt *p1 = (sched_pkt *)item1;
731 sched_pkt *p2 = (sched_pkt *)item2;
732 if (p1->runtime < p2->runtime) {
734 } else if (p1->runtime > p2->runtime) {
737 if (p1->priority < p2->priority) {
739 } else if (p1->priority > p2->priority) {
746 * Find all jobs to be run in roughly the
749 static void list_scheduled_jobs(UAContext *ua)
754 int level, num_jobs = 0;
756 bool hdr_printed = false;
757 char sched_name[MAX_NAME_LENGTH];
762 Dmsg0(200, "enter list_sched_jobs()\n");
765 i = find_arg_with_value(ua, NT_("days"));
767 days = atoi(ua->argv[i]);
768 if (((days < 0) || (days > 500)) && !ua->api) {
769 ua->send_msg(_("Ignoring invalid value for days. Max is 500.\n"));
773 i = find_arg_with_value(ua, NT_("schedule"));
775 bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
780 /* Loop through all jobs */
782 foreach_res(job, R_JOB) {
783 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
786 if (sched_name[0] && job->schedule &&
787 strcasecmp(job->schedule->name(), sched_name) != 0) {
790 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
792 level = job->JobLevel;
796 priority = job->Priority;
798 priority = run->Priority;
804 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
807 sp->priority = priority;
808 sp->runtime = runtime;
809 sp->pool = run->pool;
810 get_job_storage(&store, job, run);
811 sp->store = store.store;
812 Dmsg3(250, "job=%s store=%s MediaType=%s\n", job->name(), sp->store->name(), sp->store->media_type);
813 sched.binary_insert_multiple(sp, my_compare);
816 } /* end for loop over resources */
818 foreach_dlist(sp, &sched) {
821 if (num_jobs == 0 && !ua->api) {
822 ua->send_msg(_("No Scheduled Jobs.\n"));
824 if (!ua->api) ua->send_msg("====\n");
825 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
828 static void list_running_jobs(UAContext *ua)
834 char *emsg; /* edited message */
835 char dt[MAX_TIME_LENGTH];
837 bool pool_mem = false;
840 if ((i = find_arg_with_value(ua, "jobid")) >= 0) {
841 jid = str_to_int64(ua->argv[i]);
844 Dmsg0(200, "enter list_run_jobs()\n");
847 ua->send_msg(_("\nRunning Jobs:\n"));
849 if (jcr->JobId == 0) { /* this is us */
850 /* this is a console or other control job. We only show console
851 * jobs in the status output.
853 if (jcr->getJobType() == JT_CONSOLE) {
854 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
855 ua->send_msg(_("Console connected %sat %s\n"),
856 (ua->UA_sock && ua->UA_sock->tls)?_("using TLS "):"",
865 /* Note the following message is used in regress -- don't change */
866 ua->send_msg(_("No Jobs running.\n====\n"));
867 Dmsg0(200, "leave list_run_jobs()\n");
873 ua->send_msg(_(" JobId Type Level Files Bytes Name Status\n"));
874 ua->send_msg(_("======================================================================\n"));
877 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
880 /* JobId keyword found in command line */
881 if (jid > 0 && jcr->JobId != jid) {
884 switch (jcr->JobStatus) {
886 msg = _("is waiting execution");
889 msg = _("is running");
892 msg = _("is blocked");
895 msg = _("has terminated");
898 msg = _("has terminated with warnings");
901 msg = _("has terminated in incomplete state");
903 case JS_ErrorTerminated:
904 msg = _("has erred");
907 msg = _("has errors");
910 msg = _("has a fatal error");
913 msg = _("has verify differences");
916 msg = _("has been canceled");
919 emsg = (char *) get_pool_memory(PM_FNAME);
921 Mmsg(emsg, _("is waiting on Client"));
923 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
929 emsg = (char *) get_pool_memory(PM_FNAME);
931 Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->wstore->name());
932 } else if (jcr->rstore) {
933 Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->rstore->name());
935 Mmsg(emsg, _("is waiting on Storage"));
940 case JS_WaitStoreRes:
941 msg = _("is waiting on max Storage jobs");
943 case JS_WaitClientRes:
944 msg = _("is waiting on max Client jobs");
947 msg = _("is waiting on max Job jobs");
950 msg = _("is waiting on max total jobs");
952 case JS_WaitStartTime:
953 emsg = (char *) get_pool_memory(PM_FNAME);
954 Mmsg(emsg, _("is waiting for its start time (%s)"),
955 bstrftime_ny(dt, sizeof(dt), jcr->sched_time));
959 case JS_WaitPriority:
960 msg = _("is waiting for higher priority jobs to finish");
963 msg = _("is waiting for a Shared Storage device");
965 case JS_DataCommitting:
966 msg = _("SD committing Data");
968 case JS_DataDespooling:
969 msg = _("SD despooling Data");
971 case JS_AttrDespooling:
972 msg = _("SD despooling Attributes");
974 case JS_AttrInserting:
975 msg = _("Dir inserting Attributes");
979 emsg = (char *)get_pool_memory(PM_FNAME);
980 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
986 * Now report Storage daemon status code
988 switch (jcr->SDJobStatus) {
991 free_pool_memory(emsg);
994 msg = _("is waiting for a mount request");
998 free_pool_memory(emsg);
1001 msg = _("is waiting for an appendable Volume");
1004 /* Special case when JobStatus=JS_WaitFD, we don't have a FD link yet
1005 * we need to stay in WaitFD status See bee mantis #1414 */
1006 if (jcr->JobStatus != JS_WaitFD) {
1008 emsg = (char *)get_pool_memory(PM_FNAME);
1011 if (!jcr->client || !jcr->wstore) {
1012 Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
1014 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
1015 jcr->client->name(), jcr->wstore->name());
1020 case JS_DataCommitting:
1021 msg = _("SD committing Data");
1023 case JS_DataDespooling:
1024 msg = _("SD despooling Data");
1026 case JS_AttrDespooling:
1027 msg = _("SD despooling Attributes");
1029 case JS_AttrInserting:
1030 msg = _("Dir inserting Attributes");
1033 switch (jcr->getJobType()) {
1036 bstrncpy(level, " ", sizeof(level));
1039 bstrncpy(level, level_to_str(jcr->getJobLevel()), sizeof(level));
1045 bash_spaces(jcr->comment);
1046 ua->send_msg(_("%6d\t%-6s\t%-20s\t%s\t%s\n"),
1047 jcr->JobId, level, jcr->Job, msg, jcr->comment);
1048 unbash_spaces(jcr->comment);
1051 char b1[50], b2[50], b3[50];
1053 bstrncpy(b1, job_type_to_str(jcr->getJobType()), sizeof(b1));
1055 ua->send_msg(_("%6d %-4s %-3s %10s %10s %-17s %s\n"),
1056 jcr->JobId, b1, level,
1057 edit_uint64_with_commas(jcr->JobFiles, b2),
1058 edit_uint64_with_suffix(jcr->JobBytes, b3),
1059 jcr->job->name(), msg);
1063 free_pool_memory(emsg);
1069 ua->send_msg("====\n");
1071 Dmsg0(200, "leave list_run_jobs()\n");
1074 static void list_terminated_jobs(UAContext *ua)
1076 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
1079 if (last_jobs->empty()) {
1080 if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
1083 lock_last_jobs_list();
1084 struct s_last_job *je;
1086 ua->send_msg(_("\nTerminated Jobs:\n"));
1087 ua->send_msg(_(" JobId Level Files Bytes Status Finished Name \n"));
1088 ua->send_msg(_("====================================================================\n"));
1090 foreach_dlist(je, last_jobs) {
1091 char JobName[MAX_NAME_LENGTH];
1092 const char *termstat;
1094 bstrncpy(JobName, je->Job, sizeof(JobName));
1095 /* There are three periods after the Job name */
1097 for (int i=0; i<3; i++) {
1098 if ((p=strrchr(JobName, '.')) != NULL) {
1103 if (!acl_access_ok(ua, Job_ACL, JobName)) {
1107 bstrftime_nc(dt, sizeof(dt), je->end_time);
1108 switch (je->JobType) {
1111 bstrncpy(level, " ", sizeof(level));
1114 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
1118 switch (je->JobStatus) {
1120 termstat = _("Created");
1123 case JS_ErrorTerminated:
1124 termstat = _("Error");
1126 case JS_Differences:
1127 termstat = _("Diffs");
1130 termstat = _("Cancel");
1136 termstat = _("OK -- with warnings");
1139 termstat = _("Incomplete");
1142 termstat = _("Other");
1146 ua->send_msg(_("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
1149 edit_uint64_with_commas(je->JobFiles, b1),
1150 edit_uint64_with_suffix(je->JobBytes, b2),
1154 ua->send_msg(_("%6d %-6s %8s %10s %-7s %-8s %s\n"),
1157 edit_uint64_with_commas(je->JobFiles, b1),
1158 edit_uint64_with_suffix(je->JobBytes, b2),
1164 ua->send_msg(_("\n"));
1166 unlock_last_jobs_list();