2 Bacula® - The Network Backup Solution
4 Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
18 * Bacula Director -- User Agent Status Command
20 * Kern Sibbald, August MMI
28 extern void *start_heap;
29 extern utime_t last_reload_time;
31 static void list_scheduled_jobs(UAContext *ua);
32 static void llist_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, char *cmd);
36 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd);
37 static void do_director_status(UAContext *ua);
38 static void do_all_status(UAContext *ua);
39 void status_slots(UAContext *ua, STORE *store);
40 void status_content(UAContext *ua, STORE *store);
42 static char OKqstatus[] = "1000 OK .status\n";
43 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
49 bool dot_status_cmd(UAContext *ua, const char *cmd)
57 Dmsg2(20, "status=\"%s\" argc=%d\n", cmd, ua->argc);
60 ua->send_msg("1900 Bad .status command, missing arguments.\n");
64 if (strcasecmp(ua->argk[1], "dir") == 0) {
65 if (strcasecmp(ua->argk[2], "current") == 0) {
66 ua->send_msg(OKqstatus, ua->argk[2]);
68 if (njcr->JobId != 0 && acl_access_ok(ua, Job_ACL, njcr->job->name())) {
69 ua->send_msg(DotStatusJob, edit_int64(njcr->JobId, ed1),
70 njcr->JobStatus, njcr->JobErrors);
74 } else if (strcasecmp(ua->argk[2], "last") == 0) {
75 ua->send_msg(OKqstatus, ua->argk[2]);
76 if ((last_jobs) && (last_jobs->size() > 0)) {
77 job = (s_last_job*)last_jobs->last();
78 if (acl_access_ok(ua, Job_ACL, job->Job)) {
79 ua->send_msg(DotStatusJob, edit_int64(job->JobId, ed1),
80 job->JobStatus, job->Errors);
83 } else if (strcasecmp(ua->argk[2], "header") == 0) {
84 list_dir_status_header(ua);
85 } else if (strcasecmp(ua->argk[2], "scheduled") == 0) {
86 list_scheduled_jobs(ua);
87 } else if (strcasecmp(ua->argk[2], "running") == 0) {
88 list_running_jobs(ua);
89 } else if (strcasecmp(ua->argk[2], "terminated") == 0) {
90 list_terminated_jobs(ua);
92 ua->send_msg("1900 Bad .status command, wrong argument.\n");
95 } else if (strcasecmp(ua->argk[1], "client") == 0) {
96 client = get_client_resource(ua);
98 Dmsg2(200, "Client=%s arg=%s\n", client->name(), NPRT(ua->argk[2]));
99 do_client_status(ua, client, ua->argk[2]);
101 } else if (strcasecmp(ua->argk[1], "storage") == 0) {
102 store = get_storage_resource(ua, false /*no default*/, true/*unique*/);
104 ua->send_msg("1900 Bad .status command, wrong argument.\n");
107 do_storage_status(ua, store, ua->argk[2]);
109 ua->send_msg("1900 Bad .status command, wrong argument.\n");
116 /* This is the *old* command handler, so we must return
117 * 1 or it closes the connection
119 int qstatus_cmd(UAContext *ua, const char *cmd)
121 dot_status_cmd(ua, cmd);
128 int status_cmd(UAContext *ua, const char *cmd)
134 Dmsg1(20, "status:%s:\n", cmd);
136 for (i=1; i<ua->argc; i++) {
137 if (strcasecmp(ua->argk[i], NT_("schedule")) == 0 ||
138 strcasecmp(ua->argk[i], NT_("scheduled")) == 0) {
139 llist_scheduled_jobs(ua);
141 } else if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
144 } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
145 strcasecmp(ua->argk[i], NT_("director")) == 0) {
146 do_director_status(ua);
148 } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
149 client = get_client_resource(ua);
151 do_client_status(ua, client, NULL);
155 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
157 if (find_arg(ua, NT_("slots")) > 0) {
158 status_slots(ua, store);
160 do_storage_status(ua, store, NULL);
166 /* If no args, ask for status type */
168 char prmt[MAX_NAME_LENGTH];
170 start_prompt(ua, _("Status available for:\n"));
171 add_prompt(ua, NT_("Director"));
172 add_prompt(ua, NT_("Storage"));
173 add_prompt(ua, NT_("Client"));
174 add_prompt(ua, NT_("Scheduled"));
175 add_prompt(ua, NT_("All"));
176 Dmsg0(20, "do_prompt: select daemon\n");
177 if ((item=do_prompt(ua, "", _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
180 Dmsg1(20, "item=%d\n", item);
182 case 0: /* Director */
183 do_director_status(ua);
186 store = select_storage_resource(ua, true/*unique*/);
188 do_storage_status(ua, store, NULL);
192 client = select_client_resource(ua);
194 do_client_status(ua, client, NULL);
198 llist_scheduled_jobs(ua);
210 static void do_all_status(UAContext *ua)
212 STORE *store, **unique_store;
213 CLIENT *client, **unique_client;
217 do_director_status(ua);
219 /* Count Storage items */
222 foreach_res(store, R_STORAGE) {
225 unique_store = (STORE **) malloc(i * sizeof(STORE));
226 /* Find Unique Storage address/port */
228 foreach_res(store, R_STORAGE) {
230 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
233 for (j=0; j<i; j++) {
234 if (strcmp(unique_store[j]->address, store->address) == 0 &&
235 unique_store[j]->SDport == store->SDport) {
241 unique_store[i++] = store;
242 Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
247 /* Call each unique Storage daemon */
248 for (j=0; j<i; j++) {
249 do_storage_status(ua, unique_store[j], NULL);
253 /* Count Client items */
256 foreach_res(client, R_CLIENT) {
259 unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
260 /* Find Unique Client address/port */
262 foreach_res(client, R_CLIENT) {
264 if (!acl_access_ok(ua, Client_ACL, client->name())) {
267 for (j=0; j<i; j++) {
268 if (strcmp(unique_client[j]->address, client->address) == 0 &&
269 unique_client[j]->FDport == client->FDport) {
275 unique_client[i++] = client;
276 Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
281 /* Call each unique File daemon */
282 for (j=0; j<i; j++) {
283 do_client_status(ua, unique_client[j], NULL);
289 void list_dir_status_header(UAContext *ua)
291 char dt[MAX_TIME_LENGTH];
292 char b1[35], b2[35], b3[35], b4[35], b5[35];
294 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
295 HOST_OS, DISTNAME, DISTVER);
296 bstrftime_nc(dt, sizeof(dt), daemon_start_time);
297 ua->send_msg(_("Daemon started %s. Jobs: run=%d, running=%d "
299 num_jobs_run, job_count(), (int)DEVELOPER_MODE, (int)BEEF);
300 ua->send_msg(_(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
301 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
302 edit_uint64_with_commas(sm_bytes, b2),
303 edit_uint64_with_commas(sm_max_bytes, b3),
304 edit_uint64_with_commas(sm_buffers, b4),
305 edit_uint64_with_commas(sm_max_buffers, b5));
307 /* TODO: use this function once for all daemons */
308 if (bplugin_list->size() > 0) {
311 POOL_MEM msg(PM_FNAME);
312 pm_strcpy(msg, " Plugin: ");
313 foreach_alist(plugin, bplugin_list) {
314 len = pm_strcat(msg, plugin->file);
316 pm_strcat(msg, "\n ");
321 ua->send_msg("%s\n", msg.c_str());
325 static void do_director_status(UAContext *ua)
327 list_dir_status_header(ua);
330 * List scheduled Jobs
332 list_scheduled_jobs(ua);
337 list_running_jobs(ua);
340 * List terminated jobs
342 list_terminated_jobs(ua);
343 ua->send_msg("====\n");
346 static void do_storage_status(UAContext *ua, STORE *store, char *cmd)
352 if (!acl_access_ok(ua, Storage_ACL, store->name())) {
353 ua->error_msg(_("No authorization for Storage \"%s\"\n"), store->name());
357 * The Storage daemon is problematic because it shows information
358 * related to multiple Jobs, so if there is a Client or Job
359 * ACL restriction, we forbid all access to the Storage.
361 if (have_restricted_acl(ua, Client_ACL) ||
362 have_restricted_acl(ua, Job_ACL)) {
363 ua->error_msg(_("Restricted Client or Job does not permit access to Storage daemons\n"));
366 lstore.store = store;
367 pm_strcpy(lstore.store_source, _("unknown source"));
368 set_wstorage(ua->jcr, &lstore);
369 /* Try connecting for up to 15 seconds */
370 if (!ua->api) ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
371 store->name(), store->address, store->SDport);
372 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
373 ua->send_msg(_("\nFailed to connect to Storage daemon %s.\n====\n"),
375 free_bsock(ua->jcr->store_bsock);
378 Dmsg0(20, "Connected to storage daemon\n");
379 sd = ua->jcr->store_bsock;
382 int i = find_arg_with_value(ua, "device");
384 Mmsg(devname, "device=%s", ua->argv[i]);
385 bash_spaces(devname.c_str());
387 sd->fsend(".status %s api=%d api_opts=%s %s",
388 cmd, ua->api, ua->api_opts, devname.c_str());
392 while (sd->recv() >= 0) {
393 ua->send_msg("%s", sd->msg);
395 sd->signal(BNET_TERMINATE);
396 free_bsock(ua->jcr->store_bsock);
400 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd)
404 if (!acl_access_ok(ua, Client_ACL, client->name())) {
405 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
408 /* Connect to File daemon */
409 ua->jcr->client = client;
410 /* Release any old dummy key */
411 if (ua->jcr->sd_auth_key) {
412 free(ua->jcr->sd_auth_key);
414 /* Create a new dummy SD auth key */
415 ua->jcr->sd_auth_key = bstrdup("dummy");
417 /* Try to connect for 15 seconds */
418 if (!ua->api) ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
419 client->name(), client->address, client->FDport);
420 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
421 ua->send_msg(_("Failed to connect to Client %s.\n====\n"),
423 free_bsock(ua->jcr->file_bsock);
426 Dmsg0(20, _("Connected to file daemon\n"));
427 fd = ua->jcr->file_bsock;
429 fd->fsend(".status %s api=%d api_opts=%s", cmd, ua->api, ua->api_opts);
433 while (fd->recv() >= 0) {
434 ua->send_msg("%s", fd->msg);
436 fd->signal(BNET_TERMINATE);
437 free_bsock(ua->jcr->file_bsock);
442 static void prt_runhdr(UAContext *ua)
445 ua->send_msg(_("\nScheduled Jobs:\n"));
446 ua->send_msg(_("Level Type Pri Scheduled Job Name Volume\n"));
447 ua->send_msg(_("===================================================================================\n"));
451 static void prt_lrunhdr(UAContext *ua)
454 ua->send_msg(_("\nScheduled Jobs:\n"));
455 ua->send_msg(_("Level Type Pri Scheduled Job Name Schedule\n"));
456 ua->send_msg(_("=====================================================================================\n"));
461 /* Scheduling packet */
463 dlink link; /* keep this as first item!!! */
472 static void prt_runtime(UAContext *ua, sched_pkt *sp)
474 char dt[MAX_TIME_LENGTH];
475 const char *level_ptr;
477 bool close_db = false;
482 orig_jobtype = jcr->getJobType();
483 if (sp->job->JobType == JT_BACKUP) {
485 ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
486 Dmsg1(250, "Using pool=%s\n", jcr->pool->name());
488 close_db = true; /* new db opened, remember to close it */
491 mr.PoolId = jcr->jr.PoolId;
492 jcr->wstore = sp->store;
493 set_storageid_in_mr(jcr->wstore, &mr);
494 Dmsg0(250, "call find_next_volume_for_append\n");
495 /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
496 ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune);
499 bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
502 bstrftime_nc(dt, sizeof(dt), sp->runtime);
503 switch (sp->job->JobType) {
509 level_ptr = level_to_str(sp->level);
513 ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
514 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
515 sp->job->name(), mr.VolumeName);
517 ua->send_msg(_("%-14s %-8s %3d %-18s %-18s %s\n"),
518 level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
519 sp->job->name(), mr.VolumeName);
522 db_close_database(jcr, jcr->db);
524 jcr->db = ua->db; /* restore ua db to jcr */
525 jcr->setJobType(orig_jobtype);
529 * Detailed listing of all scheduler jobs
531 static void llist_scheduled_jobs(UAContext *ua)
536 int level, num_jobs = 0;
538 bool hdr_printed = false;
539 char sched_name[MAX_NAME_LENGTH];
540 char job_name[MAX_NAME_LENGTH];
543 time_t now = time(NULL);
545 const char *level_ptr;
547 Dmsg0(200, "enter list_sched_jobs()\n");
549 i = find_arg_with_value(ua, NT_("days"));
551 days = atoi(ua->argv[i]);
552 if (((days < 0) || (days > 500)) && !ua->api) {
553 ua->send_msg(_("Ignoring invalid value for days. Max is 500.\n"));
560 i = find_arg_with_value(ua, NT_("limit"));
562 limit = atoi(ua->argv[i]);
563 if (((limit < 0) || (limit > 2000)) && !ua->api) {
564 ua->send_msg(_("Ignoring invalid value for limit. Max is 2000.\n"));
571 i = find_arg_with_value(ua, NT_("time"));
573 now = str_to_utime(ua->argv[i]);
575 ua->send_msg(_("Ignoring invalid time.\n"));
580 i = find_arg_with_value(ua, NT_("schedule"));
582 bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
587 i = find_arg_with_value(ua, NT_("job"));
589 bstrncpy(job_name, ua->argv[i], sizeof(job_name));
594 /* Loop through all jobs */
596 foreach_res(job, R_JOB) {
597 sched = job->schedule;
598 if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
599 continue; /* no, skip this job */
601 if (job_name[0] && bstrcmp(job_name, job->name()) != 0) {
604 for (run=sched->run; run; run=run->next) {
606 for (i=0; i<days; i++) {
608 int mday, wday, month, wom, woy, ldom;
609 char dt[MAX_TIME_LENGTH];
612 /* compute values for next time */
613 (void)localtime_r(&next, &tm);
614 mday = tm.tm_mday - 1;
618 woy = tm_woy(next); /* get week of year */
619 ldom = tm_ldom(month, tm.tm_year + 1900);
623 Dmsg6(000, "m=%d md=%d wd=%d wom=%d woy=%d ldom=%d\n",
624 month, mday, wday, wom, woy, ldom);
625 Dmsg6(000, "bitset bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d bsldom=%d\n",
626 bit_is_set(month, run->month),
627 bit_is_set(mday, run->mday),
628 bit_is_set(wday, run->wday),
629 bit_is_set(wom, run->wom),
630 bit_is_set(woy, run->woy),
631 bit_is_set(31, run->mday));
634 ok = (bit_is_set(mday, run->mday) &&
635 bit_is_set(wday, run->wday) &&
636 bit_is_set(month, run->month) &&
637 bit_is_set(wom, run->wom) &&
638 bit_is_set(woy, run->woy)) ||
639 (bit_is_set(month, run->month) &&
640 bit_is_set(31, run->mday) && mday == ldom);
642 next += 24 * 60 * 60; /* Add one day */
645 for (int j=0; j < 24; j++) {
646 if (bit_is_set(j, run->hour)) {
648 tm.tm_min = run->minute;
650 runtime = mktime(&tm);
651 bstrftime_dn(dt, sizeof(dt), runtime);
656 level = job->JobLevel;
660 switch (job->JobType) {
666 level_ptr = level_to_str(level);
669 priority = job->Priority;
671 priority = run->Priority;
678 ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
679 level_ptr, job_type_to_str(job->JobType), priority, dt,
680 job->name(), sched->name());
682 ua->send_msg(_("%-14s %-8s %3d %-18s %-18s %s\n"),
683 level_ptr, job_type_to_str(job->JobType), priority, dt,
684 job->name(), sched->name());
686 next += 24 * 60 * 60; /* Add one day */
688 if (num_jobs >= limit) {
692 } /* end loop over run pkts */
693 } /* end for loop over resources */
696 if (num_jobs == 0 && !ua->api) {
697 ua->send_msg(_("No Scheduled Jobs.\n"));
699 if (!ua->api) ua->send_msg("====\n");
700 Dmsg0(200, "Leave ;list_sched_jobs_runs()\n");
705 * Sort items by runtime, priority
707 static int my_compare(void *item1, void *item2)
709 sched_pkt *p1 = (sched_pkt *)item1;
710 sched_pkt *p2 = (sched_pkt *)item2;
711 if (p1->runtime < p2->runtime) {
713 } else if (p1->runtime > p2->runtime) {
716 if (p1->priority < p2->priority) {
718 } else if (p1->priority > p2->priority) {
725 * Find all jobs to be run in roughly the
728 static void list_scheduled_jobs(UAContext *ua)
733 int level, num_jobs = 0;
735 bool hdr_printed = false;
736 char sched_name[MAX_NAME_LENGTH];
741 Dmsg0(200, "enter list_sched_jobs()\n");
744 i = find_arg_with_value(ua, NT_("days"));
746 days = atoi(ua->argv[i]);
747 if (((days < 0) || (days > 500)) && !ua->api) {
748 ua->send_msg(_("Ignoring invalid value for days. Max is 500.\n"));
752 i = find_arg_with_value(ua, NT_("schedule"));
754 bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
759 /* Loop through all jobs */
761 foreach_res(job, R_JOB) {
762 if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
765 if (sched_name[0] && job->schedule &&
766 strcasecmp(job->schedule->name(), sched_name) != 0) {
769 for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
771 level = job->JobLevel;
775 priority = job->Priority;
777 priority = run->Priority;
783 sp = (sched_pkt *)malloc(sizeof(sched_pkt));
786 sp->priority = priority;
787 sp->runtime = runtime;
788 sp->pool = run->pool;
789 get_job_storage(&store, job, run);
790 sp->store = store.store;
791 Dmsg3(250, "job=%s store=%s MediaType=%s\n", job->name(), sp->store->name(), sp->store->media_type);
792 sched.binary_insert_multiple(sp, my_compare);
795 } /* end for loop over resources */
797 foreach_dlist(sp, &sched) {
800 if (num_jobs == 0 && !ua->api) {
801 ua->send_msg(_("No Scheduled Jobs.\n"));
803 if (!ua->api) ua->send_msg("====\n");
804 Dmsg0(200, "Leave list_sched_jobs_runs()\n");
807 static void list_running_jobs(UAContext *ua)
812 char *emsg; /* edited message */
813 char dt[MAX_TIME_LENGTH];
815 bool pool_mem = false;
817 Dmsg0(200, "enter list_run_jobs()\n");
818 if (!ua->api) ua->send_msg(_("\nRunning Jobs:\n"));
820 if (jcr->JobId == 0) { /* this is us */
821 /* this is a console or other control job. We only show console
822 * jobs in the status output.
824 if (jcr->getJobType() == JT_CONSOLE && !ua->api) {
825 bstrftime_nc(dt, sizeof(dt), jcr->start_time);
826 ua->send_msg(_("Console connected at %s\n"), dt);
835 /* Note the following message is used in regress -- don't change */
837 ua->send_msg(_("No Jobs running.\n====\n"));
839 Dmsg0(200, "leave list_run_jobs()\n");
844 ua->send_msg(_(" JobId Type Level Files Bytes Name Status\n"));
845 ua->send_msg(_("======================================================================\n"));
848 if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
852 switch (jcr->JobStatus) {
854 msg = _("is waiting execution");
857 msg = _("is running");
860 msg = _("is blocked");
863 msg = _("has terminated");
866 msg = _("has terminated with warnings");
868 case JS_ErrorTerminated:
869 msg = _("has erred");
872 msg = _("has errors");
875 msg = _("has a fatal error");
878 msg = _("has verify differences");
881 msg = _("has been canceled");
884 emsg = (char *) get_pool_memory(PM_FNAME);
886 Mmsg(emsg, _("is waiting on Client"));
888 Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
894 emsg = (char *) get_pool_memory(PM_FNAME);
896 Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->wstore->name());
897 } else if (jcr->rstore) {
898 Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->rstore->name());
900 Mmsg(emsg, _("is waiting on Storage"));
905 case JS_WaitStoreRes:
906 msg = _("is waiting on max Storage jobs");
908 case JS_WaitClientRes:
909 msg = _("is waiting on max Client jobs");
912 msg = _("is waiting on max Job jobs");
915 msg = _("is waiting on max total jobs");
917 case JS_WaitStartTime:
918 emsg = (char *) get_pool_memory(PM_FNAME);
919 Mmsg(emsg, _("is waiting for its start time (%s)"),
920 bstrftime_ny(dt, sizeof(dt), jcr->sched_time));
924 case JS_WaitPriority:
925 msg = _("is waiting for higher priority jobs to finish");
928 msg = _("is waiting for a Shared Storage device");
930 case JS_DataCommitting:
931 msg = _("SD committing Data");
933 case JS_DataDespooling:
934 msg = _("SD despooling Data");
936 case JS_AttrDespooling:
937 msg = _("SD despooling Attributes");
939 case JS_AttrInserting:
940 msg = _("Dir inserting Attributes");
944 emsg = (char *)get_pool_memory(PM_FNAME);
945 Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
951 * Now report Storage daemon status code
953 switch (jcr->SDJobStatus) {
956 free_pool_memory(emsg);
959 msg = _("is waiting for a mount request");
963 free_pool_memory(emsg);
966 msg = _("is waiting for an appendable Volume");
970 emsg = (char *)get_pool_memory(PM_FNAME);
973 if (!jcr->client || !jcr->wstore) {
974 Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
976 Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
977 jcr->client->name(), jcr->wstore->name());
981 case JS_DataCommitting:
982 msg = _("SD committing Data");
984 case JS_DataDespooling:
985 msg = _("SD despooling Data");
987 case JS_AttrDespooling:
988 msg = _("SD despooling Attributes");
990 case JS_AttrInserting:
991 msg = _("Dir inserting Attributes");
994 switch (jcr->getJobType()) {
997 bstrncpy(level, " ", sizeof(level));
1000 bstrncpy(level, level_to_str(jcr->getJobLevel()), sizeof(level));
1006 bash_spaces(jcr->comment);
1007 ua->send_msg(_("%6d\t%-6s\t%-20s\t%s\t%s\n"),
1008 jcr->JobId, level, jcr->Job, msg, jcr->comment);
1009 unbash_spaces(jcr->comment);
1011 char b1[50], b2[50], b3[50];
1013 bstrncpy(b1, job_type_to_str(jcr->getJobType()), sizeof(b1));
1015 ua->send_msg(_("%6d %-4s %-3s %10s %10s %-17s %s\n"),
1016 jcr->JobId, b1, level,
1017 edit_uint64_with_commas(jcr->JobFiles, b2),
1018 edit_uint64_with_suffix(jcr->JobBytes, b3),
1019 jcr->job->name(), msg);
1023 free_pool_memory(emsg);
1029 ua->send_msg("====\n");
1031 Dmsg0(200, "leave list_run_jobs()\n");
1034 static void list_terminated_jobs(UAContext *ua)
1036 char dt[MAX_TIME_LENGTH], b1[30], b2[30];
1039 if (last_jobs->empty()) {
1040 if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
1043 lock_last_jobs_list();
1044 struct s_last_job *je;
1046 ua->send_msg(_("\nTerminated Jobs:\n"));
1047 ua->send_msg(_(" JobId Level Files Bytes Status Finished Name \n"));
1048 ua->send_msg(_("====================================================================\n"));
1050 foreach_dlist(je, last_jobs) {
1051 char JobName[MAX_NAME_LENGTH];
1052 const char *termstat;
1054 bstrncpy(JobName, je->Job, sizeof(JobName));
1055 /* There are three periods after the Job name */
1057 for (int i=0; i<3; i++) {
1058 if ((p=strrchr(JobName, '.')) != NULL) {
1063 if (!acl_access_ok(ua, Job_ACL, JobName)) {
1067 bstrftime_nc(dt, sizeof(dt), je->end_time);
1068 switch (je->JobType) {
1071 bstrncpy(level, " ", sizeof(level));
1074 bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
1078 switch (je->JobStatus) {
1080 termstat = _("Created");
1083 case JS_ErrorTerminated:
1084 termstat = _("Error");
1086 case JS_Differences:
1087 termstat = _("Diffs");
1090 termstat = _("Cancel");
1096 termstat = _("OK -- with warnings");
1099 termstat = _("Other");
1103 ua->send_msg(_("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
1106 edit_uint64_with_commas(je->JobFiles, b1),
1107 edit_uint64_with_suffix(je->JobBytes, b2),
1111 ua->send_msg(_("%6d %-6s %8s %10s %-7s %-8s %s\n"),
1114 edit_uint64_with_commas(je->JobFiles, b1),
1115 edit_uint64_with_suffix(je->JobBytes, b2),
1121 ua->send_msg(_("\n"));
1123 unlock_last_jobs_list();