1 #include "../../burp.h"
2 #include "../../alloc.h"
3 #include "../../asfd.h"
4 #include "../../async.h"
5 #include "../../bu.h"
6 #include "../../cmd.h"
7 #include "../../cstat.h"
8 #include "../../fzp.h"
9 #include "../../handy.h"
10 #include "../../iobuf.h"
11 #include "../../prepend.h"
12 #include "../../strlist.h"
13 #include "../../yajl_gen_w.h"
14 #include "../timestamp.h"
15 #include "browse.h"
16 #include "json_output.h"
17
18 static int pretty_print=1;
19 static long version_2_1_8=0;
20
json_set_pretty_print(int value)21 void json_set_pretty_print(int value)
22 {
23 pretty_print=value;
24 }
25
write_all(struct asfd * asfd)26 static int write_all(struct asfd *asfd)
27 {
28 int ret=-1;
29 size_t w=0;
30 size_t len=0;
31 const unsigned char *buf;
32 struct iobuf wbuf;
33
34 yajl_gen_get_buf(yajl, &buf, &len);
35 while(len)
36 {
37 w=len;
38 if(w>ASYNC_BUF_LEN) w=ASYNC_BUF_LEN;
39 iobuf_set(&wbuf, CMD_GEN /* not used */, (char *)buf, w);
40 if((ret=asfd->write(asfd, &wbuf)))
41 break;
42 buf+=w;
43 len-=w;
44 }
45 if(!ret && !pretty_print)
46 {
47 iobuf_set(&wbuf, CMD_GEN /* not used */, (char *)"\n", 1);
48 ret=asfd->write(asfd, &wbuf);
49 }
50
51 yajl_gen_clear(yajl);
52 return ret;
53 }
54
json_start(void)55 static int json_start(void)
56 {
57 if(!yajl)
58 {
59 if(!(yajl=yajl_gen_alloc(NULL)))
60 return -1;
61 yajl_gen_config(yajl, yajl_gen_beautify, pretty_print);
62 if(!version_2_1_8)
63 version_2_1_8=version_to_long("2.1.8");
64 }
65 if(yajl_map_open_w()) return -1;
66 return 0;
67 }
68
json_clients(void)69 static int json_clients(void)
70 {
71 if(yajl_gen_str_w("clients")
72 || yajl_array_open_w())
73 return -1;
74 return 0;
75 }
76
json_clients_end(void)77 static int json_clients_end(void)
78 {
79 if(yajl_array_close_w()) return -1;
80 return 0;
81 }
82
json_end(struct asfd * asfd)83 static int json_end(struct asfd *asfd)
84 {
85 int ret=-1;
86 if(yajl_map_close_w())
87 goto end;
88 ret=write_all(asfd);
89 end:
90 yajl_gen_free(yajl);
91 yajl=NULL;
92 return ret;
93 }
94
flag_matches(struct bu * bu,uint16_t flag)95 static int flag_matches(struct bu *bu, uint16_t flag)
96 {
97 return (bu && (bu->flags & flag));
98 }
99
flag_wrap_str(struct bu * bu,uint16_t flag,const char * field)100 static int flag_wrap_str(struct bu *bu, uint16_t flag, const char *field)
101 {
102 if(!flag_matches(bu, flag)) return 0;
103 return yajl_gen_str_w(field);
104 }
105
open_backup_log(struct bu * bu,const char * logfile)106 static struct fzp *open_backup_log(struct bu *bu, const char *logfile)
107 {
108 char *path=NULL;
109 struct fzp *fzp=NULL;
110
111 char logfilereal[32]="";
112 if(!strcmp(logfile, "backup"))
113 snprintf(logfilereal, sizeof(logfilereal), "log");
114 else if(!strcmp(logfile, "restore"))
115 snprintf(logfilereal, sizeof(logfilereal), "restorelog");
116 else if(!strcmp(logfile, "verify"))
117 snprintf(logfilereal, sizeof(logfilereal), "verifylog");
118 else if(!strcmp(logfile, "backup_stats"))
119 snprintf(logfilereal, sizeof(logfilereal), "backup_stats");
120 else if(!strcmp(logfile, "restore_stats"))
121 snprintf(logfilereal, sizeof(logfilereal), "restore_stats");
122 else if(!strcmp(logfile, "verify_stats"))
123 snprintf(logfilereal, sizeof(logfilereal), "verify_stats");
124
125 if(!(path=prepend_s(bu->path, logfilereal)))
126 goto end;
127 if(!(fzp=fzp_gzopen(path, "rb")))
128 {
129 if(astrcat(&path, ".gz", __func__)
130 || !(fzp=fzp_gzopen(path, "rb")))
131 goto end;
132 }
133 end:
134 free_w(&path);
135 return fzp;
136
137 }
138
flag_wrap_str_zp(struct bu * bu,uint16_t flag,const char * field,const char * logfile)139 static int flag_wrap_str_zp(struct bu *bu, uint16_t flag, const char *field,
140 const char *logfile)
141 {
142 int ret=-1;
143 struct fzp *fzp=NULL;
144 if(!flag_matches(bu, flag)
145 || !logfile || strcmp(logfile, field))
146 return 0;
147 if(!(fzp=open_backup_log(bu, logfile))) goto end;
148 if(yajl_gen_str_w(field)) goto end;
149 if(yajl_array_open_w()) goto end;
150 if(fzp)
151 {
152 char *cp=NULL;
153 char buf[1024]="";
154 while(fzp_gets(fzp, buf, sizeof(buf)))
155 {
156 if((cp=strrchr(buf, '\n'))) *cp='\0';
157 if(yajl_gen_str_w(buf))
158 goto end;
159 }
160 }
161 if(yajl_array_close_w()) goto end;
162 ret=0;
163 end:
164 fzp_close(&fzp);
165 return ret;
166 }
167
do_counters(struct cntr * cntr)168 static int do_counters(struct cntr *cntr)
169 {
170 static char type[2];
171 struct cntr_ent *e;
172
173 #ifndef UTEST
174 cntr->ent[(uint8_t)CMD_TIMESTAMP_END]->count=(uint64_t)time(NULL);
175 #endif
176 if(yajl_gen_str_w("counters")
177 || yajl_array_open_w()) return -1;
178 for(e=cntr->list; e; e=e->next)
179 {
180 if(e->flags & CNTR_SINGLE_FIELD)
181 {
182 if(!e->count) continue;
183 snprintf(type, sizeof(type), "%c", e->cmd);
184 if(yajl_map_open_w()
185 || yajl_gen_str_pair_w("name", e->field)
186 || yajl_gen_str_pair_w("type", type)
187 || yajl_gen_int_pair_w("count", e->count)
188 || yajl_map_close_w())
189 return -1;
190 }
191 else if(e->flags & CNTR_TABULATE)
192 {
193 if(!e->count
194 && !e->changed
195 && !e->same
196 && !e->deleted
197 && !e->phase1)
198 continue;
199 snprintf(type, sizeof(type), "%c", e->cmd);
200 if(yajl_map_open_w()
201 || yajl_gen_str_pair_w("name", e->field)
202 || yajl_gen_str_pair_w("type", type)
203 || yajl_gen_int_pair_w("count", e->count)
204 || yajl_gen_int_pair_w("changed", e->changed)
205 || yajl_gen_int_pair_w("same", e->same)
206 || yajl_gen_int_pair_w("deleted", e->deleted)
207 || yajl_gen_int_pair_w("scanned", e->phase1)
208 || yajl_map_close_w())
209 return -1;
210 }
211 }
212
213 if(yajl_array_close_w())
214 return -1;
215 return 0;
216 }
217
json_send_backup(struct cstat * cstat,struct bu * bu,int print_flags,const char * logfile,const char * browse,int use_cache,long peer_version)218 static int json_send_backup(struct cstat *cstat, struct bu *bu,
219 int print_flags, const char *logfile, const char *browse,
220 int use_cache, long peer_version)
221 {
222 long long bno=0;
223 long long timestamp=0;
224 if(!bu) return 0;
225 bno=(long long)bu->bno;
226 timestamp=(long long)timestamp_to_long(bu->timestamp);
227
228 if(yajl_map_open_w()
229 || yajl_gen_int_pair_w("number", bno)
230 || yajl_gen_int_pair_w("timestamp", timestamp)
231 || yajl_gen_str_w("flags")
232 || yajl_array_open_w()
233 || flag_wrap_str(bu, BU_HARDLINKED, "hardlinked")
234 || flag_wrap_str(bu, BU_DELETABLE, "deletable")
235 || flag_wrap_str(bu, BU_WORKING, "working")
236 || flag_wrap_str(bu, BU_FINISHING, "finishing")
237 || flag_wrap_str(bu, BU_CURRENT, "current")
238 || flag_wrap_str(bu, BU_MANIFEST, "manifest")
239 || yajl_array_close_w())
240 return -1;
241 if(bu->flags & (BU_WORKING|BU_FINISHING))
242 {
243 if(peer_version<=version_2_1_8)
244 {
245 if(do_counters(cstat->cntrs))
246 return -1;
247 }
248 }
249 if(print_flags
250 && (bu->flags & (BU_LOG_BACKUP|BU_LOG_RESTORE|BU_LOG_VERIFY
251 |BU_STATS_BACKUP|BU_STATS_RESTORE|BU_STATS_VERIFY)))
252 {
253 if(yajl_gen_str_w("logs")
254 || yajl_map_open_w()
255 || yajl_gen_str_w("list")
256 || yajl_array_open_w()
257 || flag_wrap_str(bu, BU_LOG_BACKUP, "backup")
258 || flag_wrap_str(bu, BU_LOG_RESTORE, "restore")
259 || flag_wrap_str(bu, BU_LOG_VERIFY, "verify")
260 || flag_wrap_str(bu, BU_STATS_BACKUP, "backup_stats")
261 || flag_wrap_str(bu, BU_STATS_RESTORE, "restore_stats")
262 || flag_wrap_str(bu, BU_STATS_VERIFY, "verify_stats")
263 || yajl_array_close_w())
264 return -1;
265 if(logfile)
266 {
267 if(flag_wrap_str_zp(bu,
268 BU_LOG_BACKUP, "backup", logfile)
269 || flag_wrap_str_zp(bu,
270 BU_LOG_RESTORE, "restore", logfile)
271 || flag_wrap_str_zp(bu,
272 BU_LOG_VERIFY, "verify", logfile)
273 || flag_wrap_str_zp(bu,
274 BU_STATS_BACKUP, "backup_stats", logfile)
275 || flag_wrap_str_zp(bu,
276 BU_STATS_RESTORE, "restore_stats", logfile)
277 || flag_wrap_str_zp(bu,
278 BU_STATS_VERIFY, "verify_stats", logfile))
279 return -1;
280 }
281 if(yajl_map_close_w())
282 return -1;
283 if(browse)
284 {
285 if(yajl_gen_str_w("browse")) return -1;
286 if(yajl_map_open_w()) return -1;
287 if(yajl_gen_str_pair_w("directory", browse)) return -1;
288 if(yajl_gen_str_w("entries")) return -1;
289 if(yajl_array_open_w()) return -1;
290 if(browse_manifest(cstat, bu, browse, use_cache))
291 return -1;
292 if(yajl_array_close_w()) return -1;
293 if(yajl_map_close_w()) return -1;
294
295 }
296 }
297 if(yajl_gen_map_close(yajl)!=yajl_gen_status_ok)
298 return -1;
299
300 return 0;
301 }
302
str_array(const char * field,struct cstat * cstat)303 static int str_array(const char *field, struct cstat *cstat)
304 {
305 struct strlist *s=NULL;
306 if(!cstat->labels) return 0;
307 if(yajl_gen_str_w(field)
308 || yajl_array_open_w())
309 return -1;
310 for(s=cstat->labels; s; s=s->next)
311 if(yajl_gen_str_w(s->path))
312 return -1;
313 if(yajl_array_close_w())
314 return -1;
315 return 0;
316 }
317
do_children(struct cntr * cntrs)318 static int do_children(struct cntr *cntrs)
319 {
320 struct cntr *c;
321 if(yajl_gen_str_w("children")
322 || yajl_array_open_w())
323 return -1;
324 for(c=cntrs; c; c=c->next)
325 {
326 if(yajl_map_open_w()
327 || yajl_gen_int_pair_w("pid", c->pid)
328 || yajl_gen_int_pair_w("backup", c->bno)
329 || yajl_gen_str_pair_w("action", cntr_status_to_action_str(c))
330 || yajl_gen_str_pair_w("phase", cntr_status_to_str(c)))
331 return -1;
332 if(do_counters(c))
333 return -1;
334 if(yajl_map_close_w())
335 return -1;
336 }
337 if(yajl_array_close_w())
338 return -1;
339 return 0;
340 }
341
json_send_client_start(struct cstat * cstat,long peer_version)342 static int json_send_client_start(struct cstat *cstat, long peer_version)
343 {
344 const char *run_status=run_status_to_str(cstat);
345
346 if(yajl_map_open_w()
347 || yajl_gen_str_pair_w("name", cstat->name))
348 return -1;
349 if(str_array("labels", cstat))
350 return -1;
351 if(yajl_gen_str_pair_w("run_status", run_status))
352 return -1;
353 if(yajl_gen_int_pair_w("protocol", cstat->protocol))
354 return -1;
355 if(peer_version>version_2_1_8)
356 {
357 if(cstat->cntrs
358 && do_children(cstat->cntrs))
359 return -1;
360 }
361 else if(cstat->cntrs)
362 {
363 // Best effort.
364 if(yajl_gen_str_pair_w("phase",
365 cntr_status_to_str(cstat->cntrs)))
366 return -1;
367 }
368 if(yajl_gen_str_w("backups")
369 || yajl_array_open_w())
370 return -1;
371 return 0;
372 }
373
json_send_client_end(void)374 static int json_send_client_end(void)
375 {
376 if(yajl_array_close_w()
377 || yajl_map_close_w())
378 return -1;
379 return 0;
380 }
381
json_send_client_backup(struct cstat * cstat,struct bu * bu1,struct bu * bu2,const char * logfile,const char * browse,int use_cache,long peer_version)382 static int json_send_client_backup(struct cstat *cstat, struct bu *bu1,
383 struct bu *bu2, const char *logfile, const char *browse, int use_cache,
384 long peer_version)
385 {
386 int ret=-1;
387 if(json_send_client_start(cstat, peer_version))
388 return -1;
389 if((ret=json_send_backup(cstat, bu1,
390 1 /* print flags */, logfile, browse, use_cache, peer_version)))
391 goto end;
392 if((ret=json_send_backup(cstat, bu2,
393 1 /* print flags */, logfile, browse, use_cache, peer_version)))
394 goto end;
395 end:
396 if(json_send_client_end()) ret=-1;
397 return ret;
398 }
399
json_send_client_backup_list(struct cstat * cstat,int use_cache,long peer_version)400 static int json_send_client_backup_list(struct cstat *cstat, int use_cache,
401 long peer_version)
402 {
403 int ret=-1;
404 struct bu *bu;
405 if(json_send_client_start(cstat, peer_version))
406 return -1;
407 for(bu=cstat->bu; bu; bu=bu->prev)
408 {
409 if(json_send_backup(cstat, bu,
410 1 /* print flags */, NULL, NULL,
411 use_cache, peer_version))
412 goto end;
413 }
414 ret=0;
415 end:
416 if(json_send_client_end()) ret=-1;
417 return ret;
418 }
419
json_send(struct asfd * asfd,struct cstat * clist,struct cstat * cstat,struct bu * bu,const char * logfile,const char * browse,int use_cache,long peer_version)420 int json_send(struct asfd *asfd, struct cstat *clist, struct cstat *cstat,
421 struct bu *bu, const char *logfile, const char *browse,
422 int use_cache, long peer_version)
423 {
424 int ret=-1;
425 struct cstat *c;
426
427 if(json_start()
428 || json_clients())
429 goto end;
430
431 if(cstat && bu)
432 {
433 if(json_send_client_backup(cstat, bu, NULL,
434 logfile, browse, use_cache, peer_version))
435 goto end;
436 }
437 else if(cstat)
438 {
439 if(json_send_client_backup_list(cstat,
440 use_cache, peer_version))
441 goto end;
442 }
443 else for(c=clist; c; c=c->next)
444 {
445 if(!c->permitted) continue;
446 if(json_send_client_backup(c,
447 bu_find_current(c->bu),
448 bu_find_working_or_finishing(c->bu),
449 NULL, NULL, use_cache, peer_version))
450 goto end;
451 }
452
453 ret=0;
454 end:
455 if(json_clients_end()
456 || json_end(asfd)) return -1;
457 return ret;
458 }
459
json_cntr(struct asfd * asfd,struct cntr * cntr)460 int json_cntr(struct asfd *asfd, struct cntr *cntr)
461 {
462 int ret=-1;
463 if(json_start()
464 || do_counters(cntr))
465 goto end;
466 ret=0;
467 end:
468 if(json_end(asfd)) return -1;
469 return ret;
470 }
471
json_from_entry(const char * path,const char * link,struct stat * statp)472 int json_from_entry(const char *path, const char *link, struct stat *statp)
473 {
474 return yajl_map_open_w()
475 || yajl_gen_str_pair_w("name", path)
476 || yajl_gen_str_pair_w("link", link? link:"")
477 || yajl_gen_int_pair_w("dev", statp->st_dev)
478 || yajl_gen_int_pair_w("ino", statp->st_ino)
479 || yajl_gen_int_pair_w("mode", statp->st_mode)
480 || yajl_gen_int_pair_w("nlink", statp->st_nlink)
481 || yajl_gen_int_pair_w("uid", statp->st_uid)
482 || yajl_gen_int_pair_w("gid", statp->st_gid)
483 || yajl_gen_int_pair_w("rdev", statp->st_rdev)
484 || yajl_gen_int_pair_w("size", statp->st_size)
485 || yajl_gen_int_pair_w("blksize", statp->st_blksize)
486 || yajl_gen_int_pair_w("blocks", statp->st_blocks)
487 || yajl_gen_int_pair_w("atime", statp->st_atime)
488 || yajl_gen_int_pair_w("ctime", statp->st_ctime)
489 || yajl_gen_int_pair_w("mtime", statp->st_mtime)
490 || yajl_map_close_w();
491 }
492
json_send_msg(struct asfd * asfd,const char * field,const char * msg)493 int json_send_msg(struct asfd *asfd, const char *field, const char *msg)
494 {
495 int save;
496 int ret=0;
497
498 // Turn off pretty printing so that we get it on one line.
499 save=pretty_print;
500 pretty_print=0;
501
502 if(json_start()
503 || yajl_gen_str_pair_w(field, msg)
504 || json_end(asfd))
505 ret=-1;
506
507 pretty_print=save;
508
509 return ret;
510 }
511
json_send_warn(struct asfd * asfd,const char * msg)512 int json_send_warn(struct asfd *asfd, const char *msg)
513 {
514 return json_send_msg(asfd, "warning", msg);
515 }
516