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