]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bsdjson.c
Apply patches from bugs #2325 and #2326 to fix FIFO bugs
[bacula/bacula] / bacula / src / stored / bsdjson.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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
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
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
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
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
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
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
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
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
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  */
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    init_hpkt(hpkt);
335
336    if (filter->do_only_data) {
337       sendit(NULL, "[");
338
339    /* List resources and directives */
340    /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... }
341     * or print a single item
342     */
343    } else if (filter->do_one || filter->do_list) {
344       sendit(NULL, "{");
345
346    } else {
347    /* [ { "Device": { "Name": "aa",.. } }, { "Director": { "Name": "bb", ... } } ]*/
348       sendit(NULL, "[");
349    }
350
351    first_res = true;
352    /* Loop over all resource types */
353    for (resinx=0; resources[resinx].name; resinx++) {
354       if (!resources[resinx].items) {
355           continue;           /* skip dummy entries */
356       }
357
358       /* Skip this resource type */
359       if (filter->resource_type &&
360           strcasecmp(filter->resource_type, resources[resinx].name) != 0) {
361          continue;
362       }
363
364       directives = 0;
365       /* Loop over all resources of this type */
366       foreach_rblist(res, res_head[resinx]->res_list) {
367          hpkt.res = res;
368          items = resources[resinx].items;
369          if (!items) {
370             continue;
371          }
372
373          /* Copy the resource into res_all */
374          memcpy(&res_all, res, sizeof(res_all));
375
376          if (filter->resource_name) {
377             bool skip=true;
378             /* The Name should be at the first place, so this is not a real loop */
379             for (item=0; items[item].name; item++) {
380                if (strcasecmp(items[item].name, "Name") == 0) {
381                   if (strcasecmp(*(items[item].value), filter->resource_name) == 0) {
382                      skip = false;
383                   }
384                   break;
385                }
386             }
387             if (skip) {         /* The name doesn't match, so skip it */
388                continue;
389             }
390          }
391
392          if (first_res) {
393             sendit(NULL, "\n");
394          } else {
395             sendit(NULL, ",\n");
396          }
397
398          if (filter->do_only_data) {
399             sendit(NULL, " {");
400
401          } else if (filter->do_one) {
402             /* Nothing to print */
403
404          /* When sending the list, the form is:
405           *  { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
406           */
407          } else if (filter->do_list) {
408             /* Search and display Name, should be the first item */
409             for (item=0; items[item].name; item++) {
410                if (strcmp(items[item].name, "Name") == 0) {
411                   sendit(NULL, "%s: {\n", quote_string(hpkt.edbuf2, *items[item].value));
412                   break;
413                }
414             }
415          } else {
416             /* Begin new resource */
417             sendit(NULL, "{\n  \"%s\": {", resources[resinx].name);
418          }
419
420          first_res = false;
421          first_directive = 0;
422          directives = 0;
423          for (item=0; items[item].name; item++) {
424             /* Check user argument -l */
425             if (filter->do_list &&
426                 regexec(&filter->directive_reg,
427                         items[item].name, 32, pmatch, 0) != 0)
428             {
429                continue;
430             }
431
432             hpkt.ritem = &items[item];
433             if (bit_is_set(item, res_all.hdr.item_present)) {
434                if (first_directive++ > 0) printf(",");
435                if (display_global_item(hpkt)) {
436                   /* Fall-through wanted */
437                } else if (items[item].handler == store_maxblocksize) {
438                   display_int32_pair(hpkt);
439                } else if (items[item].handler == store_devtype) {
440                   display_devtype(hpkt);
441                } else if (items[item].handler == store_label) {
442                   display_label(hpkt);
443                } else if (items[item].handler == store_cloud_driver) {
444                   display_cloud_driver(hpkt);
445                } else if (items[item].handler == store_protocol) {
446                   display_protocol(hpkt);
447                } else if (items[item].handler == store_uri_style) {
448                   display_uri_style(hpkt);
449                } else if (items[item].handler == store_truncate) {
450                   display_truncate_cache(hpkt);
451                } else if (items[item].handler == store_upload) {
452                   display_upload(hpkt);
453                } else {
454                   printf("\n      \"%s\": \"null\"", items[item].name);
455                }
456                directives++;
457             } else { /* end if is present */
458                /* For some directive, the bitmap is not set (like addresses) */
459                if (me && strcmp(resources[resinx].name, "Storage") == 0) {
460                   if (strcmp(items[item].name, "SdPort") == 0) {
461                      if (get_first_port_host_order(me->sdaddrs) != items[item].default_value) {
462                         if (first_directive++ > 0) sendit(NULL, ",");
463                         sendit(NULL, "\n    \"SdPort\": %d",
464                            get_first_port_host_order(me->sdaddrs));
465                      }
466                   } else if (me && strcmp(items[item].name, "SdAddress") == 0) {
467                      char buf[500];
468                      get_first_address(me->sdaddrs, buf, sizeof(buf));
469                      if (strcmp(buf, "0.0.0.0") != 0) {
470                         if (first_directive++ > 0) sendit(NULL, ",");
471                         sendit(NULL, "\n    \"SdAddress\": \"%s\"", buf);
472                      }
473                   }
474                }
475             }
476             if (items[item].flags & ITEM_LAST) {
477                display_last(hpkt);    /* If last bit set always call to cleanup */
478             }
479          }
480
481          /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... } */
482          if (filter->do_only_data || filter->do_list) {
483             sendit(NULL, "\n }"); /* Finish the Resource with a single } */
484
485          } else {
486             if (filter->do_one) {
487                /* don't print anything */
488
489             } else if (first_directive > 0) {
490                sendit(NULL, "\n  }\n}");  /* end of resource */
491
492             } else {
493                sendit(NULL, "}\n }");
494             }
495          }
496
497       } /* End loop over all resources of this type */
498    } /* End loop all resource types */
499
500    if (filter->do_only_data) {
501       sendit(NULL, "\n]\n");
502
503    /* In list context, we are dealing with a hash */
504    } else if (filter->do_one || filter->do_list) {
505       sendit(NULL, "\n}\n");
506
507    } else {
508       sendit(NULL, "\n]\n");
509    }
510    term_hpkt(hpkt);
511 }
512
513
514 /* Check Configuration file for necessary info */
515 static int check_resources()
516 {
517    bool OK = true;
518    bool tls_needed;
519    AUTOCHANGER *changer;
520    DEVRES *device;
521
522    me = (STORES *)GetNextRes(R_STORAGE, NULL);
523    if (!me) {
524       Jmsg1(NULL, M_ERROR, 0, _("No Storage resource defined in %s. Cannot continue.\n"),
525          configfile);
526       OK = false;
527    }
528
529    if (GetNextRes(R_STORAGE, (RES *)me) != NULL) {
530       Jmsg1(NULL, M_ERROR, 0, _("Only one Storage resource permitted in %s\n"),
531          configfile);
532       OK = false;
533    }
534    if (GetNextRes(R_DIRECTOR, NULL) == NULL) {
535       Jmsg1(NULL, M_ERROR, 0, _("No Director resource defined in %s. Cannot continue.\n"),
536          configfile);
537       OK = false;
538    }
539    if (GetNextRes(R_DEVICE, NULL) == NULL){
540       Jmsg1(NULL, M_ERROR, 0, _("No Device resource defined in %s. Cannot continue.\n"),
541            configfile);
542       OK = false;
543    }
544
545    if (!me->messages) {
546       me->messages = (MSGS *)GetNextRes(R_MSGS, NULL);
547       if (!me->messages) {
548          Jmsg1(NULL, M_ERROR, 0, _("No Messages resource defined in %s. Cannot continue.\n"),
549             configfile);
550          OK = false;
551       }
552    }
553
554    if (!me->working_directory) {
555       Jmsg1(NULL, M_ERROR, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
556          configfile);
557       OK = false;
558    }
559
560    DIRRES *director;
561    STORES *store;
562    foreach_res(store, R_STORAGE) {
563       /* tls_require implies tls_enable */
564       if (store->tls_require) {
565          if (have_tls) {
566             store->tls_enable = true;
567          } else {
568             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
569             OK = false;
570             continue;
571          }
572       }
573
574       tls_needed = store->tls_enable || store->tls_authenticate;
575
576       if (!store->tls_certfile && tls_needed) {
577          Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Storage \"%s\" in %s.\n"),
578               store->hdr.name, configfile);
579          OK = false;
580       }
581
582       if (!store->tls_keyfile && tls_needed) {
583          Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Storage \"%s\" in %s.\n"),
584               store->hdr.name, configfile);
585          OK = false;
586       }
587
588       if ((!store->tls_ca_certfile && !store->tls_ca_certdir) && tls_needed && store->tls_verify_peer) {
589          Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
590               " or \"TLS CA Certificate Dir\" are defined for Storage \"%s\" in %s."
591               " At least one CA certificate store is required"
592               " when using \"TLS Verify Peer\".\n"),
593               store->hdr.name, configfile);
594          OK = false;
595       }
596    }
597
598    foreach_res(director, R_DIRECTOR) {
599       /* tls_require implies tls_enable */
600       if (director->tls_require) {
601          director->tls_enable = true;
602       }
603
604       tls_needed = director->tls_enable || director->tls_authenticate;
605
606       if (!director->tls_certfile && tls_needed) {
607          Jmsg(NULL, M_FATAL, 0, _("\"TLS Certificate\" file not defined for Director \"%s\" in %s.\n"),
608               director->hdr.name, configfile);
609          OK = false;
610       }
611
612       if (!director->tls_keyfile && tls_needed) {
613          Jmsg(NULL, M_FATAL, 0, _("\"TLS Key\" file not defined for Director \"%s\" in %s.\n"),
614               director->hdr.name, configfile);
615          OK = false;
616       }
617
618       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed && director->tls_verify_peer) {
619          Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
620               " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
621               " At least one CA certificate store is required"
622               " when using \"TLS Verify Peer\".\n"),
623               director->hdr.name, configfile);
624          OK = false;
625       }
626    }
627
628    foreach_res(changer, R_AUTOCHANGER) {
629       foreach_alist(device, changer->device) {
630          device->cap_bits |= CAP_AUTOCHANGER;
631       }
632    }
633
634    return OK;
635 }
636
637 /* Clean up and then exit */
638 void terminate_stored(int sig)
639 {
640    static bool in_here = false;
641
642    if (in_here) {                     /* prevent loops */
643       bmicrosleep(2, 0);              /* yield */
644       exit(1);
645    }
646    in_here = true;
647    debug_level = 0;                   /* turn off any debug */
648
649    if (configfile) {
650       free(configfile);
651       configfile = NULL;
652    }
653    if (config) {
654       delete config;
655       config = NULL;
656    }
657
658    if (debug_level > 10) {
659       print_memory_pool_stats();
660    }
661    term_msg();
662    free(res_head);
663    res_head = NULL;
664    close_memory_pool();
665
666    //sm_dump(false);                    /* dump orphaned buffers */
667    exit(sig);
668 }
669
670 static void sendit(void *sock, const char *fmt, ...)
671 {
672    char buf[3000];
673    va_list arg_ptr;
674
675    va_start(arg_ptr, fmt);
676    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
677    va_end(arg_ptr);
678    fputs(buf, stdout);
679    fflush(stdout);
680 }