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