Line data Source code
1 : #include "../../burp.h"
2 : #include "../../alloc.h"
3 : #include "../../asfd.h"
4 : #include "../../async.h"
5 : #include "../../bu.h"
6 : #include "../../cstat.h"
7 : #include "../../handy.h"
8 : #include "../../iobuf.h"
9 : #include "../../log.h"
10 : #include "json_input.h"
11 : #include "lline.h"
12 : #include "sel.h"
13 : #ifdef HAVE_WIN32
14 : #include <yajl/yajl_parse.h>
15 : #else
16 : #include "../../yajl/api/yajl_parse.h"
17 : #endif
18 :
19 : static int map_depth=0;
20 :
21 : // FIX THIS: should pass around a ctx instead of keeping track of a bunch
22 : // of globals.
23 : static unsigned long number=0;
24 : static char *timestamp=NULL;
25 : static uint16_t flags=0;
26 : static struct cstat *cnew=NULL;
27 : static struct cstat *current=NULL;
28 : static struct cstat **cslist=NULL;
29 : static struct cntr_ent *cntr_ent=NULL;
30 : static char lastkey[32]="";
31 : static int in_backups=0;
32 : static int in_flags=0;
33 : static int in_counters=0;
34 : static int in_logslist=0;
35 : static int in_log_content=0;
36 : static struct bu **sselbu=NULL;
37 : // For server side log files.
38 : static struct lline *ll_list=NULL;
39 : static struct lline **sllines=NULL;
40 : // For recording 'loglines' in json input.
41 : static struct lline *jsll_list=NULL;
42 :
43 : static int is_wrap(const char *val, const char *key, uint16_t bit)
44 : {
45 899 : if(!strcmp(val, key))
46 : {
47 204 : flags|=bit;
48 : return 1;
49 : }
50 : return 0;
51 : }
52 :
53 406 : static int input_integer(__attribute__ ((unused)) void *ctx, long long val)
54 : {
55 406 : if(in_counters)
56 : {
57 60 : if(!strcmp(lastkey, "count"))
58 : {
59 60 : if(!cntr_ent) goto error;
60 60 : cntr_ent->count=(uint64_t)val;
61 : }
62 0 : else if(!strcmp(lastkey, "changed"))
63 : {
64 0 : if(!cntr_ent) goto error;
65 0 : cntr_ent->changed=(uint64_t)val;
66 : }
67 0 : else if(!strcmp(lastkey, "same"))
68 : {
69 0 : if(!cntr_ent) goto error;
70 0 : cntr_ent->same=(uint64_t)val;
71 : }
72 0 : else if(!strcmp(lastkey, "deleted"))
73 : {
74 0 : if(!cntr_ent) goto error;
75 0 : cntr_ent->deleted=(uint64_t)val;
76 : }
77 0 : else if(!strcmp(lastkey, "scanned"))
78 : {
79 0 : if(!cntr_ent) goto error;
80 0 : cntr_ent->phase1=(uint64_t)val;
81 : }
82 : else
83 : {
84 : goto error;
85 : }
86 : return 1;
87 : }
88 346 : else if(in_backups && !in_flags && !in_counters && !in_logslist)
89 : {
90 254 : if(!current) goto error;
91 254 : if(!strcmp(lastkey, "number"))
92 : {
93 127 : number=(unsigned long)val;
94 127 : return 1;
95 : }
96 127 : else if(!strcmp(lastkey, "timestamp"))
97 : {
98 : time_t t;
99 127 : t=(unsigned long)val;
100 127 : free_w(×tamp);
101 127 : if(!(timestamp=strdup_w(getdatestr(t), __func__)))
102 : return 0;
103 127 : return 1;
104 : }
105 : }
106 : else
107 : {
108 92 : if(!strcmp(lastkey, "protocol"))
109 : {
110 : return 1;
111 : }
112 : }
113 : error:
114 0 : logp("Unexpected integer: '%s' %"PRIu64"\n", lastkey, (uint64_t)val);
115 0 : return 0;
116 : }
117 :
118 543 : static int input_string(__attribute__ ((unused)) void *ctx,
119 : const unsigned char *val, size_t len)
120 : {
121 : char *str;
122 543 : if(!(str=(char *)malloc_w(len+1, __func__)))
123 : return 0;
124 543 : snprintf(str, len+1, "%s", val);
125 543 : str[len]='\0';
126 :
127 543 : if(in_counters)
128 : {
129 120 : if(!strcmp(lastkey, "name"))
130 : {
131 : // Ignore 'name' in a counters object. We use 'type'
132 : // instead.
133 : }
134 60 : else if(!strcmp(lastkey, "type"))
135 : {
136 60 : if(!current) goto error;
137 60 : cntr_ent=current->cntr->ent[(uint8_t)*str];
138 : }
139 : else
140 : {
141 : goto error;
142 : }
143 : goto end;
144 : }
145 423 : else if(!strcmp(lastkey, "name"))
146 : {
147 92 : if(cnew) goto error;
148 92 : if(!(current=cstat_get_by_name(*cslist, str)))
149 : {
150 44 : if(!(cnew=cstat_alloc())
151 44 : || cstat_init_with_cntr(cnew, str, NULL))
152 : goto error;
153 44 : current=cnew;
154 : }
155 : goto end;
156 : }
157 331 : else if(!strcmp(lastkey, "labels"))
158 : {
159 30 : if(!current) goto error;
160 : goto end;
161 : }
162 301 : else if(!strcmp(lastkey, "run_status"))
163 : {
164 92 : if(!current) goto error;
165 92 : current->run_status=run_str_to_status(str);
166 92 : goto end;
167 : }
168 209 : else if(!strcmp(lastkey, "phase"))
169 : {
170 0 : if(!current) goto error;
171 0 : current->cntr->cntr_status=cntr_str_to_status(str);
172 0 : goto end;
173 : }
174 209 : else if(!strcmp(lastkey, "flags"))
175 : {
176 204 : if(!current) goto error;
177 408 : if(is_wrap(str, "hardlinked", BU_HARDLINKED)
178 398 : || is_wrap(str, "deletable", BU_DELETABLE)
179 324 : || is_wrap(str, "working", BU_WORKING)
180 294 : || is_wrap(str, "finishing", BU_FINISHING)
181 264 : || is_wrap(str, "current", BU_CURRENT)
182 110 : || is_wrap(str, "manifest", BU_MANIFEST))
183 : goto end;
184 : }
185 5 : else if(!strcmp(lastkey, "counters")) // Do we need this?
186 : {
187 : goto end;
188 : }
189 5 : else if(!strcmp(lastkey, "list"))
190 : {
191 0 : if(is_wrap(str, "backup", BU_LOG_BACKUP)
192 0 : || is_wrap(str, "restore", BU_LOG_RESTORE)
193 0 : || is_wrap(str, "verify", BU_LOG_VERIFY)
194 0 : || is_wrap(str, "backup_stats", BU_STATS_BACKUP)
195 0 : || is_wrap(str, "restore_stats", BU_STATS_RESTORE)
196 0 : || is_wrap(str, "verify_stats", BU_STATS_VERIFY))
197 : goto end;
198 : }
199 5 : else if(!strcmp(lastkey, "logs"))
200 : {
201 : goto end;
202 : }
203 5 : else if(!strcmp(lastkey, "logline"))
204 : {
205 0 : if(lline_add(&jsll_list, str))
206 : goto error;
207 : goto end;
208 : }
209 5 : else if(!strcmp(lastkey, "backup")
210 5 : || !strcmp(lastkey, "restore")
211 5 : || !strcmp(lastkey, "verify")
212 5 : || !strcmp(lastkey, "backup_stats")
213 5 : || !strcmp(lastkey, "restore_stats")
214 5 : || !strcmp(lastkey, "verify_stats"))
215 : {
216 : // Log file contents.
217 0 : if(lline_add(&ll_list, str))
218 : goto error;
219 : goto end;
220 : }
221 5 : else if(!strcmp(lastkey, "warning"))
222 : {
223 : goto end;
224 : }
225 : error:
226 0 : logp("Unexpected string: '%s' '%s'\n", lastkey, str);
227 0 : free_w(&str);
228 0 : return 0;
229 : end:
230 543 : free_w(&str);
231 543 : return 1;
232 : }
233 :
234 1018 : static int input_map_key(__attribute__((unused)) void *ctx,
235 : const unsigned char *val, size_t len)
236 : {
237 1018 : snprintf(lastkey, len+1, "%s", val);
238 1018 : lastkey[len]='\0';
239 : // logp("mapkey: %s\n", lastkey);
240 1018 : return 1;
241 : }
242 :
243 : static struct bu *bu_list=NULL;
244 :
245 219 : static int add_to_bu_list(void)
246 : {
247 : struct bu *bu;
248 : struct bu *last;
249 219 : if(!number) return 0;
250 127 : if(!(bu=bu_alloc())) return -1;
251 127 : bu->bno=number;
252 127 : bu->flags=flags;
253 127 : bu->timestamp=timestamp;
254 :
255 : // FIX THIS: Inefficient to find the end each time.
256 127 : for(last=bu_list; last && last->next; last=last->next) { }
257 127 : if(last)
258 : {
259 50 : last->next=bu;
260 50 : bu->prev=last;
261 : }
262 : else
263 : {
264 77 : bu_list=bu;
265 77 : bu_list->prev=NULL;
266 : }
267 :
268 127 : number=0;
269 127 : flags=0;
270 127 : timestamp=NULL;
271 127 : return 0;
272 : }
273 :
274 326 : static int input_start_map(__attribute__ ((unused)) void *ctx)
275 : {
276 326 : map_depth++;
277 : //logp("startmap: %d\n", map_depth);
278 326 : return 1;
279 : }
280 :
281 325 : static int input_end_map(__attribute__ ((unused)) void *ctx)
282 : {
283 325 : map_depth--;
284 : //logp("endmap: %d\n", map_depth);
285 325 : if(in_backups && !in_flags && !in_counters && !in_logslist)
286 : {
287 127 : if(add_to_bu_list()) return 0;
288 : }
289 : return 1;
290 : }
291 :
292 303 : static int input_start_array(__attribute__ ((unused)) void *ctx)
293 : {
294 : //logp("start arr\n");
295 303 : if(!strcmp(lastkey, "backups"))
296 : {
297 92 : in_backups=1;
298 : }
299 211 : else if(!strcmp(lastkey, "flags"))
300 : {
301 127 : in_flags=1;
302 : }
303 84 : else if(!strcmp(lastkey, "counters"))
304 : {
305 30 : in_counters=1;
306 : }
307 54 : else if(!strcmp(lastkey, "list"))
308 : {
309 0 : in_logslist=1;
310 : }
311 54 : else if(!strcmp(lastkey, "backup")
312 54 : || !strcmp(lastkey, "restore")
313 54 : || !strcmp(lastkey, "verify")
314 54 : || !strcmp(lastkey, "backup_stats")
315 54 : || !strcmp(lastkey, "restore_stats")
316 54 : || !strcmp(lastkey, "verify_stats"))
317 : {
318 0 : in_log_content=1;
319 : }
320 303 : return 1;
321 : }
322 :
323 92 : static void merge_bu_lists(void)
324 : {
325 : struct bu *n;
326 : struct bu *o;
327 92 : struct bu *lastn=NULL;
328 92 : struct bu *lasto=NULL;
329 :
330 253 : for(o=current->bu; o; )
331 : {
332 69 : int found_in_new=0;
333 69 : lastn=NULL;
334 87 : for(n=bu_list; n; n=n->next)
335 : {
336 87 : if(o->bno==n->bno)
337 : {
338 : // Found o in new list.
339 : // Copy the fields from new to old.
340 69 : found_in_new=1;
341 69 : o->flags=n->flags;
342 69 : free_w(&o->timestamp);
343 69 : o->timestamp=n->timestamp;
344 69 : n->timestamp=NULL;
345 :
346 : // Remove it from new list.
347 69 : if(lastn)
348 : {
349 18 : lastn->next=n->next;
350 18 : if(n->next) n->next->prev=lastn;
351 : }
352 : else
353 : {
354 51 : bu_list=n->next;
355 51 : if(bu_list) bu_list->prev=NULL;
356 : }
357 69 : bu_free(&n);
358 69 : n=lastn;
359 69 : break;
360 : }
361 18 : lastn=n;
362 : }
363 69 : if(!found_in_new)
364 : {
365 : // Could not find o in new list.
366 : // Remove it from old list.
367 0 : if(lasto)
368 : {
369 0 : lasto->next=o->next;
370 0 : if(o->next) o->next->prev=lasto;
371 : }
372 : else
373 : {
374 0 : current->bu=o->next;
375 0 : if(current->bu) current->bu->prev=NULL;
376 : }
377 : // Need to reset if the one that was removed was
378 : // selected in ncurses.
379 0 : if(o==*sselbu) *sselbu=NULL;
380 0 : bu_free(&o);
381 0 : o=lasto;
382 : }
383 69 : lasto=o;
384 69 : if(o) o=o->next;
385 : }
386 :
387 : // Now, new list only has entries missing from old list.
388 92 : n=bu_list;
389 92 : lastn=NULL;
390 242 : while(n)
391 : {
392 58 : o=current->bu;
393 58 : lasto=NULL;
394 136 : while(o && n->bno < o->bno)
395 : {
396 20 : lasto=o;
397 20 : o=o->next;
398 : }
399 : // Found the place to insert it.
400 58 : if(lasto)
401 : {
402 8 : lasto->next=n;
403 8 : n->prev=lasto;
404 : }
405 : else
406 : {
407 50 : if(current->bu) current->bu->prev=n;
408 50 : current->bu=n;
409 50 : current->bu->prev=NULL;
410 : }
411 58 : lastn=n->next;
412 58 : n->next=o;
413 58 : n=lastn;
414 : }
415 92 : }
416 :
417 303 : static int input_end_array(__attribute__ ((unused)) void *ctx)
418 : {
419 303 : if(in_backups && !in_flags && !in_counters && !in_logslist)
420 : {
421 92 : in_backups=0;
422 92 : if(add_to_bu_list()) return 0;
423 : // Now may have two lists. Want to keep the old one is intact
424 : // as possible, so that we can keep a pointer to its entries
425 : // in the ncurses stuff.
426 : // Merge the new list into the old.
427 92 : merge_bu_lists();
428 92 : bu_list=NULL;
429 92 : if(cnew)
430 : {
431 44 : cstat_add_to_list(cslist, cnew);
432 44 : cnew=NULL;
433 : }
434 92 : current=NULL;
435 : }
436 211 : else if(in_flags)
437 : {
438 127 : in_flags=0;
439 : }
440 84 : else if(in_counters)
441 : {
442 30 : in_counters=0;
443 : }
444 54 : else if(in_logslist)
445 : {
446 0 : in_logslist=0;
447 : }
448 54 : else if(in_log_content)
449 : {
450 0 : in_log_content=0;
451 0 : llines_free(sllines);
452 0 : *sllines=ll_list;
453 0 : ll_list=NULL;
454 : }
455 : return 1;
456 : }
457 :
458 : static yajl_callbacks callbacks = {
459 : NULL,
460 : NULL,
461 : input_integer,
462 : NULL,
463 : NULL,
464 : input_string,
465 : input_start_map,
466 : input_map_key,
467 : input_end_map,
468 : input_start_array,
469 : input_end_array
470 : };
471 :
472 2 : static void do_yajl_error(yajl_handle yajl, struct asfd *asfd)
473 : {
474 : unsigned char *str;
475 4 : str=yajl_get_error(yajl, 1,
476 4 : (const unsigned char *)asfd->rbuf->buf, asfd->rbuf->len);
477 2 : logp("yajl error: %s\n", str?(const char *)str:"unknown");
478 2 : if(str) yajl_free_error(yajl, str);
479 2 : }
480 :
481 : static yajl_handle yajl=NULL;
482 :
483 48 : int json_input_init(void)
484 : {
485 48 : if(!(yajl=yajl_alloc(&callbacks, NULL, NULL)))
486 : return -1;
487 48 : yajl_config(yajl, yajl_dont_validate_strings, 1);
488 48 : return 0;
489 : }
490 :
491 25 : void json_input_free(void)
492 : {
493 98 : if(!yajl) return;
494 48 : yajl_free(yajl);
495 48 : yajl=NULL;
496 : }
497 :
498 0 : struct lline *json_input_get_loglines(void)
499 : {
500 0 : return jsll_list;
501 : }
502 :
503 : // Client records will be coming through in alphabetical order.
504 : // FIX THIS: If a client is deleted on the server, it is not deleted from
505 : // clist.
506 5287 : int json_input(struct asfd *asfd, struct sel *sel)
507 : {
508 5285 : cslist=&sel->clist;
509 5285 : sselbu=&sel->backup;
510 5285 : sllines=&sel->llines;
511 :
512 5285 : if(!yajl && json_input_init()) goto error;
513 :
514 10570 : if(yajl_parse(yajl, (const unsigned char *)asfd->rbuf->buf,
515 10570 : asfd->rbuf->len)!=yajl_status_ok)
516 : {
517 2 : do_yajl_error(yajl, asfd);
518 1 : goto error;
519 : }
520 :
521 5284 : if(!map_depth)
522 : {
523 : // Got to the end of the JSON object.
524 47 : if(yajl_complete_parse(yajl)!=yajl_status_ok)
525 : {
526 2 : do_yajl_error(yajl, asfd);
527 1 : goto error;
528 : }
529 : json_input_free();
530 : return 1;
531 : }
532 :
533 : return 0;
534 : error:
535 : json_input_free();
536 : return -1;
537 : }
|