]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/bfdjson.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / filed / bfdjson.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 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 File Daemon to Json
21  *
22  *    Kern Sibbald, Sept MMXII
23  *
24  */
25
26 #include "bacula.h"
27 #include "filed.h"
28
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);
32
33 typedef struct
34 {
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;        /* [ {}, {}, {}, ] */
39    char *resource_type;
40    char *resource_name;
41    regex_t directive_reg;
42 } display_filter;
43
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);
48
49 /* Exported variables */
50 CLIENT *me;                           /* my resource */
51 #if defined(_MSC_VER)
52 extern "C" { // work around visual compiler mangling variables
53    extern URES res_all;
54 }
55 #else
56 extern URES res_all;
57 #endif
58 extern s_kw msg_types[];
59 extern RES_TABLE resources[];
60
61 #define CONFIG_FILE "bacula-fd.conf" /* default config file */
62
63 char *configfile = NULL;
64 static CONFIG *config;
65
66 static void usage()
67 {
68    fprintf(stderr, _(
69 PROG_COPYRIGHT
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"
75 "        -D          get only data\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);
83
84    exit(1);
85 }
86
87
88 /*********************************************************************
89  *
90  *  Bacula File daemon to Json
91  *
92  */
93
94 int main (int argc, char *argv[])
95 {
96    int ch;
97    bool test_config = false;
98    display_filter filter;
99    memset(&filter, 0, sizeof(filter));
100
101    setlocale(LC_ALL, "");
102    bindtextdomain("bacula", LOCALEDIR);
103    textdomain("bacula");
104
105    my_name_is(argc, argv, "bacula-fd");
106    init_msg(NULL, NULL);
107
108    while ((ch = getopt(argc, argv, "Dr:n:c:d:tv?l:")) != -1) {
109       switch (ch) {
110       case 'D':
111          filter.do_only_data = true;
112          break;
113       case 'l':
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);
119          }
120          break;
121
122       case 'r':
123          filter.resource_type = optarg;
124          break;
125
126       case 'n':
127          filter.resource_name = optarg;
128          break;
129
130       case 'c':                    /* configuration file */
131          if (configfile != NULL) {
132             free(configfile);
133          }
134          configfile = bstrdup(optarg);
135          break;
136
137       case 'd':                    /* debug level */
138          if (*optarg == 't') {
139             dbg_timestamp = true;
140          } else {
141             debug_level = atoi(optarg);
142             if (debug_level <= 0) {
143                debug_level = 1;
144             }
145          }
146          break;
147
148       case 't':
149          test_config = true;
150          break;
151
152       case 'v':                    /* verbose */
153          verbose++;
154          break;
155
156       case '?':
157       default:
158          usage();
159
160       }
161    }
162    argc -= optind;
163    argv += optind;
164
165    if (argc) {
166       if (configfile != NULL) {
167          free(configfile);
168       }
169       configfile = bstrdup(*argv);
170       argc--;
171       argv++;
172    }
173    if (argc) {
174       usage();
175    }
176
177    if (filter.do_list && !filter.resource_type) {
178       usage();
179    }
180
181    if (filter.resource_type && filter.resource_name) {
182       filter.do_one = true;
183    }
184
185    if (configfile == NULL || configfile[0] == 0) {
186       configfile = bstrdup(CONFIG_FILE);
187    }
188
189    if (test_config && verbose > 0) {
190       char buf[1024];
191       find_config_file(configfile, buf, sizeof(buf));
192       sendit(NULL, "config_file=%s\n", buf);
193    }
194
195    config = New(CONFIG());
196    config->encode_password(false);
197    parse_fd_config(config, configfile, M_ERROR_TERM);
198
199    if (!check_resources()) {
200       Emsg1(M_ERROR, 0, _("Please correct configuration file: %s\n"), configfile);
201       terminate_filed(1);
202    }
203
204    if (test_config) {
205       terminate_filed(0);
206    }
207
208    dump_json(&filter);
209
210    if (filter.do_list) {
211       regfree(&filter.directive_reg);
212    }
213
214    terminate_filed(0);
215    exit(0);                           /* should never get here */
216 }
217
218 void terminate_filed(int sig)
219 {
220    static bool already_here = false;
221
222    if (already_here) {
223       bmicrosleep(2, 0);              /* yield */
224       exit(1);                        /* prevent loops */
225    }
226    already_here = true;
227    debug_level = 0;                   /* turn off debug */
228
229    if (configfile != NULL) {
230       free(configfile);
231    }
232
233    if (debug_level > 0) {
234       print_memory_pool_stats();
235    }
236    if (config) {
237       delete config;
238       config = NULL;
239    }
240    term_msg();
241    free(res_head);
242    res_head = NULL;
243    close_memory_pool();               /* release free memory in pool */
244    //sm_dump(false);                    /* dump orphaned buffers */
245    exit(sig);
246 }
247
248 /*
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.
253  */
254 static void dump_json(display_filter *filter)
255 {
256    int resinx, item, directives, first_directive;
257    bool first_res;
258    RES_ITEM *items;
259    RES *res;
260    HPKT hpkt;
261    regmatch_t pmatch[32];
262
263    init_hpkt(hpkt);
264
265    me = (CLIENT *)GetNextRes(R_CLIENT, NULL);
266
267    if (filter->do_only_data) {
268       sendit(NULL, "[");
269
270    /* List resources and directives */
271    /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... }
272     * or print a single item
273     */
274    } else if (filter->do_one || filter->do_list) {
275       sendit(NULL, "{");
276
277    } else {
278    /* [ { "Client": { "Name": "aa",.. } }, { "Director": { "Name": "bb", ... } } ]*/
279       sendit(NULL, "[");
280    }
281
282    first_res = true;
283
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) {
288          continue;
289       }
290
291       /* Skip this resource type */
292       if (filter->resource_type &&
293           strcasecmp(filter->resource_type, resources[resinx].name) != 0) {
294          continue;
295       }
296
297       directives = 0;
298       /* Loop over all resources of this type */
299       foreach_rblist(res, res_head[resinx]->res_list) {
300          hpkt.res = res;
301          items = resources[resinx].items;
302          if (!items) {
303             break;
304          }
305          /* Copy the resource into res_all */
306          memcpy(&res_all, res, sizeof(res_all));
307
308          if (filter->resource_name) {
309             bool skip=true;
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) {
314                      skip = false;
315                   }
316                   break;
317                }
318             }
319             if (skip) {         /* The name doesn't match, so skip it */
320                continue;
321             }
322          }
323
324          if (!first_res) {
325             printf(",\n");
326          }
327
328          first_directive = 0;
329
330          if (filter->do_only_data) {
331             sendit(NULL, " {");
332
333          } else if (filter->do_one) {
334             /* Nothing to print */
335
336          /* When sending the list, the form is:
337           *  { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
338           */
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));
344                   break;
345                }
346             }
347          } else {
348             /* Begin new resource */
349             sendit(NULL, "{\n  \"%s\": {", resources[resinx].name);
350          }
351
352          /* dirtry trick for a deprecated directive */
353          bool dedup_index_directory_set = false;
354
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)
361             {
362                continue;
363             }
364
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);
368                continue;
369             }
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;
374                }
375             }
376
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 */
382                } else {
383                   printf("\n    \"%s\": null", items[item].name);
384                }
385                directives++;
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));
394                      }
395                   } else if (me && strcmp(items[item].name, "FdAddress") == 0) {
396                      char buf[500];
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);
401                      }
402                   } else if (me && strcmp(items[item].name, "FdSourceAddress") == 0
403                              && me->FDsrc_addr) {
404                      char buf[500];
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);
409                      }
410                   }
411                }
412             }
413             if (items[item].flags & ITEM_LAST) {
414                display_last(hpkt);    /* If last bit set always call to cleanup */
415             }
416          }
417
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 } */
421
422          } else {
423             if (filter->do_one) {
424                /* don't print anything */
425             } else if (first_directive > 0) {
426                sendit(NULL, "\n  }\n}");  /* end of resource */
427             } else {
428                sendit(NULL, "}\n}");
429             }
430          }
431          first_res = false;
432       } /* End loop over all resources of this type */
433    } /* End loop all resource types */
434
435    if (filter->do_only_data) {
436       sendit(NULL, "\n]\n");
437
438    /* In list context, we are dealing with a hash */
439    } else if (filter->do_one || filter->do_list) {
440       sendit(NULL, "\n}\n");
441
442    } else {
443       sendit(NULL, "\n]\n");
444    }
445    term_hpkt(hpkt);
446 }
447
448
449 /*
450 * Make a quick check to see that we have all the
451 * resources needed.
452 */
453 static bool check_resources()
454 {
455    bool OK = true;
456    DIRRES *director;
457    CONSRES *cons;
458    bool need_tls;
459
460    LockRes();
461
462    me = (CLIENT *)GetNextRes(R_CLIENT, NULL);
463    if (!me) {
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);
466       OK = false;
467    } else {
468       if (GetNextRes(R_CLIENT, (RES *) me) != NULL) {
469          Emsg1(M_FATAL, 0, _("Only one Client resource permitted in %s\n"),
470               configfile);
471          OK = false;
472       }
473       my_name_is(0, NULL, me->hdr.name);
474       if (!me->messages) {
475          me->messages = (MSGS *)GetNextRes(R_MSGS, NULL);
476          if (!me->messages) {
477              Emsg1(M_FATAL, 0, _("No Messages resource defined in %s\n"), configfile);
478              OK = false;
479          }
480       }
481       /* tls_require implies tls_enable */
482       if (me->tls_require) {
483 #ifndef HAVE_TLS
484          Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
485          OK = false;
486 #else
487          me->tls_enable = true;
488 #endif
489       }
490       need_tls = me->tls_enable || me->tls_authenticate;
491
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"),
495                             configfile);
496         OK = false;
497       }
498
499       /* pki_encrypt implies pki_sign */
500       if (me->pki_encrypt) {
501          me->pki_sign = true;
502       }
503
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);
508          OK = false;
509       }
510    }
511
512
513    /* Verify that a director record exists */
514    LockRes();
515    director = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
516    UnlockRes();
517    if (!director) {
518       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"),
519             configfile);
520       OK = false;
521    }
522
523    foreach_res(director, R_DIRECTOR) {
524       /* tls_require implies tls_enable */
525       if (director->tls_require) {
526 #ifndef HAVE_TLS
527          Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
528          OK = false;
529          continue;
530 #else
531          director->tls_enable = true;
532 #endif
533       }
534       need_tls = director->tls_enable || director->tls_authenticate;
535
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);
539          OK = false;
540       }
541
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);
545          OK = false;
546       }
547
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);
554          OK = false;
555       }
556    }
557
558    foreach_res(cons, R_CONSOLE) {
559       /* tls_require implies tls_enable */
560       if (cons->tls_require) {
561 #ifndef HAVE_TLS
562          Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
563          OK = false;
564          continue;
565 #else
566          cons->tls_enable = true;
567 #endif
568       }
569       need_tls = cons->tls_enable || cons->tls_authenticate;
570
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);
574          OK = false;
575       }
576
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);
580          OK = false;
581       }
582
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);
589          OK = false;
590       }
591    }
592
593    UnlockRes();
594
595    return OK;
596 }
597
598 static void sendit(void *sock, const char *fmt, ...)
599 {
600    char buf[3000];
601    va_list arg_ptr;
602
603    va_start(arg_ptr, fmt);
604    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
605    va_end(arg_ptr);
606    fputs(buf, stdout);
607    fflush(stdout);
608 }