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, 6);
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, name_pos=0;
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 /* 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) {
978 if (filter->do_only_data) {
981 } else if (filter->do_one) {
982 /* Nothing to print */
984 /* When sending the list, the form is:
985 * { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
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));
996 /* Begin new resource */
997 sendit(NULL, "{\n \"%s\": {", resources[resinx].name);
1001 first_directive = 0;
1004 * Here we walk through a resource displaying all the
1005 * directives and sub-resources in the resource.
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)
1016 hpkt.ritem = &items[item];
1018 if (bit_is_set(item, res_all.hdr.item_present)) {
1020 /* Skip Directive in lowercase, but check if the next
1021 * one is pointing to the same location (for example User and dbuser)
1023 if (!B_ISUPPER(*(items[item].name))) {
1025 while(!B_ISUPPER(*(items[i].name)) && items[i].value == items[item].value) {
1028 if (items[i].value == items[item].value) {
1029 set_bit(i, res_all.hdr.item_present);
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) {
1054 /* A different alist for each item.code */
1055 } else if (items[item].handler == store_acl) {
1057 } else if (items[item].handler == store_device) {
1058 display_alist_res(hpkt);
1059 } else if (items[item].handler == store_run) {
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 */
1066 sendit(NULL, "\n \"%s\": null", items[item].name);
1068 } else { /* end if is present */
1069 /* For some directive, the bitmap is not set (like addresses) */
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 */
1078 if (first_directive++ > 0) sendit(NULL, ",");
1079 sendit(NULL, "\n \"Autochanger\": %s", quote_string(hpkt.edbuf2, *items[name_pos].value));
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));
1090 } else if (strcmp(items[item].name, "DirAddress") == 0) {
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);
1098 } else if (strcmp(items[item].name, "DirSourceAddress") == 0 && director->DIRsrc_addr) {
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);
1108 if (items[item].flags & ITEM_LAST) {
1109 display_last(hpkt); /* If last bit set always call to cleanup */
1111 } /* loop over directive names */
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 } */
1118 if (filter->do_one) {
1119 /* don't print anything */
1121 } else if (first_directive > 0) {
1122 sendit(NULL, "\n }\n}"); /* end of resource */
1125 sendit(NULL, "}\n}");
1128 } /* End loop over all resources of this type */
1129 } /* End loop all resource types */
1131 if (filter->do_only_data) {
1132 sendit(NULL, "\n]\n");
1134 /* In list context, we are dealing with a hash */
1135 } else if (filter->do_one || filter->do_list) {
1136 sendit(NULL, "\n}\n");
1139 sendit(NULL, "\n]\n");
1145 * Make a quick check to see that we have all the
1148 * **** FIXME **** this routine could be a lot more
1149 * intelligent and comprehensive.
1151 static bool check_resources(bool apply_jobdefs)
1159 job = (JOB *)GetNextRes(R_JOB, NULL);
1160 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
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);
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);
1174 if (GetNextRes(R_DIRECTOR, (RES *)director) != NULL) {
1175 Jmsg(NULL, M_FATAL, 0, _("Only one Director resource permitted in %s\n"),
1179 /* tls_require implies tls_enable */
1180 if (director->tls_require) {
1182 director->tls_enable = true;
1184 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1189 need_tls = director->tls_enable || director->tls_authenticate;
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);
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);
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);
1214 /* Loop over Consoles */
1216 foreach_res(cons, R_CONSOLE) {
1217 /* tls_require implies tls_enable */
1218 if (cons->tls_require) {
1220 cons->tls_enable = true;
1222 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1228 need_tls = cons->tls_enable || cons->tls_authenticate;
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);
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);
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);
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);
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);
1269 /* Loop over Clients */
1271 foreach_res(client, R_CLIENT) {
1272 /* tls_require implies tls_enable */
1273 if (client->tls_require) {
1275 client->tls_enable = true;
1277 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
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);
1293 Jmsg(NULL, M_FATAL, 0, _("No Job records defined in %s\n"), configfile);
1297 /* TODO: We can't really update all job, we need to show only the real configuration
1298 * and not Job+JobDefs
1300 if (!apply_jobdefs) {
1305 foreach_res(job, R_JOB) {
1309 JOB *jobdefs = job->jobdefs;
1310 /* Handle RunScripts alists specifically */
1311 if (jobdefs->RunScripts) {
1312 RUNSCRIPT *rs, *elt;
1314 if (!job->RunScripts) {
1315 job->RunScripts = New(alist(10, not_owned_by_alist));
1318 foreach_alist(rs, jobdefs->RunScripts) {
1319 elt = copy_runscript(rs);
1320 job->RunScripts->append(elt); /* we have to free it */
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 */
1331 alist **def_avalue, **avalue; /* alist value */
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));
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;
1344 * Handle strings and directory strings
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);
1353 Pmsg1(000, _("Hey something is wrong. p=0x%lu\n"), *svalue);
1355 *svalue = bstrdup(*def_svalue);
1356 set_bit(i, job->hdr.item_present);
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);
1366 Pmsg1(000, _("Hey something is wrong. p=0x%lu\n"), *svalue);
1368 *svalue = *def_svalue;
1369 set_bit(i, job->hdr.item_present);
1371 * Handle alist resources
1373 } else if (job_items[i].handler == store_alist_str) {
1376 def_avalue = (alist **)((char *)(job->jobdefs) + offset);
1377 avalue = (alist **)((char *)job + offset);
1379 *avalue = New(alist(10, owned_by_alist));
1381 foreach_alist(elt, (*def_avalue)) {
1382 (*avalue)->append(bstrdup(elt));
1384 set_bit(i, job->hdr.item_present);
1386 } else if (job_items[i].handler == store_alist_res) {
1389 def_avalue = (alist **)((char *)(job->jobdefs) + offset);
1390 avalue = (alist **)((char *)job + offset);
1392 *avalue = New(alist(10, not_owned_by_alist));
1394 foreach_alist(elt, (*def_avalue)) {
1395 (*avalue)->append(elt);
1397 set_bit(i, job->hdr.item_present);
1399 * Handle integer fields
1400 * Note, our store_bit does not handle bitmaped fields
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);
1417 * Handle 64 bit integer fields
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);
1429 * Handle bool fields
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);
1440 Dmsg1(10, "Handler missing for job_items[%d]\n", i);
1441 ASSERTD(0, "JobDefs -> Job handler missing\n");
1447 * Ensure that all required items are present
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());
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"));
1462 if (!job->storage && !job->pool->storage) {
1463 Jmsg(NULL, M_FATAL, 0, _("No storage specified in Job \"%s\" nor in Pool.\n"),
1467 } /* End loop over Job res */
1473 static void sendit(void *sock, const char *fmt, ...)
1478 va_start(arg_ptr, fmt);
1479 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);