2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 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.
20 * Bacula File Daemon to Json
22 * Kern Sibbald, Sept MMXII
29 /* Imported Functions */
30 extern bool parse_fd_config(CONFIG *config, const char *configfile, int exit_code);
31 void store_msgs(LEX *lc, RES_ITEM *item, int index, int pass);
35 /* default { { "Director": { "Name": aa, ...} }, { "Job": {..} */
36 bool do_list; /* [ {}, {}, ..] or { "aa": {}, "bb": {}, ...} */
37 bool do_one; /* { "Name": "aa", "Description": "test, ... } */
38 bool do_only_data; /* [ {}, {}, {}, ] */
41 regex_t directive_reg;
44 /* Forward referenced functions */
45 static bool check_resources();
46 static void sendit(void *sock, const char *fmt, ...);
47 static void dump_json(display_filter *filter);
49 /* Exported variables */
50 CLIENT *me; /* my resource */
52 extern "C" { // work around visual compiler mangling variables
58 extern s_kw msg_types[];
59 extern RES_TABLE resources[];
61 #define CONFIG_FILE "bacula-fd.conf" /* default config file */
63 char *configfile = NULL;
64 static CONFIG *config;
70 "\n%sVersion: %s (%s)\n\n"
71 "Usage: bfdjson [options] [config_file]\n"
72 " -r <res> get resource type <res>\n"
73 " -n <name> get resource <name>\n"
74 " -l <dirs> get only directives matching dirs (use with -r)\n"
76 " -c <file> use <file> as configuration file\n"
77 " -d <nn> set debug level to <nn>\n"
78 " -dt print a timestamp in debug output\n"
79 " -t test configuration file and exit\n"
80 " -v verbose user messages\n"
81 " -? print this message.\n"
82 "\n"), 2012, "", VERSION, BDATE);
88 /*********************************************************************
90 * Bacula File daemon to Json
94 int main (int argc, char *argv[])
97 bool test_config = false;
98 display_filter filter;
99 memset(&filter, 0, sizeof(filter));
101 setlocale(LC_ALL, "");
102 bindtextdomain("bacula", LOCALEDIR);
103 textdomain("bacula");
105 my_name_is(argc, argv, "bacula-fd");
106 init_msg(NULL, NULL);
108 while ((ch = getopt(argc, argv, "Dr:n:c:d:tv?l:")) != -1) {
111 filter.do_only_data = true;
114 /* Might use something like -l '^(Name|Description)$' */
115 filter.do_list = true;
116 if (regcomp(&filter.directive_reg, optarg, REG_EXTENDED) != 0) {
117 Jmsg((JCR *)NULL, M_ERROR_TERM, 0,
118 _("Please use valid -l argument: %s\n"), optarg);
123 filter.resource_type = optarg;
127 filter.resource_name = optarg;
130 case 'c': /* configuration file */
131 if (configfile != NULL) {
134 configfile = bstrdup(optarg);
137 case 'd': /* debug level */
138 if (*optarg == 't') {
139 dbg_timestamp = true;
141 debug_level = atoi(optarg);
142 if (debug_level <= 0) {
152 case 'v': /* verbose */
166 if (configfile != NULL) {
169 configfile = bstrdup(*argv);
177 if (filter.do_list && !filter.resource_type) {
181 if (filter.resource_type && filter.resource_name) {
182 filter.do_one = true;
185 if (configfile == NULL || configfile[0] == 0) {
186 configfile = bstrdup(CONFIG_FILE);
189 if (test_config && verbose > 0) {
191 find_config_file(configfile, buf, sizeof(buf));
192 sendit(NULL, "config_file=%s\n", buf);
195 config = New(CONFIG());
196 config->encode_password(false);
197 parse_fd_config(config, configfile, M_ERROR_TERM);
199 if (!check_resources()) {
200 Emsg1(M_ERROR, 0, _("Please correct configuration file: %s\n"), configfile);
210 if (filter.do_list) {
211 regfree(&filter.directive_reg);
215 exit(0); /* should never get here */
218 void terminate_filed(int sig)
220 static bool already_here = false;
223 bmicrosleep(2, 0); /* yield */
224 exit(1); /* prevent loops */
227 debug_level = 0; /* turn off debug */
229 if (configfile != NULL) {
233 if (debug_level > 0) {
234 print_memory_pool_stats();
243 close_memory_pool(); /* release free memory in pool */
244 //sm_dump(false); /* dump orphaned buffers */
249 * Dump out all resources in json format.
250 * Note!!!! This routine must be in this file rather
251 * than in src/lib/parser_conf.c otherwise the pointers
252 * will be all messed up.
254 static void dump_json(display_filter *filter)
256 int resinx, item, directives, first_directive;
261 regmatch_t pmatch[32];
265 me = (CLIENT *)GetNextRes(R_CLIENT, NULL);
267 if (filter->do_only_data) {
270 /* List resources and directives */
271 /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... }
272 * or print a single item
274 } else if (filter->do_one || filter->do_list) {
278 /* [ { "Client": { "Name": "aa",.. } }, { "Director": { "Name": "bb", ... } } ]*/
284 /* Loop over all resource types */
285 for (resinx=0; resources[resinx].name; resinx++) {
286 /* Skip Client alias */
287 if (strcmp(resources[resinx].name, "Client") == 0) {
291 /* Skip this resource type */
292 if (filter->resource_type &&
293 strcasecmp(filter->resource_type, resources[resinx].name) != 0) {
298 /* Loop over all resources of this type */
299 foreach_rblist(res, res_head[resinx]->res_list) {
301 items = resources[resinx].items;
305 /* Copy the resource into res_all */
306 memcpy(&res_all, res, sizeof(res_all));
308 if (filter->resource_name) {
310 /* The Name should be at the first place, so this is not a real loop */
311 for (item=0; items[item].name; item++) {
312 if (strcasecmp(items[item].name, "Name") == 0) {
313 if (strcasecmp(*(items[item].value), filter->resource_name) == 0) {
319 if (skip) { /* The name doesn't match, so skip it */
330 if (filter->do_only_data) {
333 } else if (filter->do_one) {
334 /* Nothing to print */
336 /* When sending the list, the form is:
337 * { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
339 } else if (filter->do_list) {
340 /* Search and display Name, should be the first item */
341 for (item=0; items[item].name; item++) {
342 if (strcmp(items[item].name, "Name") == 0) {
343 sendit(NULL, "%s: {\n", quote_string(hpkt.edbuf2, *items[item].value));
348 /* Begin new resource */
349 sendit(NULL, "{\n \"%s\": {", resources[resinx].name);
352 /* dirtry trick for a deprecated directive */
353 bool dedup_index_directory_set = false;
355 /* Loop over all items in the resource */
356 for (item=0; items[item].name; item++) {
357 /* Check user argument -l */
358 if (filter->do_list &&
359 regexec(&filter->directive_reg,
360 items[item].name, 32, pmatch, 0) != 0)
365 /* Special tweak for a deprecated variable */
366 if (strcmp(items[item].name, "DedupIndexDirectory") == 0) {
367 dedup_index_directory_set = bit_is_set(item, res_all.hdr.item_present);
370 if (strcmp(items[item].name, "EnableClientRehydration") == 0) {
371 if (dedup_index_directory_set && !bit_is_set(item, res_all.hdr.item_present)) {
372 set_bit(item, res_all.hdr.item_present);
373 *(bool *)(items[item].value) = true;
377 hpkt.ritem = &items[item];
378 if (bit_is_set(item, res_all.hdr.item_present)) {
379 if (first_directive++ > 0) printf(",");
380 if (display_global_item(hpkt)) {
381 /* Fall-through wanted */
383 printf("\n \"%s\": null", items[item].name);
386 } else { /* end if is present */
387 /* For some directives, the bit_is_set() is not set (e.g. addresses) */
388 if (me && strcmp(resources[resinx].name, "FileDaemon") == 0) {
389 if (strcmp(items[item].name, "FdPort") == 0) {
390 if (get_first_port_host_order(me->FDaddrs) != items[item].default_value) {
391 if (first_directive++ > 0) printf(",");
392 printf("\n \"FdPort\": %d",
393 get_first_port_host_order(me->FDaddrs));
395 } else if (me && strcmp(items[item].name, "FdAddress") == 0) {
397 get_first_address(me->FDaddrs, buf, sizeof(buf));
398 if (strcmp(buf, "0.0.0.0") != 0) {
399 if (first_directive++ > 0) printf(",");
400 printf("\n \"FdAddress\": \"%s\"", buf);
402 } else if (me && strcmp(items[item].name, "FdSourceAddress") == 0
405 get_first_address(me->FDsrc_addr, buf, sizeof(buf));
406 if (strcmp(buf, "0.0.0.0") != 0) {
407 if (first_directive++ > 0) printf(",");
408 printf("\n \"FdSourceAddress\": \"%s\"", buf);
413 if (items[item].flags & ITEM_LAST) {
414 display_last(hpkt); /* If last bit set always call to cleanup */
418 /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... } */
419 if (filter->do_only_data || filter->do_list) {
420 sendit(NULL, "\n }"); /* Finish the Resource with a single } */
423 if (filter->do_one) {
424 /* don't print anything */
425 } else if (first_directive > 0) {
426 sendit(NULL, "\n }\n}"); /* end of resource */
428 sendit(NULL, "}\n}");
432 } /* End loop over all resources of this type */
433 } /* End loop all resource types */
435 if (filter->do_only_data) {
436 sendit(NULL, "\n]\n");
438 /* In list context, we are dealing with a hash */
439 } else if (filter->do_one || filter->do_list) {
440 sendit(NULL, "\n}\n");
443 sendit(NULL, "\n]\n");
450 * Make a quick check to see that we have all the
453 static bool check_resources()
462 me = (CLIENT *)GetNextRes(R_CLIENT, NULL);
464 Emsg1(M_FATAL, 0, _("No File daemon resource defined in %s\n"
465 "Without that I don't know who I am :-(\n"), configfile);
468 if (GetNextRes(R_CLIENT, (RES *) me) != NULL) {
469 Emsg1(M_FATAL, 0, _("Only one Client resource permitted in %s\n"),
473 my_name_is(0, NULL, me->hdr.name);
475 me->messages = (MSGS *)GetNextRes(R_MSGS, NULL);
477 Emsg1(M_FATAL, 0, _("No Messages resource defined in %s\n"), configfile);
481 /* tls_require implies tls_enable */
482 if (me->tls_require) {
484 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
487 me->tls_enable = true;
490 need_tls = me->tls_enable || me->tls_authenticate;
492 if ((!me->tls_ca_certfile && !me->tls_ca_certdir) && need_tls) {
493 Emsg1(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
494 " or \"TLS CA Certificate Dir\" are defined for File daemon in %s.\n"),
499 /* pki_encrypt implies pki_sign */
500 if (me->pki_encrypt) {
504 if ((me->pki_encrypt || me->pki_sign) && !me->pki_keypair_file) {
505 Emsg2(M_FATAL, 0, _("\"PKI Key Pair\" must be defined for File"
506 " daemon \"%s\" in %s if either \"PKI Sign\" or"
507 " \"PKI Encrypt\" are enabled.\n"), me->hdr.name, configfile);
513 /* Verify that a director record exists */
515 director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
518 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"),
523 foreach_res(director, R_DIRECTOR) {
524 /* tls_require implies tls_enable */
525 if (director->tls_require) {
527 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
531 director->tls_enable = true;
534 need_tls = director->tls_enable || director->tls_authenticate;
536 if (!director->tls_certfile && need_tls) {
537 Emsg2(M_FATAL, 0, _("\"TLS Certificate\" file not defined for Director \"%s\" in %s.\n"),
538 director->hdr.name, configfile);
542 if (!director->tls_keyfile && need_tls) {
543 Emsg2(M_FATAL, 0, _("\"TLS Key\" file not defined for Director \"%s\" in %s.\n"),
544 director->hdr.name, configfile);
548 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && need_tls && director->tls_verify_peer) {
549 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
550 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
551 " At least one CA certificate store is required"
552 " when using \"TLS Verify Peer\".\n"),
553 director->hdr.name, configfile);
558 foreach_res(cons, R_CONSOLE) {
559 /* tls_require implies tls_enable */
560 if (cons->tls_require) {
562 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
566 cons->tls_enable = true;
569 need_tls = cons->tls_enable || cons->tls_authenticate;
571 if (!cons->tls_certfile && need_tls) {
572 Emsg2(M_FATAL, 0, _("\"TLS Certificate\" file not defined for Console \"%s\" in %s.\n"),
573 cons->hdr.name, configfile);
577 if (!cons->tls_keyfile && need_tls) {
578 Emsg2(M_FATAL, 0, _("\"TLS Key\" file not defined for Console \"%s\" in %s.\n"),
579 cons->hdr.name, configfile);
583 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && need_tls && cons->tls_verify_peer) {
584 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
585 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s."
586 " At least one CA certificate store is required"
587 " when using \"TLS Verify Peer\".\n"),
588 cons->hdr.name, configfile);
598 static void sendit(void *sock, const char *fmt, ...)
603 va_start(arg_ptr, fmt);
604 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);