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