]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
ccf0b8492380f2eb023b34de7360b4014bbd3c21
[bacula/bacula] / bacula / src / dird / ua_status.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5
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.
8
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.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *   Bacula Director -- User Agent Status Command
22  *
23  *     Kern Sibbald, August MMI
24  *
25  */
26
27
28 #include "bacula.h"
29 #include "dird.h"
30
31 extern void *start_heap;
32 extern utime_t last_reload_time;
33
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);
44
45 static char OKqstatus[]   = "1000 OK .status\n";
46 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
47
48 /*
49  * .status command
50  */
51
52 bool dot_status_cmd(UAContext *ua, const char *cmd)
53 {
54    STORE *store;
55    CLIENT *client;
56    JCR* njcr = NULL;
57    s_last_job* job;
58    char ed1[50];
59
60    Dmsg2(20, "status=\"%s\" argc=%d\n", cmd, ua->argc);
61
62    if (ua->argc < 3) {
63       ua->send_msg("1900 Bad .status command, missing arguments.\n");
64       return false;
65    }
66
67    if (strcasecmp(ua->argk[1], "dir") == 0) {
68       if (strcasecmp(ua->argk[2], "current") == 0) {
69          ua->send_msg(OKqstatus, ua->argk[2]);
70          foreach_jcr(njcr) {
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);
74             }
75          }
76          endeach_jcr(njcr);
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);
84             }
85          }
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);
94       } else {
95          ua->send_msg("1900 Bad .status command, wrong argument.\n");
96          return false;
97       }
98    } else if (strcasecmp(ua->argk[1], "client") == 0) {
99       client = get_client_resource(ua);
100       if (client) {
101          Dmsg2(200, "Client=%s arg=%s\n", client->name(), NPRT(ua->argk[2]));
102          do_client_status(ua, client, ua->argk[2]);
103       }
104    } else if (strcasecmp(ua->argk[1], "storage") == 0) {
105       store = get_storage_resource(ua, false /*no default*/, true/*unique*/);
106       if (!store) {
107          ua->send_msg("1900 Bad .status command, wrong argument.\n");
108          return false;
109       }
110       do_storage_status(ua, store, ua->argk[2]);
111    } else {
112       ua->send_msg("1900 Bad .status command, wrong argument.\n");
113       return false;
114    }
115
116    return true;
117 }
118
119 /* This is the *old* command handler, so we must return
120  *  1 or it closes the connection
121  */
122 int qstatus_cmd(UAContext *ua, const char *cmd)
123 {
124    dot_status_cmd(ua, cmd);
125    return 1;
126 }
127
128 /*
129  * status command
130  */
131 int status_cmd(UAContext *ua, const char *cmd)
132 {
133    STORE *store;
134    CLIENT *client;
135    int item, i;
136
137    Dmsg1(20, "status:%s:\n", cmd);
138
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);
143          return 1;
144       } else if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
145          do_all_status(ua);
146          return 1;
147       } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
148                  strcasecmp(ua->argk[i], NT_("director")) == 0) {
149          do_director_status(ua);
150          return 1;
151       } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
152          client = get_client_resource(ua);
153          if (client) {
154             do_client_status(ua, client, NULL);
155          }
156          return 1;
157       } else {
158          store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
159          if (store) {
160             if (find_arg(ua, NT_("slots")) > 0) {
161                status_slots(ua, store);
162             } else {
163                do_storage_status(ua, store, NULL);
164             }
165          }
166          return 1;
167       }
168    }
169    /* If no args, ask for status type */
170    if (ua->argc == 1) {
171        char prmt[MAX_NAME_LENGTH];
172
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) {
181          return 1;
182       }
183       Dmsg1(20, "item=%d\n", item);
184       switch (item) {
185       case 0:                         /* Director */
186          do_director_status(ua);
187          break;
188       case 1:
189          store = select_storage_resource(ua, true/*unique*/);
190          if (store) {
191             do_storage_status(ua, store, NULL);
192          }
193          break;
194       case 2:
195          client = select_client_resource(ua);
196          if (client) {
197             do_client_status(ua, client, NULL);
198          }
199          break;
200       case 3:
201          llist_scheduled_jobs(ua);
202          break;
203       case 4:
204          do_all_status(ua);
205          break;
206       default:
207          break;
208       }
209    }
210    return 1;
211 }
212
213 static void do_all_status(UAContext *ua)
214 {
215    STORE *store, **unique_store;
216    CLIENT *client, **unique_client;
217    int i, j;
218    bool found;
219
220    do_director_status(ua);
221
222    /* Count Storage items */
223    LockRes();
224    i = 0;
225    foreach_res(store, R_STORAGE) {
226       i++;
227    }
228    unique_store = (STORE **) malloc(i * sizeof(STORE));
229    /* Find Unique Storage address/port */
230    i = 0;
231    foreach_res(store, R_STORAGE) {
232       found = false;
233       if (!acl_access_ok(ua, Storage_ACL, store->name())) {
234          continue;
235       }
236       for (j=0; j<i; j++) {
237          if (strcmp(unique_store[j]->address, store->address) == 0 &&
238              unique_store[j]->SDport == store->SDport) {
239             found = true;
240             break;
241          }
242       }
243       if (!found) {
244          unique_store[i++] = store;
245          Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
246       }
247    }
248    UnlockRes();
249
250    /* Call each unique Storage daemon */
251    for (j=0; j<i; j++) {
252       do_storage_status(ua, unique_store[j], NULL);
253    }
254    free(unique_store);
255
256    /* Count Client items */
257    LockRes();
258    i = 0;
259    foreach_res(client, R_CLIENT) {
260       i++;
261    }
262    unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
263    /* Find Unique Client address/port */
264    i = 0;
265    foreach_res(client, R_CLIENT) {
266       found = false;
267       if (!acl_access_ok(ua, Client_ACL, client->name())) {
268          continue;
269       }
270       for (j=0; j<i; j++) {
271          if (strcmp(unique_client[j]->address, client->address) == 0 &&
272              unique_client[j]->FDport == client->FDport) {
273             found = true;
274             break;
275          }
276       }
277       if (!found) {
278          unique_client[i++] = client;
279          Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
280       }
281    }
282    UnlockRes();
283
284    /* Call each unique File daemon */
285    for (j=0; j<i; j++) {
286       do_client_status(ua, unique_client[j], NULL);
287    }
288    free(unique_client);
289
290 }
291
292 void list_dir_status_header(UAContext *ua)
293 {
294    char dt[MAX_TIME_LENGTH], dt1[MAX_TIME_LENGTH];
295    char b1[35], b2[35], b3[35], b4[35], b5[35];
296
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));
310
311    /* TODO: use this function once for all daemons */
312    if (b_plugin_list && b_plugin_list->size() > 0) {
313       int len;
314       Plugin *plugin;
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);
319          if (len > 80) {
320             pm_strcat(msg, "\n   ");
321          } else {
322             pm_strcat(msg, " ");
323          }
324       }
325       ua->send_msg("%s\n", msg.c_str());
326    }
327 }
328
329 static void do_director_status(UAContext *ua)
330 {
331    list_dir_status_header(ua);
332
333    /*
334     * List scheduled Jobs
335     */
336    list_scheduled_jobs(ua);
337
338    /*
339     * List running jobs
340     */
341    list_running_jobs(ua);
342
343    /*
344     * List terminated jobs
345     */
346    list_terminated_jobs(ua);
347    ua->send_msg("====\n");
348 }
349
350 static void do_storage_status(UAContext *ua, STORE *store, char *cmd)
351 {
352    BSOCK *sd;
353    USTORE lstore;
354
355
356    if (!acl_access_ok(ua, Storage_ACL, store->name())) {
357       ua->error_msg(_("No authorization for Storage \"%s\"\n"), store->name());
358       return;
359    }
360    /*
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.
364     */
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"));
368       return;
369    }
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"),
378          store->name());
379       free_bsock(ua->jcr->store_bsock);
380       return;
381    }
382    Dmsg0(20, "Connected to storage daemon\n");
383    sd = ua->jcr->store_bsock;
384    if (cmd) {
385       POOL_MEM devname;
386       /*
387        * For .status storage=xxx shstore list
388        *  send .status shstore list xxx-device
389        */
390       if (strcasecmp(cmd, "shstore") == 0) {
391          if (!ua->argk[3]) {
392             ua->send_msg(_("Must have three aguments\n"));
393             return;
394          }
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);
400       } else {
401          int i = find_arg_with_value(ua, "device");
402          if (i>0) {
403             Mmsg(devname, "device=%s", ua->argv[i]);
404             bash_spaces(devname.c_str());
405          }
406          sd->fsend(".status %s api=%d api_opts=%s %s",
407                    cmd, ua->api, ua->api_opts, devname.c_str());
408       }
409    } else {
410       sd->fsend("status");
411    }
412    while (sd->recv() >= 0) {
413       ua->send_msg("%s", sd->msg);
414    }
415    sd->signal(BNET_TERMINATE);
416    free_bsock(ua->jcr->store_bsock);
417    return;
418 }
419
420 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd)
421 {
422    BSOCK *fd;
423
424    if (!acl_access_ok(ua, Client_ACL, client->name())) {
425       ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
426       return;
427    }
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);
433    }
434    /* Create a new dummy SD auth key */
435    ua->jcr->sd_auth_key = bstrdup("dummy");
436
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"),
442          client->name());
443       free_bsock(ua->jcr->file_bsock);
444       return;
445    }
446    Dmsg0(20, _("Connected to file daemon\n"));
447    fd = ua->jcr->file_bsock;
448    if (cmd) {
449       fd->fsend(".status %s api=%d api_opts=%s", cmd, ua->api, ua->api_opts);
450    } else {
451       fd->fsend("status");
452    }
453    while (fd->recv() >= 0) {
454       ua->send_msg("%s", fd->msg);
455    }
456    fd->signal(BNET_TERMINATE);
457    free_bsock(ua->jcr->file_bsock);
458
459    return;
460 }
461
462 static void prt_runhdr(UAContext *ua)
463 {
464    if (!ua->api) {
465       ua->send_msg(_("\nScheduled Jobs:\n"));
466       ua->send_msg(_("Level          Type     Pri  Scheduled          Job Name           Volume\n"));
467       ua->send_msg(_("===================================================================================\n"));
468    }
469 }
470
471 static void prt_lrunhdr(UAContext *ua)
472 {
473    if (!ua->api) {
474       ua->send_msg(_("\nScheduled Jobs:\n"));
475       ua->send_msg(_("Level          Type     Pri  Scheduled          Job Name           Schedule\n"));
476       ua->send_msg(_("=====================================================================================\n"));
477    }
478 }
479
480
481 /* Scheduling packet */
482 struct sched_pkt {
483    dlink link;                        /* keep this as first item!!! */
484    JOB *job;
485    int level;
486    int priority;
487    utime_t runtime;
488    POOL *pool;
489    STORE *store;
490 };
491
492 static void prt_runtime(UAContext *ua, sched_pkt *sp)
493 {
494    char dt[MAX_TIME_LENGTH];
495    const char *level_ptr;
496    bool ok = false;
497    bool close_db = false;
498    JCR *jcr = ua->jcr;
499    MEDIA_DBR mr;
500    int orig_jobtype;
501
502    orig_jobtype = jcr->getJobType();
503    if (sp->job->JobType == JT_BACKUP) {
504       jcr->db = NULL;
505       ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
506       Dmsg1(250, "Using pool=%s\n", jcr->pool->name());
507       if (jcr->db) {
508          close_db = true;             /* new db opened, remember to close it */
509       }
510       if (ok) {
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);
517       }
518       if (!ok) {
519          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
520       }
521    }
522    bstrftime_nc(dt, sizeof(dt), sp->runtime);
523    switch (sp->job->JobType) {
524    case JT_ADMIN:
525    case JT_RESTORE:
526       level_ptr = " ";
527       break;
528    default:
529       level_ptr = level_to_str(sp->level);
530       break;
531    }
532    if (ua->api == 1) {
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);
536    } else {
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);
540    }
541    if (close_db) {
542       db_close_database(jcr, jcr->db);
543    }
544    jcr->db = ua->db;                  /* restore ua db to jcr */
545    jcr->setJobType(orig_jobtype);
546 }
547
548 /*
549  * Detailed listing of all scheduler jobs
550  */
551 static void llist_scheduled_jobs(UAContext *ua)
552 {
553    utime_t runtime;
554    RUN *run;
555    JOB *job;
556    int level, num_jobs = 0;
557    int priority;
558    bool hdr_printed = false;
559    char sched_name[MAX_NAME_LENGTH];
560    char job_name[MAX_NAME_LENGTH];
561    SCHED *sched;
562    int days, i, limit;
563    time_t now = time(NULL);
564    time_t next;
565    const char *level_ptr;
566
567    Dmsg0(200, "enter list_sched_jobs()\n");
568
569    i = find_arg_with_value(ua, NT_("days"));
570    if (i >= 0) {
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"));
574        days = 10;
575      }
576    } else {
577       days = 10;
578    }
579
580    i = find_arg_with_value(ua, NT_("limit"));
581    if (i >= 0) {
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"));
585        limit = 100;
586      }
587    } else {
588       limit = 100;
589    }
590
591    i = find_arg_with_value(ua, NT_("time"));
592    if (i >= 0) {
593       now = str_to_utime(ua->argv[i]);
594       if (now == 0) {
595          ua->send_msg(_("Ignoring invalid time.\n"));
596          now = time(NULL);
597       }
598    }
599
600    i = find_arg_with_value(ua, NT_("schedule"));
601    if (i >= 0) {
602       bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
603    } else {
604       sched_name[0] = 0;
605    }
606
607    i = find_arg_with_value(ua, NT_("job"));
608    if (i >= 0) {
609       bstrncpy(job_name, ua->argv[i], sizeof(job_name));
610    } else {
611       job_name[0] = 0;
612    }
613
614    /* Loop through all jobs */
615    LockRes();
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 */
621       }
622       if (job_name[0] && strcmp(job_name, job->name()) != 0) {
623          continue;
624       }
625       for (run=sched->run; run; run=run->next) {
626          next = now;
627          for (i=0; i<days; i++) {
628             struct tm tm;
629             int mday, wday, month, wom, woy, ldom;
630             char dt[MAX_TIME_LENGTH];
631             bool ok;
632
633             /* compute values for next time */
634             (void)localtime_r(&next, &tm);
635             mday = tm.tm_mday - 1;
636             wday = tm.tm_wday;
637             month = tm.tm_mon;
638             wom = mday / 7;
639             woy = tm_woy(next);                    /* get week of year */
640             ldom = tm_ldom(month, tm.tm_year + 1900);
641
642 //#define xxx_debug
643 #ifdef xxx_debug
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));
653 #endif
654
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);
662             if (!ok) {
663                next += 24 * 60 * 60;   /* Add one day */
664                continue;
665             }
666             for (int j=0; j < 24; j++) {
667                if (bit_is_set(j, run->hour)) {
668                   tm.tm_hour = j;
669                   tm.tm_min = run->minute;
670                   tm.tm_sec = 0;
671                   runtime = mktime(&tm);
672                   bstrftime_dn(dt, sizeof(dt), runtime);
673                   break;
674                }
675             }
676
677             level = job->JobLevel;
678             if (run->level) {
679                level = run->level;
680             }
681             switch (job->JobType) {
682             case JT_ADMIN:
683             case JT_RESTORE:
684                level_ptr = " ";
685                break;
686             default:
687                level_ptr = level_to_str(level);
688                break;
689             }
690             priority = job->Priority;
691             if (run->Priority) {
692                priority = run->Priority;
693             }
694             if (!hdr_printed) {
695                prt_lrunhdr(ua);
696                hdr_printed = true;
697             }
698             if (ua->api) {
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());
702             } else {
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());
706             }
707             next += 24 * 60 * 60;   /* Add one day */
708             num_jobs++;
709             if (num_jobs >= limit) {
710                goto get_out;
711             }
712          }
713       } /* end loop over run pkts */
714    } /* end for loop over resources */
715 get_out:
716    UnlockRes();
717    if (num_jobs == 0 && !ua->api) {
718       ua->send_msg(_("No Scheduled Jobs.\n"));
719    }
720    if (!ua->api) ua->send_msg("====\n");
721    Dmsg0(200, "Leave ;list_sched_jobs_runs()\n");
722 }
723
724
725 /*
726  * Sort items by runtime, priority
727  */
728 static int my_compare(void *item1, void *item2)
729 {
730    sched_pkt *p1 = (sched_pkt *)item1;
731    sched_pkt *p2 = (sched_pkt *)item2;
732    if (p1->runtime < p2->runtime) {
733       return -1;
734    } else if (p1->runtime > p2->runtime) {
735       return 1;
736    }
737    if (p1->priority < p2->priority) {
738       return -1;
739    } else if (p1->priority > p2->priority) {
740       return 1;
741    }
742    return 0;
743 }
744
745 /*
746  * Find all jobs to be run in roughly the
747  *  next 24 hours.
748  */
749 static void list_scheduled_jobs(UAContext *ua)
750 {
751    utime_t runtime;
752    RUN *run;
753    JOB *job;
754    int level, num_jobs = 0;
755    int priority;
756    bool hdr_printed = false;
757    char sched_name[MAX_NAME_LENGTH];
758    dlist sched;
759    sched_pkt *sp;
760    int days, i;
761
762    Dmsg0(200, "enter list_sched_jobs()\n");
763
764    days = 1;
765    i = find_arg_with_value(ua, NT_("days"));
766    if (i >= 0) {
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"));
770        days = 1;
771      }
772    }
773    i = find_arg_with_value(ua, NT_("schedule"));
774    if (i >= 0) {
775       bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
776    } else {
777       sched_name[0] = 0;
778    }
779
780    /* Loop through all jobs */
781    LockRes();
782    foreach_res(job, R_JOB) {
783       if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
784          continue;
785       }
786       if (sched_name[0] && job->schedule &&
787           strcasecmp(job->schedule->name(), sched_name) != 0) {
788          continue;
789       }
790       for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
791          USTORE store;
792          level = job->JobLevel;
793          if (run->level) {
794             level = run->level;
795          }
796          priority = job->Priority;
797          if (run->Priority) {
798             priority = run->Priority;
799          }
800          if (!hdr_printed) {
801             prt_runhdr(ua);
802             hdr_printed = true;
803          }
804          sp = (sched_pkt *)malloc(sizeof(sched_pkt));
805          sp->job = job;
806          sp->level = level;
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);
814          num_jobs++;
815       }
816    } /* end for loop over resources */
817    UnlockRes();
818    foreach_dlist(sp, &sched) {
819       prt_runtime(ua, sp);
820    }
821    if (num_jobs == 0 && !ua->api) {
822       ua->send_msg(_("No Scheduled Jobs.\n"));
823    }
824    if (!ua->api) ua->send_msg("====\n");
825    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
826 }
827
828 static void list_running_jobs(UAContext *ua)
829 {
830    JCR *jcr;
831    int njobs = 0;
832    int i;
833    const char *msg;
834    char *emsg;                        /* edited message */
835    char dt[MAX_TIME_LENGTH];
836    char level[10];
837    bool pool_mem = false;
838    JobId_t jid = 0;
839
840    if ((i = find_arg_with_value(ua, "jobid")) >= 0) {
841       jid = str_to_int64(ua->argv[i]);
842    }
843
844    Dmsg0(200, "enter list_run_jobs()\n");
845
846    if (!ua->api) {
847       ua->send_msg(_("\nRunning Jobs:\n"));
848       foreach_jcr(jcr) {
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.
852              */
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 "):"",
857                             dt);
858             }
859             continue;
860          }
861          njobs++;
862       }
863       endeach_jcr(jcr);
864       if (njobs == 0) {
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");
868          return;
869       }
870    }
871
872    if (!ua->api) {
873       ua->send_msg(_(" JobId  Type Level     Files     Bytes  Name              Status\n"));
874       ua->send_msg(_("======================================================================\n"));
875    }
876    foreach_jcr(jcr) {
877       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
878          continue;
879       }
880       /* JobId keyword found in command line */
881       if (jid > 0 && jcr->JobId != jid) {
882          continue;
883       }
884       switch (jcr->JobStatus) {
885       case JS_Created:
886          msg = _("is waiting execution");
887          break;
888       case JS_Running:
889          msg = _("is running");
890          break;
891       case JS_Blocked:
892          msg = _("is blocked");
893          break;
894       case JS_Terminated:
895          msg = _("has terminated");
896          break;
897       case JS_Warnings:
898          msg = _("has terminated with warnings");
899          break;
900       case JS_Incomplete:
901          msg = _("has terminated in incomplete state");
902          break;
903       case JS_ErrorTerminated:
904          msg = _("has erred");
905          break;
906       case JS_Error:
907          msg = _("has errors");
908          break;
909       case JS_FatalError:
910          msg = _("has a fatal error");
911          break;
912       case JS_Differences:
913          msg = _("has verify differences");
914          break;
915       case JS_Canceled:
916          msg = _("has been canceled");
917          break;
918       case JS_WaitFD:
919          emsg = (char *) get_pool_memory(PM_FNAME);
920          if (!jcr->client) {
921             Mmsg(emsg, _("is waiting on Client"));
922          } else {
923             Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
924          }
925          pool_mem = true;
926          msg = emsg;
927          break;
928       case JS_WaitSD:
929          emsg = (char *) get_pool_memory(PM_FNAME);
930          if (jcr->wstore) {
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());
934          } else {
935             Mmsg(emsg, _("is waiting on Storage"));
936          }
937          pool_mem = true;
938          msg = emsg;
939          break;
940       case JS_WaitStoreRes:
941          msg = _("is waiting on max Storage jobs");
942          break;
943       case JS_WaitClientRes:
944          msg = _("is waiting on max Client jobs");
945          break;
946       case JS_WaitJobRes:
947          msg = _("is waiting on max Job jobs");
948          break;
949       case JS_WaitMaxJobs:
950          msg = _("is waiting on max total jobs");
951          break;
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));
956          pool_mem = true;
957          msg = emsg;
958          break;
959       case JS_WaitPriority:
960          msg = _("is waiting for higher priority jobs to finish");
961          break;
962       case JS_WaitDevice:
963          msg = _("is waiting for a Shared Storage device");
964          break;
965       case JS_DataCommitting:
966          msg = _("SD committing Data");
967          break;
968       case JS_DataDespooling:
969          msg = _("SD despooling Data");
970          break;
971       case JS_AttrDespooling:
972          msg = _("SD despooling Attributes");
973          break;
974       case JS_AttrInserting:
975          msg = _("Dir inserting Attributes");
976          break;
977
978       default:
979          emsg = (char *)get_pool_memory(PM_FNAME);
980          Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
981          pool_mem = true;
982          msg = emsg;
983          break;
984       }
985       /*
986        * Now report Storage daemon status code
987        */
988       switch (jcr->SDJobStatus) {
989       case JS_WaitMount:
990          if (pool_mem) {
991             free_pool_memory(emsg);
992             pool_mem = false;
993          }
994          msg = _("is waiting for a mount request");
995          break;
996       case JS_WaitMedia:
997          if (pool_mem) {
998             free_pool_memory(emsg);
999             pool_mem = false;
1000          }
1001          msg = _("is waiting for an appendable Volume");
1002          break;
1003       case JS_WaitFD:
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) {
1007             if (!pool_mem) {
1008                emsg = (char *)get_pool_memory(PM_FNAME);
1009                pool_mem = true;
1010             }
1011             if (!jcr->client || !jcr->wstore) {
1012                Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
1013             } else {
1014                Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
1015                     jcr->client->name(), jcr->wstore->name());
1016             }
1017             msg = emsg;
1018          }
1019         break;
1020       case JS_DataCommitting:
1021          msg = _("SD committing Data");
1022          break;
1023       case JS_DataDespooling:
1024          msg = _("SD despooling Data");
1025          break;
1026       case JS_AttrDespooling:
1027          msg = _("SD despooling Attributes");
1028          break;
1029       case JS_AttrInserting:
1030          msg = _("Dir inserting Attributes");
1031          break;
1032       }
1033       switch (jcr->getJobType()) {
1034       case JT_ADMIN:
1035       case JT_RESTORE:
1036          bstrncpy(level, "      ", sizeof(level));
1037          break;
1038       default:
1039          bstrncpy(level, level_to_str(jcr->getJobLevel()), sizeof(level));
1040          level[7] = 0;
1041          break;
1042       }
1043
1044       if (ua->api == 1) {
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);
1049
1050       } else {
1051          char b1[50], b2[50], b3[50];
1052          level[4] = 0;
1053          bstrncpy(b1, job_type_to_str(jcr->getJobType()), sizeof(b1));
1054          b1[4] = 0;
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);
1060       }
1061
1062       if (pool_mem) {
1063          free_pool_memory(emsg);
1064          pool_mem = false;
1065       }
1066    }
1067    endeach_jcr(jcr);
1068    if (!ua->api) {
1069       ua->send_msg("====\n");
1070    }
1071    Dmsg0(200, "leave list_run_jobs()\n");
1072 }
1073
1074 static void list_terminated_jobs(UAContext *ua)
1075 {
1076    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
1077    char level[10];
1078
1079    if (last_jobs->empty()) {
1080       if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
1081       return;
1082    }
1083    lock_last_jobs_list();
1084    struct s_last_job *je;
1085    if (!ua->api) {
1086       ua->send_msg(_("\nTerminated Jobs:\n"));
1087       ua->send_msg(_(" JobId  Level    Files      Bytes   Status   Finished        Name \n"));
1088       ua->send_msg(_("====================================================================\n"));
1089    }
1090    foreach_dlist(je, last_jobs) {
1091       char JobName[MAX_NAME_LENGTH];
1092       const char *termstat;
1093
1094       bstrncpy(JobName, je->Job, sizeof(JobName));
1095       /* There are three periods after the Job name */
1096       char *p;
1097       for (int i=0; i<3; i++) {
1098          if ((p=strrchr(JobName, '.')) != NULL) {
1099             *p = 0;
1100          }
1101       }
1102
1103       if (!acl_access_ok(ua, Job_ACL, JobName)) {
1104          continue;
1105       }
1106
1107       bstrftime_nc(dt, sizeof(dt), je->end_time);
1108       switch (je->JobType) {
1109       case JT_ADMIN:
1110       case JT_RESTORE:
1111          bstrncpy(level, "    ", sizeof(level));
1112          break;
1113       default:
1114          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
1115          level[4] = 0;
1116          break;
1117       }
1118       switch (je->JobStatus) {
1119       case JS_Created:
1120          termstat = _("Created");
1121          break;
1122       case JS_FatalError:
1123       case JS_ErrorTerminated:
1124          termstat = _("Error");
1125          break;
1126       case JS_Differences:
1127          termstat = _("Diffs");
1128          break;
1129       case JS_Canceled:
1130          termstat = _("Cancel");
1131          break;
1132       case JS_Terminated:
1133          termstat = _("OK");
1134          break;
1135       case JS_Warnings:
1136          termstat = _("OK -- with warnings");
1137          break;
1138       case JS_Incomplete:
1139          termstat = _("Incomplete");
1140          break;
1141       default:
1142          termstat = _("Other");
1143          break;
1144       }
1145       if (ua->api == 1) {
1146          ua->send_msg(_("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
1147             je->JobId,
1148             level,
1149             edit_uint64_with_commas(je->JobFiles, b1),
1150             edit_uint64_with_suffix(je->JobBytes, b2),
1151             termstat,
1152             dt, JobName);
1153       } else {
1154          ua->send_msg(_("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
1155             je->JobId,
1156             level,
1157             edit_uint64_with_commas(je->JobFiles, b1),
1158             edit_uint64_with_suffix(je->JobBytes, b2),
1159             termstat,
1160             dt, JobName);
1161       }
1162    }
1163    if (!ua->api) {
1164       ua->send_msg(_("\n"));
1165    }
1166    unlock_last_jobs_list();
1167 }