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->is_internal_job() && 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 && 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) {
528 level_ptr = "Restore";
531 level_ptr = level_to_str(sp->level);
535 ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
536 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
537 sp->job->name(), mr.VolumeName);
539 ua->send_msg(_("%-14s %-8s %3d %-18s %-18s %s\n"),
540 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
541 sp->job->name(), mr.VolumeName);
544 db_close_database(jcr, jcr->db);
546 jcr->db = ua->db; /* restore ua db to jcr */
547 jcr->setJobType(orig_jobtype);
551 * Detailed listing of all scheduler jobs
553 static void llist_scheduled_jobs(UAContext *ua)
558 int level, num_jobs = 0;
560 bool hdr_printed = false;
561 char sched_name[MAX_NAME_LENGTH];
562 char job_name[MAX_NAME_LENGTH];
565 time_t now = time(NULL);
567 const char *level_ptr;
569 Dmsg0(200, "enter list_sched_jobs()\n");
571 i = find_arg_with_value(ua, NT_("days"));
573 days = atoi(ua->argv[i]);
574 if (((days < 0) || (days > 3000)) && !ua->api) {
575 ua->send_msg(_("Ignoring invalid value for days. Max is 3000.\n"));
582 i = find_arg_with_value(ua, NT_("limit"));
584 limit = atoi(ua->argv[i]);
585 if (((limit < 0) || (limit > 2000)) && !ua->api) {
586 ua->send_msg(_("Ignoring invalid value for limit. Max is 2000.\n"));
593 i = find_arg_with_value(ua, NT_("time"));
595 now = str_to_utime(ua->argv[i]);
597 ua->send_msg(_("Ignoring invalid time.\n"));
602 i = find_arg_with_value(ua, NT_("schedule"));
604 bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
609 i = find_arg_with_value(ua, NT_("job"));
611 bstrncpy(job_name, ua->argv[i], sizeof(job_name));
616 /* Loop through all jobs */
618 foreach_res(job, R_JOB) {
619 sched = job->schedule;
620 if (!sched || !job->enabled || (sched && !sched->enabled) ||
621 (job->client && !job->client->enabled)) {
622 continue; /* no, skip this job */
624 if (job_name[0] && strcmp(job_name, job->name()) != 0) {
627 for (run=sched->run; run; run=run->next) {
629 for (i=0; i<days; i++) {
631 int mday, wday, month, wom, woy, ldom;
632 char dt[MAX_TIME_LENGTH];
635 /* compute values for next time */
636 (void)localtime_r(&next, &tm);
637 mday = tm.tm_mday - 1;
641 woy = tm_woy(next); /* get week of year */
642 ldom = tm_ldom(month, tm.tm_year + 1900);
646 Dmsg6(000, "m=%d md=%d wd=%d wom=%d woy=%d ldom=%d\n",
647 month, mday, wday, wom, woy, ldom);
648 Dmsg6(000, "bitset bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d bsldom=%d\n",
649 bit_is_set(month, run->month),
650 bit_is_set(mday, run->mday),
651 bit_is_set(wday, run->wday),
652 bit_is_set(wom, run->wom),
653 bit_is_set(woy, run->woy),
654 bit_is_set(31, run->mday));
657 ok = (bit_is_set(mday, run->mday) &&
658 bit_is_set(wday, run->wday) &&
659 bit_is_set(month, run->month) &&
660 bit_is_set(wom, run->wom) &&
661 bit_is_set(woy, run->woy)) ||
662 (bit_is_set(month, run->month) &&
663 bit_is_set(31, run->mday) && mday == ldom);
665 next += 24 * 60 * 60; /* Add one day */
668 for (int j=0; j < 24; j++) {
669 if (bit_is_set(j, run->hour)) {
671 tm.tm_min = run->minute;
673 runtime = mktime(&tm);
674 bstrftime_dn(dt, sizeof(dt), runtime);
679 level = job->JobLevel;
683 switch (job->JobType) {
688 level_ptr = "Restore";
691 level_ptr = level_to_str(level);
694 priority = job->Priority;
696 priority = run->Priority;
703 ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
704 level_ptr, job_type_to_str(job->JobType), priority, dt,
705 job->name(), sched->name());
707 ua->send_msg(_("%-14s %-8s %3d %-18s %-18s %s\n"),
708 level_ptr, job_type_to_str(job->JobType), priority, dt,
709 job->name(), sched->name());
711 next += 24 * 60 * 60; /* Add one day */
713 if (num_jobs >= limit) {
717 } /* end loop over run pkts */
718 } /* end for loop over resources */
721 if (num_jobs == 0 && !ua->api) {
722 ua->send_msg(_("No Scheduled Jobs.\n"));
724 if (!ua->api) ua->send_msg("====\n");
725 Dmsg0(200, "Leave ;list_sched_jobs_runs()\n");
730 * Sort items by runtime, priority
732 static int my_compare(void *item1, void *item2)
734 sched_pkt *p1 = (sched_pkt *)item1;
735 sched_pkt *p2 = (sched_pkt *)item2;
736 if (p1->runtime < p2->runtime) {
738 } else if (p1->runtime > p2->runtime) {
741 if (p1->priority < p2->priority) {
743 } else if (p1->priority > p2->priority) {
750 * Find all jobs to be run in roughly the
753 static void list_scheduled_jobs(UAContext *ua)
758 int level, num_jobs = 0;
760 bool hdr_printed = false;
761 char sched_name[MAX_NAME_LENGTH];
766 Dmsg0(200, "enter list_sched_jobs()\n");
769 i = find_arg_with_value(ua, NT_("days"));
771 days = atoi(ua->argv[i]);
772 if (((days < 0) || (days > 500)) && !ua->api) {
773 ua->send_msg(_("Ignoring invalid value for days. Max is 500.\n"));
777 i = find_arg_with_value(ua, NT_("schedule"));
779 bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
784 /* Loop through all jobs */
786 foreach_res(job, R_JOB) {
787 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
790 if (sched_name[0] && job->schedule &&
791 strcasecmp(job->schedule->name(), sched_name) != 0) {
794 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
796 level = job->JobLevel;
800 priority = job->Priority;
802 priority = run->Priority;
808 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
811 sp->priority = priority;
812 sp->runtime = runtime;
813 sp->pool = run->pool;
814 get_job_storage(&store, job, run);
815 sp->store = store.store;
816 Dmsg3(250, "job=%s store=%s MediaType=%s\n", job->name(), sp->store->name(), sp->store->media_type);
817 sched.binary_insert_multiple(sp, my_compare);
820 } /* end for loop over resources */
822 foreach_dlist(sp, &sched) {
825 if (num_jobs == 0 && !ua->api) {
826 ua->send_msg(_("No Scheduled Jobs.\n"));
828 if (!ua->api) ua->send_msg("====\n");
829 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
832 static void list_running_jobs(UAContext *ua)
838 char *emsg; /* edited message */
839 char dt[MAX_TIME_LENGTH];
841 bool pool_mem = false;
844 if ((i = find_arg_with_value(ua, "jobid")) >= 0) {
845 jid = str_to_int64(ua->argv[i]);
848 Dmsg0(200, "enter list_run_jobs()\n");
851 ua->send_msg(_("\nRunning Jobs:\n"));
853 if (jcr->JobId == 0) { /* this is us */
854 /* this is a console or other control job. We only show console
855 * jobs in the status output.
857 if (jcr->getJobType() == JT_CONSOLE) {
858 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
859 ua->send_msg(_("Console connected %sat %s\n"),
860 (ua->UA_sock && ua->UA_sock->tls)?_("using TLS "):"",
869 /* Note the following message is used in regress -- don't change */
870 ua->send_msg(_("No Jobs running.\n====\n"));
871 Dmsg0(200, "leave list_run_jobs()\n");
877 ua->send_msg(_(" JobId Type Level Files Bytes Name Status\n"));
878 ua->send_msg(_("======================================================================\n"));
881 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
884 /* JobId keyword found in command line */
885 if (jid > 0 && jcr->JobId != jid) {
888 switch (jcr->JobStatus) {
890 msg = _("is waiting execution");
893 msg = _("is running");
896 msg = _("is blocked");
899 msg = _("has terminated");
902 msg = _("has terminated with warnings");
905 msg = _("has terminated in incomplete state");
907 case JS_ErrorTerminated:
908 msg = _("has erred");
911 msg = _("has errors");
914 msg = _("has a fatal error");
917 msg = _("has verify differences");
920 msg = _("has been canceled");
923 emsg = (char *) get_pool_memory(PM_FNAME);
925 Mmsg(emsg, _("is waiting on Client"));
927 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
933 emsg = (char *) get_pool_memory(PM_FNAME);
935 Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->wstore->name());
936 } else if (jcr->rstore) {
937 Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->rstore->name());
939 Mmsg(emsg, _("is waiting on Storage"));
944 case JS_WaitStoreRes:
945 msg = _("is waiting on max Storage jobs");
947 case JS_WaitClientRes:
948 msg = _("is waiting on max Client jobs");
951 msg = _("is waiting on max Job jobs");
954 msg = _("is waiting on max total jobs");
956 case JS_WaitStartTime:
957 emsg = (char *) get_pool_memory(PM_FNAME);
958 Mmsg(emsg, _("is waiting for its start time (%s)"),
959 bstrftime_ny(dt, sizeof(dt), jcr->sched_time));
963 case JS_WaitPriority:
964 msg = _("is waiting for higher priority jobs to finish");
967 msg = _("is waiting for a Shared Storage device");
969 case JS_DataCommitting:
970 msg = _("SD committing Data");
972 case JS_DataDespooling:
973 msg = _("SD despooling Data");
975 case JS_AttrDespooling:
976 msg = _("SD despooling Attributes");
978 case JS_AttrInserting:
979 msg = _("Dir inserting Attributes");
983 emsg = (char *)get_pool_memory(PM_FNAME);
984 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
990 * Now report Storage daemon status code
992 switch (jcr->SDJobStatus) {
995 free_pool_memory(emsg);
998 msg = _("is waiting for a mount request");
1002 free_pool_memory(emsg);
1005 msg = _("is waiting for an appendable Volume");
1008 /* Special case when JobStatus=JS_WaitFD, we don't have a FD link yet
1009 * we need to stay in WaitFD status See bee mantis #1414 */
1010 if (jcr->JobStatus != JS_WaitFD) {
1012 emsg = (char *)get_pool_memory(PM_FNAME);
1015 if (!jcr->client || !jcr->wstore) {
1016 Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
1018 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
1019 jcr->client->name(), jcr->wstore->name());
1024 case JS_DataCommitting:
1025 msg = _("SD committing Data");
1027 case JS_DataDespooling:
1028 msg = _("SD despooling Data");
1030 case JS_AttrDespooling:
1031 msg = _("SD despooling Attributes");
1033 case JS_AttrInserting:
1034 msg = _("Dir inserting Attributes");
1037 switch (jcr->getJobType()) {
1039 bstrncpy(level, "Admin", sizeof(level));
1042 bstrncpy(level, "Restore", sizeof(level));
1045 bstrncpy(level, level_to_str(jcr->getJobLevel()), sizeof(level));
1051 bash_spaces(jcr->comment);
1052 ua->send_msg(_("%6d\t%-6s\t%-20s\t%s\t%s\n"),
1053 jcr->JobId, level, jcr->Job, msg, jcr->comment);
1054 unbash_spaces(jcr->comment);
1057 char b1[50], b2[50], b3[50];
1059 bstrncpy(b1, job_type_to_str(jcr->getJobType()), sizeof(b1));
1061 ua->send_msg(_("%6d %-4s %-3s %10s %10s %-17s %s\n"),
1062 jcr->JobId, b1, level,
1063 edit_uint64_with_commas(jcr->JobFiles, b2),
1064 edit_uint64_with_suffix(jcr->JobBytes, b3),
1065 jcr->job->name(), msg);
1069 free_pool_memory(emsg);
1075 ua->send_msg("====\n");
1077 Dmsg0(200, "leave list_run_jobs()\n");
1080 static void list_terminated_jobs(UAContext *ua)
1082 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
1085 if (last_jobs->empty()) {
1086 if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
1089 lock_last_jobs_list();
1090 struct s_last_job *je;
1092 ua->send_msg(_("\nTerminated Jobs:\n"));
1093 ua->send_msg(_(" JobId Level Files Bytes Status Finished Name \n"));
1094 ua->send_msg(_("====================================================================\n"));
1096 foreach_dlist(je, last_jobs) {
1097 char JobName[MAX_NAME_LENGTH];
1098 const char *termstat;
1100 bstrncpy(JobName, je->Job, sizeof(JobName));
1101 /* There are three periods after the Job name */
1103 for (int i=0; i<3; i++) {
1104 if ((p=strrchr(JobName, '.')) != NULL) {
1109 if (!acl_access_ok(ua, Job_ACL, JobName)) {
1113 bstrftime_nc(dt, sizeof(dt), je->end_time);
1114 switch (je->JobType) {
1116 bstrncpy(level, "Admin", sizeof(level));
1119 bstrncpy(level, "Restore", sizeof(level));
1122 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
1126 switch (je->JobStatus) {
1128 termstat = _("Created");
1131 case JS_ErrorTerminated:
1132 termstat = _("Error");
1134 case JS_Differences:
1135 termstat = _("Diffs");
1138 termstat = _("Cancel");
1144 termstat = _("OK -- with warnings");
1147 termstat = _("Incomplete");
1150 termstat = _("Other");
1154 ua->send_msg(_("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
1157 edit_uint64_with_commas(je->JobFiles, b1),
1158 edit_uint64_with_suffix(je->JobBytes, b2),
1162 ua->send_msg(_("%6d %-7s %8s %10s %-7s %-8s %s\n"),
1165 edit_uint64_with_commas(je->JobFiles, b1),
1166 edit_uint64_with_suffix(je->JobBytes, b2),
1172 ua->send_msg(_("\n"));
1174 unlock_last_jobs_list();