2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Bacula Director conf to Json
23 * Kern Sibbald, September MMXII
30 /* Exported subroutines */
31 extern bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code);
33 static CONFIG *config;
35 /* Globals Exported */
36 DIRRES *director; /* Director resource */
39 char *configfile = NULL;
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[];
57 extern "C" { // work around visual compiler mangling variables
65 #define CONFIG_FILE "bacula-dir.conf" /* default configuration file */
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"
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);
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; /* [ {}, {}, {}, ] */
98 regex_t directive_reg;
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);
107 /*********************************************************************
109 * Bacula Director conf to Json
112 int main (int argc, char *argv[])
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));
121 setlocale(LC_ALL, "");
122 bindtextdomain("bacula", LOCALEDIR);
123 textdomain("bacula");
125 my_name_is(argc, argv, "bacula-dir");
126 init_msg(NULL, NULL);
128 while ((ch = getopt(argc, argv, "RCDc:d:stv?l:r:n:")) != -1) {
131 apply_jobdefs = false;
135 filter.do_only_data = true;
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);
148 filter.resource_type = optarg;
152 filter.resource_name = optarg;
155 case 'c': /* specify config file */
156 if (configfile != NULL) {
159 configfile = bstrdup(optarg);
162 case 'd': /* set debug level */
163 if (*optarg == 't') {
164 dbg_timestamp = true;
166 debug_level = atoi(optarg);
167 if (debug_level <= 0) {
171 Dmsg1(10, "Debug level = %d\n", debug_level);
174 case 's': /* Show text format */
175 do_show_format = true;
178 case 't': /* test config */
182 case 'v': /* verbose */
197 if (configfile != NULL) {
200 configfile = bstrdup(*argv);
208 if (filter.do_list && !filter.resource_type) {
212 if (filter.resource_type && filter.resource_name) {
213 filter.do_one = true;
216 if (configfile == NULL || configfile[0] == 0) {
217 configfile = bstrdup(CONFIG_FILE);
220 if (test_config && verbose > 0) {
222 find_config_file(configfile, buf, sizeof(buf));
223 sendit(NULL, "config_file=%s\n", buf);
226 config = New(CONFIG());
227 config->encode_password(false);
228 parse_dir_config(config, configfile, M_ERROR_TERM);
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...
233 if (!check_resources(apply_jobdefs)) {
234 Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
241 my_name_is(0, NULL, director->name()); /* set user defined name */
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);
252 if (filter.do_list) {
253 regfree(&filter.directive_reg);
261 /* Cleanup and then exit */
262 void terminate_dird(int sig)
264 static bool already_here = false;
266 if (already_here) { /* avoid recursive temination problems */
267 bmicrosleep(2, 0); /* yield */
271 debug_level = 0; /* turn off debug */
272 if (configfile != NULL) {
275 if (debug_level > 5) {
276 print_memory_pool_stats();
285 close_memory_pool(); /* release free memory in pool */
291 static void display_jobtype(HPKT &hpkt)
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));
303 static void display_label(HPKT &hpkt)
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));
315 static void display_joblevel(HPKT &hpkt)
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));
327 static void display_replace(HPKT &hpkt)
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));
339 static void display_migtype(HPKT &hpkt)
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));
351 static void display_actiononpurge(HPKT &hpkt)
353 sendit(NULL, "\n \"%s\":", hpkt.ritem->name);
354 if (*(uint32_t *)(hpkt.ritem->value) | ON_PURGE_TRUNCATE) {
355 sendit(NULL, "\"Truncate\"");
357 sendit(NULL, "null");
361 static void display_acl(HPKT &hpkt)
363 sendit(NULL, "\n \"%s\":", hpkt.ritem->name);
364 hpkt.list = ((alist **)hpkt.ritem->value)[hpkt.ritem->code];
369 static void display_options(HPKT &hpkt, INCEXE *ie)
372 bool first_opt = true;
377 sendit(NULL, " \"Options\": [ \n {\n");
378 for (i=0; i<ie->num_opts; i++) {
379 FOPTS *fo = ie->opts_list[i];
381 sendit(NULL, ",\n {\n");
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;
390 case 2: /* RegexFile */
391 list = &fo->regexfile;
397 if (list->size() > 0) {
401 sendit(NULL, " \"%s\":", options_items[j].name);
407 } else if (options_items[j].handler == store_wild) {
408 switch (options_items[j].code) {
409 case 1: /* WildDir */
412 case 2: /* WildFile */
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.
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.
426 list = &fo->wildfile;
427 foreach_alist(elt, list) {
428 fo->wildbase.append(bstrdup(elt));
430 list = &fo->wildbase;
436 if (list->size() > 0) {
440 sendit(NULL, " \"%s\":", options_items[j].name);
446 } else if (options_items[j].handler == store_base) {
448 if (list->size() > 0) {
452 sendit(NULL, " \"%s\":", options_items[j].name);
458 } else if (options_items[j].handler == store_opts) {
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) {
464 strip_long_opts(lopts, fo->opts);
465 if (strstr(lopts, FS_options[k].option)) {
469 sendit(NULL, " \"%s\": %s", options_items[j].name,
470 quote_string(hpkt.edbuf, FS_options[k].name));
481 } else if (options_items[j].handler == store_lopts) {
483 if (bit_is_set(options_items[j].flags, ie->opt_present)) {
485 /* Search long_options for code (V, J, C, P) */
486 if ((pos=strchr(fo->opts, options_items[j].code))) {
489 pos++; /* point to beginning of options */
490 bstrncpy(lopts, pos, sizeof(lopts));
491 /* Now terminate at first : */
492 end = strchr(pos, ':');
494 bkp = *end; /* save the original char */
495 *end = 0; /* terminate this string */
500 sendit(NULL, " \"%s\": %s", options_items[j].name,
501 quote_string(hpkt.edbuf, pos));
503 if (end) { /* Still have other options to parse */
512 } else if (options_items[j].handler == store_plugin) {
517 sendit(NULL, " \"%s\": %s", options_items[j].name,
518 quote_string(hpkt.edbuf, fo->plugin));
522 } else if (options_items[j].handler == store_fstype) {
524 if (list->size() > 0) {
528 sendit(NULL, " \"%s\":", options_items[j].name);
534 } else if (options_items[j].handler == store_drivetype) {
535 list = &fo->drivetype;
536 if (list->size() > 0) {
540 sendit(NULL, " \"%s\":", options_items[j].name);
548 sendit(NULL, "\n }");
550 sendit(NULL, "\n ]");
554 * Include or Exclude in a FileSet
555 * TODO: Not working with multiple Include{}
558 * I /tmp/regress/build
565 static void display_include_exclude(HPKT &hpkt)
569 FILESET *fs = (FILESET *)hpkt.res;
571 if (hpkt.ritem->code == 0) { /* Include */
573 sendit(NULL, "\n \"%s\": [{\n", hpkt.ritem->name);
574 for (j=0; j<fs->num_includes; j++) {
576 sendit(NULL, ",\n {\n");
579 ie = fs->include_items[j];
580 for (i=0; newinc_items[i].name; i++) {
581 if (strcasecmp(newinc_items[i].name, "File") == 0) {
585 sendit(NULL, " \"%s\":", newinc_items[i].name);
587 hpkt.list = &ie->name_list;
589 } if (strcasecmp(newinc_items[i].name, "Plugin") == 0 &&
590 ie->plugin_list.size() > 0) {
594 sendit(NULL, " \"%s\":", newinc_items[i].name);
596 hpkt.list = &ie->plugin_list;
598 } if (strcasecmp(newinc_items[i].name, "Options") == 0 &&
603 display_options(hpkt, ie);
604 } if (strcasecmp(newinc_items[i].name, "ExcludeDirContaining") == 0 &&
609 sendit(NULL, " \"%s\": %s ", newinc_items[i].name,
610 quote_string(hpkt.edbuf, ie->ignoredir));
614 sendit(NULL, "\n }");
619 sendit(NULL, "\n \"%s\": {\n", hpkt.ritem->name);
621 for (int i=0; newinc_items[i].name; i++) {
623 if (strcasecmp(newinc_items[i].name, "File") == 0) {
627 sendit(NULL, " \"%s\": ", newinc_items[i].name);
629 ie = fs->exclude_items[0];
630 hpkt.list = &ie->name_list;
634 sendit(NULL, "\n }");
638 static bool display_runscript(HPKT &hpkt)
641 RUNSCRIPT *def = new_runscript();
642 alist **runscripts = (alist **)(hpkt.ritem->value) ;
645 if (!*runscripts || (*runscripts)->size() == 0) {
649 sendit(NULL, "\n \"Runscript\": [\n");
651 foreach_alist(script, *runscripts) {
653 sendit(NULL, " {\n");
655 sendit(NULL, ",\n {\n");
657 if (script->when == SCRIPT_Any) {
658 sendit(NULL, " \"RunsWhen\": \"Any\",\n");
660 } else if (script->when == SCRIPT_After) {
661 sendit(NULL, " \"RunsWhen\": \"After\",\n");
663 } else if (script->when == SCRIPT_Before) {
664 sendit(NULL, " \"RunsWhen\": \"Before\",\n");
666 } else if (script->when == SCRIPT_AfterVSS) {
667 sendit(NULL, " \"RunsWhen\": \"AfterVSS\",\n");
670 if (script->fail_on_error != def->fail_on_error) {
671 sendit(NULL, " \"FailJobOnError\": %s,\n", script->fail_on_error?"true":"false");
674 if (script->on_success != def->on_success) {
675 sendit(NULL, " \"RunsOnSuccess\": %s,\n", script->on_success?"true":"false");
678 if (script->on_failure != def->on_failure) {
679 sendit(NULL, " \"RunsOnFailure\": %s,\n", script->on_failure?"true":"false");
682 if (script->is_local()) {
683 sendit(NULL, " \"RunsOnClient\": false,\n");
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));
695 sendit(NULL, "\n ]\n");
700 static void display_run(HPKT &hpkt)
703 RUN **prun = (RUN **)hpkt.ritem->value;
706 bool first_run = true;
709 sendit(NULL, "\n \"%s\": [\n", hpkt.ritem->name);
710 for ( ; run; run=run->next) {
711 if (!first_run) sendit(NULL, ",\n");
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));
727 case 'i': /* IncrementalPool */
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));
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));
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));
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);
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));
776 case 'S': /* 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));
785 case 'M': /* Messages */
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));
794 case 'p': /* priority */
795 if (run->priority_set) {
796 if (!first) sendit(NULL, ",\n");
797 sendit(NULL, " \"%s\": %d", RunFields[i].name,
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");
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");
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);
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");
837 } /* End all RunFields (overrides) */
838 /* Now handle timing */
840 if (!first) sendit(NULL, ",\n");
841 sendit(NULL, " \"Minute\": %d", run->minute);
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);
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);
857 if (run->last_day_set) {
858 if (!first) sendit(NULL, ",\n");
859 sendit(NULL, " \"LastDay\": 1");
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);
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);
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, 5);
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);
886 sendit(NULL, "\n }");
888 } /* End this Run directive */
889 sendit(NULL, "\n ]");
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.
898 static void dump_json(display_filter *filter)
900 int resinx, item, first_directive;
905 regmatch_t pmatch[32];
909 /* List resources and directives */
910 if (filter->do_only_data) {
915 * { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... }
916 * or print a single item
918 } else if (filter->do_one || filter->do_list) {
922 /* [ { "Client": { "Name": "aa",.. } }, { "Director": { "Name": "bb", ... } } ]*/
927 /* Main loop over all resources */
928 for (resinx=0; resources[resinx].items; resinx++) {
930 /* Skip this resource type? */
931 if (filter->resource_type &&
932 strcasecmp(filter->resource_type, resources[resinx].name) != 0) {
936 /* Loop over each resource of this type */
937 foreach_rblist(res, res_head[resinx]->res_list) {
939 items = resources[resinx].items;
944 /* Copy the resource into res_all */
945 memcpy(&res_all, res, sizeof(res_all));
947 /* If needed, skip this resource type */
948 if (filter->resource_name) {
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) {
959 if (skip) { /* The name doesn't match, so skip it */
970 if (filter->do_only_data) {
973 } else if (filter->do_one) {
974 /* Nothing to print */
976 /* When sending the list, the form is:
977 * { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
979 } else if (filter->do_list) {
980 /* Search and display Name, should be the first item */
981 for (item=0; items[item].name; item++) {
982 if (strcmp(items[item].name, "Name") == 0) {
983 sendit(NULL, "%s: {\n", quote_string(hpkt.edbuf2, *items[item].value));
988 /* Begin new resource */
989 sendit(NULL, "{\n \"%s\": {", resources[resinx].name);
996 * Here we walk through a resource displaying all the
997 * directives and sub-resources in the resource.
999 for (item=0; items[item].name; item++) {
1000 /* Check user argument -l */
1001 if (filter->do_list &&
1002 regexec(&filter->directive_reg,
1003 items[item].name, 32, pmatch, 0) != 0)
1008 hpkt.ritem = &items[item];
1010 if (bit_is_set(item, res_all.hdr.item_present)) {
1012 /* Skip Directive in lowercase, but check if the next
1013 * one is pointing to the same location (for example User and dbuser)
1015 if (!B_ISUPPER(*(items[item].name))) {
1017 while(!B_ISUPPER(*(items[i].name)) && items[i].value == items[item].value) {
1020 if (items[i].value == items[item].value) {
1021 set_bit(i, res_all.hdr.item_present);
1026 if (first_directive++ > 0) sendit(NULL, ",");
1027 if (display_global_item(hpkt)) {
1028 /* Fall-through wanted */
1029 } else if (items[item].handler == store_jobtype) {
1030 display_jobtype(hpkt);
1031 } else if (items[item].handler == store_label) {
1032 display_label(hpkt);
1033 } else if (items[item].handler == store_level) {
1034 display_joblevel(hpkt);
1035 } else if (items[item].handler == store_replace) {
1036 display_replace(hpkt);
1037 } else if (items[item].handler == store_migtype) {
1038 display_migtype(hpkt);
1039 } else if (items[item].handler == store_actiononpurge) {
1040 display_actiononpurge(hpkt);
1041 /* FileSet Include/Exclude directive */
1042 } else if (items[item].handler == store_inc) {
1043 display_include_exclude(hpkt);
1044 } else if (items[item].handler == store_ac_res) {
1046 /* A different alist for each item.code */
1047 } else if (items[item].handler == store_acl) {
1049 } else if (items[item].handler == store_device) {
1050 display_alist_res(hpkt);
1051 } else if (items[item].handler == store_run) {
1053 } else if (items[item].handler == store_runscript) {
1054 if (!display_runscript(hpkt)) {
1055 first_directive = 0; /* Do not print a comma after this empty runscript */
1058 sendit(NULL, "\n \"%s\": null", items[item].name);
1060 } else { /* end if is present */
1061 /* For some directive, the bitmap is not set (like addresses) */
1062 if (strcmp(resources[resinx].name, "Director") == 0) {
1063 if (strcmp(items[item].name, "DirPort") == 0) {
1064 if (get_first_port_host_order(director->DIRaddrs) != items[item].default_value) {
1065 if (first_directive++ > 0) sendit(NULL, ",");
1066 sendit(NULL, "\n \"DirPort\": %d",
1067 get_first_port_host_order(director->DIRaddrs));
1070 } else if (strcmp(items[item].name, "DirAddress") == 0) {
1072 get_first_address(director->DIRaddrs, buf, sizeof(buf));
1073 if (strcmp(buf, "0.0.0.0") != 0) {
1074 if (first_directive++ > 0) sendit(NULL, ",");
1075 sendit(NULL, "\n \"DirAddress\": \"%s\"", buf);
1078 } else if (strcmp(items[item].name, "DirSourceAddress") == 0 && director->DIRsrc_addr) {
1080 get_first_address(director->DIRsrc_addr, buf, sizeof(buf));
1081 if (strcmp(buf, "0.0.0.0") != 0) {
1082 if (first_directive++ > 0) sendit(NULL, ",");
1083 sendit(NULL, "\n \"DirSourceAddress\": \"%s\"", buf);
1088 if (items[item].flags & ITEM_LAST) {
1089 display_last(hpkt); /* If last bit set always call to cleanup */
1091 } /* loop over directive names */
1093 /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... } */
1094 if (filter->do_only_data || filter->do_list) {
1095 sendit(NULL, "\n }"); /* Finish the Resource with a single } */
1098 if (filter->do_one) {
1099 /* don't print anything */
1101 } else if (first_directive > 0) {
1102 sendit(NULL, "\n }\n}"); /* end of resource */
1105 sendit(NULL, "}\n}");
1108 } /* End loop over all resources of this type */
1109 } /* End loop all resource types */
1111 if (filter->do_only_data) {
1112 sendit(NULL, "\n]\n");
1114 /* In list context, we are dealing with a hash */
1115 } else if (filter->do_one || filter->do_list) {
1116 sendit(NULL, "\n}\n");
1119 sendit(NULL, "\n]\n");
1125 * Make a quick check to see that we have all the
1128 * **** FIXME **** this routine could be a lot more
1129 * intelligent and comprehensive.
1131 static bool check_resources(bool apply_jobdefs)
1139 job = (JOB *)GetNextRes(R_JOB, NULL);
1140 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
1142 Jmsg(NULL, M_FATAL, 0, _("No Director resource defined in %s\n"
1143 "Without that I don't know who I am :-(\n"), configfile);
1146 set_working_directory(director->working_directory);
1147 if (!director->messages) { /* If message resource not specified */
1148 director->messages = (MSGS *)GetNextRes(R_MSGS, NULL);
1149 if (!director->messages) {
1150 Jmsg(NULL, M_FATAL, 0, _("No Messages resource defined in %s\n"), configfile);
1154 if (GetNextRes(R_DIRECTOR, (RES *)director) != NULL) {
1155 Jmsg(NULL, M_FATAL, 0, _("Only one Director resource permitted in %s\n"),
1159 /* tls_require implies tls_enable */
1160 if (director->tls_require) {
1162 director->tls_enable = true;
1164 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1169 need_tls = director->tls_enable || director->tls_authenticate;
1171 if (!director->tls_certfile && need_tls) {
1172 Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Director \"%s\" in %s.\n"),
1173 director->name(), configfile);
1177 if (!director->tls_keyfile && need_tls) {
1178 Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Director \"%s\" in %s.\n"),
1179 director->name(), configfile);
1183 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) &&
1184 need_tls && director->tls_verify_peer) {
1185 Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\" or \"TLS CA"
1186 " Certificate Dir\" are defined for Director \"%s\" in %s."
1187 " At least one CA certificate store is required"
1188 " when using \"TLS Verify Peer\".\n"),
1189 director->name(), configfile);
1194 /* Loop over Consoles */
1196 foreach_res(cons, R_CONSOLE) {
1197 /* tls_require implies tls_enable */
1198 if (cons->tls_require) {
1200 cons->tls_enable = true;
1202 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1208 need_tls = cons->tls_enable || cons->tls_authenticate;
1210 if (!cons->tls_certfile && need_tls) {
1211 Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Console \"%s\" in %s.\n"),
1212 cons->name(), configfile);
1216 if (!cons->tls_keyfile && need_tls) {
1217 Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Console \"%s\" in %s.\n"),
1218 cons->name(), configfile);
1222 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir)
1223 && need_tls && cons->tls_verify_peer) {
1224 Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\" or \"TLS CA"
1225 " Certificate Dir\" are defined for Console \"%s\" in %s."
1226 " At least one CA certificate store is required"
1227 " when using \"TLS Verify Peer\".\n"),
1228 cons->name(), configfile);
1231 /* If everything is well, attempt to initialize our per-resource TLS context */
1232 if (OK && (need_tls || cons->tls_require)) {
1233 /* Initialize TLS context:
1234 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1235 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1236 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1237 cons->tls_ca_certdir, cons->tls_certfile,
1238 cons->tls_keyfile, NULL, NULL, cons->tls_dhfile, cons->tls_verify_peer);
1240 if (!cons->tls_ctx) {
1241 Jmsg(NULL, M_FATAL, 0, _("Failed to initialize TLS context for File daemon \"%s\" in %s.\n"),
1242 cons->name(), configfile);
1249 /* Loop over Clients */
1251 foreach_res(client, R_CLIENT) {
1252 /* tls_require implies tls_enable */
1253 if (client->tls_require) {
1255 client->tls_enable = true;
1257 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1262 need_tls = client->tls_enable || client->tls_authenticate;
1263 if ((!client->tls_ca_certfile && !client->tls_ca_certdir) && need_tls) {
1264 Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1265 " or \"TLS CA Certificate Dir\" are defined for File daemon \"%s\" in %s.\n"),
1266 client->name(), configfile);
1273 Jmsg(NULL, M_FATAL, 0, _("No Job records defined in %s\n"), configfile);
1277 /* TODO: We can't really update all job, we need to show only the real configuration
1278 * and not Job+JobDefs
1280 if (!apply_jobdefs) {
1285 foreach_res(job, R_JOB) {
1289 JOB *jobdefs = job->jobdefs;
1290 /* Handle RunScripts alists specifically */
1291 if (jobdefs->RunScripts) {
1292 RUNSCRIPT *rs, *elt;
1294 if (!job->RunScripts) {
1295 job->RunScripts = New(alist(10, not_owned_by_alist));
1298 foreach_alist(rs, jobdefs->RunScripts) {
1299 elt = copy_runscript(rs);
1300 job->RunScripts->append(elt); /* we have to free it */
1304 /* Transfer default items from JobDefs Resource */
1305 for (i=0; job_items[i].name; i++) {
1306 char **def_svalue, **svalue; /* string value */
1307 uint32_t *def_ivalue, *ivalue; /* integer value */
1308 bool *def_bvalue, *bvalue; /* bool value */
1309 int64_t *def_lvalue, *lvalue; /* 64 bit values */
1311 alist **def_avalue, **avalue; /* alist value */
1313 Dmsg4(1400, "Job \"%s\", field \"%s\" bit=%d def=%d\n",
1314 job->name(), job_items[i].name,
1315 bit_is_set(i, job->hdr.item_present),
1316 bit_is_set(i, job->jobdefs->hdr.item_present));
1318 if (!bit_is_set(i, job->hdr.item_present) &&
1319 bit_is_set(i, job->jobdefs->hdr.item_present)) {
1320 Dmsg2(400, "Job \"%s\", field \"%s\": getting default.\n",
1321 job->name(), job_items[i].name);
1322 offset = (char *)(job_items[i].value) - (char *)&res_all;
1324 * Handle strings and directory strings
1326 if (job_items[i].handler == store_str ||
1327 job_items[i].handler == store_dir) {
1328 def_svalue = (char **)((char *)(job->jobdefs) + offset);
1329 Dmsg5(400, "Job \"%s\", field \"%s\" def_svalue=%s item %d offset=%u\n",
1330 job->name(), job_items[i].name, *def_svalue, i, offset);
1331 svalue = (char **)((char *)job + offset);
1333 Pmsg1(000, _("Hey something is wrong. p=0x%lu\n"), *svalue);
1335 *svalue = bstrdup(*def_svalue);
1336 set_bit(i, job->hdr.item_present);
1340 } else if (job_items[i].handler == store_res) {
1341 def_svalue = (char **)((char *)(job->jobdefs) + offset);
1342 Dmsg4(400, "Job \"%s\", field \"%s\" item %d offset=%u\n",
1343 job->name(), job_items[i].name, i, offset);
1344 svalue = (char **)((char *)job + offset);
1346 Pmsg1(000, _("Hey something is wrong. p=0x%lu\n"), *svalue);
1348 *svalue = *def_svalue;
1349 set_bit(i, job->hdr.item_present);
1351 * Handle alist resources
1353 } else if (job_items[i].handler == store_alist_str) {
1356 def_avalue = (alist **)((char *)(job->jobdefs) + offset);
1357 avalue = (alist **)((char *)job + offset);
1359 *avalue = New(alist(10, owned_by_alist));
1361 foreach_alist(elt, (*def_avalue)) {
1362 (*avalue)->append(bstrdup(elt));
1364 set_bit(i, job->hdr.item_present);
1366 } else if (job_items[i].handler == store_alist_res) {
1369 def_avalue = (alist **)((char *)(job->jobdefs) + offset);
1370 avalue = (alist **)((char *)job + offset);
1372 *avalue = New(alist(10, not_owned_by_alist));
1374 foreach_alist(elt, (*def_avalue)) {
1375 (*avalue)->append(elt);
1377 set_bit(i, job->hdr.item_present);
1379 * Handle integer fields
1380 * Note, our store_bit does not handle bitmaped fields
1382 } else if (job_items[i].handler == store_bit ||
1383 job_items[i].handler == store_pint32 ||
1384 job_items[i].handler == store_jobtype ||
1385 job_items[i].handler == store_level ||
1386 job_items[i].handler == store_int32 ||
1387 job_items[i].handler == store_size32 ||
1388 job_items[i].handler == store_migtype ||
1389 job_items[i].handler == store_replace) {
1390 def_ivalue = (uint32_t *)((char *)(job->jobdefs) + offset);
1391 Dmsg5(400, "Job \"%s\", field \"%s\" def_ivalue=%d item %d offset=%u\n",
1392 job->name(), job_items[i].name, *def_ivalue, i, offset);
1393 ivalue = (uint32_t *)((char *)job + offset);
1394 *ivalue = *def_ivalue;
1395 set_bit(i, job->hdr.item_present);
1397 * Handle 64 bit integer fields
1399 } else if (job_items[i].handler == store_time ||
1400 job_items[i].handler == store_size64 ||
1401 job_items[i].handler == store_int64) {
1402 def_lvalue = (int64_t *)((char *)(job->jobdefs) + offset);
1403 Dmsg5(400, "Job \"%s\", field \"%s\" def_lvalue=%" lld " item %d offset=%u\n",
1404 job->name(), job_items[i].name, *def_lvalue, i, offset);
1405 lvalue = (int64_t *)((char *)job + offset);
1406 *lvalue = *def_lvalue;
1407 set_bit(i, job->hdr.item_present);
1409 * Handle bool fields
1411 } else if (job_items[i].handler == store_bool) {
1412 def_bvalue = (bool *)((char *)(job->jobdefs) + offset);
1413 Dmsg5(400, "Job \"%s\", field \"%s\" def_bvalue=%d item %d offset=%u\n",
1414 job->name(), job_items[i].name, *def_bvalue, i, offset);
1415 bvalue = (bool *)((char *)job + offset);
1416 *bvalue = *def_bvalue;
1417 set_bit(i, job->hdr.item_present);
1420 Dmsg1(10, "Handler missing for job_items[%d]\n", i);
1421 ASSERTD(0, "JobDefs -> Job handler missing\n");
1427 * Ensure that all required items are present
1429 for (i=0; job_items[i].name; i++) {
1430 if (job_items[i].flags & ITEM_REQUIRED) {
1431 if (!bit_is_set(i, job->hdr.item_present)) {
1432 Jmsg(NULL, M_ERROR_TERM, 0, _("\"%s\" directive in Job \"%s\" resource is required, but not found.\n"),
1433 job_items[i].name, job->name());
1437 /* If this triggers, take a look at lib/parse_conf.h */
1438 if (i >= MAX_RES_ITEMS) {
1439 Emsg0(M_ERROR_TERM, 0, _("Too many items in Job resource\n"));
1442 if (!job->storage && !job->pool->storage) {
1443 Jmsg(NULL, M_FATAL, 0, _("No storage specified in Job \"%s\" nor in Pool.\n"),
1447 } /* End loop over Job res */
1453 static void sendit(void *sock, const char *fmt, ...)
1458 va_start(arg_ptr, fmt);
1459 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);