1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * Bacula conf to json
21 *
22 * Kern Sibbald, MMXII
23 */
24
25 #include "bacula.h"
26 #include "stored.h"
27
28 /* Imported functions */
29 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
30
31 /* Imported variables */
32 #if defined(_MSC_VER)
33 extern "C" { // work around visual compiler mangling variables
34 extern URES res_all;
35 }
36 #else
37 extern URES res_all;
38 #endif
39 extern s_kw msg_types[];
40 extern s_kw dev_types[];
41 extern s_kw tapelabels[];
42 extern s_kw cloud_drivers[];
43 extern s_kw trunc_opts[];
44 extern s_kw upload_opts[];
45 extern s_kw proto_opts[];
46 extern s_kw uri_opts[];
47
48 extern RES_TABLE resources[];
49
50 typedef struct
51 {
52 bool do_list;
53 bool do_one;
54 bool do_only_data;
55 char *resource_type;
56 char *resource_name;
57 regex_t directive_reg;
58 } display_filter;
59
60 /* Forward referenced functions */
61 void terminate_stored(int sig);
62 static int check_resources();
63 static void sendit(void *sock, const char *fmt, ...);
64 static void dump_json(display_filter *filter);
65
66 #define CONFIG_FILE "bacula-sd.conf" /* Default config file */
67
68 /* Global variables exported */
69 STORES *me = NULL; /* our Global resource */
70
71 char *configfile = NULL;
72
73 /* Global static variables */
74 static CONFIG *config;
75
76
usage()77 static void usage()
78 {
79 fprintf(stderr, _(
80 PROG_COPYRIGHT
81 "\n%sVersion: %s (%s)\n\n"
82 "Usage: bsdjson [options] [config_file]\n"
83 " -r <res> get resource type <res>\n"
84 " -n <name> get resource <name>\n"
85 " -l <dirs> get only directives matching dirs (use with -r)\n"
86 " -D get only data\n"
87 " -c <file> use <file> as configuration file\n"
88 " -d <nn> set debug level to <nn>\n"
89 " -dt print timestamp in debug output\n"
90 " -t test - read config and exit\n"
91 " -v verbose user messages\n"
92 " -? print this message.\n"
93 "\n"), 2012, "", VERSION, BDATE);
94
95 exit(1);
96 }
97
98 /*********************************************************************
99 *
100 * Main Bacula Unix Storage Daemon
101 *
102 */
103 #if defined(HAVE_WIN32)
104 #define main BaculaMain
105 #endif
106
main(int argc,char * argv[])107 int main (int argc, char *argv[])
108 {
109 int ch;
110 bool test_config = false;
111 display_filter filter;
112 memset(&filter, 0, sizeof(filter));
113
114 setlocale(LC_ALL, "");
115 bindtextdomain("bacula", LOCALEDIR);
116 textdomain("bacula");
117
118 my_name_is(argc, argv, "bacula-sd");
119 init_msg(NULL, NULL);
120
121 while ((ch = getopt(argc, argv, "Dc:d:tv?r:n:l:")) != -1) {
122 switch (ch) {
123 case 'D':
124 filter.do_only_data = true;
125 break;
126
127 case 'l':
128 filter.do_list = true;
129 /* Might use something like -l '^(Name|Description)$' */
130 filter.do_list = true;
131 if (regcomp(&filter.directive_reg, optarg, REG_EXTENDED) != 0) {
132 Jmsg((JCR *)NULL, M_ERROR_TERM, 0,
133 _("Please use valid -l argument: %s\n"), optarg);
134 }
135 break;
136
137 case 'r':
138 filter.resource_type = optarg;
139 break;
140
141 case 'n':
142 filter.resource_name = optarg;
143 break;
144
145 case 'c': /* configuration file */
146 if (configfile != NULL) {
147 free(configfile);
148 }
149 configfile = bstrdup(optarg);
150 break;
151
152 case 'd': /* debug level */
153 if (*optarg == 't') {
154 dbg_timestamp = true;
155 } else {
156 debug_level = atoi(optarg);
157 if (debug_level <= 0) {
158 debug_level = 1;
159 }
160 }
161 break;
162
163 case 't':
164 test_config = true;
165 break;
166
167 case 'v': /* verbose */
168 verbose++;
169 break;
170
171 case '?':
172 default:
173 usage();
174 break;
175 }
176 }
177 argc -= optind;
178 argv += optind;
179
180 if (argc) {
181 if (configfile != NULL) {
182 free(configfile);
183 }
184 configfile = bstrdup(*argv);
185 argc--;
186 argv++;
187 }
188
189 if (argc) {
190 usage();
191 }
192
193 if (filter.do_list && !filter.resource_type) {
194 usage();
195 }
196
197 if (filter.resource_type && filter.resource_name) {
198 filter.do_one = true;
199 }
200
201 if (configfile == NULL || configfile[0] == 0) {
202 configfile = bstrdup(CONFIG_FILE);
203 }
204
205 if (test_config && verbose > 0) {
206 char buf[1024];
207 find_config_file(configfile, buf, sizeof(buf));
208 sendit(NULL, "config_file=%s\n", buf);
209 }
210
211 config = New(CONFIG());
212 config->encode_password(false);
213 parse_sd_config(config, configfile, M_ERROR_TERM);
214
215 if (!check_resources()) {
216 Jmsg((JCR *)NULL, M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
217 }
218
219 if (test_config) {
220 terminate_stored(0);
221 }
222
223 my_name_is(0, (char **)NULL, me->hdr.name); /* Set our real name */
224
225 dump_json(&filter);
226
227 if (filter.do_list) {
228 regfree(&filter.directive_reg);
229 }
230
231 terminate_stored(0);
232 }
233
display_devtype(HPKT & hpkt)234 static void display_devtype(HPKT &hpkt)
235 {
236 int i;
237 for (i=0; dev_types[i].name; i++) {
238 if (*(int32_t *)(hpkt.ritem->value) == dev_types[i].token) {
239 sendit(NULL, "\n \"%s\": \"%s\"", hpkt.ritem->name,
240 dev_types[i].name);
241 return;
242 }
243 }
244 }
245
display_label(HPKT & hpkt)246 static void display_label(HPKT &hpkt)
247 {
248 int i;
249 for (i=0; tapelabels[i].name; i++) {
250 if (*(int32_t *)(hpkt.ritem->value) == tapelabels[i].token) {
251 sendit(NULL, "\n \"%s\": \"%s\"", hpkt.ritem->name,
252 tapelabels[i].name);
253 return;
254 }
255 }
256 }
257
display_cloud_driver(HPKT & hpkt)258 static void display_cloud_driver(HPKT &hpkt)
259 {
260 int i;
261 for (i=0; cloud_drivers[i].name; i++) {
262 if (*(int32_t *)(hpkt.ritem->value) == cloud_drivers[i].token) {
263 sendit(NULL, "\n \"%s\": \"%s\"", hpkt.ritem->name,
264 cloud_drivers[i].name);
265 return;
266 }
267 }
268 }
269
display_protocol(HPKT & hpkt)270 static void display_protocol(HPKT &hpkt)
271 {
272 int i;
273 for (i=0; proto_opts[i].name; i++) {
274 if (*(int32_t *)(hpkt.ritem->value) == proto_opts[i].token) {
275 sendit(NULL, "\n \"%s\": \"%s\"", hpkt.ritem->name,
276 proto_opts[i].name);
277 return;
278 }
279 }
280 }
281
display_truncate_cache(HPKT & hpkt)282 static void display_truncate_cache(HPKT &hpkt)
283 {
284 int i;
285 for (i=0; trunc_opts[i].name; i++) {
286 if (*(int32_t *)(hpkt.ritem->value) == trunc_opts[i].token) {
287 sendit(NULL, "\n \"%s\": \"%s\"", hpkt.ritem->name,
288 trunc_opts[i].name);
289 return;
290 }
291 }
292 }
293
display_uri_style(HPKT & hpkt)294 static void display_uri_style(HPKT &hpkt)
295 {
296 int i;
297 for (i=0; uri_opts[i].name; i++) {
298 if (*(int32_t *)(hpkt.ritem->value) == uri_opts[i].token) {
299 sendit(NULL, "\n \"%s\": \"%s\"", hpkt.ritem->name,
300 uri_opts[i].name);
301 return;
302 }
303 }
304 }
305
display_upload(HPKT & hpkt)306 static void display_upload(HPKT &hpkt)
307 {
308 int i;
309 for (i=0; upload_opts[i].name; i++) {
310 if (*(int32_t *)(hpkt.ritem->value) == upload_opts[i].token) {
311 sendit(NULL, "\n \"%s\": \"%s\"", hpkt.ritem->name,
312 upload_opts[i].name);
313 return;
314 }
315 }
316 }
317
318 /*
319 * Dump out all resources in json format.
320 * Note!!!! This routine must be in this file rather
321 * than in src/lib/parser_conf.c otherwise the pointers
322 * will be all messed up.
323 */
dump_json(display_filter * filter)324 static void dump_json(display_filter *filter)
325 {
326 int resinx, item, directives, first_directive;
327 bool first_res;
328 RES_ITEM *items;
329 RES *res;
330 HPKT hpkt;
331 regmatch_t pmatch[32];
332 STORES *me = (STORES *)GetNextRes(R_STORAGE, NULL);
333
334 if (init_crypto() != 0) {
335 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
336 }
337
338 init_hpkt(hpkt);
339
340 if (filter->do_only_data) {
341 sendit(NULL, "[");
342
343 /* List resources and directives */
344 /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... }
345 * or print a single item
346 */
347 } else if (filter->do_one || filter->do_list) {
348 sendit(NULL, "{");
349
350 } else {
351 /* [ { "Device": { "Name": "aa",.. } }, { "Director": { "Name": "bb", ... } } ]*/
352 sendit(NULL, "[");
353 }
354
355 first_res = true;
356 /* Loop over all resource types */
357 for (resinx=0; resources[resinx].name; resinx++) {
358 if (!resources[resinx].items) {
359 continue; /* skip dummy entries */
360 }
361
362 /* Skip this resource type */
363 if (filter->resource_type &&
364 strcasecmp(filter->resource_type, resources[resinx].name) != 0) {
365 continue;
366 }
367
368 directives = 0;
369 /* Loop over all resources of this type */
370 foreach_rblist(res, res_head[resinx]->res_list) {
371 hpkt.res = res;
372 items = resources[resinx].items;
373 if (!items) {
374 continue;
375 }
376
377 /* Copy the resource into res_all */
378 memcpy(&res_all, res, sizeof(res_all));
379
380 if (filter->resource_name) {
381 bool skip=true;
382 /* The Name should be at the first place, so this is not a real loop */
383 for (item=0; items[item].name; item++) {
384 if (strcasecmp(items[item].name, "Name") == 0) {
385 if (strcasecmp(*(items[item].value), filter->resource_name) == 0) {
386 skip = false;
387 }
388 break;
389 }
390 }
391 if (skip) { /* The name doesn't match, so skip it */
392 continue;
393 }
394 }
395
396 if (first_res) {
397 sendit(NULL, "\n");
398 } else {
399 sendit(NULL, ",\n");
400 }
401
402 if (filter->do_only_data) {
403 sendit(NULL, " {");
404
405 } else if (filter->do_one) {
406 /* Nothing to print */
407
408 /* When sending the list, the form is:
409 * { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
410 */
411 } else if (filter->do_list) {
412 /* Search and display Name, should be the first item */
413 for (item=0; items[item].name; item++) {
414 if (strcmp(items[item].name, "Name") == 0) {
415 sendit(NULL, "%s: {\n", quote_string(hpkt.edbuf2, *items[item].value));
416 break;
417 }
418 }
419 } else {
420 /* Begin new resource */
421 sendit(NULL, "{\n \"%s\": {", resources[resinx].name);
422 }
423
424 first_res = false;
425 first_directive = 0;
426 directives = 0;
427 for (item=0; items[item].name; item++) {
428 /* Check user argument -l */
429 if (filter->do_list &&
430 regexec(&filter->directive_reg,
431 items[item].name, 32, pmatch, 0) != 0)
432 {
433 continue;
434 }
435
436 hpkt.ritem = &items[item];
437 if (bit_is_set(item, res_all.hdr.item_present)) {
438 if (first_directive++ > 0) printf(",");
439 if (display_global_item(hpkt)) {
440 /* Fall-through wanted */
441 } else if (items[item].handler == store_maxblocksize) {
442 display_int32_pair(hpkt);
443 } else if (items[item].handler == store_devtype) {
444 display_devtype(hpkt);
445 } else if (items[item].handler == store_label) {
446 display_label(hpkt);
447 } else if (items[item].handler == store_cloud_driver) {
448 display_cloud_driver(hpkt);
449 } else if (items[item].handler == store_protocol) {
450 display_protocol(hpkt);
451 } else if (items[item].handler == store_uri_style) {
452 display_uri_style(hpkt);
453 } else if (items[item].handler == store_truncate) {
454 display_truncate_cache(hpkt);
455 } else if (items[item].handler == store_upload) {
456 display_upload(hpkt);
457 } else if (items[item].handler == store_coll_type) {
458 display_collector_types(hpkt);
459 } else {
460 printf("\n \"%s\": \"null\"", items[item].name);
461 }
462 directives++;
463 } else { /* end if is present */
464 /* For some directive, the bitmap is not set (like addresses) */
465 if (me && strcmp(resources[resinx].name, "Storage") == 0) {
466 if (strcmp(items[item].name, "SdPort") == 0) {
467 if (get_first_port_host_order(me->sdaddrs) != items[item].default_value) {
468 if (first_directive++ > 0) sendit(NULL, ",");
469 sendit(NULL, "\n \"SdPort\": %d",
470 get_first_port_host_order(me->sdaddrs));
471 }
472 } else if (me && strcmp(items[item].name, "SdAddress") == 0) {
473 char buf[500];
474 get_first_address(me->sdaddrs, buf, sizeof(buf));
475 if (strcmp(buf, "0.0.0.0") != 0) {
476 if (first_directive++ > 0) sendit(NULL, ",");
477 sendit(NULL, "\n \"SdAddress\": \"%s\"", buf);
478 }
479 }
480 }
481 }
482 if (items[item].flags & ITEM_LAST) {
483 display_last(hpkt); /* If last bit set always call to cleanup */
484 }
485 }
486
487 /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... } */
488 if (filter->do_only_data || filter->do_list) {
489 sendit(NULL, "\n }"); /* Finish the Resource with a single } */
490
491 } else {
492 if (filter->do_one) {
493 /* don't print anything */
494
495 } else if (first_directive > 0) {
496 sendit(NULL, "\n }\n}"); /* end of resource */
497
498 } else {
499 sendit(NULL, "}\n }");
500 }
501 }
502
503 } /* End loop over all resources of this type */
504 } /* End loop all resource types */
505
506 if (filter->do_only_data) {
507 sendit(NULL, "\n]\n");
508
509 /* In list context, we are dealing with a hash */
510 } else if (filter->do_one || filter->do_list) {
511 sendit(NULL, "\n}\n");
512
513 } else {
514 sendit(NULL, "\n]\n");
515 }
516 term_hpkt(hpkt);
517 }
518
519
520 /* Check Configuration file for necessary info */
check_resources()521 static int check_resources()
522 {
523 bool OK = true;
524 bool tls_needed;
525 AUTOCHANGER *changer;
526 DEVRES *device;
527
528 me = (STORES *)GetNextRes(R_STORAGE, NULL);
529 if (!me) {
530 Jmsg1(NULL, M_ERROR, 0, _("No Storage resource defined in %s. Cannot continue.\n"),
531 configfile);
532 OK = false;
533 }
534
535 if (GetNextRes(R_STORAGE, (RES *)me) != NULL) {
536 Jmsg1(NULL, M_ERROR, 0, _("Only one Storage resource permitted in %s\n"),
537 configfile);
538 OK = false;
539 }
540 if (GetNextRes(R_DIRECTOR, NULL) == NULL) {
541 Jmsg1(NULL, M_ERROR, 0, _("No Director resource defined in %s. Cannot continue.\n"),
542 configfile);
543 OK = false;
544 }
545 if (GetNextRes(R_DEVICE, NULL) == NULL){
546 Jmsg1(NULL, M_ERROR, 0, _("No Device resource defined in %s. Cannot continue.\n"),
547 configfile);
548 OK = false;
549 }
550
551 if (!me->messages) {
552 me->messages = (MSGS *)GetNextRes(R_MSGS, NULL);
553 if (!me->messages) {
554 Jmsg1(NULL, M_ERROR, 0, _("No Messages resource defined in %s. Cannot continue.\n"),
555 configfile);
556 OK = false;
557 }
558 }
559
560 if (!me->working_directory) {
561 Jmsg1(NULL, M_ERROR, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
562 configfile);
563 OK = false;
564 }
565
566 DIRRES *director;
567 STORES *store;
568 foreach_res(store, R_STORAGE) {
569 /* tls_require implies tls_enable */
570 if (store->tls_require) {
571 if (have_tls) {
572 store->tls_enable = true;
573 } else {
574 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
575 OK = false;
576 continue;
577 }
578 }
579
580 tls_needed = store->tls_enable || store->tls_authenticate;
581
582 if (!store->tls_certfile && tls_needed) {
583 Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Storage \"%s\" in %s.\n"),
584 store->hdr.name, configfile);
585 OK = false;
586 }
587
588 if (!store->tls_keyfile && tls_needed) {
589 Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Storage \"%s\" in %s.\n"),
590 store->hdr.name, configfile);
591 OK = false;
592 }
593
594 if ((!store->tls_ca_certfile && !store->tls_ca_certdir) && tls_needed && store->tls_verify_peer) {
595 Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
596 " or \"TLS CA Certificate Dir\" are defined for Storage \"%s\" in %s."
597 " At least one CA certificate store is required"
598 " when using \"TLS Verify Peer\".\n"),
599 store->hdr.name, configfile);
600 OK = false;
601 }
602 }
603
604 foreach_res(director, R_DIRECTOR) {
605 /* tls_require implies tls_enable */
606 if (director->tls_require) {
607 director->tls_enable = true;
608 }
609
610 tls_needed = director->tls_enable || director->tls_authenticate;
611
612 if (!director->tls_certfile && tls_needed) {
613 Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Director \"%s\" in %s.\n"),
614 director->hdr.name, configfile);
615 OK = false;
616 }
617
618 if (!director->tls_keyfile && tls_needed) {
619 Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Director \"%s\" in %s.\n"),
620 director->hdr.name, configfile);
621 OK = false;
622 }
623
624 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed && director->tls_verify_peer) {
625 Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
626 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
627 " At least one CA certificate store is required"
628 " when using \"TLS Verify Peer\".\n"),
629 director->hdr.name, configfile);
630 OK = false;
631 }
632 }
633
634 foreach_res(changer, R_AUTOCHANGER) {
635 foreach_alist(device, changer->device) {
636 device->cap_bits |= CAP_AUTOCHANGER;
637 }
638 }
639
640 return OK;
641 }
642
643 /* Clean up and then exit */
terminate_stored(int sig)644 void terminate_stored(int sig)
645 {
646 static bool in_here = false;
647
648 if (in_here) { /* prevent loops */
649 bmicrosleep(2, 0); /* yield */
650 exit(1);
651 }
652 in_here = true;
653 debug_level = 0; /* turn off any debug */
654
655 if (configfile) {
656 free(configfile);
657 configfile = NULL;
658 }
659 if (config) {
660 delete config;
661 config = NULL;
662 }
663
664 if (debug_level > 10) {
665 print_memory_pool_stats();
666 }
667 term_msg();
668 free(res_head);
669 res_head = NULL;
670 close_memory_pool();
671
672 //sm_dump(false); /* dump orphaned buffers */
673 exit(sig);
674 }
675
sendit(void * sock,const char * fmt,...)676 static void sendit(void *sock, const char *fmt, ...)
677 {
678 char buf[3000];
679 va_list arg_ptr;
680
681 va_start(arg_ptr, fmt);
682 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
683 va_end(arg_ptr);
684 fputs(buf, stdout);
685 fflush(stdout);
686 }
687