]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
52d25c34b6876a8d12e9c4c31b9bdd3abc16e6fe
[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       level_ptr = "Admin";
526       break;
527    case JT_RESTORE:
528       level_ptr = "Restore";
529       break;
530    default:
531       level_ptr = level_to_str(sp->level);
532       break;
533    }
534    if (ua->api == 1) {
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);
538    } else {
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);
542    }
543    if (close_db) {
544       db_close_database(jcr, jcr->db);
545    }
546    jcr->db = ua->db;                  /* restore ua db to jcr */
547    jcr->setJobType(orig_jobtype);
548 }
549
550 /*
551  * Detailed listing of all scheduler jobs
552  */
553 static void llist_scheduled_jobs(UAContext *ua)
554 {
555    utime_t runtime;
556    RUN *run;
557    JOB *job;
558    int level, num_jobs = 0;
559    int priority;
560    bool hdr_printed = false;
561    char sched_name[MAX_NAME_LENGTH];
562    char job_name[MAX_NAME_LENGTH];
563    SCHED *sched;
564    int days, i, limit;
565    time_t now = time(NULL);
566    time_t next;
567    const char *level_ptr;
568
569    Dmsg0(200, "enter list_sched_jobs()\n");
570
571    i = find_arg_with_value(ua, NT_("days"));
572    if (i >= 0) {
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"));
576        days = 10;
577      }
578    } else {
579       days = 10;
580    }
581
582    i = find_arg_with_value(ua, NT_("limit"));
583    if (i >= 0) {
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"));
587        limit = 100;
588      }
589    } else {
590       limit = 100;
591    }
592
593    i = find_arg_with_value(ua, NT_("time"));
594    if (i >= 0) {
595       now = str_to_utime(ua->argv[i]);
596       if (now == 0) {
597          ua->send_msg(_("Ignoring invalid time.\n"));
598          now = time(NULL);
599       }
600    }
601
602    i = find_arg_with_value(ua, NT_("schedule"));
603    if (i >= 0) {
604       bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
605    } else {
606       sched_name[0] = 0;
607    }
608
609    i = find_arg_with_value(ua, NT_("job"));
610    if (i >= 0) {
611       bstrncpy(job_name, ua->argv[i], sizeof(job_name));
612    } else {
613       job_name[0] = 0;
614    }
615
616    /* Loop through all jobs */
617    LockRes();
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 */
623       }
624       if (job_name[0] && strcmp(job_name, job->name()) != 0) {
625          continue;
626       }
627       for (run=sched->run; run; run=run->next) {
628          next = now;
629          for (i=0; i<days; i++) {
630             struct tm tm;
631             int mday, wday, month, wom, woy, ldom;
632             char dt[MAX_TIME_LENGTH];
633             bool ok;
634
635             /* compute values for next time */
636             (void)localtime_r(&next, &tm);
637             mday = tm.tm_mday - 1;
638             wday = tm.tm_wday;
639             month = tm.tm_mon;
640             wom = mday / 7;
641             woy = tm_woy(next);                    /* get week of year */
642             ldom = tm_ldom(month, tm.tm_year + 1900);
643
644 //#define xxx_debug
645 #ifdef xxx_debug
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));
655 #endif
656
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);
664             if (!ok) {
665                next += 24 * 60 * 60;   /* Add one day */
666                continue;
667             }
668             for (int j=0; j < 24; j++) {
669                if (bit_is_set(j, run->hour)) {
670                   tm.tm_hour = j;
671                   tm.tm_min = run->minute;
672                   tm.tm_sec = 0;
673                   runtime = mktime(&tm);
674                   bstrftime_dn(dt, sizeof(dt), runtime);
675                   break;
676                }
677             }
678
679             level = job->JobLevel;
680             if (run->level) {
681                level = run->level;
682             }
683             switch (job->JobType) {
684             case JT_ADMIN:
685                level_ptr = "Admin";
686                break;
687             case JT_RESTORE:
688                level_ptr = "Restore";
689                break;
690             default:
691                level_ptr = level_to_str(level);
692                break;
693             }
694             priority = job->Priority;
695             if (run->Priority) {
696                priority = run->Priority;
697             }
698             if (!hdr_printed) {
699                prt_lrunhdr(ua);
700                hdr_printed = true;
701             }
702             if (ua->api) {
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());
706             } else {
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());
710             }
711             next += 24 * 60 * 60;   /* Add one day */
712             num_jobs++;
713             if (num_jobs >= limit) {
714                goto get_out;
715             }
716          }
717       } /* end loop over run pkts */
718    } /* end for loop over resources */
719 get_out:
720    UnlockRes();
721    if (num_jobs == 0 && !ua->api) {
722       ua->send_msg(_("No Scheduled Jobs.\n"));
723    }
724    if (!ua->api) ua->send_msg("====\n");
725    Dmsg0(200, "Leave ;list_sched_jobs_runs()\n");
726 }
727
728
729 /*
730  * Sort items by runtime, priority
731  */
732 static int my_compare(void *item1, void *item2)
733 {
734    sched_pkt *p1 = (sched_pkt *)item1;
735    sched_pkt *p2 = (sched_pkt *)item2;
736    if (p1->runtime < p2->runtime) {
737       return -1;
738    } else if (p1->runtime > p2->runtime) {
739       return 1;
740    }
741    if (p1->priority < p2->priority) {
742       return -1;
743    } else if (p1->priority > p2->priority) {
744       return 1;
745    }
746    return 0;
747 }
748
749 /*
750  * Find all jobs to be run in roughly the
751  *  next 24 hours.
752  */
753 static void list_scheduled_jobs(UAContext *ua)
754 {
755    utime_t runtime;
756    RUN *run;
757    JOB *job;
758    int level, num_jobs = 0;
759    int priority;
760    bool hdr_printed = false;
761    char sched_name[MAX_NAME_LENGTH];
762    dlist sched;
763    sched_pkt *sp;
764    int days, i;
765
766    Dmsg0(200, "enter list_sched_jobs()\n");
767
768    days = 1;
769    i = find_arg_with_value(ua, NT_("days"));
770    if (i >= 0) {
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"));
774        days = 1;
775      }
776    }
777    i = find_arg_with_value(ua, NT_("schedule"));
778    if (i >= 0) {
779       bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
780    } else {
781       sched_name[0] = 0;
782    }
783
784    /* Loop through all jobs */
785    LockRes();
786    foreach_res(job, R_JOB) {
787       if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
788          continue;
789       }
790       if (sched_name[0] && job->schedule &&
791           strcasecmp(job->schedule->name(), sched_name) != 0) {
792          continue;
793       }
794       for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
795          USTORE store;
796          level = job->JobLevel;
797          if (run->level) {
798             level = run->level;
799          }
800          priority = job->Priority;
801          if (run->Priority) {
802             priority = run->Priority;
803          }
804          if (!hdr_printed) {
805             prt_runhdr(ua);
806             hdr_printed = true;
807          }
808          sp = (sched_pkt *)malloc(sizeof(sched_pkt));
809          sp->job = job;
810          sp->level = level;
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);
818          num_jobs++;
819       }
820    } /* end for loop over resources */
821    UnlockRes();
822    foreach_dlist(sp, &sched) {
823       prt_runtime(ua, sp);
824    }
825    if (num_jobs == 0 && !ua->api) {
826       ua->send_msg(_("No Scheduled Jobs.\n"));
827    }
828    if (!ua->api) ua->send_msg("====\n");
829    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
830 }
831
832 static void list_running_jobs(UAContext *ua)
833 {
834    JCR *jcr;
835    int njobs = 0;
836    int i;
837    const char *msg;
838    char *emsg;                        /* edited message */
839    char dt[MAX_TIME_LENGTH];
840    char level[10];
841    bool pool_mem = false;
842    JobId_t jid = 0;
843
844    if ((i = find_arg_with_value(ua, "jobid")) >= 0) {
845       jid = str_to_int64(ua->argv[i]);
846    }
847
848    Dmsg0(200, "enter list_run_jobs()\n");
849
850    if (!ua->api) {
851       ua->send_msg(_("\nRunning Jobs:\n"));
852       foreach_jcr(jcr) {
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.
856              */
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 "):"",
861                             dt);
862             }
863             continue;
864          }
865          njobs++;
866       }
867       endeach_jcr(jcr);
868       if (njobs == 0) {
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");
872          return;
873       }
874    }
875
876    if (!ua->api) {
877       ua->send_msg(_(" JobId  Type Level     Files     Bytes  Name              Status\n"));
878       ua->send_msg(_("======================================================================\n"));
879    }
880    foreach_jcr(jcr) {
881       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
882          continue;
883       }
884       /* JobId keyword found in command line */
885       if (jid > 0 && jcr->JobId != jid) {
886          continue;
887       }
888       switch (jcr->JobStatus) {
889       case JS_Created:
890          msg = _("is waiting execution");
891          break;
892       case JS_Running:
893          msg = _("is running");
894          break;
895       case JS_Blocked:
896          msg = _("is blocked");
897          break;
898       case JS_Terminated:
899          msg = _("has terminated");
900          break;
901       case JS_Warnings:
902          msg = _("has terminated with warnings");
903          break;
904       case JS_Incomplete:
905          msg = _("has terminated in incomplete state");
906          break;
907       case JS_ErrorTerminated:
908          msg = _("has erred");
909          break;
910       case JS_Error:
911          msg = _("has errors");
912          break;
913       case JS_FatalError:
914          msg = _("has a fatal error");
915          break;
916       case JS_Differences:
917          msg = _("has verify differences");
918          break;
919       case JS_Canceled:
920          msg = _("has been canceled");
921          break;
922       case JS_WaitFD:
923          emsg = (char *) get_pool_memory(PM_FNAME);
924          if (!jcr->client) {
925             Mmsg(emsg, _("is waiting on Client"));
926          } else {
927             Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
928          }
929          pool_mem = true;
930          msg = emsg;
931          break;
932       case JS_WaitSD:
933          emsg = (char *) get_pool_memory(PM_FNAME);
934          if (jcr->wstore) {
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());
938          } else {
939             Mmsg(emsg, _("is waiting on Storage"));
940          }
941          pool_mem = true;
942          msg = emsg;
943          break;
944       case JS_WaitStoreRes:
945          msg = _("is waiting on max Storage jobs");
946          break;
947       case JS_WaitClientRes:
948          msg = _("is waiting on max Client jobs");
949          break;
950       case JS_WaitJobRes:
951          msg = _("is waiting on max Job jobs");
952          break;
953       case JS_WaitMaxJobs:
954          msg = _("is waiting on max total jobs");
955          break;
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));
960          pool_mem = true;
961          msg = emsg;
962          break;
963       case JS_WaitPriority:
964          msg = _("is waiting for higher priority jobs to finish");
965          break;
966       case JS_WaitDevice:
967          msg = _("is waiting for a Shared Storage device");
968          break;
969       case JS_DataCommitting:
970          msg = _("SD committing Data");
971          break;
972       case JS_DataDespooling:
973          msg = _("SD despooling Data");
974          break;
975       case JS_AttrDespooling:
976          msg = _("SD despooling Attributes");
977          break;
978       case JS_AttrInserting:
979          msg = _("Dir inserting Attributes");
980          break;
981
982       default:
983          emsg = (char *)get_pool_memory(PM_FNAME);
984          Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
985          pool_mem = true;
986          msg = emsg;
987          break;
988       }
989       /*
990        * Now report Storage daemon status code
991        */
992       switch (jcr->SDJobStatus) {
993       case JS_WaitMount:
994          if (pool_mem) {
995             free_pool_memory(emsg);
996             pool_mem = false;
997          }
998          msg = _("is waiting for a mount request");
999          break;
1000       case JS_WaitMedia:
1001          if (pool_mem) {
1002             free_pool_memory(emsg);
1003             pool_mem = false;
1004          }
1005          msg = _("is waiting for an appendable Volume");
1006          break;
1007       case JS_WaitFD:
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) {
1011             if (!pool_mem) {
1012                emsg = (char *)get_pool_memory(PM_FNAME);
1013                pool_mem = true;
1014             }
1015             if (!jcr->client || !jcr->wstore) {
1016                Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
1017             } else {
1018                Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
1019                     jcr->client->name(), jcr->wstore->name());
1020             }
1021             msg = emsg;
1022          }
1023         break;
1024       case JS_DataCommitting:
1025          msg = _("SD committing Data");
1026          break;
1027       case JS_DataDespooling:
1028          msg = _("SD despooling Data");
1029          break;
1030       case JS_AttrDespooling:
1031          msg = _("SD despooling Attributes");
1032          break;
1033       case JS_AttrInserting:
1034          msg = _("Dir inserting Attributes");
1035          break;
1036       }
1037       switch (jcr->getJobType()) {
1038       case JT_ADMIN:
1039          bstrncpy(level, "Admin", sizeof(level));
1040          break;
1041       case JT_RESTORE:
1042          bstrncpy(level, "Restore", sizeof(level));
1043          break;
1044       default:
1045          bstrncpy(level, level_to_str(jcr->getJobLevel()), sizeof(level));
1046          level[7] = 0;
1047          break;
1048       }
1049
1050       if (ua->api == 1) {
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);
1055
1056       } else {
1057          char b1[50], b2[50], b3[50];
1058          level[4] = 0;
1059          bstrncpy(b1, job_type_to_str(jcr->getJobType()), sizeof(b1));
1060          b1[4] = 0;
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);
1066       }
1067
1068       if (pool_mem) {
1069          free_pool_memory(emsg);
1070          pool_mem = false;
1071       }
1072    }
1073    endeach_jcr(jcr);
1074    if (!ua->api) {
1075       ua->send_msg("====\n");
1076    }
1077    Dmsg0(200, "leave list_run_jobs()\n");
1078 }
1079
1080 static void list_terminated_jobs(UAContext *ua)
1081 {
1082    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
1083    char level[10];
1084
1085    if (last_jobs->empty()) {
1086       if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
1087       return;
1088    }
1089    lock_last_jobs_list();
1090    struct s_last_job *je;
1091    if (!ua->api) {
1092       ua->send_msg(_("\nTerminated Jobs:\n"));
1093       ua->send_msg(_(" JobId  Level      Files    Bytes   Status   Finished        Name \n"));
1094       ua->send_msg(_("====================================================================\n"));
1095    }
1096    foreach_dlist(je, last_jobs) {
1097       char JobName[MAX_NAME_LENGTH];
1098       const char *termstat;
1099
1100       bstrncpy(JobName, je->Job, sizeof(JobName));
1101       /* There are three periods after the Job name */
1102       char *p;
1103       for (int i=0; i<3; i++) {
1104          if ((p=strrchr(JobName, '.')) != NULL) {
1105             *p = 0;
1106          }
1107       }
1108
1109       if (!acl_access_ok(ua, Job_ACL, JobName)) {
1110          continue;
1111       }
1112
1113       bstrftime_nc(dt, sizeof(dt), je->end_time);
1114       switch (je->JobType) {
1115       case JT_ADMIN:
1116          bstrncpy(level, "Admin", sizeof(level));
1117          break;
1118       case JT_RESTORE:
1119          bstrncpy(level, "Restore", sizeof(level));
1120          break;
1121       default:
1122          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
1123          level[4] = 0;
1124          break;
1125       }
1126       switch (je->JobStatus) {
1127       case JS_Created:
1128          termstat = _("Created");
1129          break;
1130       case JS_FatalError:
1131       case JS_ErrorTerminated:
1132          termstat = _("Error");
1133          break;
1134       case JS_Differences:
1135          termstat = _("Diffs");
1136          break;
1137       case JS_Canceled:
1138          termstat = _("Cancel");
1139          break;
1140       case JS_Terminated:
1141          termstat = _("OK");
1142          break;
1143       case JS_Warnings:
1144          termstat = _("OK -- with warnings");
1145          break;
1146       case JS_Incomplete:
1147          termstat = _("Incomplete");
1148          break;
1149       default:
1150          termstat = _("Other");
1151          break;
1152       }
1153       if (ua->api == 1) {
1154          ua->send_msg(_("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%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       } else {
1162          ua->send_msg(_("%6d  %-7s %8s %10s  %-7s  %-8s %s\n"),
1163             je->JobId,
1164             level,
1165             edit_uint64_with_commas(je->JobFiles, b1),
1166             edit_uint64_with_suffix(je->JobBytes, b2),
1167             termstat,
1168             dt, JobName);
1169       }
1170    }
1171    if (!ua->api) {
1172       ua->send_msg(_("\n"));
1173    }
1174    unlock_last_jobs_list();
1175 }