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