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