]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/bbconsjson.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / console / bbconsjson.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  *
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
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, "", HOST_OS, DISTNAME, DISTVER);
97
98    exit(1);
99 }
100
101 /*********************************************************************
102  *
103  *        Bacula console conf to Json
104  *
105  */
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    init_stack_dump();
119    lmgr_init_thread();
120    my_name_is(argc, argv, "bconsole");
121    init_msg(NULL, NULL);
122    working_directory = "/tmp";
123    args = get_pool_memory(PM_FNAME);
124
125    while ((ch = getopt(argc, argv, "n:vDabc:d:jl:r:t?")) != -1) {
126       switch (ch) {
127       case 'D':
128          filter.do_only_data = true;
129          break;
130       case 'a':
131 //         list_all = true;
132          break;
133
134       case 'c':                    /* configuration file */
135          if (configfile != NULL) {
136             free(configfile);
137          }
138          configfile = bstrdup(optarg);
139          break;
140
141          break;
142
143       case 'd':
144          if (*optarg == 't') {
145             dbg_timestamp = true;
146          } else {
147             debug_level = atoi(optarg);
148             if (debug_level <= 0) {
149                debug_level = 1;
150             }
151          }
152
153       case 'l':
154          filter.do_list = true;
155          if (regcomp(&filter.directive_reg, optarg, REG_EXTENDED) != 0) {
156             Jmsg((JCR *)NULL, M_ERROR_TERM, 0,
157                  _("Please use valid -l argument: %s\n"), optarg);
158          }
159          break;
160
161       case 'r':
162          filter.resource_type = optarg;
163          break;
164
165       case 'n':
166          filter.resource_name = optarg;
167          break;
168
169       case 't':
170          test_config = true;
171          break;
172
173       case 'v':                    /* verbose */
174          verbose++;
175          break;
176
177       case '?':
178       default:
179          usage();
180          exit(1);
181       }
182    }
183    argc -= optind;
184    argv += optind;
185
186    OSDependentInit();
187
188    if (argc) {
189       usage();
190       exit(1);
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       printf("config_file=%s\n", buf);
209    }
210
211    config = New(CONFIG());
212    config->encode_password(false);
213    parse_cons_config(config, configfile, M_ERROR_TERM);
214
215    if (!check_resources()) {
216       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
217    }
218
219    if (test_config) {
220       terminate_console(0);
221       exit(0);
222    }
223
224    dump_json(&filter);
225
226    terminate_console(0);
227    return rtn;
228 }
229
230 /* Cleanup and then exit */
231 static void terminate_console(int sig)
232 {
233    static bool already_here = false;
234
235    if (already_here) {                /* avoid recursive temination problems */
236       exit(1);
237    }
238    already_here = true;
239    stop_watchdog();
240    delete config;
241    config = NULL;
242    free(res_head);
243    res_head = NULL;
244    free_pool_memory(args);
245    lmgr_cleanup_main();
246
247    if (sig != 0) {
248       exit(1);
249    }
250    return;
251 }
252
253
254 /*
255  * Dump out all resources in json format.
256  * Note!!!! This routine must be in this file rather
257  *  than in src/lib/parser_conf.c otherwise the pointers
258  *  will be all messed up.
259  */
260 static void dump_json(display_filter *filter)
261 {
262    int resinx, item;
263    int directives;
264    bool first_res;
265    bool first_directive;
266    RES_ITEM *items;
267    RES *res;
268    HPKT hpkt;
269    regmatch_t pmatch[32];
270
271    init_hpkt(hpkt);
272
273    /* List resources and directives */
274    if (filter->do_only_data) {
275       printf("[");
276
277    /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... }
278     * or print a single item
279     */
280    } else if (filter->do_one ||  filter->do_list) {
281       printf("{");
282
283    } else {
284    /* [ { "Client": { "Name": "aa",.. } }, { "Director": { "Name": "bb", ... } } ]*/
285       printf("[");
286    }
287
288    first_res = true;
289    /* Loop over all resource types */
290    for (resinx=0; resources[resinx].name; resinx++) {
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
306          /* Copy the resource into res_all */
307          memcpy(&res_all, res, sizeof(res_all));
308
309          if (filter->resource_name) {
310             bool skip=true;
311             /* The Name should be at the first place, so this is not a real loop */
312             for (item=0; items[item].name; item++) {
313                if (strcasecmp(items[item].name, "Name") == 0) {
314                   if (strcasecmp(*(items[item].value), filter->resource_name) == 0) {
315                      skip = false;
316                   }
317                   break;
318                }
319             }
320             if (skip) {         /* The name doesn't match, so skip it */
321                continue;
322             }
323          }
324
325          if (first_res) {
326             printf("\n");
327
328          } else {
329             printf(",\n");
330          }
331
332          if (filter->do_only_data) {
333             printf(" {");
334
335          } else if (filter->do_one) {
336             /* Nothing to print */
337
338          /* When sending the list, the form is:
339           *  { aa: { Name: aa, Description: aadesc...}, bb: { Name: bb
340           */
341          } else if (filter->do_list) {
342             /* Search and display Name, should be the first item */
343             for (item=0; items[item].name; item++) {
344                if (strcmp(items[item].name, "Name") == 0) {
345                   printf("%s: {\n", quote_string(hpkt.edbuf, *items[item].value));
346                   break;
347                }
348             }
349          } else {
350             /* Begin new resource */
351             printf("{\n  \"%s\": {", resources[resinx].name);
352          }
353
354          first_res = false;
355          first_directive = true;
356          directives = 0;
357
358          for (item=0; items[item].name; item++) {
359             /* Check user argument -l */
360             if (filter->do_list &&
361                 regexec(&filter->directive_reg,
362                         items[item].name, 32, pmatch, 0) != 0)
363             {
364                continue;
365             }
366
367             hpkt.ritem = &items[item];
368             if (bit_is_set(item, res_all.hdr.item_present)) {
369                if (!first_directive) printf(",");
370                if (display_global_item(hpkt)) {
371                   /* Fall-through wanted */
372                } else {
373                   printf("\n      \"%s\": null", items[item].name);
374                }
375                directives++;
376                first_directive = false;
377             }
378             if (items[item].flags & ITEM_LAST) {
379                display_last(hpkt);    /* If last bit set always call to cleanup */
380             }
381          }
382          /* { "aa": { "Name": "aa",.. }, "bb": { "Name": "bb", ... } */
383          if (filter->do_only_data || filter->do_list) {
384             printf("\n }"); /* Finish the Resource with a single } */
385
386          } else {
387             if (filter->do_one) {
388                /* don't print anything */
389             } else if (directives) {
390                printf("\n   }\n}");  /* end of resource */
391             } else {
392                printf("}\n}");
393             }
394          }
395       } /* End loop over all resources of this type */
396    } /* End loop all resource types */
397
398    if (filter->do_only_data) {
399       printf("\n]\n");
400
401    } else  if (filter->do_one || filter->do_list) {
402       printf("\n}\n");
403
404    } else {
405       printf("\n]\n");
406    }
407    term_hpkt(hpkt);
408 }
409
410
411 /*
412  * Make a quick check to see that we have all the
413  * resources needed.
414  */
415 static int check_resources()
416 {
417    bool OK = true;
418    DIRRES *director;
419    bool tls_needed;
420
421    LockRes();
422
423    numdir = 0;
424    foreach_res(director, R_DIRECTOR) {
425
426       numdir++;
427       /* tls_require implies tls_enable */
428       if (director->tls_require) {
429          if (have_tls) {
430             director->tls_enable = true;
431          } else {
432             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
433             OK = false;
434             continue;
435          }
436       }
437
438       tls_needed = director->tls_enable || director->tls_authenticate;
439
440       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
441          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
442                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
443                              " At least one CA certificate store is required.\n"),
444                              director->hdr.name, configfile);
445          OK = false;
446       }
447    }
448
449    if (numdir == 0) {
450       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
451                           "Without that I don't how to speak to the Director :-(\n"), configfile);
452       OK = false;
453    }
454
455    CONRES *cons;
456    /* Loop over Consoles */
457    foreach_res(cons, R_CONSOLE) {
458       /* tls_require implies tls_enable */
459       if (cons->tls_require) {
460          if (have_tls) {
461             cons->tls_enable = true;
462          } else {
463             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
464             OK = false;
465             continue;
466          }
467       }
468       tls_needed = cons->tls_enable || cons->tls_authenticate;
469       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
470          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
471                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
472                              cons->hdr.name, configfile);
473          OK = false;
474       }
475    }
476
477    UnlockRes();
478
479    return OK;
480 }
481
482 #ifdef needed
483 static void ressendit(void *sock, const char *fmt, ...)
484 {
485    char buf[3000];
486    va_list arg_ptr;
487
488    va_start(arg_ptr, fmt);
489    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
490    va_end(arg_ptr);
491    sendit(buf);
492 }
493
494 static void dump_resource_types()
495 {
496    int i;
497    bool first;
498
499    /* List resources and their code */
500    printf("[\n");
501    first = true;
502    for (i=0; resources[i].name; i++) {
503       if (!first) {
504          printf(",\n");
505       }
506       printf("   \"%s\": %d", resources[i].name, resources[i].rcode);
507       first = false;
508    }
509    printf("\n]\n");
510 }
511
512 static void dump_directives()
513 {
514    int i, j;
515    bool first_res;
516    bool first_directive;
517    RES_ITEM *items;
518
519    /* List resources and directives */
520    printf("[\n");
521    first_res = true;
522    for (i=0; resources[i].name; i++) {
523       if (!first_res) {
524          printf(",\n");
525       }
526       printf("{\n   \"%s\": {\n", resources[i].name);
527       first_res = false;
528       first_directive = true;
529       items = resources[i].items;
530       for (j=0; items[j].name; j++) {
531          if (!first_directive) {
532             printf(",\n");
533          }
534          printf("      \"%s\": null", items[j].name);
535          first_directive = false;
536       }
537       printf("\n   }");  /* end of resource */
538    }
539    printf("\n]\n");
540 }
541 #endif
542
543 /*
544  * Send a line to the output file and or the terminal
545  */
546 void senditf(const char *fmt,...)
547 {
548    char buf[3000];
549    va_list arg_ptr;
550
551    va_start(arg_ptr, fmt);
552    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
553    va_end(arg_ptr);
554    sendit(buf);
555 }
556
557 void sendit(const char *buf)
558 {
559 #ifdef CONIO_FIX
560    char obuf[3000];
561    if (output == stdout || teeout) {
562       const char *p, *q;
563       /*
564        * Here, we convert every \n into \r\n because the
565        *  terminal is in raw mode when we are using
566        *  conio.
567        */
568       for (p=q=buf; (p=strchr(q, '\n')); ) {
569          int len = p - q;
570          if (len > 0) {
571             memcpy(obuf, q, len);
572          }
573          memcpy(obuf+len, "\r\n", 3);
574          q = ++p;                    /* point after \n */
575          fputs(obuf, output);
576       }
577       if (*q) {
578          fputs(q, output);
579       }
580       fflush(output);
581    }
582    if (output != stdout) {
583       fputs(buf, output);
584    }
585 #else
586
587    fputs(buf, output);
588    fflush(output);
589    if (teeout) {
590       fputs(buf, stdout);
591       fflush(stdout);
592    }
593 #endif
594 }