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