]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_status.c
Increase status schedule days from 500 to 3000
[bacula/bacula] / bacula / src / dird / ua_status.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
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    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *
18  *   Bacula Director -- User Agent Status Command
19  *
20  *     Kern Sibbald, August MMI
21  *
22  */
23
24
25 #include "bacula.h"
26 #include "dird.h"
27
28 extern void *start_heap;
29 extern utime_t last_reload_time;
30
31 static void list_scheduled_jobs(UAContext *ua);
32 static void llist_scheduled_jobs(UAContext *ua);
33 static void list_running_jobs(UAContext *ua);
34 static void list_terminated_jobs(UAContext *ua);
35 static void do_storage_status(UAContext *ua, STORE *store, char *cmd);
36 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd);
37 static void do_director_status(UAContext *ua);
38 static void do_all_status(UAContext *ua);
39 void status_slots(UAContext *ua, STORE *store);
40 void status_content(UAContext *ua, STORE *store);
41
42 static char OKqstatus[]   = "1000 OK .status\n";
43 static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n";
44
45 /*
46  * .status command
47  */
48
49 bool dot_status_cmd(UAContext *ua, const char *cmd)
50 {
51    STORE *store;
52    CLIENT *client;
53    JCR* njcr = NULL;
54    s_last_job* job;
55    char ed1[50];
56
57    Dmsg2(20, "status=\"%s\" argc=%d\n", cmd, ua->argc);
58
59    if (ua->argc < 3) {
60       ua->send_msg("1900 Bad .status command, missing arguments.\n");
61       return false;
62    }
63
64    if (strcasecmp(ua->argk[1], "dir") == 0) {
65       if (strcasecmp(ua->argk[2], "current") == 0) {
66          ua->send_msg(OKqstatus, ua->argk[2]);
67          foreach_jcr(njcr) {
68             if (njcr->JobId != 0 && acl_access_ok(ua, Job_ACL, njcr->job->name())) {
69                ua->send_msg(DotStatusJob, edit_int64(njcr->JobId, ed1),
70                         njcr->JobStatus, njcr->JobErrors);
71             }
72          }
73          endeach_jcr(njcr);
74       } else if (strcasecmp(ua->argk[2], "last") == 0) {
75          ua->send_msg(OKqstatus, ua->argk[2]);
76          if ((last_jobs) && (last_jobs->size() > 0)) {
77             job = (s_last_job*)last_jobs->last();
78             if (acl_access_ok(ua, Job_ACL, job->Job)) {
79                ua->send_msg(DotStatusJob, edit_int64(job->JobId, ed1),
80                      job->JobStatus, job->Errors);
81             }
82          }
83       } else if (strcasecmp(ua->argk[2], "header") == 0) {
84           list_dir_status_header(ua);
85       } else if (strcasecmp(ua->argk[2], "scheduled") == 0) {
86           list_scheduled_jobs(ua);
87       } else if (strcasecmp(ua->argk[2], "running") == 0) {
88           list_running_jobs(ua);
89       } else if (strcasecmp(ua->argk[2], "terminated") == 0) {
90           list_terminated_jobs(ua);
91       } else {
92          ua->send_msg("1900 Bad .status command, wrong argument.\n");
93          return false;
94       }
95    } else if (strcasecmp(ua->argk[1], "client") == 0) {
96       client = get_client_resource(ua);
97       if (client) {
98          Dmsg2(200, "Client=%s arg=%s\n", client->name(), NPRT(ua->argk[2]));
99          do_client_status(ua, client, ua->argk[2]);
100       }
101    } else if (strcasecmp(ua->argk[1], "storage") == 0) {
102       store = get_storage_resource(ua, false /*no default*/, true/*unique*/);
103       if (!store) {
104          ua->send_msg("1900 Bad .status command, wrong argument.\n");
105          return false;
106       }
107       do_storage_status(ua, store, ua->argk[2]);
108    } else {
109       ua->send_msg("1900 Bad .status command, wrong argument.\n");
110       return false;
111    }
112
113    return true;
114 }
115
116 /* This is the *old* command handler, so we must return
117  *  1 or it closes the connection
118  */
119 int qstatus_cmd(UAContext *ua, const char *cmd)
120 {
121    dot_status_cmd(ua, cmd);
122    return 1;
123 }
124
125 /*
126  * status command
127  */
128 int status_cmd(UAContext *ua, const char *cmd)
129 {
130    STORE *store;
131    CLIENT *client;
132    int item, i;
133
134    Dmsg1(20, "status:%s:\n", cmd);
135
136    for (i=1; i<ua->argc; i++) {
137       if (strcasecmp(ua->argk[i], NT_("schedule")) == 0 ||
138           strcasecmp(ua->argk[i], NT_("scheduled")) == 0) {
139          llist_scheduled_jobs(ua);
140          return 1;
141       } else if (strcasecmp(ua->argk[i], NT_("all")) == 0) {
142          do_all_status(ua);
143          return 1;
144       } else if (strcasecmp(ua->argk[i], NT_("dir")) == 0 ||
145                  strcasecmp(ua->argk[i], NT_("director")) == 0) {
146          do_director_status(ua);
147          return 1;
148       } else if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
149          client = get_client_resource(ua);
150          if (client) {
151             do_client_status(ua, client, NULL);
152          }
153          return 1;
154       } else {
155          store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
156          if (store) {
157             if (find_arg(ua, NT_("slots")) > 0) {
158                status_slots(ua, store);
159             } else {
160                do_storage_status(ua, store, NULL);
161             }
162          }
163          return 1;
164       }
165    }
166    /* If no args, ask for status type */
167    if (ua->argc == 1) {
168        char prmt[MAX_NAME_LENGTH];
169
170       start_prompt(ua, _("Status available for:\n"));
171       add_prompt(ua, NT_("Director"));
172       add_prompt(ua, NT_("Storage"));
173       add_prompt(ua, NT_("Client"));
174       add_prompt(ua, NT_("Scheduled"));
175       add_prompt(ua, NT_("All"));
176       Dmsg0(20, "do_prompt: select daemon\n");
177       if ((item=do_prompt(ua, "",  _("Select daemon type for status"), prmt, sizeof(prmt))) < 0) {
178          return 1;
179       }
180       Dmsg1(20, "item=%d\n", item);
181       switch (item) {
182       case 0:                         /* Director */
183          do_director_status(ua);
184          break;
185       case 1:
186          store = select_storage_resource(ua, true/*unique*/);
187          if (store) {
188             do_storage_status(ua, store, NULL);
189          }
190          break;
191       case 2:
192          client = select_client_resource(ua);
193          if (client) {
194             do_client_status(ua, client, NULL);
195          }
196          break;
197       case 3:
198          llist_scheduled_jobs(ua);
199          break;
200       case 4:
201          do_all_status(ua);
202          break;
203       default:
204          break;
205       }
206    }
207    return 1;
208 }
209
210 static void do_all_status(UAContext *ua)
211 {
212    STORE *store, **unique_store;
213    CLIENT *client, **unique_client;
214    int i, j;
215    bool found;
216
217    do_director_status(ua);
218
219    /* Count Storage items */
220    LockRes();
221    i = 0;
222    foreach_res(store, R_STORAGE) {
223       i++;
224    }
225    unique_store = (STORE **) malloc(i * sizeof(STORE));
226    /* Find Unique Storage address/port */
227    i = 0;
228    foreach_res(store, R_STORAGE) {
229       found = false;
230       if (!acl_access_ok(ua, Storage_ACL, store->name())) {
231          continue;
232       }
233       for (j=0; j<i; j++) {
234          if (strcmp(unique_store[j]->address, store->address) == 0 &&
235              unique_store[j]->SDport == store->SDport) {
236             found = true;
237             break;
238          }
239       }
240       if (!found) {
241          unique_store[i++] = store;
242          Dmsg2(40, "Stuffing: %s:%d\n", store->address, store->SDport);
243       }
244    }
245    UnlockRes();
246
247    /* Call each unique Storage daemon */
248    for (j=0; j<i; j++) {
249       do_storage_status(ua, unique_store[j], NULL);
250    }
251    free(unique_store);
252
253    /* Count Client items */
254    LockRes();
255    i = 0;
256    foreach_res(client, R_CLIENT) {
257       i++;
258    }
259    unique_client = (CLIENT **)malloc(i * sizeof(CLIENT));
260    /* Find Unique Client address/port */
261    i = 0;
262    foreach_res(client, R_CLIENT) {
263       found = false;
264       if (!acl_access_ok(ua, Client_ACL, client->name())) {
265          continue;
266       }
267       for (j=0; j<i; j++) {
268          if (strcmp(unique_client[j]->address, client->address) == 0 &&
269              unique_client[j]->FDport == client->FDport) {
270             found = true;
271             break;
272          }
273       }
274       if (!found) {
275          unique_client[i++] = client;
276          Dmsg2(40, "Stuffing: %s:%d\n", client->address, client->FDport);
277       }
278    }
279    UnlockRes();
280
281    /* Call each unique File daemon */
282    for (j=0; j<i; j++) {
283       do_client_status(ua, unique_client[j], NULL);
284    }
285    free(unique_client);
286
287 }
288
289 void list_dir_status_header(UAContext *ua)
290 {
291    char dt[MAX_TIME_LENGTH];
292    char b1[35], b2[35], b3[35], b4[35], b5[35];
293
294    ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
295             HOST_OS, DISTNAME, DISTVER);
296    bstrftime_nc(dt, sizeof(dt), daemon_start_time);
297    ua->send_msg(_("Daemon started %s. Jobs: run=%d, running=%d "
298                   "mode=%d,%d\n"), dt,
299                 num_jobs_run, job_count(), (int)DEVELOPER_MODE, (int)BEEF);
300    ua->send_msg(_(" Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n"),
301             edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
302             edit_uint64_with_commas(sm_bytes, b2),
303             edit_uint64_with_commas(sm_max_bytes, b3),
304             edit_uint64_with_commas(sm_buffers, b4),
305             edit_uint64_with_commas(sm_max_buffers, b5));
306
307    /* TODO: use this function once for all daemons */
308    if (bplugin_list->size() > 0) {
309       int len;
310       Plugin *plugin;
311       POOL_MEM msg(PM_FNAME);
312       pm_strcpy(msg, " Plugin: ");
313       foreach_alist(plugin, bplugin_list) {
314          len = pm_strcat(msg, plugin->file);
315          if (len > 80) {
316             pm_strcat(msg, "\n   ");
317          } else {
318             pm_strcat(msg, " ");
319          }
320       }
321       ua->send_msg("%s\n", msg.c_str());
322    }
323 }
324
325 static void do_director_status(UAContext *ua)
326 {
327    list_dir_status_header(ua);
328
329    /*
330     * List scheduled Jobs
331     */
332    list_scheduled_jobs(ua);
333
334    /*
335     * List running jobs
336     */
337    list_running_jobs(ua);
338
339    /*
340     * List terminated jobs
341     */
342    list_terminated_jobs(ua);
343    ua->send_msg("====\n");
344 }
345
346 static void do_storage_status(UAContext *ua, STORE *store, char *cmd)
347 {
348    BSOCK *sd;
349    USTORE lstore;
350
351
352    if (!acl_access_ok(ua, Storage_ACL, store->name())) {
353       ua->error_msg(_("No authorization for Storage \"%s\"\n"), store->name());
354       return;
355    }
356    /*
357     * The Storage daemon is problematic because it shows information
358     *  related to multiple Jobs, so if there is a Client or Job
359     *  ACL restriction, we forbid all access to the Storage.
360     */
361    if (have_restricted_acl(ua, Client_ACL) ||
362        have_restricted_acl(ua, Job_ACL)) {
363       ua->error_msg(_("Restricted Client or Job does not permit access to  Storage daemons\n"));
364       return;
365    }
366    lstore.store = store;
367    pm_strcpy(lstore.store_source, _("unknown source"));
368    set_wstorage(ua->jcr, &lstore);
369    /* Try connecting for up to 15 seconds */
370    if (!ua->api) ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
371       store->name(), store->address, store->SDport);
372    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
373       ua->send_msg(_("\nFailed to connect to Storage daemon %s.\n====\n"),
374          store->name());
375       free_bsock(ua->jcr->store_bsock);
376       return;
377    }
378    Dmsg0(20, "Connected to storage daemon\n");
379    sd = ua->jcr->store_bsock;
380    if (cmd) {
381       POOL_MEM devname;
382       int i = find_arg_with_value(ua, "device");
383       if (i>0) {
384          Mmsg(devname, "device=%s", ua->argv[i]);
385          bash_spaces(devname.c_str());
386       }
387       sd->fsend(".status %s api=%d api_opts=%s %s",
388                 cmd, ua->api, ua->api_opts, devname.c_str());
389    } else {
390       sd->fsend("status");
391    }
392    while (sd->recv() >= 0) {
393       ua->send_msg("%s", sd->msg);
394    }
395    sd->signal(BNET_TERMINATE);
396    free_bsock(ua->jcr->store_bsock);
397    return;
398 }
399
400 static void do_client_status(UAContext *ua, CLIENT *client, char *cmd)
401 {
402    BSOCK *fd;
403
404    if (!acl_access_ok(ua, Client_ACL, client->name())) {
405       ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
406       return;
407    }
408    /* Connect to File daemon */
409    ua->jcr->client = client;
410    /* Release any old dummy key */
411    if (ua->jcr->sd_auth_key) {
412       free(ua->jcr->sd_auth_key);
413    }
414    /* Create a new dummy SD auth key */
415    ua->jcr->sd_auth_key = bstrdup("dummy");
416
417    /* Try to connect for 15 seconds */
418    if (!ua->api) ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
419       client->name(), client->address, client->FDport);
420    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
421       ua->send_msg(_("Failed to connect to Client %s.\n====\n"),
422          client->name());
423       free_bsock(ua->jcr->file_bsock);
424       return;
425    }
426    Dmsg0(20, _("Connected to file daemon\n"));
427    fd = ua->jcr->file_bsock;
428    if (cmd) {
429       fd->fsend(".status %s api=%d api_opts=%s", cmd, ua->api, ua->api_opts);
430    } else {
431       fd->fsend("status");
432    }
433    while (fd->recv() >= 0) {
434       ua->send_msg("%s", fd->msg);
435    }
436    fd->signal(BNET_TERMINATE);
437    free_bsock(ua->jcr->file_bsock);
438
439    return;
440 }
441
442 static void prt_runhdr(UAContext *ua)
443 {
444    if (!ua->api) {
445       ua->send_msg(_("\nScheduled Jobs:\n"));
446       ua->send_msg(_("Level          Type     Pri  Scheduled          Job Name           Volume\n"));
447       ua->send_msg(_("===================================================================================\n"));
448    }
449 }
450
451 static void prt_lrunhdr(UAContext *ua)
452 {
453    if (!ua->api) {
454       ua->send_msg(_("\nScheduled Jobs:\n"));
455       ua->send_msg(_("Level          Type     Pri  Scheduled          Job Name           Schedule\n"));
456       ua->send_msg(_("=====================================================================================\n"));
457    }
458 }
459
460
461 /* Scheduling packet */
462 struct sched_pkt {
463    dlink link;                        /* keep this as first item!!! */
464    JOB *job;
465    int level;
466    int priority;
467    utime_t runtime;
468    POOL *pool;
469    STORE *store;
470 };
471
472 static void prt_runtime(UAContext *ua, sched_pkt *sp)
473 {
474    char dt[MAX_TIME_LENGTH];
475    const char *level_ptr;
476    bool ok = false;
477    bool close_db = false;
478    JCR *jcr = ua->jcr;
479    MEDIA_DBR mr;
480    int orig_jobtype;
481
482    orig_jobtype = jcr->getJobType();
483    if (sp->job->JobType == JT_BACKUP) {
484       jcr->db = NULL;
485       ok = complete_jcr_for_job(jcr, sp->job, sp->pool);
486       Dmsg1(250, "Using pool=%s\n", jcr->pool->name());
487       if (jcr->db) {
488          close_db = true;             /* new db opened, remember to close it */
489       }
490       if (ok) {
491          mr.PoolId = jcr->jr.PoolId;
492          jcr->wstore = sp->store;
493          set_storageid_in_mr(jcr->wstore, &mr);
494          Dmsg0(250, "call find_next_volume_for_append\n");
495          /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
496          ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune);
497       }
498       if (!ok) {
499          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
500       }
501    }
502    bstrftime_nc(dt, sizeof(dt), sp->runtime);
503    switch (sp->job->JobType) {
504    case JT_ADMIN:
505    case JT_RESTORE:
506       level_ptr = " ";
507       break;
508    default:
509       level_ptr = level_to_str(sp->level);
510       break;
511    }
512    if (ua->api) {
513       ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
514          level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
515          sp->job->name(), mr.VolumeName);
516    } else {
517       ua->send_msg(_("%-14s %-8s %3d  %-18s %-18s %s\n"),
518          level_ptr, job_type_to_str(sp->job->JobType), sp->priority, dt,
519          sp->job->name(), mr.VolumeName);
520    }
521    if (close_db) {
522       db_close_database(jcr, jcr->db);
523    }
524    jcr->db = ua->db;                  /* restore ua db to jcr */
525    jcr->setJobType(orig_jobtype);
526 }
527
528 /*
529  * Detailed listing of all scheduler jobs
530  */
531 static void llist_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    char sched_name[MAX_NAME_LENGTH];
540    char job_name[MAX_NAME_LENGTH];
541    SCHED *sched;
542    int days, i, limit;
543    time_t now = time(NULL);
544    time_t next;
545    const char *level_ptr;
546
547    Dmsg0(200, "enter list_sched_jobs()\n");
548
549    i = find_arg_with_value(ua, NT_("days"));
550    if (i >= 0) {
551      days = atoi(ua->argv[i]);
552      if (((days < 0) || (days > 3000)) && !ua->api) {
553        ua->send_msg(_("Ignoring invalid value for days. Max is 3000.\n"));
554        days = 10;
555      }
556    } else {
557       days = 10;
558    }
559
560    i = find_arg_with_value(ua, NT_("limit"));
561    if (i >= 0) {
562      limit = atoi(ua->argv[i]);
563      if (((limit < 0) || (limit > 2000)) && !ua->api) {
564        ua->send_msg(_("Ignoring invalid value for limit. Max is 2000.\n"));
565        limit = 100;
566      }
567    } else {
568       limit = 100;
569    }
570
571    i = find_arg_with_value(ua, NT_("time"));
572    if (i >= 0) {
573       now = str_to_utime(ua->argv[i]);
574       if (now == 0) {
575          ua->send_msg(_("Ignoring invalid time.\n"));
576          now = time(NULL);
577       }
578    }
579
580    i = find_arg_with_value(ua, NT_("schedule"));
581    if (i >= 0) {
582       bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
583    } else {
584       sched_name[0] = 0;
585    }
586
587    i = find_arg_with_value(ua, NT_("job"));
588    if (i >= 0) {
589       bstrncpy(job_name, ua->argv[i], sizeof(job_name));
590    } else {
591       job_name[0] = 0;
592    }
593
594    /* Loop through all jobs */
595    LockRes();
596    foreach_res(job, R_JOB) {
597       sched = job->schedule;
598       if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
599          continue;                    /* no, skip this job */
600       }
601       if (job_name[0] && bstrcmp(job_name, job->name()) != 0) {
602          continue;
603       }
604       for (run=sched->run; run; run=run->next) {
605          next = now;
606          for (i=0; i<days; i++) {
607             struct tm tm;
608             int mday, wday, month, wom, woy, ldom;
609             char dt[MAX_TIME_LENGTH];
610             bool ok;
611
612             /* compute values for next time */
613             (void)localtime_r(&next, &tm);
614             mday = tm.tm_mday - 1;
615             wday = tm.tm_wday;
616             month = tm.tm_mon;
617             wom = mday / 7;
618             woy = tm_woy(next);                    /* get week of year */
619             ldom = tm_ldom(month, tm.tm_year + 1900);
620
621 //#define xxx_debug
622 #ifdef xxx_debug
623             Dmsg6(000, "m=%d md=%d wd=%d wom=%d woy=%d ldom=%d\n",
624                month, mday, wday, wom, woy, ldom);
625             Dmsg6(000, "bitset bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d bsldom=%d\n",
626                bit_is_set(month, run->month),
627                bit_is_set(mday, run->mday),
628                bit_is_set(wday, run->wday),
629                bit_is_set(wom, run->wom),
630                bit_is_set(woy, run->woy),
631                bit_is_set(31, run->mday));
632 #endif
633
634             ok = (bit_is_set(mday, run->mday) &&
635                   bit_is_set(wday, run->wday) &&
636                   bit_is_set(month, run->month) &&
637                   bit_is_set(wom, run->wom) &&
638                   bit_is_set(woy, run->woy)) ||
639                  (bit_is_set(month, run->month) &&
640                   bit_is_set(31, run->mday) && mday == ldom);
641             if (!ok) {
642                next += 24 * 60 * 60;   /* Add one day */
643                continue;
644             }
645             for (int j=0; j < 24; j++) {
646                if (bit_is_set(j, run->hour)) {
647                   tm.tm_hour = j;
648                   tm.tm_min = run->minute;
649                   tm.tm_sec = 0;
650                   runtime = mktime(&tm);
651                   bstrftime_dn(dt, sizeof(dt), runtime);
652                   break;
653                }
654             }
655
656             level = job->JobLevel;
657             if (run->level) {
658                level = run->level;
659             }
660             switch (job->JobType) {
661             case JT_ADMIN:
662             case JT_RESTORE:
663                level_ptr = " ";
664                break;
665             default:
666                level_ptr = level_to_str(level);
667                break;
668             }
669             priority = job->Priority;
670             if (run->Priority) {
671                priority = run->Priority;
672             }
673             if (!hdr_printed) {
674                prt_lrunhdr(ua);
675                hdr_printed = true;
676             }
677             if (ua->api) {
678                ua->send_msg(_("%-14s\t%-8s\t%3d\t%-18s\t%-18s\t%s\n"),
679                   level_ptr, job_type_to_str(job->JobType), priority, dt,
680                   job->name(), sched->name());
681             } else {
682                ua->send_msg(_("%-14s %-8s %3d  %-18s %-18s %s\n"),
683                   level_ptr, job_type_to_str(job->JobType), priority, dt,
684                   job->name(), sched->name());
685             }
686             next += 24 * 60 * 60;   /* Add one day */
687             num_jobs++;
688             if (num_jobs >= limit) {
689                goto get_out;
690             }
691          }
692       } /* end loop over run pkts */
693    } /* end for loop over resources */
694 get_out:
695    UnlockRes();
696    if (num_jobs == 0 && !ua->api) {
697       ua->send_msg(_("No Scheduled Jobs.\n"));
698    }
699    if (!ua->api) ua->send_msg("====\n");
700    Dmsg0(200, "Leave ;list_sched_jobs_runs()\n");
701 }
702
703
704 /*
705  * Sort items by runtime, priority
706  */
707 static int my_compare(void *item1, void *item2)
708 {
709    sched_pkt *p1 = (sched_pkt *)item1;
710    sched_pkt *p2 = (sched_pkt *)item2;
711    if (p1->runtime < p2->runtime) {
712       return -1;
713    } else if (p1->runtime > p2->runtime) {
714       return 1;
715    }
716    if (p1->priority < p2->priority) {
717       return -1;
718    } else if (p1->priority > p2->priority) {
719       return 1;
720    }
721    return 0;
722 }
723
724 /*
725  * Find all jobs to be run in roughly the
726  *  next 24 hours.
727  */
728 static void list_scheduled_jobs(UAContext *ua)
729 {
730    utime_t runtime;
731    RUN *run;
732    JOB *job;
733    int level, num_jobs = 0;
734    int priority;
735    bool hdr_printed = false;
736    char sched_name[MAX_NAME_LENGTH];
737    dlist sched;
738    sched_pkt *sp;
739    int days, i;
740
741    Dmsg0(200, "enter list_sched_jobs()\n");
742
743    days = 1;
744    i = find_arg_with_value(ua, NT_("days"));
745    if (i >= 0) {
746      days = atoi(ua->argv[i]);
747      if (((days < 0) || (days > 500)) && !ua->api) {
748        ua->send_msg(_("Ignoring invalid value for days. Max is 500.\n"));
749        days = 1;
750      }
751    }
752    i = find_arg_with_value(ua, NT_("schedule"));
753    if (i >= 0) {
754       bstrncpy(sched_name, ua->argv[i], sizeof(sched_name));
755    } else {
756       sched_name[0] = 0;
757    }
758
759    /* Loop through all jobs */
760    LockRes();
761    foreach_res(job, R_JOB) {
762       if (!acl_access_ok(ua, Job_ACL, job->name()) || !job->enabled) {
763          continue;
764       }
765       if (sched_name[0] && job->schedule &&
766           strcasecmp(job->schedule->name(), sched_name) != 0) {
767          continue;
768       }
769       for (run=NULL; (run = find_next_run(run, job, runtime, days)); ) {
770          USTORE store;
771          level = job->JobLevel;
772          if (run->level) {
773             level = run->level;
774          }
775          priority = job->Priority;
776          if (run->Priority) {
777             priority = run->Priority;
778          }
779          if (!hdr_printed) {
780             prt_runhdr(ua);
781             hdr_printed = true;
782          }
783          sp = (sched_pkt *)malloc(sizeof(sched_pkt));
784          sp->job = job;
785          sp->level = level;
786          sp->priority = priority;
787          sp->runtime = runtime;
788          sp->pool = run->pool;
789          get_job_storage(&store, job, run);
790          sp->store = store.store;
791          Dmsg3(250, "job=%s store=%s MediaType=%s\n", job->name(), sp->store->name(), sp->store->media_type);
792          sched.binary_insert_multiple(sp, my_compare);
793          num_jobs++;
794       }
795    } /* end for loop over resources */
796    UnlockRes();
797    foreach_dlist(sp, &sched) {
798       prt_runtime(ua, sp);
799    }
800    if (num_jobs == 0 && !ua->api) {
801       ua->send_msg(_("No Scheduled Jobs.\n"));
802    }
803    if (!ua->api) ua->send_msg("====\n");
804    Dmsg0(200, "Leave list_sched_jobs_runs()\n");
805 }
806
807 static void list_running_jobs(UAContext *ua)
808 {
809    JCR *jcr;
810    int njobs = 0;
811    const char *msg;
812    char *emsg;                        /* edited message */
813    char dt[MAX_TIME_LENGTH];
814    char level[10];
815    bool pool_mem = false;
816
817    Dmsg0(200, "enter list_run_jobs()\n");
818    if (!ua->api) ua->send_msg(_("\nRunning Jobs:\n"));
819    foreach_jcr(jcr) {
820       if (jcr->JobId == 0) {      /* this is us */
821          /* this is a console or other control job. We only show console
822           * jobs in the status output.
823           */
824          if (jcr->getJobType() == JT_CONSOLE && !ua->api) {
825             bstrftime_nc(dt, sizeof(dt), jcr->start_time);
826             ua->send_msg(_("Console connected at %s\n"), dt);
827          }
828          continue;
829       }
830       njobs++;
831    }
832    endeach_jcr(jcr);
833
834    if (njobs == 0) {
835       /* Note the following message is used in regress -- don't change */
836       if (!ua->api)  {
837          ua->send_msg(_("No Jobs running.\n====\n"));
838       }
839       Dmsg0(200, "leave list_run_jobs()\n");
840       return;
841    }
842    njobs = 0;
843    if (!ua->api) {
844       ua->send_msg(_(" JobId  Type Level     Files     Bytes  Name              Status\n"));
845       ua->send_msg(_("======================================================================\n"));
846    }
847    foreach_jcr(jcr) {
848       if (jcr->JobId == 0 || !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
849          continue;
850       }
851       njobs++;
852       switch (jcr->JobStatus) {
853       case JS_Created:
854          msg = _("is waiting execution");
855          break;
856       case JS_Running:
857          msg = _("is running");
858          break;
859       case JS_Blocked:
860          msg = _("is blocked");
861          break;
862       case JS_Terminated:
863          msg = _("has terminated");
864          break;
865       case JS_Warnings:
866          msg = _("has terminated with warnings");
867          break;
868       case JS_ErrorTerminated:
869          msg = _("has erred");
870          break;
871       case JS_Error:
872          msg = _("has errors");
873          break;
874       case JS_FatalError:
875          msg = _("has a fatal error");
876          break;
877       case JS_Differences:
878          msg = _("has verify differences");
879          break;
880       case JS_Canceled:
881          msg = _("has been canceled");
882          break;
883       case JS_WaitFD:
884          emsg = (char *) get_pool_memory(PM_FNAME);
885          if (!jcr->client) {
886             Mmsg(emsg, _("is waiting on Client"));
887          } else {
888             Mmsg(emsg, _("is waiting on Client %s"), jcr->client->name());
889          }
890          pool_mem = true;
891          msg = emsg;
892          break;
893       case JS_WaitSD:
894          emsg = (char *) get_pool_memory(PM_FNAME);
895          if (jcr->wstore) {
896             Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->wstore->name());
897          } else if (jcr->rstore) {
898             Mmsg(emsg, _("is waiting on Storage \"%s\""), jcr->rstore->name());
899          } else {
900             Mmsg(emsg, _("is waiting on Storage"));
901          }
902          pool_mem = true;
903          msg = emsg;
904          break;
905       case JS_WaitStoreRes:
906          msg = _("is waiting on max Storage jobs");
907          break;
908       case JS_WaitClientRes:
909          msg = _("is waiting on max Client jobs");
910          break;
911       case JS_WaitJobRes:
912          msg = _("is waiting on max Job jobs");
913          break;
914       case JS_WaitMaxJobs:
915          msg = _("is waiting on max total jobs");
916          break;
917       case JS_WaitStartTime:
918          emsg = (char *) get_pool_memory(PM_FNAME);
919          Mmsg(emsg, _("is waiting for its start time (%s)"),
920               bstrftime_ny(dt, sizeof(dt), jcr->sched_time));
921          pool_mem = true;
922          msg = emsg;
923          break;
924       case JS_WaitPriority:
925          msg = _("is waiting for higher priority jobs to finish");
926          break;
927       case JS_WaitDevice:
928          msg = _("is waiting for a Shared Storage device");
929          break;
930       case JS_DataCommitting:
931          msg = _("SD committing Data");
932          break;
933       case JS_DataDespooling:
934          msg = _("SD despooling Data");
935          break;
936       case JS_AttrDespooling:
937          msg = _("SD despooling Attributes");
938          break;
939       case JS_AttrInserting:
940          msg = _("Dir inserting Attributes");
941          break;
942
943       default:
944          emsg = (char *)get_pool_memory(PM_FNAME);
945          Mmsg(emsg, _("is in unknown state %c"), jcr->JobStatus);
946          pool_mem = true;
947          msg = emsg;
948          break;
949       }
950       /*
951        * Now report Storage daemon status code
952        */
953       switch (jcr->SDJobStatus) {
954       case JS_WaitMount:
955          if (pool_mem) {
956             free_pool_memory(emsg);
957             pool_mem = false;
958          }
959          msg = _("is waiting for a mount request");
960          break;
961       case JS_WaitMedia:
962          if (pool_mem) {
963             free_pool_memory(emsg);
964             pool_mem = false;
965          }
966          msg = _("is waiting for an appendable Volume");
967          break;
968       case JS_WaitFD:
969          if (!pool_mem) {
970             emsg = (char *)get_pool_memory(PM_FNAME);
971             pool_mem = true;
972          }
973          if (!jcr->client || !jcr->wstore) {
974             Mmsg(emsg, _("is waiting for Client to connect to Storage daemon"));
975          } else {
976             Mmsg(emsg, _("is waiting for Client %s to connect to Storage %s"),
977                  jcr->client->name(), jcr->wstore->name());
978         }
979         msg = emsg;
980         break;
981       case JS_DataCommitting:
982          msg = _("SD committing Data");
983          break;
984       case JS_DataDespooling:
985          msg = _("SD despooling Data");
986          break;
987       case JS_AttrDespooling:
988          msg = _("SD despooling Attributes");
989          break;
990       case JS_AttrInserting:
991          msg = _("Dir inserting Attributes");
992          break;
993       }
994       switch (jcr->getJobType()) {
995       case JT_ADMIN:
996       case JT_RESTORE:
997          bstrncpy(level, "      ", sizeof(level));
998          break;
999       default:
1000          bstrncpy(level, level_to_str(jcr->getJobLevel()), sizeof(level));
1001          level[7] = 0;
1002          break;
1003       }
1004
1005       if (ua->api == 1) {
1006          bash_spaces(jcr->comment);
1007          ua->send_msg(_("%6d\t%-6s\t%-20s\t%s\t%s\n"),
1008                       jcr->JobId, level, jcr->Job, msg, jcr->comment);
1009          unbash_spaces(jcr->comment);
1010       } else {
1011          char b1[50], b2[50], b3[50];
1012          level[4] = 0;
1013          bstrncpy(b1, job_type_to_str(jcr->getJobType()), sizeof(b1));
1014          b1[4] = 0;
1015          ua->send_msg(_("%6d  %-4s %-3s %10s %10s %-17s %s\n"),
1016             jcr->JobId, b1, level,
1017             edit_uint64_with_commas(jcr->JobFiles, b2),
1018             edit_uint64_with_suffix(jcr->JobBytes, b3),
1019             jcr->job->name(), msg);
1020       }
1021
1022       if (pool_mem) {
1023          free_pool_memory(emsg);
1024          pool_mem = false;
1025       }
1026    }
1027    endeach_jcr(jcr);
1028    if (!ua->api) {
1029       ua->send_msg("====\n");
1030    }
1031    Dmsg0(200, "leave list_run_jobs()\n");
1032 }
1033
1034 static void list_terminated_jobs(UAContext *ua)
1035 {
1036    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
1037    char level[10];
1038
1039    if (last_jobs->empty()) {
1040       if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
1041       return;
1042    }
1043    lock_last_jobs_list();
1044    struct s_last_job *je;
1045    if (!ua->api) {
1046       ua->send_msg(_("\nTerminated Jobs:\n"));
1047       ua->send_msg(_(" JobId  Level    Files      Bytes   Status   Finished        Name \n"));
1048       ua->send_msg(_("====================================================================\n"));
1049    }
1050    foreach_dlist(je, last_jobs) {
1051       char JobName[MAX_NAME_LENGTH];
1052       const char *termstat;
1053
1054       bstrncpy(JobName, je->Job, sizeof(JobName));
1055       /* There are three periods after the Job name */
1056       char *p;
1057       for (int i=0; i<3; i++) {
1058          if ((p=strrchr(JobName, '.')) != NULL) {
1059             *p = 0;
1060          }
1061       }
1062
1063       if (!acl_access_ok(ua, Job_ACL, JobName)) {
1064          continue;
1065       }
1066
1067       bstrftime_nc(dt, sizeof(dt), je->end_time);
1068       switch (je->JobType) {
1069       case JT_ADMIN:
1070       case JT_RESTORE:
1071          bstrncpy(level, "    ", sizeof(level));
1072          break;
1073       default:
1074          bstrncpy(level, level_to_str(je->JobLevel), sizeof(level));
1075          level[4] = 0;
1076          break;
1077       }
1078       switch (je->JobStatus) {
1079       case JS_Created:
1080          termstat = _("Created");
1081          break;
1082       case JS_FatalError:
1083       case JS_ErrorTerminated:
1084          termstat = _("Error");
1085          break;
1086       case JS_Differences:
1087          termstat = _("Diffs");
1088          break;
1089       case JS_Canceled:
1090          termstat = _("Cancel");
1091          break;
1092       case JS_Terminated:
1093          termstat = _("OK");
1094          break;
1095       case JS_Warnings:
1096          termstat = _("OK -- with warnings");
1097          break;
1098       default:
1099          termstat = _("Other");
1100          break;
1101       }
1102       if (ua->api == 1) {
1103          ua->send_msg(_("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
1104             je->JobId,
1105             level,
1106             edit_uint64_with_commas(je->JobFiles, b1),
1107             edit_uint64_with_suffix(je->JobBytes, b2),
1108             termstat,
1109             dt, JobName);
1110       } else {
1111          ua->send_msg(_("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
1112             je->JobId,
1113             level,
1114             edit_uint64_with_commas(je->JobFiles, b1),
1115             edit_uint64_with_suffix(je->JobBytes, b2),
1116             termstat,
1117             dt, JobName);
1118       }
1119    }
1120    if (!ua->api) {
1121       ua->send_msg(_("\n"));
1122    }
1123    unlock_last_jobs_list();
1124 }