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 The licensor of Bacula is the Free Software Foundation Europe
19 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
20 Switzerland, email:ftf@fsfeurope.org.
21 */
22 /*
23 *
24 * Bacula Console .conf to Json program.
25 *
26 * Kern Sibbald, September MMXII
27 *
28 */
29
30 #include "bacula.h"
31 #include "lib/breg.h"
32 #include "console_conf.h"
33 #include "jcr.h"
34
35 /* Imported variables */
36 #if defined(_MSC_VER)
37 extern "C" { // work around visual compiler mangling variables
38 extern URES res_all;
39 }
40 #else
41 extern URES res_all;
42 #endif
43 extern s_kw msg_types[];
44 extern RES_TABLE resources[];
45
46 /* Exported functions */
47 void senditf(const char *fmt, ...);
48 void sendit(const char *buf);
49
50 /* Imported functions */
51 extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
52
53 typedef struct
54 {
55 /* default { { "Director": { "Name": aa, ...} }, { "Job": {..} */
56 bool do_list; /* [ {}, {}, ..] or { "aa": {}, "bb": {}, ...} */
57 bool do_one; /* { "Name": "aa", "Description": "test, ... } */
58 bool do_only_data; /* [ {}, {}, {}, ] */
59 char *resource_type;
60 char *resource_name;
61 regex_t directive_reg;
62 } display_filter;
63
64 /* Forward referenced functions */
65 static void terminate_console(int sig);
66 static int check_resources();
67 //static void ressendit(void *ua, const char *fmt, ...);
68 //static void dump_resource_types();
69 //static void dump_directives();
70 static void dump_json(display_filter *filter);
71
72 /* Static variables */
73 static char *configfile = NULL;
74 static FILE *output = stdout;
75 static int numdir;
76 static POOLMEM *args;
77 static CONFIG *config;
78
79
80 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
81
usage()82 static void usage()
83 {
84 fprintf(stderr, _(
85 PROG_COPYRIGHT
86 "\n%sVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
87 "Usage: bconsjson [options] [config_file]\n"
88 " -r <res> get resource type <res>\n"
89 " -n <name> get resource <name>\n"
90 " -l <dirs> get only directives matching dirs (use with -r)\n"
91 " -D get only data\n"
92 " -c <file> set configuration file to file\n"
93 " -d <nn> set debug level to <nn>\n"
94 " -dt print timestamp in debug output\n"
95 " -t test - read configuration and exit\n"
96 " -v verbose\n"
97 " -? print this message.\n"
98 "\n"), 2012, BDEMO, HOST_OS, DISTNAME, DISTVER);
99
100 exit(1);
101 }
102
103 /*********************************************************************
104 *
105 * Bacula console conf to Json
106 *
107 */
main(int argc,char * argv[])108 int main(int argc, char *argv[])
109 {
110 int ch;
111 bool test_config = false;
112 display_filter filter;
113 memset(&filter, 0, sizeof(filter));
114 int rtn = 0;
115
116 setlocale(LC_ALL, "");
117 bindtextdomain("bacula", LOCALEDIR);
118 textdomain("bacula");
119
120 if (init_crypto() != 0) {
121 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
122 }
123 init_stack_dump();
124 init_signals(terminate_console);
125 lmgr_init_thread();
126 my_name_is(argc, argv, "bconsole");
127 init_msg(NULL, NULL);
128 working_directory = "/tmp";
129 args = get_pool_memory(PM_FNAME);
130
131 while ((ch = getopt(argc, argv, "n:vDabc:d:jl:r:t?")) != -1) {
132 switch (ch) {
133 case 'D':
134 filter.do_only_data = true;
135 break;
136 case 'a':
137 // list_all = true;
138 break;
139
140 case 'c': /* configuration file */
141 if (configfile != NULL) {
142 free(configfile);
143 }
144 configfile = bstrdup(optarg);
145 break;
146
147 break;
148
149 case 'd':
150 if (*optarg == 't') {
151 dbg_timestamp = true;
152 } else {
153 debug_level = atoi(optarg);
154 if (debug_level <= 0) {
155 debug_level = 1;
156 }
157 }
158
159 case 'l':
160 filter.do_list = true;
161 if (regcomp(&filter.directive_reg, optarg, REG_EXTENDED) != 0) {
162 Jmsg((JCR *)NULL, M_ERROR_TERM, 0,
163 _("Please use valid -l argument: %s\n"), optarg);
164 }
165 break;
166
167 case 'r':
168 filter.resource_type = optarg;
169 break;
170
171 case 'n':
172 filter.resource_name = optarg;
173 break;
174
175 case 't':
176 test_config = true;
177 break;
178
179 case 'v': /* verbose */
180 verbose++;
181 break;
182
183 case '?':
184 default:
185 usage();
186 exit(1);
187 }
188 }
189 argc -= optind;
190 argv += optind;
191
192 OSDependentInit();
193
194 if (argc) {
195 usage();
196 exit(1);
197 }
198
199 if (filter.do_list && !filter.resource_type) {
200 usage();
201 }
202
203 if (filter.resource_type && filter.resource_name) {
204 filter.do_one = true;
205 }
206
207 if (configfile == NULL || configfile[0] == 0) {
208 configfile = bstrdup(CONFIG_FILE);
209 }
210
211 if (test_config && verbose > 0) {
212 char buf[1024];
213 find_config_file(configfile, buf, sizeof(buf));
214 printf("config_file=%s\n", buf);
215 }
216
217 config = New(CONFIG());
218 config->encode_password(false);
219 parse_cons_config(config, configfile, M_ERROR_TERM);
220
221 if (!check_resources()) {
222 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
223 }
224
225 if (test_config) {
226 terminate_console(0);
227 exit(0);
228 }
229
230 dump_json(&filter);
231
232 terminate_console(0);
233 return rtn;
234 }
235
236 /* Cleanup and then exit */
terminate_console(int sig)237 static void terminate_console(int sig)
238 {
239 static bool already_here = false;
240
241 if (already_here) { /* avoid recursive temination problems */
242 exit(1);
243 }
244 already_here = true;
245 stop_watchdog();
246 delete config;
247 config = NULL;
248 free(res_head);
249 res_head = NULL;
250 free_pool_memory(args);
251 lmgr_cleanup_main();
252
253 if (sig != 0) {
254 exit(1);
255 }
256 return;
257 }
258
259
260 /*
261 * Dump out all resources in json format.
262 * Note!!!! This routine must be in this file rather
263 * than in src/lib/parser_conf.c otherwise the pointers
264 * will be all messed up.
265 */
dump_json(display_filter * filter)266 static void dump_json(display_filter *filter)
267 {
268 int resinx, item, sz;
269 int directives;
270 bool first_res;
271 bool first_directive;
272 RES_ITEM *items;
273 RES *res;
274 HPKT hpkt;
275 regmatch_t pmatch[32];
276
277 init_hpkt(hpkt);
278
279 /* List resources and directives */
280 if (filter->do_only_data) {
281 printf("[");
282
283 /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... }
284 * or print a single item
285 */
286 } else if (filter->do_one || filter->do_list) {
287 printf("{");
288
289 } else {
290 /* [ { "Client": { "Name": "aa",.. } }, { "Director": { "Name": "bb", ... } } ]*/
291 printf("[");
292 }
293
294 first_res = true;
295 /* Loop over all resource types */
296 for (resinx=0; resources[resinx].name; resinx++) {
297 /* Skip this resource type */
298 if (filter->resource_type &&
299 strcasecmp(filter->resource_type, resources[resinx].name) != 0) {
300 continue;
301 }
302
303 directives = 0;
304 /* Loop over all resources of this type */
305 foreach_rblist(res, res_head[resinx]->res_list) {
306 hpkt.res = res;
307 items = resources[resinx].items;
308 if (!items) {
309 break;
310 }
311
312 sz = get_resource_size(resinx + r_first);
313 if (sz < 0) {
314 Dmsg1(0, "Unknown resource type %d\n", resinx);
315 continue;
316 }
317
318 /* Copy the resource into res_all */
319 memcpy(&res_all, res, sz);
320
321 if (filter->resource_name) {
322 bool skip=true;
323 /* The Name should be at the first place, so this is not a real loop */
324 for (item=0; items[item].name; item++) {
325 if (strcasecmp(items[item].name, "Name") == 0) {
326 if (strcasecmp(*(items[item].value), filter->resource_name) == 0) {
327 skip = false;
328 }
329 break;
330 }
331 }
332 if (skip) { /* The name doesn't match, so skip it */
333 continue;
334 }
335 }
336
337 if (first_res) {
338 printf("\n");
339
340 } else {
341 printf(",\n");
342 }
343
344 if (filter->do_only_data) {
345 printf(" {");
346
347 } else if (filter->do_one) {
348 /* Nothing to print */
349
350 /* When sending the list, the form is:
351 * { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
352 */
353 } else if (filter->do_list) {
354 /* Search and display Name, should be the first item */
355 for (item=0; items[item].name; item++) {
356 if (strcmp(items[item].name, "Name") == 0) {
357 printf("%s: {\n", quote_string(hpkt.edbuf, *items[item].value));
358 break;
359 }
360 }
361 } else {
362 /* Begin new resource */
363 printf("{\n \"%s\": {", resources[resinx].name);
364 }
365
366 first_res = false;
367 first_directive = true;
368 directives = 0;
369
370 for (item=0; items[item].name; item++) {
371 /* Check user argument -l */
372 if (filter->do_list &&
373 regexec(&filter->directive_reg,
374 items[item].name, 32, pmatch, 0) != 0)
375 {
376 continue;
377 }
378
379 hpkt.ritem = &items[item];
380 if (bit_is_set(item, res_all.hdr.item_present)) {
381 if (!first_directive) printf(",");
382 /* 1: found, 0: not found, -1 found but empty */
383 int ret = display_global_item(hpkt);
384 if (ret == -1) {
385 /* Do not print a comma after this empty directive */
386 first_directive = 0;
387 } else if (ret == 1) {
388 /* Fall-through wanted */
389
390 } else {
391 printf("\n \"%s\": null", items[item].name);
392 }
393 directives++;
394 first_directive = false;
395 }
396 if (items[item].flags & ITEM_LAST) {
397 display_last(hpkt); /* If last bit set always call to cleanup */
398 }
399 }
400 /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... } */
401 if (filter->do_only_data || filter->do_list) {
402 printf("\n }"); /* Finish the Resource with a single } */
403
404 } else {
405 if (filter->do_one) {
406 /* don't print anything */
407 } else if (directives) {
408 printf("\n }\n}"); /* end of resource */
409 } else {
410 printf("}\n}");
411 }
412 }
413 } /* End loop over all resources of this type */
414 } /* End loop all resource types */
415
416 if (filter->do_only_data) {
417 printf("\n]\n");
418
419 } else if (filter->do_one || filter->do_list) {
420 printf("\n}\n");
421
422 } else {
423 printf("\n]\n");
424 }
425 term_hpkt(hpkt);
426 }
427
428
429 /*
430 * Make a quick check to see that we have all the
431 * resources needed.
432 */
check_resources()433 static int check_resources()
434 {
435 bool OK = true;
436 DIRRES *director;
437 bool tls_needed;
438
439 LockRes();
440
441 numdir = 0;
442 foreach_res(director, R_DIRECTOR) {
443
444 numdir++;
445 /* tls_require implies tls_enable */
446 if (director->tls_require) {
447 if (have_tls) {
448 if (director->tls_ca_certfile || director->tls_ca_certdir) {
449 director->tls_enable = true;
450 }
451 } else {
452 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
453 OK = false;
454 continue;
455 }
456 }
457
458 tls_needed = director->tls_enable || director->tls_authenticate;
459
460 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
461 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
462 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
463 " At least one CA certificate store is required.\n"),
464 director->hdr.name, configfile);
465 OK = false;
466 }
467 }
468
469 if (numdir == 0) {
470 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
471 "Without that I don't how to speak to the Director :-(\n"), configfile);
472 OK = false;
473 }
474
475 CONRES *cons;
476 /* Loop over Consoles */
477 foreach_res(cons, R_CONSOLE) {
478 /* tls_require implies tls_enable */
479 if (cons->tls_require) {
480 if (have_tls) {
481 cons->tls_enable = true;
482 } else {
483 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
484 OK = false;
485 continue;
486 }
487 }
488 tls_needed = cons->tls_enable || cons->tls_authenticate;
489 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
490 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
491 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
492 cons->hdr.name, configfile);
493 OK = false;
494 }
495 }
496
497 UnlockRes();
498
499 return OK;
500 }
501
502 #ifdef needed
ressendit(void * sock,const char * fmt,...)503 static void ressendit(void *sock, const char *fmt, ...)
504 {
505 char buf[3000];
506 va_list arg_ptr;
507
508 va_start(arg_ptr, fmt);
509 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
510 va_end(arg_ptr);
511 sendit(buf);
512 }
513
dump_resource_types()514 static void dump_resource_types()
515 {
516 int i;
517 bool first;
518
519 /* List resources and their code */
520 printf("[\n");
521 first = true;
522 for (i=0; resources[i].name; i++) {
523 if (!first) {
524 printf(",\n");
525 }
526 printf(" \"%s\": %d", resources[i].name, resources[i].rcode);
527 first = false;
528 }
529 printf("\n]\n");
530 }
531
dump_directives()532 static void dump_directives()
533 {
534 int i, j;
535 bool first_res;
536 bool first_directive;
537 RES_ITEM *items;
538
539 /* List resources and directives */
540 printf("[\n");
541 first_res = true;
542 for (i=0; resources[i].name; i++) {
543 if (!first_res) {
544 printf(",\n");
545 }
546 printf("{\n \"%s\": {\n", resources[i].name);
547 first_res = false;
548 first_directive = true;
549 items = resources[i].items;
550 for (j=0; items[j].name; j++) {
551 if (!first_directive) {
552 printf(",\n");
553 }
554 printf(" \"%s\": null", items[j].name);
555 first_directive = false;
556 }
557 printf("\n }"); /* end of resource */
558 }
559 printf("\n]\n");
560 }
561
562 /*
563 * Send a line to the output file and or the terminal
564 */
senditf(const char * fmt,...)565 void senditf(const char *fmt,...)
566 {
567 char buf[3000];
568 va_list arg_ptr;
569
570 va_start(arg_ptr, fmt);
571 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
572 va_end(arg_ptr);
573 sendit(buf);
574 }
575
576 static bool teeout = false; /* output to output and stdout */
577
sendit(const char * buf)578 void sendit(const char *buf)
579 {
580 #ifdef CONIO_FIX
581 char obuf[3000];
582 if (output == stdout || teeout) {
583 const char *p, *q;
584 /*
585 * Here, we convert every \n into \r\n because the
586 * terminal is in raw mode when we are using
587 * conio.
588 */
589 for (p=q=buf; (p=strchr(q, '\n')); ) {
590 int len = p - q;
591 if (len > 0) {
592 memcpy(obuf, q, len);
593 }
594 memcpy(obuf+len, "\r\n", 3);
595 q = ++p; /* point after \n */
596 fputs(obuf, output);
597 }
598 if (*q) {
599 fputs(q, output);
600 }
601 fflush(output);
602 }
603 if (output != stdout) {
604 fputs(buf, output);
605 }
606 #else
607
608 fputs(buf, output);
609 fflush(output);
610 if (teeout) {
611 fputs(buf, stdout);
612 fflush(stdout);
613 }
614 #endif
615 }
616 #endif
617