]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
Merge remote branch 'sf/master' into purge
[bacula/bacula] / bacula / src / dird / ua_status.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- User Agent Status Command
31  *
32  *     Kern Sibbald, August MMI
33  *
34  *   Version $Id$
35  */
36
37
38 #include "bacula.h"
39 #include "dird.h"
40
41 extern void *start_heap;
42
43 static void list_scheduled_jobs(UAContext *ua);
44 static void list_running_jobs(UAContext *ua);
45 static void list_terminated_jobs(UAContext *ua);
46 static void do_storage_status(UAContext *ua, STORE *store, char *cmd);
47 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd);
48 static void do_director_status(UAContext *ua);
49 static void do_all_status(UAContext *ua);
50 void status_slots(UAContext *ua, STORE *store);
51 void status_content(UAContext *ua, STORE *store);
52
53 static char OKqstatus[]   = "1000 OK .status\n";
54 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
55
56 /*
57  * .status command
58  */
59
60 bool dot_status_cmd(UAContext *ua, const char *cmd)
61 {
62    STORE *store;
63    CLIENT *client;
64    JCR* njcr = NULL;
65    s_last_job* job;
66    char ed1[50];
67
68    Dmsg2(20, "status=\"%s\" argc=%d\n", cmd, ua->argc);
69
70    if (ua->argc < 3) {
71       ua->send_msg("1900 Bad .status command, missing arguments.\n");
72       return false;
73    }
74
75    if (strcasecmp(ua->argk[1], "dir") == 0) {
76       if (strcasecmp(ua->argk[2], "current") == 0) {
77          ua->send_msg(OKqstatus, ua->argk[2]);
78          foreach_jcr(njcr) {
79             if (njcr->JobId != 0 && acl_access_ok(ua, Job_ACL, njcr->job->name())) {
80                ua->send_msg(DotStatusJob, edit_int64(njcr->JobId, ed1), 
81                         njcr->JobStatus, njcr->JobErrors);
82             }
83          }
84          endeach_jcr(njcr);
85       } else if (strcasecmp(ua->argk[2], "last") == 0) {
86          ua->send_msg(OKqstatus, ua->argk[2]);
87          if ((last_jobs) && (last_jobs->size() > 0)) {
88             job = (s_last_job*)last_jobs->last();
89             if (acl_access_ok(ua, Job_ACL, job->Job)) {
90                ua->send_msg(DotStatusJob, edit_int64(job->JobId, ed1), 
91                      job->JobStatus, job->Errors);
92             }
93          }
94       } else if (strcasecmp(ua->argk[2], "header") == 0) {
95           list_dir_status_header(ua);
96       } else if (strcasecmp(ua->argk[2], "scheduled") == 0) {
97           list_scheduled_jobs(ua);
98       } else if (strcasecmp(ua->argk[2], "running") == 0) {
99           list_running_jobs(ua);
100       } else if (strcasecmp(ua->argk[2], "terminated") == 0) {
101           list_terminated_jobs(ua);
102       } else {
103          ua->send_msg("1900 Bad .status command, wrong argument.\n");
104          return false;
105       }
106    } else if (strcasecmp(ua->argk[1], "client") == 0) {
107       client = get_client_resource(ua);
108       if (client) {
109          Dmsg2(200, "Client=%s arg=%s\n", client->name(), NPRT(ua->argk[2]));
110          do_client_status(ua, client, ua->argk[2]);
111       }
112    } else if (strcasecmp(ua->argk[1], "storage") == 0) {
113       store = get_storage_resource(ua, false /*no default*/);
114       if (store) {
115          do_storage_status(ua, store, ua->argk[2]);
116       }
117    } else {
118       ua->send_msg("1900 Bad .status command, wrong argument.\n");
119       return false;
120    }
121
122    return true;
123 }
124
125 /* This is the *old* command handler, so we must return
126  *  1 or it closes the connection
127  */
128 int qstatus_cmd(UAContext *ua, const char *cmd)
129 {
130    dot_status_cmd(ua, cmd);
131    return 1;
132 }
133
134 /*
135  * status command
136  */
137 int status_cmd(UAContext *ua, const char *cmd)
138 {
139    STORE *store;
140    CLIENT *client;
141    int item, i;
142
143    Dmsg1(20, "status:%s:\n", cmd);
144
145    for (i=1; i<ua->argc; i++) {
146       if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
147          do_all_status(ua);
148          return 1;
149       } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
150                  strcasecmp(ua->argk[i], NT_("director")) == 0) {
151          do_director_status(ua);
152          return 1;
153       } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
154          client = get_client_resource(ua);
155          if (client) {
156             do_client_status(ua, client, NULL);
157          }
158          return 1;
159       } else {
160          store = get_storage_resource(ua, false/*no default*/);
161          if (store) {
162             if (find_arg(ua, NT_("slots")) > 0) {
163                status_slots(ua, store);
164             } else {
165                do_storage_status(ua, store, NULL);
166             }
167          }
168          return 1;
169       }
170    }
171    /* If no args, ask for status type */
172    if (ua->argc == 1) {
173        char prmt[MAX_NAME_LENGTH];
174
175       start_prompt(ua, _("Status available for:\n"));
176       add_prompt(ua, NT_("Director"));
177       add_prompt(ua, NT_("Storage"));
178       add_prompt(ua, NT_("Client"));
179       add_prompt(ua, NT_("All"));
180       Dmsg0(20, "do_prompt: select daemon\n");
181       if ((item=do_prompt(ua, "",  _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
182          return 1;
183       }
184       Dmsg1(20, "item=%d\n", item);
185       switch (item) {
186       case 0:                         /* Director */
187          do_director_status(ua);
188          break;
189       case 1:
190          store = select_storage_resource(ua);
191          if (store) {
192             do_storage_status(ua, store, NULL);
193          }
194          break;
195       case 2:
196          client = select_client_resource(ua);
197          if (client) {
198             do_client_status(ua, client, NULL);
199          }
200          break;
201       case 3:
202          do_all_status(ua);
203          break;
204       default:
205          break;
206       }
207    }
208    return 1;
209 }
210
211 static void do_all_status(UAContext *ua)
212 {
213    STORE *store, **unique_store;
214    CLIENT *client, **unique_client;
215    int i, j;
216    bool found;
217
218    do_director_status(ua);
219
220    /* Count Storage items */
221    LockRes();
222    i = 0;
223    foreach_res(store, R_STORAGE) {
224       i++;
225    }
226    unique_store = (STORE **) malloc(i * sizeof(STORE));
227    /* Find Unique Storage address/port */
228    i = 0;
229    foreach_res(store, R_STORAGE) {
230       found = false;
231       if (!acl_access_ok(ua, Storage_ACL, store->name())) {
232          continue;
233       }
234       for (j=0; j<i; j++) {
235          if (strcmp(unique_store[j]->address, store->address) == 0 &&
236              unique_store[j]->SDport == store->SDport) {
237             found = true;
238             break;
239          }
240       }
241       if (!found) {
242          unique_store[i++] = store;
243          Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
244       }
245    }
246    UnlockRes();
247
248    /* Call each unique Storage daemon */
249    for (j=0; j<i; j++) {
250       do_storage_status(ua, unique_store[j], NULL);
251    }
252    free(unique_store);
253
254    /* Count Client items */
255    LockRes();
256    i = 0;
257    foreach_res(client, R_CLIENT) {
258       i++;
259    }
260    unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
261    /* Find Unique Client address/port */
262    i = 0;
263    foreach_res(client, R_CLIENT) {
264       found = false;
265       if (!acl_access_ok(ua, Client_ACL, client->name())) {
266          continue;
267       }
268       for (j=0; j<i; j++) {
269          if (strcmp(unique_client[j]->address, client->address) == 0 &&
270              unique_client[j]->FDport == client->FDport) {
271             found = true;
272             break;
273          }
274       }
275       if (!found) {
276          unique_client[i++] = client;
277          Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
278       }
279    }
280    UnlockRes();
281
282    /* Call each unique File daemon */
283    for (j=0; j<i; j++) {
284       do_client_status(ua, unique_client[j], NULL);
285    }
286    free(unique_client);
287
288 }
289
290 void list_dir_status_header(UAContext *ua)
291 {
292    char dt[MAX_TIME_LENGTH];
293    char b1[35], b2[35], b3[35], b4[35], b5[35];
294
295    ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
296             HOST_OS, DISTNAME, DISTVER);
297    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
298    if (num_jobs_run == 1) {
299       ua->send_msg(_("Daemon started %s, 1 Job run since started.\n"), dt);
300    }
301    else {
302       ua->send_msg(_("Daemon started %s, %d Jobs run since started.\n"),
303         dt, num_jobs_run);
304    }
305    ua->send_msg(_(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
306             edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
307             edit_uint64_with_commas(sm_bytes, b2),
308             edit_uint64_with_commas(sm_max_bytes, b3),
309             edit_uint64_with_commas(sm_buffers, b4),
310             edit_uint64_with_commas(sm_max_buffers, b5));
311
312    /* TODO: use this function once for all daemons */
313    if (debug_level > 0 && plugin_list->size() > 0) {
314       int len;
315       Plugin *plugin;
316       POOL_MEM msg(PM_FNAME);
317       pm_strcpy(msg, " Plugin: ");
318       foreach_alist(plugin, plugin_list) {
319          len = pm_strcat(msg, plugin->file);
320          if (len > 80) {
321             pm_strcat(msg, "\n   ");
322          } else {
323             pm_strcat(msg, " ");
324          }
325       }
326       ua->send_msg("%s\n", msg.c_str());
327    }
328 }
329
330 static void do_director_status(UAContext *ua)
331 {
332    list_dir_status_header(ua);
333
334    /*
335     * List scheduled Jobs
336     */
337    list_scheduled_jobs(ua);
338
339    /*
340     * List running jobs
341     */
342    list_running_jobs(ua);
343
344    /*
345     * List terminated jobs
346     */
347    list_terminated_jobs(ua);
348    ua->send_msg("====\n");
349 }
350
351 static void do_storage_status(UAContext *ua, STORE *store, char *cmd)
352 {
353    BSOCK *sd;
354    USTORE lstore;
355
356    lstore.store = store;
357    pm_strcpy(lstore.store_source, _("unknown source"));
358    set_wstorage(ua->jcr, &lstore);
359    /* Try connecting for up to 15 seconds */
360    if (!ua->api) ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
361       store->name(), store->address, store->SDport);
362    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
363       ua->send_msg(_("\nFailed to connect to Storage daemon %s.\n====\n"),
364          store->name());
365       if (ua->jcr->store_bsock) {
366          bnet_close(ua->jcr->store_bsock);
367          ua->jcr->store_bsock = NULL;
368       }
369       return;
370    }
371    Dmsg0(20, _("Connected to storage daemon\n"));
372    sd = ua->jcr->store_bsock;
373    if (cmd) {
374       sd->fsend(".status %s", cmd);
375    } else {
376       sd->fsend("status");
377    }
378    while (sd->recv() >= 0) {
379       ua->send_msg("%s", sd->msg);
380    }
381    sd->signal( BNET_TERMINATE);
382    sd->close();
383    ua->jcr->store_bsock = NULL;
384    return;
385 }
386
387 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd)
388 {
389    BSOCK *fd;
390
391    /* Connect to File daemon */
392
393    ua->jcr->client = client;
394    /* Release any old dummy key */
395    if (ua->jcr->sd_auth_key) {
396       free(ua->jcr->sd_auth_key);
397    }
398    /* Create a new dummy SD auth key */
399    ua->jcr->sd_auth_key = bstrdup("dummy");
400
401    /* Try to connect for 15 seconds */
402    if (!ua->api) ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
403       client->name(), client->address, client->FDport);
404    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
405       ua->send_msg(_("Failed to connect to Client %s.\n====\n"),
406          client->name());
407       if (ua->jcr->file_bsock) {
408          bnet_close(ua->jcr->file_bsock);
409          ua->jcr->file_bsock = NULL;
410       }
411       return;
412    }
413    Dmsg0(20, _("Connected to file daemon\n"));
414    fd = ua->jcr->file_bsock;
415    if (cmd) {
416       fd->fsend(".status %s", cmd);
417    } else {
418       fd->fsend("status");
419    }
420    while (fd->recv() >= 0) {
421       ua->send_msg("%s", fd->msg);
422    }
423    fd->signal(BNET_TERMINATE);
424    fd->close();
425    ua->jcr->file_bsock = NULL;
426
427    return;
428 }
429
430 static void prt_runhdr(UAContext *ua)
431 {
432    if (!ua->api) {
433       ua->send_msg(_("\nScheduled Jobs:\n"));
434       ua->send_msg(_("Level          Type     Pri  Scheduled          Name               Volume\n"));
435       ua->send_msg(_("===================================================================================\n"));
436    }
437 }
438
439 /* Scheduling packet */
440 struct sched_pkt {
441    dlink link;                        /* keep this as first item!!! */
442    JOB *job;
443    int level;
444    int priority;
445    utime_t runtime;
446    POOL *pool;
447    STORE *store;
448 };
449
450 static void prt_runtime(UAContext *ua, sched_pkt *sp)
451 {
452    char dt[MAX_TIME_LENGTH];
453    const char *level_ptr;
454    bool ok = false;
455    bool close_db = false;
456    JCR *jcr = ua->jcr;
457    MEDIA_DBR mr;
458    int orig_jobtype;
459
460    orig_jobtype = jcr->get_JobType();
461    memset(&mr, 0, sizeof(mr));
462    if (sp->job->JobType == JT_BACKUP) {
463       jcr->db = NULL;
464       ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
465       Dmsg1(250, "Using pool=%s\n", jcr->pool->name());
466       if (jcr->db) {
467          close_db = true;             /* new db opened, remember to close it */
468       }
469       if (ok) {
470          mr.PoolId = jcr->jr.PoolId;
471          mr.StorageId = sp->store->StorageId;
472          jcr->wstore = sp->store;
473          Dmsg0(250, "call find_next_volume_for_append\n");
474          /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
475          ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune);
476       }
477       if (!ok) {
478          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
479       }
480    }
481    bstrftime_nc(dt, sizeof(dt), sp->runtime);
482    switch (sp->job->JobType) {
483    case JT_ADMIN:
484    case JT_RESTORE:
485       level_ptr = " ";
486       break;
487    default:
488       level_ptr = level_to_str(sp->level);
489       break;
490    }
491    if (ua->api) {
492       ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
493          level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
494          sp->job->name(), mr.VolumeName);
495    } else {
496       ua->send_msg(_("%-14s %-8s %3d  %-18s %-18s %s\n"),
497          level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
498          sp->job->name(), mr.VolumeName);
499    }
500    if (close_db) {
501       db_close_database(jcr, jcr->db);
502    }
503    jcr->db = ua->db;                  /* restore ua db to jcr */
504    jcr->set_JobType(orig_jobtype);
505 }
506
507 /*
508  * Sort items by runtime, priority
509  */
510 static int my_compare(void *item1, void *item2)
511 {
512    sched_pkt *p1 = (sched_pkt *)item1;
513    sched_pkt *p2 = (sched_pkt *)item2;
514    if (p1->runtime < p2->runtime) {
515       return -1;
516    } else if (p1->runtime > p2->runtime) {
517       return 1;
518    }
519    if (p1->priority < p2->priority) {
520       return -1;
521    } else if (p1->priority > p2->priority) {
522       return 1;
523    }
524    return 0;
525 }
526
527 /*
528  * Find all jobs to be run in roughly the
529  *  next 24 hours.
530  */
531 static void list_scheduled_jobs(UAContext *ua)
532 {
533    utime_t runtime;
534    RUN *run;
535    JOB *job;
536    int level, num_jobs = 0;
537    int priority;
538    bool hdr_printed = false;
539    dlist sched;
540    sched_pkt *sp;
541    int days, i;
542
543    Dmsg0(200, "enter list_sched_jobs()\n");
544
545    days = 1;
546    i = find_arg_with_value(ua, NT_("days"));
547    if (i >= 0) {
548      days = atoi(ua->argv[i]);
549      if (((days < 0) || (days > 500)) && !ua->api) {
550        ua->send_msg(_("Ignoring invalid value for days. Max is 500.\n"));
551        days = 1;
552      }
553    }
554
555    /* Loop through all jobs */
556    LockRes();
557    foreach_res(job, R_JOB) {
558       if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
559          continue;
560       }
561       for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
562          USTORE store;
563          level = job->JobLevel;
564          if (run->level) {
565             level = run->level;
566          }
567          priority = job->Priority;
568          if (run->Priority) {
569             priority = run->Priority;
570          }
571          if (!hdr_printed) {
572             prt_runhdr(ua);
573             hdr_printed = true;
574          }
575          sp = (sched_pkt *)malloc(sizeof(sched_pkt));
576          sp->job = job;
577          sp->level = level;
578          sp->priority = priority;
579          sp->runtime = runtime;
580          sp->pool = run->pool;
581          get_job_storage(&store, job, run);
582          sp->store = store.store;
583          Dmsg3(250, "job=%s store=%s MediaType=%s\n", job->name(), sp->store->name(), sp->store->media_type);
584          sched.binary_insert_multiple(sp, my_compare);
585          num_jobs++;
586       }
587    } /* end for loop over resources */
588    UnlockRes();
589    foreach_dlist(sp, &sched) {
590       prt_runtime(ua, sp);
591    }
592    if (num_jobs == 0 && !ua->api) {
593       ua->send_msg(_("No Scheduled Jobs.\n"));
594    }
595    if (!ua->api) ua->send_msg("====\n");
596    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
597 }
598
599 static void list_running_jobs(UAContext *ua)
600 {
601    JCR *jcr;
602    int njobs = 0;
603    const char *msg;
604    char *emsg;                        /* edited message */
605    char dt[MAX_TIME_LENGTH];
606    char level[10];
607    bool pool_mem = false;
608
609    Dmsg0(200, "enter list_run_jobs()\n");
610    if (!ua->api) ua->send_msg(_("\nRunning Jobs:\n"));
611    foreach_jcr(jcr) {
612       if (jcr->JobId == 0) {      /* this is us */
613          /* this is a console or other control job. We only show console
614           * jobs in the status output.
615           */
616          if (jcr->get_JobType() == JT_CONSOLE && !ua->api) {
617             bstrftime_nc(dt, sizeof(dt), jcr->start_time);
618             ua->send_msg(_("Console connected at %s\n"), dt);
619          }
620          continue;
621       }       
622       njobs++;
623    }
624    endeach_jcr(jcr);
625
626    if (njobs == 0) {
627       /* Note the following message is used in regress -- don't change */
628       if (!ua->api)  ua->send_msg(_("No Jobs running.\n====\n"));
629       Dmsg0(200, "leave list_run_jobs()\n");
630       return;
631    }
632    njobs = 0;
633    if (!ua->api) {
634       ua->send_msg(_(" JobId Level   Name                       Status\n"));
635       ua->send_msg(_("======================================================================\n"));
636    }
637    foreach_jcr(jcr) {
638       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
639          continue;
640       }
641       njobs++;
642       switch (jcr->JobStatus) {
643       case JS_Created:
644          msg = _("is waiting execution");
645          break;
646       case JS_Running:
647          msg = _("is running");
648          break;
649       case JS_Blocked:
650          msg = _("is blocked");
651          break;
652       case JS_Terminated:
653          msg = _("has terminated");
654          break;
655       case JS_Warnings:
656          msg = _("has terminated with warnings");
657          break;
658       case JS_ErrorTerminated:
659          msg = _("has erred");
660          break;
661       case JS_Error:
662          msg = _("has errors");
663          break;
664       case JS_FatalError:
665          msg = _("has a fatal error");
666          break;
667       case JS_Differences:
668          msg = _("has verify differences");
669          break;
670       case JS_Canceled:
671          msg = _("has been canceled");
672          break;
673       case JS_WaitFD:
674          emsg = (char *) get_pool_memory(PM_FNAME);
675          if (!jcr->client) {
676             Mmsg(emsg, _("is waiting on Client"));
677          } else {
678             Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
679          }
680          pool_mem = true;
681          msg = emsg;
682          break;
683       case JS_WaitSD:
684          emsg = (char *) get_pool_memory(PM_FNAME);
685          if (jcr->wstore) {
686             Mmsg(emsg, _("is waiting on Storage %s"), jcr->wstore->name());
687          } else if (jcr->rstore) {
688             Mmsg(emsg, _("is waiting on Storage %s"), jcr->rstore->name());
689          } else {
690             Mmsg(emsg, _("is waiting on Storage"));
691          }
692          pool_mem = true;
693          msg = emsg;
694          break;
695       case JS_WaitStoreRes:
696          msg = _("is waiting on max Storage jobs");
697          break;
698       case JS_WaitClientRes:
699          msg = _("is waiting on max Client jobs");
700          break;
701       case JS_WaitJobRes:
702          msg = _("is waiting on max Job jobs");
703          break;
704       case JS_WaitMaxJobs:
705          msg = _("is waiting on max total jobs");
706          break;
707       case JS_WaitStartTime:
708          msg = _("is waiting for its start time");
709          break;
710       case JS_WaitPriority:
711          msg = _("is waiting for higher priority jobs to finish");
712          break;
713       case JS_DataCommitting:
714          msg = _("SD committing Data");
715          break;
716       case JS_DataDespooling:
717          msg = _("SD despooling Data");
718          break;
719       case JS_AttrDespooling:
720          msg = _("SD despooling Attributes");
721          break;
722       case JS_AttrInserting:
723          msg = _("Dir inserting Attributes");
724          break;
725
726       default:
727          emsg = (char *)get_pool_memory(PM_FNAME);
728          Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
729          pool_mem = true;
730          msg = emsg;
731          break;
732       }
733       /*
734        * Now report Storage daemon status code
735        */
736       switch (jcr->SDJobStatus) {
737       case JS_WaitMount:
738          if (pool_mem) {
739             free_pool_memory(emsg);
740             pool_mem = false;
741          }
742          msg = _("is waiting for a mount request");
743          break;
744       case JS_WaitMedia:
745          if (pool_mem) {
746             free_pool_memory(emsg);
747             pool_mem = false;
748          }
749          msg = _("is waiting for an appendable Volume");
750          break;
751       case JS_WaitFD:
752          if (!pool_mem) {
753             emsg = (char *)get_pool_memory(PM_FNAME);
754             pool_mem = true;
755          }
756          if (!jcr->client || !jcr->wstore) {
757             Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
758          } else {
759             Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
760                  jcr->client->name(), jcr->wstore->name());
761         }
762         msg = emsg;
763         break;
764       case JS_DataCommitting:
765          msg = _("SD committing Data");
766          break;
767       case JS_DataDespooling:
768          msg = _("SD despooling Data");
769          break;
770       case JS_AttrDespooling:
771          msg = _("SD despooling Attributes");
772          break;
773       case JS_AttrInserting:
774          msg = _("Dir inserting Attributes");
775          break;
776       }
777       switch (jcr->get_JobType()) {
778       case JT_ADMIN:
779       case JT_RESTORE:
780          bstrncpy(level, "      ", sizeof(level));
781          break;
782       default:
783          bstrncpy(level, level_to_str(jcr->get_JobLevel()), sizeof(level));
784          level[7] = 0;
785          break;
786       }
787
788       if (ua->api) {
789          ua->send_msg(_("%6d\t%-6s\t%-20s\t%s\n"),
790             jcr->JobId, level, jcr->Job, msg);
791       } else {
792          ua->send_msg(_("%6d %-6s  %-20s %s\n"),
793             jcr->JobId, level, jcr->Job, msg);
794       }
795
796       if (pool_mem) {
797          free_pool_memory(emsg);
798          pool_mem = false;
799       }
800    }
801    endeach_jcr(jcr);
802    if (!ua->api) ua->send_msg("====\n");
803    Dmsg0(200, "leave list_run_jobs()\n");
804 }
805
806 static void list_terminated_jobs(UAContext *ua)
807 {
808    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
809    char level[10];
810
811    if (last_jobs->empty()) {
812       if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
813       return;
814    }
815    lock_last_jobs_list();
816    struct s_last_job *je;
817    if (!ua->api) {
818       ua->send_msg(_("\nTerminated Jobs:\n"));
819       ua->send_msg(_(" JobId  Level    Files      Bytes   Status   Finished        Name \n"));
820       ua->send_msg(_("====================================================================\n"));
821    }
822    foreach_dlist(je, last_jobs) {
823       char JobName[MAX_NAME_LENGTH];
824       const char *termstat;
825
826       bstrncpy(JobName, je->Job, sizeof(JobName));
827       /* There are three periods after the Job name */
828       char *p;
829       for (int i=0; i<3; i++) {
830          if ((p=strrchr(JobName, '.')) != NULL) {
831             *p = 0;
832          }
833       }
834
835       if (!acl_access_ok(ua, Job_ACL, JobName)) {
836          continue;
837       }
838
839       bstrftime_nc(dt, sizeof(dt), je->end_time);
840       switch (je->JobType) {
841       case JT_ADMIN:
842       case JT_RESTORE:
843          bstrncpy(level, "    ", sizeof(level));
844          break;
845       default:
846          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
847          level[4] = 0;
848          break;
849       }
850       switch (je->JobStatus) {
851       case JS_Created:
852          termstat = _("Created");
853          break;
854       case JS_FatalError:
855       case JS_ErrorTerminated:
856          termstat = _("Error");
857          break;
858       case JS_Differences:
859          termstat = _("Diffs");
860          break;
861       case JS_Canceled:
862          termstat = _("Cancel");
863          break;
864       case JS_Terminated:
865          termstat = _("OK");
866          break;
867       case JS_Warnings:
868          termstat = _("OK -- with warnings");
869          break;
870       default:
871          termstat = _("Other");
872          break;
873       }
874       if (ua->api) {
875          ua->send_msg(_("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
876             je->JobId,
877             level,
878             edit_uint64_with_commas(je->JobFiles, b1),
879             edit_uint64_with_suffix(je->JobBytes, b2),
880             termstat,
881             dt, JobName);
882       } else {
883          ua->send_msg(_("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
884             je->JobId,
885             level,
886             edit_uint64_with_commas(je->JobFiles, b1),
887             edit_uint64_with_suffix(je->JobBytes, b2),
888             termstat,
889             dt, JobName);
890       }
891    }
892    if (!ua->api) ua->send_msg(_("\n"));
893    unlock_last_jobs_list();
894 }