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