]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/bdirjson.c
f44f77ea4db5a80c8e965aa41cf11a04376ba6dc
[bacula/bacula] / bacula / src / dird / bdirjson.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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 conf to Json
22  *
23  *     Kern Sibbald, September MMXII
24  *
25  */
26
27 #include "bacula.h"
28 #include "dird.h"
29
30 /* Exported subroutines */
31 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
32
33 static CONFIG *config;
34
35 /* Globals Exported */
36 DIRRES *director;                     /* Director resource */
37 int FDConnectTimeout;
38 int SDConnectTimeout;
39 char *configfile = NULL;
40 void *start_heap;
41
42 /* Globals Imported */
43 extern RES_ITEM job_items[];
44 extern s_jt jobtypes[];
45 extern s_jl joblevels[];
46 extern s_jt migtypes[];
47 extern s_kw ReplaceOptions[];
48 extern RES_ITEM2 newinc_items[];
49 extern RES_ITEM options_items[];
50 extern s_fs_opt FS_options[];
51 extern s_kw RunFields[];
52 extern s_kw tapelabels[];
53 extern s_kw msg_types[];
54 extern RES_TABLE resources[];
55
56 #if defined(_MSC_VER)
57 extern "C" { // work around visual compiler mangling variables
58    extern URES res_all;
59 }
60 #else
61 extern URES res_all;
62 #endif
63
64
65 #define CONFIG_FILE "bacula-dir.conf" /* default configuration file */
66
67 static void usage()
68 {
69    fprintf(stderr, _(
70 PROG_COPYRIGHT
71 "\n%sVersion: %s (%s)\n\n"
72 "Usage: bdirjson [<options>] [config_file]\n"
73 "       -r <res>    get resource type <res>\n"
74 "       -n <name>   get resource <name>\n"
75 "       -l <dirs>   get only directives matching dirs (use with -r)\n"
76 "       -D          get only data\n"
77 "       -R          do not apply JobDefs to Job\n"
78 "       -c <file>   set configuration file to file\n"
79 "       -d <nn>     set debug level to <nn>\n"
80 "       -dt         print timestamp in debug output\n"
81 "       -t          test - read configuration and exit\n"
82 "       -s          output in show text format\n"
83 "       -v          verbose user messages\n"
84 "       -?          print this message.\n"
85 "\n"), 2012, "", VERSION, BDATE);
86
87    exit(1);
88 }
89
90 typedef struct
91 {
92    /* default                   { { "Director": { "Name": aa, ...} }, { "Job": {..} */
93    bool do_list;             /* [ {}, {}, ..] or { "aa": {}, "bb": {}, ...} */
94    bool do_one;              /* { "Name": "aa", "Description": "test, ... } */
95    bool do_only_data;        /* [ {}, {}, {}, ] */
96    char *resource_type;
97    char *resource_name;
98    regex_t directive_reg;
99 } display_filter;
100
101 /* Forward referenced subroutines */
102 void terminate_dird(int sig);
103 static bool check_resources(bool config_test);
104 static void sendit(void *ua, const char *fmt, ...);
105 static void dump_json(display_filter *filter);
106
107 /*********************************************************************
108  *
109  *         Bacula Director conf to Json
110  *
111  */
112 int main (int argc, char *argv[])
113 {
114    int ch;
115    bool test_config = false;
116    bool apply_jobdefs = true;
117    bool do_show_format = false;
118    display_filter filter;
119    memset(&filter, 0, sizeof(filter));
120
121    setlocale(LC_ALL, "");
122    bindtextdomain("bacula", LOCALEDIR);
123    textdomain("bacula");
124
125    my_name_is(argc, argv, "bacula-dir");
126    init_msg(NULL, NULL);
127
128    while ((ch = getopt(argc, argv, "RCDc:d:stv?l:r:n:")) != -1) {
129       switch (ch) {
130       case 'R':
131          apply_jobdefs = false;
132          break;
133
134       case 'D':
135          filter.do_only_data = true;
136          break;
137
138       case 'l':
139          /* Might use something like -l '^(Name|Description)$' */
140          filter.do_list = true;
141          if (regcomp(&filter.directive_reg, optarg, REG_EXTENDED) != 0) {
142             Jmsg((JCR *)NULL, M_ERROR_TERM, 0,
143                  _("Please use valid -l argument: %s\n"), optarg);
144          }
145          break;
146
147       case 'r':
148          filter.resource_type = optarg;
149          break;
150
151       case 'n':
152          filter.resource_name = optarg;
153          break;
154
155       case 'c':                    /* specify config file */
156          if (configfile != NULL) {
157             free(configfile);
158          }
159          configfile = bstrdup(optarg);
160          break;
161
162       case 'd':                    /* set debug level */
163          if (*optarg == 't') {
164             dbg_timestamp = true;
165          } else {
166             debug_level = atoi(optarg);
167             if (debug_level <= 0) {
168                debug_level = 1;
169             }
170          }
171          Dmsg1(10, "Debug level = %d\n", debug_level);
172          break;
173
174       case 's':                    /* Show text format */
175          do_show_format = true;
176          break;
177
178       case 't':                    /* test config */
179          test_config = true;
180          break;
181
182       case 'v':                    /* verbose */
183          verbose++;
184          break;
185
186       case '?':
187       default:
188          usage();
189
190       }
191    }
192    argc -= optind;
193    argv += optind;
194
195
196    if (argc) {
197       if (configfile != NULL) {
198          free(configfile);
199       }
200       configfile = bstrdup(*argv);
201       argc--;
202       argv++;
203    }
204    if (argc) {
205       usage();
206    }
207
208    if (filter.do_list && !filter.resource_type) {
209       usage();
210    }
211
212    if (filter.resource_type && filter.resource_name) {
213       filter.do_one = true;
214    }
215
216    if (configfile == NULL || configfile[0] == 0) {
217       configfile = bstrdup(CONFIG_FILE);
218    }
219
220    if (test_config && verbose > 0) {
221       char buf[1024];
222       find_config_file(configfile, buf, sizeof(buf));
223       sendit(NULL, "config_file=%s\n", buf);
224    }
225
226    config = New(CONFIG());
227    config->encode_password(false);
228    parse_dir_config(config, configfile, M_ERROR_TERM);
229
230    /* TODO: If we run check_resources, jobdefs will be copied to Job, and the job resource
231     *  will no longer be the real job...
232     */
233    if (!check_resources(apply_jobdefs)) {
234       Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
235    }
236
237    if (test_config) {
238       terminate_dird(0);
239    }
240
241    my_name_is(0, NULL, director->name());    /* set user defined name */
242
243    if (do_show_format) {
244       /* Do show output in text */
245       for (int i=r_first; i<=r_last; i++) {
246          dump_each_resource(i, sendit, NULL);
247       }
248    } else {
249       dump_json(&filter);
250    }
251
252    if (filter.do_list) {
253       regfree(&filter.directive_reg);
254    }
255
256    terminate_dird(0);
257
258    return 0;
259 }
260
261 /* Cleanup and then exit */
262 void terminate_dird(int sig)
263 {
264    static bool already_here = false;
265
266    if (already_here) {                /* avoid recursive temination problems */
267       bmicrosleep(2, 0);              /* yield */
268       exit(1);
269    }
270    already_here = true;
271    debug_level = 0;                   /* turn off debug */
272    if (configfile != NULL) {
273       free(configfile);
274    }
275    if (debug_level > 5) {
276       print_memory_pool_stats();
277    }
278    if (config) {
279       delete config;
280       config = NULL;
281    }
282    term_msg();
283    free(res_head);
284    res_head = NULL;
285    close_memory_pool();               /* release free memory in pool */
286    //sm_dump(false);
287    exit(sig);
288 }
289
290
291 static void display_jobtype(HPKT &hpkt)
292 {
293    int i;
294    for (i=0; jobtypes[i].type_name; i++) {
295       if (*(int32_t *)(hpkt.ritem->value) == jobtypes[i].job_type) {
296          sendit(NULL, "\n    \"%s\": %s", hpkt.ritem->name,
297             quote_string(hpkt.edbuf, jobtypes[i].type_name));
298          return;
299       }
300    }
301 }
302
303 static void display_label(HPKT &hpkt)
304 {
305    int i;
306    for (i=0; tapelabels[i].name; i++) {
307       if (*(int32_t *)(hpkt.ritem->value) == tapelabels[i].token) {
308          sendit(NULL, "\n    \"%s\": %s", hpkt.ritem->name,
309             quote_string(hpkt.edbuf, tapelabels[i].name));
310          return;
311       }
312    }
313 }
314
315 static void display_joblevel(HPKT &hpkt)
316 {
317    int i;
318    for (i=0; joblevels[i].level_name; i++) {
319       if (*(int32_t *)(hpkt.ritem->value) == joblevels[i].level) {
320          sendit(NULL, "\n    \"%s\": %s", hpkt.ritem->name,
321             quote_string(hpkt.edbuf, joblevels[i].level_name));
322          return;
323       }
324    }
325 }
326
327 static void display_replace(HPKT &hpkt)
328 {
329    int i;
330    for (i=0; ReplaceOptions[i].name; i++) {
331       if (*(int32_t *)(hpkt.ritem->value) == ReplaceOptions[i].token) {
332          sendit(NULL, "\n    \"%s\": %s", hpkt.ritem->name,
333             quote_string(hpkt.edbuf, ReplaceOptions[i].name));
334          return;
335       }
336    }
337 }
338
339 static void display_migtype(HPKT &hpkt)
340 {
341    int i;
342    for (i=0; migtypes[i].type_name; i++) {
343       if (*(int32_t *)(hpkt.ritem->value) == migtypes[i].job_type) {
344          sendit(NULL, "\n    \"%s\": %s", hpkt.ritem->name,
345             quote_string(hpkt.edbuf, migtypes[i].type_name));
346          return;
347       }
348    }
349 }
350
351 static void display_actiononpurge(HPKT &hpkt)
352 {
353    sendit(NULL, "\n    \"%s\":", hpkt.ritem->name);
354    if (*(uint32_t *)(hpkt.ritem->value) | ON_PURGE_TRUNCATE) {
355       sendit(NULL, "\"Truncate\"");
356    } else {
357       sendit(NULL, "null");
358    }
359 }
360
361 static void display_acl(HPKT &hpkt)
362 {
363    sendit(NULL, "\n    \"%s\":", hpkt.ritem->name);
364    hpkt.list = ((alist **)hpkt.ritem->value)[hpkt.ritem->code];
365    display_alist(hpkt);
366 }
367
368
369 static void display_options(HPKT &hpkt, INCEXE *ie)
370 {
371    char *elt;
372    bool first_opt = true;
373    bool first_dir;
374    int i, j, k;
375    alist *list;
376
377    sendit(NULL, "      \"Options\": [ \n       {\n");
378    for (i=0; i<ie->num_opts; i++) {
379       FOPTS *fo = ie->opts_list[i];
380       if (!first_opt) {
381          sendit(NULL, ",\n       {\n");
382       }
383       first_dir = true;
384       for (j=0; options_items[j].name; j++) {
385          if (options_items[j].handler == store_regex) {
386             switch (options_items[j].code) {
387             case 1:  /* RegexDir */
388                list = &fo->regexdir;
389                break;
390             case 2:    /* RegexFile */
391                list = &fo->regexfile;
392                break;
393             default:
394                list = &fo->regex;
395                break;
396             }
397             if (list->size() > 0) {
398                if (!first_dir) {
399                   sendit(NULL, ",\n");
400                }
401                sendit(NULL, "         \"%s\":", options_items[j].name);
402                hpkt.list = list;
403                display_alist(hpkt);
404                first_dir = false;
405                first_opt = false;
406             }
407          } else if (options_items[j].handler == store_wild) {
408             switch (options_items[j].code) {
409             case 1:  /* WildDir */
410                list = &fo->wilddir;
411                break;
412             case 2:    /* WildFile */
413                /*
414                 * Note: There used to be an enhanced wild card feature,
415                 *  which was not documented so it is removed, and
416                 *  apparently the wildfile patterns are stored in the
417                 *  wildbase list, so we dump it here.
418                 * The old enhanced wild implementation appears to be poorly
419                 *  done, because either there should be two clearly named
420                 *  lists, or one with everything.
421                 */
422                /* We copy one list to the other, else we may print two
423                 * times the WildFile list. I don't know how, but sometime
424                 * the two lists contain elements.
425                 */
426                list = &fo->wildfile;
427                foreach_alist(elt, list) {
428                   fo->wildbase.append(bstrdup(elt));
429                }
430                list = &fo->wildbase;
431                break;
432             default:
433                list = &fo->wild;
434                break;
435             }
436             if (list->size() > 0) {
437                if (!first_dir) {
438                   sendit(NULL, ",\n");
439                }
440                sendit(NULL, "         \"%s\":", options_items[j].name);
441                hpkt.list = list;
442                display_alist(hpkt);
443                first_dir = false;
444                first_opt = false;
445             }
446          } else if (options_items[j].handler == store_base) {
447             list = &fo->base;
448             if (list->size() > 0) {
449                if (!first_dir) {
450                   sendit(NULL, ",\n");
451                }
452                sendit(NULL, "         \"%s\":", options_items[j].name);
453                hpkt.list = list;
454                display_alist(hpkt);
455                first_dir = false;
456                first_opt = false;
457             }
458          } else if (options_items[j].handler == store_opts) {
459             bool found = false;
460             if (bit_is_set(options_items[j].flags, ie->opt_present)) {
461                for (k=0; FS_options[k].name; k++) {
462                   if (FS_options[k].keyword == (int)options_items[j].flags) {
463                      char lopts[100];
464                      strip_long_opts(lopts, fo->opts);
465                      if (strstr(lopts, FS_options[k].option)) {
466                         if (!first_dir) {
467                            sendit(NULL, ",\n");
468                         }
469                         sendit(NULL, "         \"%s\": %s", options_items[j].name,
470                            quote_string(hpkt.edbuf, FS_options[k].name));
471                         found = true;
472                         break;
473                      }
474                   }
475                }
476                if (found) {
477                   first_dir = false;
478                   first_opt = false;
479                }
480             }
481          } else if (options_items[j].handler == store_lopts) {
482             bool found = false;
483             if (bit_is_set(options_items[j].flags, ie->opt_present)) {
484                char *pos;
485                /* Search long_options for code (V, J, C, P) */
486                if ((pos=strchr(fo->opts, options_items[j].code))) {
487                   char lopts[100];
488                   char *end, bkp;
489                   pos++;                   /* point to beginning of options */
490                   bstrncpy(lopts, pos, sizeof(lopts));
491                   /* Now terminate at first : */
492                   end = strchr(pos, ':');
493                   if (end) {
494                      bkp = *end;     /* save the original char */
495                      *end = 0;       /* terminate this string */
496                   }
497                   if (!first_dir) {
498                      sendit(NULL, ",\n");
499                   }
500                   sendit(NULL, "         \"%s\": %s", options_items[j].name,
501                      quote_string(hpkt.edbuf, pos));
502                   found = true;
503                   if (end) {    /* Still have other options to parse */
504                      *end = bkp;
505                   }
506                }
507                if (found) {
508                   first_dir = false;
509                   first_opt = false;
510                }
511             }
512          } else if (options_items[j].handler == store_plugin) {
513             if (fo->plugin) {
514                if (!first_dir) {
515                   sendit(NULL, ",\n");
516                }
517                sendit(NULL, "         \"%s\": %s", options_items[j].name,
518                   quote_string(hpkt.edbuf, fo->plugin));
519                first_dir = false;
520                first_opt = false;
521             }
522          } else if (options_items[j].handler == store_fstype) {
523             list = &fo->fstype;
524             if (list->size() > 0) {
525                if (!first_dir) {
526                   sendit(NULL, ",\n");
527                }
528                sendit(NULL, "         \"%s\":", options_items[j].name);
529                hpkt.list = list;
530                display_alist(hpkt);
531                first_dir = false;
532                first_opt = false;
533             }
534          } else if (options_items[j].handler == store_drivetype) {
535             list = &fo->drivetype;
536             if (list->size() > 0) {
537                if (!first_dir) {
538                   sendit(NULL, ",\n");
539                }
540                sendit(NULL, "         \"%s\":", options_items[j].name);
541                hpkt.list = list;
542                display_alist(hpkt);
543                first_dir = false;
544                first_opt = false;
545             }
546          }
547       }
548       sendit(NULL, "\n       }");
549    }
550    sendit(NULL, "\n      ]");
551 }
552
553 /*
554  * Include or Exclude in a FileSet
555  * TODO: Not working with multiple Include{}
556  *    O M
557  *    N
558  *    I /tmp/regress/build
559  *    N
560  *    O Z1
561  *    N
562  *    I /tmp
563  *    N
564  */
565 static void display_include_exclude(HPKT &hpkt)
566 {
567    bool first_dir;
568    int i, j;
569    FILESET *fs = (FILESET *)hpkt.res;
570
571    if (hpkt.ritem->code == 0) { /* Include */
572       INCEXE *ie;
573       sendit(NULL, "\n    \"%s\": [{\n", hpkt.ritem->name);
574       for (j=0; j<fs->num_includes; j++) {
575          if (j > 0) {
576             sendit(NULL, ",\n    {\n");
577          }
578          first_dir = true;
579          ie = fs->include_items[j];
580          for (i=0; newinc_items[i].name; i++) {
581             if (strcasecmp(newinc_items[i].name, "File") == 0) {
582                if (!first_dir) {
583                   sendit(NULL, ",\n");
584                }
585                sendit(NULL, "      \"%s\":", newinc_items[i].name);
586                first_dir = false;
587                hpkt.list = &ie->name_list;
588                display_alist(hpkt);
589             } if (strcasecmp(newinc_items[i].name, "Plugin") == 0 &&
590                   ie->plugin_list.size() > 0) {
591                if (!first_dir) {
592                   sendit(NULL, ",\n");
593                }
594                sendit(NULL, "      \"%s\":", newinc_items[i].name);
595                first_dir = false;
596                hpkt.list = &ie->plugin_list;
597                display_alist(hpkt);
598             } if (strcasecmp(newinc_items[i].name, "Options") == 0 &&
599                   ie->num_opts > 0) {
600                if (!first_dir) {
601                   sendit(NULL, ",\n");
602                }
603                display_options(hpkt, ie);
604             } if (strcasecmp(newinc_items[i].name, "ExcludeDirContaining") == 0 &&
605                   ie->ignoredir) {
606                if (!first_dir) {
607                   sendit(NULL, ",\n");
608                }
609                sendit(NULL, "      \"%s\": %s ", newinc_items[i].name,
610                   quote_string(hpkt.edbuf, ie->ignoredir));
611                first_dir = false;
612             }
613          }
614          sendit(NULL, "\n    }");
615       }
616       sendit(NULL, "]");
617    } else {
618       /* Exclude */
619       sendit(NULL, "\n    \"%s\": {\n", hpkt.ritem->name);
620       first_dir = true;
621       for (int i=0; newinc_items[i].name; i++) {
622          INCEXE *ie;
623          if (strcasecmp(newinc_items[i].name, "File") == 0) {
624             if (!first_dir) {
625                sendit(NULL, ",\n");
626             }
627             sendit(NULL, "      \"%s\": ", newinc_items[i].name);
628             first_dir = false;
629             ie = fs->exclude_items[0];
630             hpkt.list = &ie->name_list;
631             display_alist(hpkt);
632          }
633       }
634       sendit(NULL, "\n    }");
635    }
636 }
637
638 static bool display_runscript(HPKT &hpkt)
639 {
640    RUNSCRIPT *script;
641    RUNSCRIPT *def = new_runscript();
642    alist **runscripts = (alist **)(hpkt.ritem->value) ;
643    bool first=true;
644
645    if (!*runscripts || (*runscripts)->size() == 0) {
646       return false;
647    }
648
649    sendit(NULL, "\n    \"Runscript\": [\n");
650
651    foreach_alist(script, *runscripts) {
652       if (first) {
653          sendit(NULL, "      {\n");
654       } else {
655          sendit(NULL, ",\n      {\n");
656       }
657       if (script->when == SCRIPT_Any) {
658          sendit(NULL, "        \"RunsWhen\": \"Any\",\n");
659
660       } else if (script->when == SCRIPT_After) {
661          sendit(NULL, "        \"RunsWhen\": \"After\",\n");
662
663       } else if (script->when == SCRIPT_Before) {
664          sendit(NULL, "        \"RunsWhen\": \"Before\",\n");
665
666       } else if (script->when == SCRIPT_AfterVSS) {
667          sendit(NULL, "        \"RunsWhen\": \"AfterVSS\",\n");
668       }
669
670       if (script->fail_on_error != def->fail_on_error) {
671          sendit(NULL, "        \"FailJobOnError\": %s,\n", script->fail_on_error?"true":"false");
672       }
673
674       if (script->on_success != def->on_success) {
675          sendit(NULL, "        \"RunsOnSuccess\": %s,\n", script->on_success?"true":"false");
676       }
677
678       if (script->on_failure != def->on_failure) {
679          sendit(NULL, "        \"RunsOnFailure\": %s,\n", script->on_failure?"true":"false");
680       }
681
682       if (script->is_local()) {
683          sendit(NULL, "        \"RunsOnClient\": false,\n");
684       }
685
686       if (script->command) {
687          sendit(NULL, "        \"%s\": %s\n",
688                 (script->cmd_type == SHELL_CMD)?"Command":"Console",
689                 quote_string(hpkt.edbuf, script->command));
690       }
691       sendit(NULL, "      }");
692       first = false;
693    }
694
695    sendit(NULL, "\n    ]\n");
696    free_runscript(def);
697    return true;
698 }
699
700 static void display_run(HPKT &hpkt)
701 {
702    int i, j;
703    RUN **prun = (RUN **)hpkt.ritem->value;
704    RUN *run = *prun;
705    bool first = true;
706    bool first_run = true;
707    RES *res;
708
709    sendit(NULL, "\n    \"%s\": [\n", hpkt.ritem->name);
710    for ( ; run; run=run->next) {
711       if (!first_run) sendit(NULL, ",\n");
712       first_run = false;
713       first = true;
714       sendit(NULL, "     {\n");
715       /* First do override fields */
716       for (i=0; RunFields[i].name; i++) {
717          switch (RunFields[i].token) {
718          case 'f':  /* FullPool */
719             if (run->full_pool) {
720                res = (RES *)run->full_pool;
721                if (!first) sendit(NULL, ",\n");
722                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
723                      quote_string(hpkt.edbuf, res->name));
724                first = false;
725             }
726             break;
727          case 'i':  /* IncrementalPool */
728             if (run->inc_pool) {
729                res = (RES *)run->inc_pool;
730                if (!first) sendit(NULL, ",\n");
731                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
732                      quote_string(hpkt.edbuf, res->name));
733                first = false;
734             }
735             break;
736          case 'd':  /* Differential Pool */
737             if (run->diff_pool) {
738                res = (RES *)run->diff_pool;
739                if (!first) sendit(NULL, ",\n");
740                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
741                      quote_string(hpkt.edbuf, res->name));
742                first = false;
743             }
744             break;
745          case 'N':  /* Next Pool */
746             if (run->next_pool) {
747                res = (RES *)run->next_pool;
748                if (!first) sendit(NULL, ",\n");
749                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
750                      quote_string(hpkt.edbuf, res->name));
751                first = false;
752             }
753             break;
754          case 'L':  /* Level */
755             /* TODO: It's not always set, only when having Level= in the line */
756             //if (run->level_set) {
757                for (j=0; joblevels[j].level_name; j++) {
758                   if ((int)run->level == joblevels[j].level) {
759                      if (!first) sendit(NULL, ",\n");
760                      sendit(NULL, "      \"%s\": \"%s\"", RunFields[i].name,
761                         joblevels[j].level_name);
762                      first = false;
763                   }
764                }
765             //}
766             break;
767          case 'P':  /* Pool */
768             if (run->pool) {
769                res = (RES *)run->pool;
770                if (!first) sendit(NULL, ",\n");
771                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
772                      quote_string(hpkt.edbuf, res->name));
773                first = false;
774             }
775             break;
776          case 'S':  /* Storage */
777             if (run->storage) {
778                res = (RES *)run->storage;
779                if (!first) sendit(NULL, ",\n");
780                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
781                      quote_string(hpkt.edbuf, res->name));
782                first = false;
783             }
784             break;
785          case 'M':  /* Messages */
786             if (run->msgs) {
787                res = (RES *)run->msgs;
788                if (!first) sendit(NULL, ",\n");
789                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
790                      quote_string(hpkt.edbuf, res->name));
791                first = false;
792             }
793             break;
794          case 'p':  /* priority */
795             if (run->priority_set) {
796                if (!first) sendit(NULL, ",\n");
797                sendit(NULL, "      \"%s\": %d", RunFields[i].name,
798                      run->Priority);
799                first = false;
800             }
801             break;
802          case 's':  /* Spool Data */
803             if (run->spool_data_set) {
804                if (!first) sendit(NULL, ",\n");
805                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
806                      run->spool_data?"true":"false");
807                first = false;
808             }
809             break;
810          case 'W':  /* Write Part After Job */
811             if (run->write_part_after_job_set) {
812                if (!first) sendit(NULL, ",\n");
813                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
814                      run->write_part_after_job?"true":"false");
815                first = false;
816             }
817             break;
818          case 'm':  /* MaxRunScheduledTime */
819             if (run->MaxRunSchedTime_set) {
820                if (!first) sendit(NULL, ",\n");
821                sendit(NULL, "      \"%s\": %lld", RunFields[i].name,
822                      run->MaxRunSchedTime);
823                first = false;
824             }
825             break;
826          case 'a':  /* Accurate */
827             if (run->accurate_set) {
828                if (!first) sendit(NULL, ",\n");
829                sendit(NULL, "      \"%s\": %s", RunFields[i].name,
830                      run->accurate?"true":"false");
831                first = false;
832             }
833             break;
834          default:
835             break;
836          }
837       } /* End all RunFields (overrides) */
838       /* Now handle timing */
839       if (run->minute) {
840          if (!first) sendit(NULL, ",\n");
841          sendit(NULL, "      \"Minute\": %d", run->minute);
842          first = false;
843       }
844       if (byte_is_set(run->hour, sizeof(run->hour))) {
845          if (!first) sendit(NULL, ",\n");
846          sendit(NULL, "      \"Hour\":");
847          display_bit_array(run->hour, 24);
848          first = false;
849       }
850       /* bit 32 is used to store the keyword LastDay, so we look up to 0-31 */
851       if (byte_is_set(run->mday, sizeof(run->mday) - 1)) {
852          if (!first) sendit(NULL, ",\n");
853          sendit(NULL, "      \"Day\":");
854          display_bit_array(run->mday, 31);
855          first = false;
856       }
857       if (run->last_day_set) {
858          if (!first) sendit(NULL, ",\n");
859          sendit(NULL, "      \"LastDay\": 1");
860          first = false;
861       }
862       if (byte_is_set(run->month, sizeof(run->month))) {
863          if (!first) sendit(NULL, ",\n");
864          sendit(NULL, "      \"Month\":");
865          display_bit_array(run->month, 12);
866          first = false;
867       }
868       if (byte_is_set(run->wday, sizeof(run->wday))) {
869          if (!first) sendit(NULL, ",\n");
870          sendit(NULL, "      \"DayOfWeek\":");
871          display_bit_array(run->wday, 7);
872          first = false;
873       }
874       if (byte_is_set(run->wom, sizeof(run->wom))) {
875          if (!first) sendit(NULL, ",\n");
876          sendit(NULL, "      \"WeekOfMonth\":");
877          display_bit_array(run->wom, 6);
878          first = false;
879       }
880       if (byte_is_set(run->woy, sizeof(run->woy))) {
881          if (!first) sendit(NULL, ",\n");
882          sendit(NULL, "      \"WeekOfYear\":");
883          display_bit_array(run->woy, 54);
884          first = false;
885       }
886       sendit(NULL, "\n     }");
887
888    } /* End this Run directive */
889    sendit(NULL, "\n    ]");
890 }
891
892 /*
893  * Dump out all resources in json format.
894  * Note!!!! This routine must be in this file rather
895  *  than in src/lib/parser_conf.c otherwise the pointers
896  *  will be all messed up.
897  */
898 static void dump_json(display_filter *filter)
899 {
900    int resinx, item, first_directive, name_pos=0;
901    bool first_res;
902    RES_ITEM *items;
903    RES *res;
904    HPKT hpkt;
905    regmatch_t pmatch[32];
906
907    init_hpkt(hpkt);
908
909    /* List resources and directives */
910    if (filter->do_only_data) {
911       /* Skip the Name */
912       sendit(NULL, "[");
913
914    /*
915     * { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... }
916     * or print a single item
917     */
918    } else if (filter->do_one || filter->do_list) {
919       sendit(NULL, "{");
920
921    } else {
922    /* [ { "Client": { "Name": "aa",.. } }, { "Director": { "Name": "bb", ... } } ]*/
923       sendit(NULL, "[");
924    }
925
926    first_res = true;
927    /* Main loop over all resources */
928    for (resinx=0; resources[resinx].items; resinx++) {
929
930       /* Skip this resource type? */
931       if (filter->resource_type &&
932           strcasecmp(filter->resource_type, resources[resinx].name) != 0) {
933          continue;
934       }
935
936       /* Loop over each resource of this type */
937       foreach_rblist(res, res_head[resinx]->res_list) {
938          hpkt.res = res;
939          items = resources[resinx].items;
940          if (!items) {
941             continue;
942          }
943
944          /* Copy the resource into res_all */
945          memcpy(&res_all, res, sizeof(res_all));
946
947          /* If needed, skip this resource type */
948          if (filter->resource_name) {
949             bool skip=true;
950             /* The Name should be at the first place, so this is not a real loop */
951             for (item=0; items[item].name; item++) {
952                if (strcasecmp(items[item].name, "Name") == 0) {
953                   if (strcmp(*(items[item].value), filter->resource_name) == 0) {
954                      skip = false;
955                   }
956                   break;
957                }
958             }
959             if (skip) {         /* The name doesn't match, so skip it */
960                continue;
961             }
962          }
963
964          if (first_res) {
965             sendit(NULL, "\n");
966          } else {
967             sendit(NULL, ",\n");
968          }
969
970          /* Find where the Name is defined, should always be 0 */
971          for (item=0; items[item].name; item++) {
972             if (strcmp(items[item].name, "Name") == 0) {
973                name_pos = item;
974                break;
975             }
976          }
977
978          if (filter->do_only_data) {
979             sendit(NULL, " {");
980
981          } else if (filter->do_one) {
982             /* Nothing to print */
983
984          /* When sending the list, the form is:
985           *  { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
986           */
987          } else if (filter->do_list) {
988             /* Search and display Name, should be the first item */
989             for (item=0; items[item].name; item++) {
990                if (strcmp(items[item].name, "Name") == 0) {
991                   sendit(NULL, "%s: {\n", quote_string(hpkt.edbuf2, *items[item].value));
992                   break;
993                }
994             }
995          } else {
996             /* Begin new resource */
997             sendit(NULL, "{\n  \"%s\": {", resources[resinx].name);
998          }
999
1000          first_res = false;
1001          first_directive = 0;
1002
1003          /*
1004           * Here we walk through a resource displaying all the
1005           *   directives and sub-resources in the resource.
1006           */
1007          for (item=0; items[item].name; item++) {
1008             /* Check user argument -l */
1009             if (filter->do_list &&
1010                 regexec(&filter->directive_reg,
1011                         items[item].name, 32, pmatch, 0) != 0)
1012             {
1013                continue;
1014             }
1015
1016             hpkt.ritem = &items[item];
1017
1018             if (bit_is_set(item, res_all.hdr.item_present)) {
1019
1020                /* Skip Directive in lowercase, but check if the next
1021                 * one is pointing to the same location (for example User and dbuser)
1022                 */
1023                if (!B_ISUPPER(*(items[item].name))) {
1024                   int i=item+1;
1025                   while(!B_ISUPPER(*(items[i].name)) && items[i].value == items[item].value) {
1026                      i++;
1027                   }
1028                   if (items[i].value == items[item].value) {
1029                      set_bit(i, res_all.hdr.item_present);
1030                   }
1031                   continue;
1032                }
1033
1034                if (first_directive++ > 0) sendit(NULL, ",");
1035                if (display_global_item(hpkt)) {
1036                   /* Fall-through wanted */
1037                } else if (items[item].handler == store_jobtype) {
1038                   display_jobtype(hpkt);
1039                } else if (items[item].handler == store_label) {
1040                   display_label(hpkt);
1041                } else if (items[item].handler == store_level) {
1042                   display_joblevel(hpkt);
1043                } else if (items[item].handler == store_replace) {
1044                   display_replace(hpkt);
1045                } else if (items[item].handler == store_migtype) {
1046                   display_migtype(hpkt);
1047                } else if (items[item].handler == store_actiononpurge) {
1048                   display_actiononpurge(hpkt);
1049                /* FileSet Include/Exclude directive */
1050                } else if (items[item].handler == store_inc) {
1051                   display_include_exclude(hpkt);
1052                } else if (items[item].handler == store_ac_res) {
1053                   display_res(hpkt);
1054                /* A different alist for each item.code */
1055                } else if (items[item].handler == store_acl) {
1056                   display_acl(hpkt);
1057                } else if (items[item].handler == store_device) {
1058                   display_alist_res(hpkt);
1059                } else if (items[item].handler == store_run) {
1060                   display_run(hpkt);
1061                } else if (items[item].handler == store_runscript) {
1062                   if (!display_runscript(hpkt)) {
1063                      first_directive = 0;  /* Do not print a comma after this empty runscript */
1064                   }
1065                } else {
1066                   sendit(NULL, "\n    \"%s\": null", items[item].name);
1067                }
1068             } else { /* end if is present */
1069                /* For some directive, the bitmap is not set (like addresses) */
1070
1071                /* Special trick for the Autochanger directive, it can be yes/no/storage */
1072                if (strcmp(resources[resinx].name, "Storage") == 0) {
1073                   if (strcmp(items[item].name, "Autochanger") == 0
1074                       && items[item].handler == store_bool /* yes or no */
1075                       && *(items[item].value) != NULL
1076                       && *(items[item-1].value) == NULL) /* The previous "Autochanger" name is not set */
1077                   {
1078                      if (first_directive++ > 0) sendit(NULL, ",");
1079                      sendit(NULL, "\n    \"Autochanger\": %s", quote_string(hpkt.edbuf2, *items[name_pos].value));
1080                   }
1081                }
1082                if (strcmp(resources[resinx].name, "Director") == 0) {
1083                   if (strcmp(items[item].name, "DirPort") == 0) {
1084                      if (get_first_port_host_order(director->DIRaddrs) != items[item].default_value) {
1085                         if (first_directive++ > 0) sendit(NULL, ",");
1086                         sendit(NULL, "\n    \"DirPort\": %d",
1087                            get_first_port_host_order(director->DIRaddrs));
1088                      }
1089
1090                   } else if (strcmp(items[item].name, "DirAddress") == 0) {
1091                      char buf[500];
1092                      get_first_address(director->DIRaddrs, buf, sizeof(buf));
1093                      if (strcmp(buf, "0.0.0.0") != 0) {
1094                         if (first_directive++ > 0) sendit(NULL, ",");
1095                         sendit(NULL, "\n    \"DirAddress\": \"%s\"", buf);
1096                      }
1097
1098                   } else if (strcmp(items[item].name, "DirSourceAddress") == 0 && director->DIRsrc_addr) {
1099                      char buf[500];
1100                      get_first_address(director->DIRsrc_addr, buf, sizeof(buf));
1101                      if (strcmp(buf, "0.0.0.0") != 0) {
1102                         if (first_directive++ > 0) sendit(NULL, ",");
1103                         sendit(NULL, "\n    \"DirSourceAddress\": \"%s\"", buf);
1104                      }
1105                   }
1106                }
1107             }
1108             if (items[item].flags & ITEM_LAST) {
1109                display_last(hpkt);    /* If last bit set always call to cleanup */
1110             }
1111          } /* loop over directive names */
1112
1113          /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... } */
1114          if (filter->do_only_data || filter->do_list) {
1115             sendit(NULL, "\n }"); /* Finish the Resource with a single } */
1116
1117          } else {
1118             if (filter->do_one) {
1119                /* don't print anything */
1120
1121             } else if (first_directive > 0) {
1122                sendit(NULL, "\n  }\n}");  /* end of resource */
1123
1124             } else {
1125                sendit(NULL, "}\n}");
1126             }
1127          }
1128       } /* End loop over all resources of this type */
1129    } /* End loop all resource types */
1130
1131    if (filter->do_only_data) {
1132       sendit(NULL, "\n]\n");
1133
1134    /* In list context, we are dealing with a hash */
1135    } else if (filter->do_one || filter->do_list) {
1136       sendit(NULL, "\n}\n");
1137
1138    } else {
1139       sendit(NULL, "\n]\n");
1140    }
1141    term_hpkt(hpkt);
1142 }
1143
1144 /*
1145  * Make a quick check to see that we have all the
1146  * resources needed.
1147  *
1148  *  **** FIXME **** this routine could be a lot more
1149  *   intelligent and comprehensive.
1150  */
1151 static bool check_resources(bool apply_jobdefs)
1152 {
1153    bool OK = true;
1154    JOB *job;
1155    bool need_tls;
1156
1157    LockRes();
1158
1159    job = (JOB *)GetNextRes(R_JOB, NULL);
1160    director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
1161    if (!director) {
1162       Jmsg(NULL, M_FATAL, 0, _("No Director resource defined in %s\n"
1163 "Without that I don't know who I am :-(\n"), configfile);
1164       OK = false;
1165    } else {
1166       set_working_directory(director->working_directory);
1167       if (!director->messages) {       /* If message resource not specified */
1168          director->messages = (MSGS *)GetNextRes(R_MSGS, NULL);
1169          if (!director->messages) {
1170             Jmsg(NULL, M_FATAL, 0, _("No Messages resource defined in %s\n"), configfile);
1171             OK = false;
1172          }
1173       }
1174       if (GetNextRes(R_DIRECTOR, (RES *)director) != NULL) {
1175          Jmsg(NULL, M_FATAL, 0, _("Only one Director resource permitted in %s\n"),
1176             configfile);
1177          OK = false;
1178       }
1179       /* tls_require implies tls_enable */
1180       if (director->tls_require) {
1181          if (have_tls) {
1182             director->tls_enable = true;
1183          } else {
1184             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1185             OK = false;
1186          }
1187       }
1188
1189       need_tls = director->tls_enable || director->tls_authenticate;
1190
1191       if (!director->tls_certfile && need_tls) {
1192          Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Director \"%s\" in %s.\n"),
1193             director->name(), configfile);
1194          OK = false;
1195       }
1196
1197       if (!director->tls_keyfile && need_tls) {
1198          Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Director \"%s\" in %s.\n"),
1199             director->name(), configfile);
1200          OK = false;
1201       }
1202
1203       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) &&
1204            need_tls && director->tls_verify_peer) {
1205          Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\" or \"TLS CA"
1206               " Certificate Dir\" are defined for Director \"%s\" in %s."
1207               " At least one CA certificate store is required"
1208               " when using \"TLS Verify Peer\".\n"),
1209               director->name(), configfile);
1210          OK = false;
1211       }
1212    }
1213
1214    /* Loop over Consoles */
1215    CONRES *cons;
1216    foreach_res(cons, R_CONSOLE) {
1217       /* tls_require implies tls_enable */
1218       if (cons->tls_require) {
1219          if (have_tls) {
1220             cons->tls_enable = true;
1221          } else {
1222             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1223             OK = false;
1224             continue;
1225          }
1226       }
1227
1228       need_tls = cons->tls_enable || cons->tls_authenticate;
1229
1230       if (!cons->tls_certfile && need_tls) {
1231          Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Console \"%s\" in %s.\n"),
1232             cons->name(), configfile);
1233          OK = false;
1234       }
1235
1236       if (!cons->tls_keyfile && need_tls) {
1237          Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Console \"%s\" in %s.\n"),
1238             cons->name(), configfile);
1239          OK = false;
1240       }
1241
1242       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir)
1243             && need_tls && cons->tls_verify_peer) {
1244          Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\" or \"TLS CA"
1245             " Certificate Dir\" are defined for Console \"%s\" in %s."
1246             " At least one CA certificate store is required"
1247             " when using \"TLS Verify Peer\".\n"),
1248             cons->name(), configfile);
1249          OK = false;
1250       }
1251       /* If everything is well, attempt to initialize our per-resource TLS context */
1252       if (OK && (need_tls || cons->tls_require)) {
1253          /* Initialize TLS context:
1254           * Args: CA certfile, CA certdir, Certfile, Keyfile,
1255           * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1256          cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1257             cons->tls_ca_certdir, cons->tls_certfile,
1258             cons->tls_keyfile, NULL, NULL, cons->tls_dhfile, cons->tls_verify_peer);
1259
1260          if (!cons->tls_ctx) {
1261             Jmsg(NULL, M_FATAL, 0, _("Failed to initialize TLS context for File daemon \"%s\" in %s.\n"),
1262                cons->name(), configfile);
1263             OK = false;
1264          }
1265       }
1266
1267    }
1268
1269    /* Loop over Clients */
1270    CLIENT *client;
1271    foreach_res(client, R_CLIENT) {
1272       /* tls_require implies tls_enable */
1273       if (client->tls_require) {
1274          if (have_tls) {
1275             client->tls_enable = true;
1276          } else {
1277             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1278             OK = false;
1279             continue;
1280          }
1281       }
1282       need_tls = client->tls_enable || client->tls_authenticate;
1283       if ((!client->tls_ca_certfile && !client->tls_ca_certdir) && need_tls) {
1284          Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1285             " or \"TLS CA Certificate Dir\" are defined for File daemon \"%s\" in %s.\n"),
1286             client->name(), configfile);
1287          OK = false;
1288       }
1289
1290    }
1291
1292    if (!job) {
1293       Jmsg(NULL, M_FATAL, 0, _("No Job records defined in %s\n"), configfile);
1294       OK = false;
1295    }
1296
1297    /* TODO: We can't really update all job, we need to show only the real configuration
1298     * and not Job+JobDefs
1299     */
1300    if (!apply_jobdefs) {
1301       UnlockRes();
1302       return OK;
1303    }
1304
1305    foreach_res(job, R_JOB) {
1306       int i;
1307
1308       if (job->jobdefs) {
1309          JOB *jobdefs = job->jobdefs;
1310          /* Handle RunScripts alists specifically */
1311          if (jobdefs->RunScripts) {
1312             RUNSCRIPT *rs, *elt;
1313
1314             if (!job->RunScripts) {
1315                job->RunScripts = New(alist(10, not_owned_by_alist));
1316             }
1317
1318             foreach_alist(rs, jobdefs->RunScripts) {
1319                elt = copy_runscript(rs);
1320                job->RunScripts->append(elt); /* we have to free it */
1321             }
1322          }
1323
1324          /* Transfer default items from JobDefs Resource */
1325          for (i=0; job_items[i].name; i++) {
1326             char **def_svalue, **svalue;  /* string value */
1327             uint32_t *def_ivalue, *ivalue;     /* integer value */
1328             bool *def_bvalue, *bvalue;    /* bool value */
1329             int64_t *def_lvalue, *lvalue; /* 64 bit values */
1330             uint32_t offset;
1331             alist **def_avalue, **avalue; /* alist value */
1332
1333             Dmsg4(1400, "Job \"%s\", field \"%s\" bit=%d def=%d\n",
1334                 job->name(), job_items[i].name,
1335                 bit_is_set(i, job->hdr.item_present),
1336                 bit_is_set(i, job->jobdefs->hdr.item_present));
1337
1338             if (!bit_is_set(i, job->hdr.item_present) &&
1339                  bit_is_set(i, job->jobdefs->hdr.item_present)) {
1340                Dmsg2(400, "Job \"%s\", field \"%s\": getting default.\n",
1341                  job->name(), job_items[i].name);
1342                offset = (char *)(job_items[i].value) - (char *)&res_all;
1343                /*
1344                 * Handle strings and directory strings
1345                 */
1346                if (job_items[i].handler == store_str ||
1347                    job_items[i].handler == store_dir) {
1348                   def_svalue = (char **)((char *)(job->jobdefs) + offset);
1349                   Dmsg5(400, "Job \"%s\", field \"%s\" def_svalue=%s item %d offset=%u\n",
1350                        job->name(), job_items[i].name, *def_svalue, i, offset);
1351                   svalue = (char **)((char *)job + offset);
1352                   if (*svalue) {
1353                      Pmsg1(000, _("Hey something is wrong. p=0x%lu\n"), *svalue);
1354                   }
1355                   *svalue = bstrdup(*def_svalue);
1356                   set_bit(i, job->hdr.item_present);
1357                /*
1358                 * Handle resources
1359                 */
1360                } else if (job_items[i].handler == store_res) {
1361                   def_svalue = (char **)((char *)(job->jobdefs) + offset);
1362                   Dmsg4(400, "Job \"%s\", field \"%s\" item %d offset=%u\n",
1363                        job->name(), job_items[i].name, i, offset);
1364                   svalue = (char **)((char *)job + offset);
1365                   if (*svalue) {
1366                      Pmsg1(000, _("Hey something is wrong. p=0x%lu\n"), *svalue);
1367                   }
1368                   *svalue = *def_svalue;
1369                   set_bit(i, job->hdr.item_present);
1370                /*
1371                 * Handle alist resources
1372                 */
1373                } else if (job_items[i].handler == store_alist_str) {
1374                   char *elt;
1375
1376                   def_avalue = (alist **)((char *)(job->jobdefs) + offset);
1377                   avalue = (alist **)((char *)job + offset);
1378                   
1379                   *avalue = New(alist(10, owned_by_alist));
1380
1381                   foreach_alist(elt, (*def_avalue)) {
1382                      (*avalue)->append(bstrdup(elt));
1383                   }
1384                   set_bit(i, job->hdr.item_present);
1385                   
1386                } else if (job_items[i].handler == store_alist_res) {
1387                   void *elt;
1388
1389                   def_avalue = (alist **)((char *)(job->jobdefs) + offset);
1390                   avalue = (alist **)((char *)job + offset);
1391                   
1392                   *avalue = New(alist(10, not_owned_by_alist));
1393
1394                   foreach_alist(elt, (*def_avalue)) {
1395                      (*avalue)->append(elt);
1396                   }
1397                   set_bit(i, job->hdr.item_present);
1398                /*
1399                 * Handle integer fields
1400                 *    Note, our store_bit does not handle bitmaped fields
1401                 */
1402                } else if (job_items[i].handler == store_bit     ||
1403                           job_items[i].handler == store_pint32  ||
1404                           job_items[i].handler == store_jobtype ||
1405                           job_items[i].handler == store_level   ||
1406                           job_items[i].handler == store_int32   ||
1407                           job_items[i].handler == store_size32  ||
1408                           job_items[i].handler == store_migtype ||
1409                           job_items[i].handler == store_replace) {
1410                   def_ivalue = (uint32_t *)((char *)(job->jobdefs) + offset);
1411                   Dmsg5(400, "Job \"%s\", field \"%s\" def_ivalue=%d item %d offset=%u\n",
1412                        job->name(), job_items[i].name, *def_ivalue, i, offset);
1413                   ivalue = (uint32_t *)((char *)job + offset);
1414                   *ivalue = *def_ivalue;
1415                   set_bit(i, job->hdr.item_present);
1416                /*
1417                 * Handle 64 bit integer fields
1418                 */
1419                } else if (job_items[i].handler == store_time   ||
1420                           job_items[i].handler == store_size64 ||
1421                           job_items[i].handler == store_int64) {
1422                   def_lvalue = (int64_t *)((char *)(job->jobdefs) + offset);
1423                   Dmsg5(400, "Job \"%s\", field \"%s\" def_lvalue=%" lld " item %d offset=%u\n",
1424                        job->name(), job_items[i].name, *def_lvalue, i, offset);
1425                   lvalue = (int64_t *)((char *)job + offset);
1426                   *lvalue = *def_lvalue;
1427                   set_bit(i, job->hdr.item_present);
1428                /*
1429                 * Handle bool fields
1430                 */
1431                } else if (job_items[i].handler == store_bool) {
1432                   def_bvalue = (bool *)((char *)(job->jobdefs) + offset);
1433                   Dmsg5(400, "Job \"%s\", field \"%s\" def_bvalue=%d item %d offset=%u\n",
1434                        job->name(), job_items[i].name, *def_bvalue, i, offset);
1435                   bvalue = (bool *)((char *)job + offset);
1436                   *bvalue = *def_bvalue;
1437                   set_bit(i, job->hdr.item_present);
1438
1439                } else {
1440                   Dmsg1(10, "Handler missing for job_items[%d]\n", i);
1441                   ASSERTD(0, "JobDefs -> Job handler missing\n");
1442                }
1443             }
1444          }
1445       }
1446       /*
1447        * Ensure that all required items are present
1448        */
1449       for (i=0; job_items[i].name; i++) {
1450          if (job_items[i].flags & ITEM_REQUIRED) {
1451                if (!bit_is_set(i, job->hdr.item_present)) {
1452                   Jmsg(NULL, M_ERROR_TERM, 0, _("\"%s\" directive in Job \"%s\" resource is required, but not found.\n"),
1453                     job_items[i].name, job->name());
1454                   OK = false;
1455                 }
1456          }
1457          /* If this triggers, take a look at lib/parse_conf.h */
1458          if (i >= MAX_RES_ITEMS) {
1459             Emsg0(M_ERROR_TERM, 0, _("Too many items in Job resource\n"));
1460          }
1461       }
1462       if (!job->storage && !job->pool->storage) {
1463          Jmsg(NULL, M_FATAL, 0, _("No storage specified in Job \"%s\" nor in Pool.\n"),
1464             job->name());
1465          OK = false;
1466       }
1467    } /* End loop over Job res */
1468
1469    UnlockRes();
1470    return OK;
1471 }
1472
1473 static void sendit(void *sock, const char *fmt, ...)
1474 {
1475    char buf[3000];
1476    va_list arg_ptr;
1477
1478    va_start(arg_ptr, fmt);
1479    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1480    va_end(arg_ptr);
1481    fputs(buf, stdout);
1482    fflush(stdout);
1483 }