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);
770 res = (RES *)run->pool;
771 if (!first) sendit(NULL, ",\n");
772 sendit(NULL, " \"%s\": %s", RunFields[i].name,
773 quote_string(hpkt.edbuf, res->name));
777 case 'S': /* Storage */
779 res = (RES *)run->storage;
780 if (!first) sendit(NULL, ",\n");
781 sendit(NULL, " \"%s\": %s", RunFields[i].name,
782 quote_string(hpkt.edbuf, res->name));
786 case 'M': /* Messages */
788 res = (RES *)run->msgs;
789 if (!first) sendit(NULL, ",\n");
790 sendit(NULL, " \"%s\": %s", RunFields[i].name,
791 quote_string(hpkt.edbuf, res->name));
795 case 'p': /* priority */
796 if (run->priority_set) {
797 if (!first) sendit(NULL, ",\n");
798 sendit(NULL, " \"%s\": %d", RunFields[i].name,
803 case 's': /* Spool Data */
804 if (run->spool_data_set) {
805 if (!first) sendit(NULL, ",\n");
806 sendit(NULL, " \"%s\": %s", RunFields[i].name,
807 run->spool_data?"true":"false");
811 case 'W': /* Write Part After Job */
812 if (run->write_part_after_job_set) {
813 if (!first) sendit(NULL, ",\n");
814 sendit(NULL, " \"%s\": %s", RunFields[i].name,
815 run->write_part_after_job?"true":"false");
819 case 'm': /* MaxRunScheduledTime */
820 if (run->MaxRunSchedTime_set) {
821 if (!first) sendit(NULL, ",\n");
822 sendit(NULL, " \"%s\": %lld", RunFields[i].name,
823 run->MaxRunSchedTime);
827 case 'a': /* Accurate */
828 if (run->accurate_set) {
829 if (!first) sendit(NULL, ",\n");
830 sendit(NULL, " \"%s\": %s", RunFields[i].name,
831 run->accurate?"true":"false");
838 } /* End all RunFields (overrides) */
839 /* Now handle timing */
840 if (byte_is_set(run->hour, sizeof(run->hour))) {
841 if (!first) sendit(NULL, ",\n");
842 sendit(NULL, " \"Hour\":");
843 display_bit_array(run->hour, 24);
844 sendit(NULL, ",\n \"Minute\": %d", run->minute);
847 /* bit 32 is used to store the keyword LastDay, so we look up to 0-31 */
848 if (byte_is_set(run->mday, sizeof(run->mday) - 1)) {
849 if (!first) sendit(NULL, ",\n");
850 sendit(NULL, " \"Day\":");
851 display_bit_array(run->mday, 31);
854 if (run->last_day_set) {
855 if (!first) sendit(NULL, ",\n");
856 sendit(NULL, " \"LastDay\": 1");
859 if (byte_is_set(run->month, sizeof(run->month))) {
860 if (!first) sendit(NULL, ",\n");
861 sendit(NULL, " \"Month\":");
862 display_bit_array(run->month, 12);
865 if (byte_is_set(run->wday, sizeof(run->wday))) {
866 if (!first) sendit(NULL, ",\n");
867 sendit(NULL, " \"DayOfWeek\":");
868 display_bit_array(run->wday, 7);
871 if (byte_is_set(run->wom, sizeof(run->wom))) {
872 if (!first) sendit(NULL, ",\n");
873 sendit(NULL, " \"WeekOfMonth\":");
874 display_bit_array(run->wom, 6);
877 if (byte_is_set(run->woy, sizeof(run->woy))) {
878 if (!first) sendit(NULL, ",\n");
879 sendit(NULL, " \"WeekOfYear\":");
880 display_bit_array(run->woy, 54);
883 sendit(NULL, "\n }");
885 } /* End this Run directive */
886 sendit(NULL, "\n ]");
890 * Dump out all resources in json format.
891 * Note!!!! This routine must be in this file rather
892 * than in src/lib/parser_conf.c otherwise the pointers
893 * will be all messed up.
895 static void dump_json(display_filter *filter)
897 int resinx, item, first_directive, name_pos=0;
902 regmatch_t pmatch[32];
906 /* List resources and directives */
907 if (filter->do_only_data) {
912 * { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... }
913 * or print a single item
915 } else if (filter->do_one || filter->do_list) {
919 /* [ { "Client": { "Name": "aa",.. } }, { "Director": { "Name": "bb", ... } } ]*/
924 /* Main loop over all resources */
925 for (resinx=0; resources[resinx].items; resinx++) {
927 /* Skip this resource type? */
928 if (filter->resource_type &&
929 strcasecmp(filter->resource_type, resources[resinx].name) != 0) {
933 /* Loop over each resource of this type */
934 foreach_rblist(res, res_head[resinx]->res_list) {
936 items = resources[resinx].items;
941 /* Copy the resource into res_all */
942 memcpy(&res_all, res, sizeof(res_all));
944 /* If needed, skip this resource type */
945 if (filter->resource_name) {
947 /* The Name should be at the first place, so this is not a real loop */
948 for (item=0; items[item].name; item++) {
949 if (strcasecmp(items[item].name, "Name") == 0) {
950 if (strcmp(*(items[item].value), filter->resource_name) == 0) {
956 if (skip) { /* The name doesn't match, so skip it */
967 /* Find where the Name is defined, should always be 0 */
968 for (item=0; items[item].name; item++) {
969 if (strcmp(items[item].name, "Name") == 0) {
975 if (filter->do_only_data) {
978 } else if (filter->do_one) {
979 /* Nothing to print */
981 /* When sending the list, the form is:
982 * { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
984 } else if (filter->do_list) {
985 /* Search and display Name, should be the first item */
986 for (item=0; items[item].name; item++) {
987 if (strcmp(items[item].name, "Name") == 0) {
988 sendit(NULL, "%s: {\n", quote_string(hpkt.edbuf2, *items[item].value));
993 /* Begin new resource */
994 sendit(NULL, "{\n \"%s\": {", resources[resinx].name);
1001 * Here we walk through a resource displaying all the
1002 * directives and sub-resources in the resource.
1004 for (item=0; items[item].name; item++) {
1005 /* Check user argument -l */
1006 if (filter->do_list &&
1007 regexec(&filter->directive_reg,
1008 items[item].name, 32, pmatch, 0) != 0)
1013 hpkt.ritem = &items[item];
1015 if (bit_is_set(item, res_all.hdr.item_present)) {
1017 /* Skip Directive in lowercase, but check if the next
1018 * one is pointing to the same location (for example User and dbuser)
1020 if (!B_ISUPPER(*(items[item].name))) {
1022 while(!B_ISUPPER(*(items[i].name)) && items[i].value == items[item].value) {
1025 if (items[i].value == items[item].value) {
1026 set_bit(i, res_all.hdr.item_present);
1031 if (first_directive++ > 0) sendit(NULL, ",");
1032 if (display_global_item(hpkt)) {
1033 /* Fall-through wanted */
1034 } else if (items[item].handler == store_jobtype) {
1035 display_jobtype(hpkt);
1036 } else if (items[item].handler == store_label) {
1037 display_label(hpkt);
1038 } else if (items[item].handler == store_level) {
1039 display_joblevel(hpkt);
1040 } else if (items[item].handler == store_replace) {
1041 display_replace(hpkt);
1042 } else if (items[item].handler == store_migtype) {
1043 display_migtype(hpkt);
1044 } else if (items[item].handler == store_actiononpurge) {
1045 display_actiononpurge(hpkt);
1046 /* FileSet Include/Exclude directive */
1047 } else if (items[item].handler == store_inc) {
1048 display_include_exclude(hpkt);
1049 } else if (items[item].handler == store_ac_res) {
1051 /* A different alist for each item.code */
1052 } else if (items[item].handler == store_acl) {
1054 } else if (items[item].handler == store_device) {
1055 display_alist_res(hpkt);
1056 } else if (items[item].handler == store_run) {
1058 } else if (items[item].handler == store_runscript) {
1059 if (!display_runscript(hpkt)) {
1060 first_directive = 0; /* Do not print a comma after this empty runscript */
1063 sendit(NULL, "\n \"%s\": null", items[item].name);
1065 } else { /* end if is present */
1066 /* For some directive, the bitmap is not set (like addresses) */
1068 /* Special trick for the Autochanger directive, it can be yes/no/storage */
1069 if (strcmp(resources[resinx].name, "Storage") == 0) {
1070 if (strcmp(items[item].name, "Autochanger") == 0
1071 && items[item].handler == store_bool /* yes or no */
1072 && *(items[item].value) != NULL
1073 && *(items[item-1].value) == NULL) /* The previous "Autochanger" name is not set */
1075 if (first_directive++ > 0) sendit(NULL, ",");
1076 sendit(NULL, "\n \"Autochanger\": %s", quote_string(hpkt.edbuf2, *items[name_pos].value));
1079 if (strcmp(resources[resinx].name, "Director") == 0) {
1080 if (strcmp(items[item].name, "DirPort") == 0) {
1081 if (get_first_port_host_order(director->DIRaddrs) != items[item].default_value) {
1082 if (first_directive++ > 0) sendit(NULL, ",");
1083 sendit(NULL, "\n \"DirPort\": %d",
1084 get_first_port_host_order(director->DIRaddrs));
1087 } else if (strcmp(items[item].name, "DirAddress") == 0) {
1089 get_first_address(director->DIRaddrs, buf, sizeof(buf));
1090 if (strcmp(buf, "0.0.0.0") != 0) {
1091 if (first_directive++ > 0) sendit(NULL, ",");
1092 sendit(NULL, "\n \"DirAddress\": \"%s\"", buf);
1095 } else if (strcmp(items[item].name, "DirSourceAddress") == 0 && director->DIRsrc_addr) {
1097 get_first_address(director->DIRsrc_addr, buf, sizeof(buf));
1098 if (strcmp(buf, "0.0.0.0") != 0) {
1099 if (first_directive++ > 0) sendit(NULL, ",");
1100 sendit(NULL, "\n \"DirSourceAddress\": \"%s\"", buf);
1105 if (items[item].flags & ITEM_LAST) {
1106 display_last(hpkt); /* If last bit set always call to cleanup */
1108 } /* loop over directive names */
1110 /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... } */
1111 if (filter->do_only_data || filter->do_list) {
1112 sendit(NULL, "\n }"); /* Finish the Resource with a single } */
1115 if (filter->do_one) {
1116 /* don't print anything */
1118 } else if (first_directive > 0) {
1119 sendit(NULL, "\n }\n}"); /* end of resource */
1122 sendit(NULL, "}\n}");
1125 } /* End loop over all resources of this type */
1126 } /* End loop all resource types */
1128 if (filter->do_only_data) {
1129 sendit(NULL, "\n]\n");
1131 /* In list context, we are dealing with a hash */
1132 } else if (filter->do_one || filter->do_list) {
1133 sendit(NULL, "\n}\n");
1136 sendit(NULL, "\n]\n");
1142 * Make a quick check to see that we have all the
1145 * **** FIXME **** this routine could be a lot more
1146 * intelligent and comprehensive.
1148 static bool check_resources(bool apply_jobdefs)
1156 job = (JOB *)GetNextRes(R_JOB, NULL);
1157 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
1159 Jmsg(NULL, M_FATAL, 0, _("No Director resource defined in %s\n"
1160 "Without that I don't know who I am :-(\n"), configfile);
1163 set_working_directory(director->working_directory);
1164 if (!director->messages) { /* If message resource not specified */
1165 director->messages = (MSGS *)GetNextRes(R_MSGS, NULL);
1166 if (!director->messages) {
1167 Jmsg(NULL, M_FATAL, 0, _("No Messages resource defined in %s\n"), configfile);
1171 if (GetNextRes(R_DIRECTOR, (RES *)director) != NULL) {
1172 Jmsg(NULL, M_FATAL, 0, _("Only one Director resource permitted in %s\n"),
1176 /* tls_require implies tls_enable */
1177 if (director->tls_require) {
1179 director->tls_enable = true;
1181 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1186 need_tls = director->tls_enable || director->tls_authenticate;
1188 if (!director->tls_certfile && need_tls) {
1189 Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Director \"%s\" in %s.\n"),
1190 director->name(), configfile);
1194 if (!director->tls_keyfile && need_tls) {
1195 Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Director \"%s\" in %s.\n"),
1196 director->name(), configfile);
1200 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) &&
1201 need_tls && director->tls_verify_peer) {
1202 Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\" or \"TLS CA"
1203 " Certificate Dir\" are defined for Director \"%s\" in %s."
1204 " At least one CA certificate store is required"
1205 " when using \"TLS Verify Peer\".\n"),
1206 director->name(), configfile);
1211 /* Loop over Consoles */
1213 foreach_res(cons, R_CONSOLE) {
1214 /* tls_require implies tls_enable */
1215 if (cons->tls_require) {
1217 cons->tls_enable = true;
1219 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1225 need_tls = cons->tls_enable || cons->tls_authenticate;
1227 if (!cons->tls_certfile && need_tls) {
1228 Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Console \"%s\" in %s.\n"),
1229 cons->name(), configfile);
1233 if (!cons->tls_keyfile && need_tls) {
1234 Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Console \"%s\" in %s.\n"),
1235 cons->name(), configfile);
1239 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir)
1240 && need_tls && cons->tls_verify_peer) {
1241 Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\" or \"TLS CA"
1242 " Certificate Dir\" are defined for Console \"%s\" in %s."
1243 " At least one CA certificate store is required"
1244 " when using \"TLS Verify Peer\".\n"),
1245 cons->name(), configfile);
1248 /* If everything is well, attempt to initialize our per-resource TLS context */
1249 if (OK && (need_tls || cons->tls_require)) {
1250 /* Initialize TLS context:
1251 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1252 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1253 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1254 cons->tls_ca_certdir, cons->tls_certfile,
1255 cons->tls_keyfile, NULL, NULL, cons->tls_dhfile, cons->tls_verify_peer);
1257 if (!cons->tls_ctx) {
1258 Jmsg(NULL, M_FATAL, 0, _("Failed to initialize TLS context for File daemon \"%s\" in %s.\n"),
1259 cons->name(), configfile);
1266 /* Loop over Clients */
1268 foreach_res(client, R_CLIENT) {
1269 /* tls_require implies tls_enable */
1270 if (client->tls_require) {
1272 client->tls_enable = true;
1274 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1279 need_tls = client->tls_enable || client->tls_authenticate;
1280 if ((!client->tls_ca_certfile && !client->tls_ca_certdir) && need_tls) {
1281 Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1282 " or \"TLS CA Certificate Dir\" are defined for File daemon \"%s\" in %s.\n"),
1283 client->name(), configfile);
1290 Jmsg(NULL, M_FATAL, 0, _("No Job records defined in %s\n"), configfile);
1294 /* TODO: We can't really update all job, we need to show only the real configuration
1295 * and not Job+JobDefs
1297 if (!apply_jobdefs) {
1302 foreach_res(job, R_JOB) {
1306 JOB *jobdefs = job->jobdefs;
1307 /* Handle RunScripts alists specifically */
1308 if (jobdefs->RunScripts) {
1309 RUNSCRIPT *rs, *elt;
1311 if (!job->RunScripts) {
1312 job->RunScripts = New(alist(10, not_owned_by_alist));
1315 foreach_alist(rs, jobdefs->RunScripts) {
1316 elt = copy_runscript(rs);
1317 job->RunScripts->append(elt); /* we have to free it */
1321 /* Transfer default items from JobDefs Resource */
1322 for (i=0; job_items[i].name; i++) {
1323 char **def_svalue, **svalue; /* string value */
1324 uint32_t *def_ivalue, *ivalue; /* integer value */
1325 bool *def_bvalue, *bvalue; /* bool value */
1326 int64_t *def_lvalue, *lvalue; /* 64 bit values */
1328 alist **def_avalue, **avalue; /* alist value */
1330 Dmsg4(1400, "Job \"%s\", field \"%s\" bit=%d def=%d\n",
1331 job->name(), job_items[i].name,
1332 bit_is_set(i, job->hdr.item_present),
1333 bit_is_set(i, job->jobdefs->hdr.item_present));
1335 if (!bit_is_set(i, job->hdr.item_present) &&
1336 bit_is_set(i, job->jobdefs->hdr.item_present)) {
1337 Dmsg2(400, "Job \"%s\", field \"%s\": getting default.\n",
1338 job->name(), job_items[i].name);
1339 offset = (char *)(job_items[i].value) - (char *)&res_all;
1341 * Handle strings and directory strings
1343 if (job_items[i].handler == store_str ||
1344 job_items[i].handler == store_dir) {
1345 def_svalue = (char **)((char *)(job->jobdefs) + offset);
1346 Dmsg5(400, "Job \"%s\", field \"%s\" def_svalue=%s item %d offset=%u\n",
1347 job->name(), job_items[i].name, *def_svalue, i, offset);
1348 svalue = (char **)((char *)job + offset);
1350 Pmsg1(000, _("Hey something is wrong. p=0x%lu\n"), *svalue);
1352 *svalue = bstrdup(*def_svalue);
1353 set_bit(i, job->hdr.item_present);
1357 } else if (job_items[i].handler == store_res) {
1358 def_svalue = (char **)((char *)(job->jobdefs) + offset);
1359 Dmsg4(400, "Job \"%s\", field \"%s\" item %d offset=%u\n",
1360 job->name(), job_items[i].name, i, offset);
1361 svalue = (char **)((char *)job + offset);
1363 Pmsg1(000, _("Hey something is wrong. p=0x%lu\n"), *svalue);
1365 *svalue = *def_svalue;
1366 set_bit(i, job->hdr.item_present);
1368 * Handle alist resources
1370 } else if (job_items[i].handler == store_alist_str) {
1373 def_avalue = (alist **)((char *)(job->jobdefs) + offset);
1374 avalue = (alist **)((char *)job + offset);
1376 *avalue = New(alist(10, owned_by_alist));
1378 foreach_alist(elt, (*def_avalue)) {
1379 (*avalue)->append(bstrdup(elt));
1381 set_bit(i, job->hdr.item_present);
1383 } else if (job_items[i].handler == store_alist_res) {
1386 def_avalue = (alist **)((char *)(job->jobdefs) + offset);
1387 avalue = (alist **)((char *)job + offset);
1389 *avalue = New(alist(10, not_owned_by_alist));
1391 foreach_alist(elt, (*def_avalue)) {
1392 (*avalue)->append(elt);
1394 set_bit(i, job->hdr.item_present);
1396 * Handle integer fields
1397 * Note, our store_bit does not handle bitmaped fields
1399 } else if (job_items[i].handler == store_bit ||
1400 job_items[i].handler == store_pint32 ||
1401 job_items[i].handler == store_jobtype ||
1402 job_items[i].handler == store_level ||
1403 job_items[i].handler == store_int32 ||
1404 job_items[i].handler == store_size32 ||
1405 job_items[i].handler == store_migtype ||
1406 job_items[i].handler == store_replace) {
1407 def_ivalue = (uint32_t *)((char *)(job->jobdefs) + offset);
1408 Dmsg5(400, "Job \"%s\", field \"%s\" def_ivalue=%d item %d offset=%u\n",
1409 job->name(), job_items[i].name, *def_ivalue, i, offset);
1410 ivalue = (uint32_t *)((char *)job + offset);
1411 *ivalue = *def_ivalue;
1412 set_bit(i, job->hdr.item_present);
1414 * Handle 64 bit integer fields
1416 } else if (job_items[i].handler == store_time ||
1417 job_items[i].handler == store_size64 ||
1418 job_items[i].handler == store_int64) {
1419 def_lvalue = (int64_t *)((char *)(job->jobdefs) + offset);
1420 Dmsg5(400, "Job \"%s\", field \"%s\" def_lvalue=%" lld " item %d offset=%u\n",
1421 job->name(), job_items[i].name, *def_lvalue, i, offset);
1422 lvalue = (int64_t *)((char *)job + offset);
1423 *lvalue = *def_lvalue;
1424 set_bit(i, job->hdr.item_present);
1426 * Handle bool fields
1428 } else if (job_items[i].handler == store_bool) {
1429 def_bvalue = (bool *)((char *)(job->jobdefs) + offset);
1430 Dmsg5(400, "Job \"%s\", field \"%s\" def_bvalue=%d item %d offset=%u\n",
1431 job->name(), job_items[i].name, *def_bvalue, i, offset);
1432 bvalue = (bool *)((char *)job + offset);
1433 *bvalue = *def_bvalue;
1434 set_bit(i, job->hdr.item_present);
1437 Dmsg1(10, "Handler missing for job_items[%d]\n", i);
1438 ASSERTD(0, "JobDefs -> Job handler missing\n");
1444 * Ensure that all required items are present
1446 for (i=0; job_items[i].name; i++) {
1447 if (job_items[i].flags & ITEM_REQUIRED) {
1448 if (!bit_is_set(i, job->hdr.item_present)) {
1449 Jmsg(NULL, M_ERROR_TERM, 0, _("\"%s\" directive in Job \"%s\" resource is required, but not found.\n"),
1450 job_items[i].name, job->name());
1454 /* If this triggers, take a look at lib/parse_conf.h */
1455 if (i >= MAX_RES_ITEMS) {
1456 Emsg0(M_ERROR_TERM, 0, _("Too many items in Job resource\n"));
1459 if (!job->storage && !job->pool->storage) {
1460 Jmsg(NULL, M_FATAL, 0, _("No storage specified in Job \"%s\" nor in Pool.\n"),
1464 } /* End loop over Job res */
1470 static void sendit(void *sock, const char *fmt, ...)
1475 va_start(arg_ptr, fmt);
1476 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);