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 Json library routines
22  *
23  *     Kern Sibbald, September MMXII
24  *
25  */
26 
27 #include "bacula.h"
28 #include "lib/breg.h"
29 
30 extern s_kw msg_types[];
31 extern s_collt collectortypes[];
32 extern RES_TABLE resources[];
33 
34 union URES {
35    MSGS  res_msgs;
36    RES hdr;
37 };
38 
39 #if defined(_MSC_VER)
40 extern "C" { // work around visual compiler mangling variables
41    extern URES res_all;
42 }
43 #else
44 extern URES res_all;
45 #endif
46 
47 struct display_filter
48 {
49    /* default                   { { "Director": { "Name": aa, ...} }, { "Job": {..} */
50    bool do_list;             /* [ {}, {}, ..] or { "aa": {}, "bb": {}, ...} */
51    bool do_one;              /* { "Name": "aa", "Description": "test, ... } */
52    bool do_only_data;        /* [ {}, {}, {}, ] */
53    char *resource_type;
54    char *resource_name;
55    regex_t directive_reg;
56 };
57 
bjson_sendit(HPKT & hpkt,const char * fmt,...)58 void bjson_sendit(HPKT &hpkt, const char *fmt, ...)
59 {
60    va_list arg_ptr;
61    bool done=false;
62 
63    while (!done) {
64       uint32_t len = sizeof_pool_memory(hpkt.out);
65       va_start(arg_ptr, fmt);
66       // The return value of our bvsnprintf() doesn't represent
67       // the full length of the string, we need to check it afterward
68       bvsnprintf(hpkt.out, len, (char *)fmt, arg_ptr);
69       va_end(arg_ptr);
70 
71       if (strlen(hpkt.out) >= (len - 1)) {
72          // We got an overflow, we need more room to display the output
73          hpkt.out = check_pool_memory_size(hpkt.out, len*2);
74 
75       } else {
76          done = true;
77       }
78    }
79 
80    fputs(hpkt.out, stdout);
81    fflush(stdout);
82 }
83 
init_hpkt(HPKT & hpkt)84 void init_hpkt(HPKT &hpkt)
85 {
86    memset(&hpkt, 0, sizeof(hpkt));
87    hpkt.edbuf = get_pool_memory(PM_EMSG);
88    hpkt.edbuf2 = get_pool_memory(PM_EMSG);
89    hpkt.out = get_pool_memory(PM_EMSG);
90    hpkt.json = true;
91    hpkt.hfunc = HF_DISPLAY;
92    hpkt.sendit = bjson_sendit;
93 }
94 
term_hpkt(HPKT & hpkt)95 void term_hpkt(HPKT &hpkt)
96 {
97    free_pool_memory(hpkt.edbuf);
98    free_pool_memory(hpkt.edbuf2);
99    free_pool_memory(hpkt.out);
100    memset(&hpkt, 0, sizeof(hpkt));
101 }
102 
103 /*
104  * Strip long options out of fo->opts string so that
105  *   they will not give us false matches for regular
106  *   1 or 2 character options.
107  */
strip_long_opts(char * out,const char * in)108 void strip_long_opts(char *out, const char *in)
109 {
110    const char *p;
111    for (p=in; *p; p++) {
112       switch (*p) {
113       /* V, C, J, and P are long options, skip them */
114       case 'V':
115       case 'C':
116       case 'J':
117       case 'P':
118          while (*p != ':') {
119             p++;       /* skip to after : */
120          }
121          break;
122       /* Copy everything else */
123       default:
124          *out++ = *p;
125          break;
126       }
127    }
128    *out = 0;           /* terminate string */
129 }
130 
edit_alist(HPKT & hpkt)131 void edit_alist(HPKT &hpkt)
132 {
133    bool f = true;
134    char *citem;
135 
136    pm_strcpy(hpkt.edbuf, " [");
137    foreach_alist(citem, hpkt.list) {
138       if (!f) {
139          pm_strcat(hpkt.edbuf, ", ");
140       }
141       pm_strcat(hpkt.edbuf, quote_string(hpkt.edbuf2, citem));
142       f = false;
143    }
144    pm_strcat(hpkt.edbuf, "]");
145 }
146 
edit_msg_types(HPKT & hpkt,DEST * dest)147 void edit_msg_types(HPKT &hpkt, DEST *dest)
148 {
149    int i, j, count = 0;
150    bool first_type = true;
151    bool found;
152 
153    pm_strcpy(hpkt.edbuf, "[");
154    for (i=1; i<=M_MAX; i++) {
155       if (bit_is_set(i, dest->msg_types)) {
156          found = false;
157          if (!first_type) pm_strcat(hpkt.edbuf, ",");
158          first_type = false;
159          for (j=0; msg_types[j].name; j++) {
160             if ((int)msg_types[j].token == i) {
161                pm_strcat(hpkt.edbuf, "\"");
162                pm_strcat(hpkt.edbuf, msg_types[j].name);
163                pm_strcat(hpkt.edbuf, "\"");
164                found = true;
165                break;
166             }
167          }
168          if (!found) {
169             bjson_sendit(hpkt, "No find for type=%d\n", i);
170          }
171          count++;
172       }
173    }
174    /*
175     * Note, if we have more than half of the total items,
176     *   redo using All and !item, which will give fewer items
177     *   total.
178     */
179    if (count > M_MAX/2) {
180       pm_strcpy(hpkt.edbuf, "[\"All\"");
181       for (i=1; i<=M_MAX; i++) {
182          if (!bit_is_set(i, dest->msg_types)) {
183             found = false;
184             for (j=0; msg_types[j].name; j++) {
185                if ((int)msg_types[j].token == i) {
186                   /* Do not display them, they are included in All */
187                   if (msg_types[j].token != M_DEBUG &&
188                       msg_types[j].token != M_EVENTS &&
189                       msg_types[j].token != M_SAVED)
190                   {
191                      pm_strcat(hpkt.edbuf, ",");
192                      pm_strcat(hpkt.edbuf, "\"!");
193                      pm_strcat(hpkt.edbuf, msg_types[j].name);
194                      pm_strcat(hpkt.edbuf, "\"");
195                   }
196                   found = true;
197                   break;
198                }
199             }
200             if (!found) {
201                bjson_sendit(hpkt, "No find for type=%d in second loop\n", i);
202             }
203          } else if (i == M_SAVED) {
204             /* Saved is not set by default, users must explicitly use it
205              * on the configuration line
206              */
207             pm_strcat(hpkt.edbuf, ",\"Saved\"");
208 
209          } else if (i == M_EVENTS) {
210             /* Events is not set by default, users must explicitly use it
211              * on the configuration line
212              */
213             pm_strcat(hpkt.edbuf, ",\"Events\"");
214          }
215       }
216    }
217    /* Now handle custom type */
218    edit_custom_type(&hpkt.edbuf, (MSGS *)hpkt.ritem->value, dest->msg_types);
219    pm_strcat(hpkt.edbuf, "]");
220 }
221 
222 /* -1 nothing displayed, 1 found, 0 not found */
display_global_item(HPKT & hpkt)223 int display_global_item(HPKT &hpkt)
224 {
225    bool found = true;
226    bool has_something = true;
227    if (hpkt.ritem->handler == store_res) {
228       display_res(hpkt);
229    } else if (hpkt.ritem->handler == store_str ||
230               hpkt.ritem->handler == store_name ||
231               hpkt.ritem->handler == store_password ||
232               hpkt.ritem->handler == store_strname ||
233               hpkt.ritem->handler == store_dir) {
234       display_string_pair(hpkt);
235    } else if (hpkt.ritem->handler == store_int32 ||
236               hpkt.ritem->handler == store_pint32 ||
237               hpkt.ritem->handler == store_size32) {
238       display_int32_pair(hpkt);
239    } else if (hpkt.ritem->handler == store_size64 ||
240               hpkt.ritem->handler == store_int64 ||
241               hpkt.ritem->handler == store_time ||
242               hpkt.ritem->handler == store_speed) {
243       display_int64_pair(hpkt);
244    } else if (hpkt.ritem->handler == store_bool) {
245       display_bool_pair(hpkt);
246    } else if (hpkt.ritem->handler == store_msgs) {
247       has_something = display_msgs(hpkt);
248    } else if (hpkt.ritem->handler == store_bit) {
249       display_bit_pair(hpkt);
250    } else if (hpkt.ritem->handler == store_alist_res) {
251       has_something = display_alist_res(hpkt); /* In some cases, the list is null... */
252    } else if (hpkt.ritem->handler == store_alist_str) {
253       has_something = display_alist_str(hpkt); /* In some cases, the list is null... */
254    } else {
255       found = false;
256    }
257 
258    if (found) {
259       return has_something ? 1 : -1;
260    } else {
261       return 0;
262    }
263 }
264 
265 /*
266  * Called here for each store_msgs resource
267  */
display_msgs(HPKT & hpkt)268 bool display_msgs(HPKT &hpkt)
269 {
270    MSGS *msgs = (MSGS *)hpkt.ritem->value;  /* Message res */
271    DEST *dest;   /* destination chain */
272    int first = true;
273 
274    if (!hpkt.in_store_msg) {
275       hpkt.in_store_msg = true;
276       bjson_sendit(hpkt, "\n    \"Destinations\": [");
277    }
278    for (dest=msgs->dest_chain; dest; dest=dest->next) {
279       if (dest->dest_code == hpkt.ritem->code) {
280          if (!first) bjson_sendit(hpkt, ",");
281          first = false;
282          edit_msg_types(hpkt, dest);
283          switch (hpkt.ritem->code) {
284          /* Output only message types */
285          case MD_STDOUT:
286          case MD_STDERR:
287          case MD_SYSLOG:
288          case MD_CONSOLE:
289          case MD_CATALOG:
290             bjson_sendit(hpkt, "\n      {\n        \"Type\": \"%s\","
291                          "\n        \"MsgTypes\": %s\n      }",
292                hpkt.ritem->name, hpkt.edbuf);
293             break;
294          /* Output MsgTypes, Where */
295          case MD_DIRECTOR:
296          case MD_FILE:
297          case MD_APPEND:
298             bjson_sendit(hpkt, "\n      {\n        \"Type\": \"%s\","
299                          "\n        \"MsgTypes\": %s,\n",
300                hpkt.ritem->name, hpkt.edbuf);
301             bjson_sendit(hpkt, "        \"Where\": [%s]\n      }",
302                quote_where(hpkt.edbuf, dest->where));
303             break;
304          /* Now we edit MsgTypes, Where, and Command */
305          case MD_MAIL:
306          case MD_OPERATOR:
307          case MD_MAIL_ON_ERROR:
308          case MD_MAIL_ON_SUCCESS:
309             bjson_sendit(hpkt, "\n      {\n        \"Type\": \"%s\","
310                          "\n        \"MsgTypes\": %s,\n",
311                hpkt.ritem->name, hpkt.edbuf);
312             bjson_sendit(hpkt, "        \"Where\": [%s],\n",
313                quote_where(hpkt.edbuf, dest->where));
314             bjson_sendit(hpkt, "        \"Command\": %s\n      }",
315                quote_string(hpkt.edbuf, dest->mail_cmd));
316             break;
317          default:
318             Dmsg1(50, "got %d\n", hpkt.ritem->code);
319          }
320       }
321    }
322    return (first == false);      // We found nothing to display
323 }
324 
325 /*
326  * Called here if the ITEM_LAST is set in flags,
327  *  that means there are no more items to examine
328  *  for this resource and that we can close any
329  *  open json list.
330  */
display_last(HPKT & hpkt)331 void display_last(HPKT &hpkt)
332 {
333    if (hpkt.in_store_msg) {
334       hpkt.in_store_msg = false;
335       bjson_sendit(hpkt, "\n    ]");
336    }
337 }
338 
display_alist(HPKT & hpkt)339 void display_alist(HPKT &hpkt)
340 {
341    edit_alist(hpkt);
342    bjson_sendit(hpkt, "%s", hpkt.edbuf);
343 }
344 
display_alist_str(HPKT & hpkt)345 bool display_alist_str(HPKT &hpkt)
346 {
347    hpkt.list = (alist *)(*(hpkt.ritem->value));
348    if (!hpkt.list) {
349       return false;
350    }
351    bjson_sendit(hpkt, "\n    \"%s\":", hpkt.ritem->name);
352    display_alist(hpkt);
353    return true;
354 }
355 
display_alist_res(HPKT & hpkt)356 bool display_alist_res(HPKT &hpkt)
357 {
358    bool f = true;
359    alist *list;
360    RES *res;
361 
362    list = (alist *)(*(hpkt.ritem->value));
363    if (!list) {
364       return false;
365    }
366    bjson_sendit(hpkt, "\n    \"%s\":", hpkt.ritem->name);
367    bjson_sendit(hpkt, " [");
368    foreach_alist(res, list) {
369       if (!f) {
370          bjson_sendit(hpkt, ", ");
371       }
372       bjson_sendit(hpkt, "%s", quote_string(hpkt.edbuf, res->name));
373       f = false;
374    }
375    bjson_sendit(hpkt, "]");
376    return true;
377 }
378 
display_res(HPKT & hpkt)379 void display_res(HPKT &hpkt)
380 {
381    RES *res;
382 
383    res = (RES *)*hpkt.ritem->value;
384    bjson_sendit(hpkt, "\n    \"%s\": %s", hpkt.ritem->name,
385       quote_string(hpkt.edbuf, res->name));
386 }
387 
display_string_pair(HPKT & hpkt)388 void display_string_pair(HPKT &hpkt)
389 {
390    bjson_sendit(hpkt, "\n    \"%s\": %s", hpkt.ritem->name,
391       quote_string(hpkt.edbuf, *hpkt.ritem->value));
392 }
393 
display_int32_pair(HPKT & hpkt)394 void display_int32_pair(HPKT &hpkt)
395 {
396    char ed1[50];
397    bjson_sendit(hpkt, "\n    \"%s\": %s", hpkt.ritem->name,
398       edit_int64(*(int32_t *)hpkt.ritem->value, ed1));
399 }
400 
display_int64_pair(HPKT & hpkt)401 void display_int64_pair(HPKT &hpkt)
402 {
403    char ed1[50];
404    bjson_sendit(hpkt, "\n    \"%s\": %s", hpkt.ritem->name,
405       edit_int64(*(int64_t *)hpkt.ritem->value, ed1));
406 }
407 
display_bool_pair(HPKT & hpkt)408 void display_bool_pair(HPKT &hpkt)
409 {
410    bjson_sendit(hpkt, "\n    \"%s\": %s", hpkt.ritem->name,
411       ((*(bool *)(hpkt.ritem->value)) == 0)?"false":"true");
412 }
413 
display_bit_pair(HPKT & hpkt)414 void display_bit_pair(HPKT &hpkt)
415 {
416    bjson_sendit(hpkt, "\n    \"%s\": %s", hpkt.ritem->name,
417       ((*(uint32_t *)(hpkt.ritem->value) & hpkt.ritem->code)
418          == 0)?"false":"true");
419 }
420 
byte_is_set(char * byte,int num)421 bool byte_is_set(char *byte, int num)
422 {
423    int i;
424    bool found = false;
425    for (i=0; i<num; i++) {
426       if (byte[i]) {
427          found = true;
428          break;
429       }
430    }
431    return found;
432 }
433 
display_bit_array(HPKT & hpkt,char * array,int num)434 void display_bit_array(HPKT &hpkt, char *array, int num)
435 {
436    int i;
437    bool first = true;
438    bjson_sendit(hpkt, " [");
439    for (i=0; i<num; i++) {
440       if (bit_is_set(i, array)) {
441          if (!first) bjson_sendit(hpkt, ", ");
442          first = false;
443          bjson_sendit(hpkt, "%d", i);
444       }
445    }
446    bjson_sendit(hpkt, "]");
447 }
448 
display_collector_types(HPKT & hpkt)449 void display_collector_types(HPKT &hpkt)
450 {
451    int i;
452    for (i=0; collectortypes[i].type_name; i++) {
453       if (*(int32_t *)(hpkt.ritem->value) == collectortypes[i].coll_type) {
454          bjson_sendit(hpkt, "\n    \"%s\": %s", hpkt.ritem->name,
455             quote_string(hpkt.edbuf, collectortypes[i].type_name));
456          return;
457       }
458    }
459 }
460