1 #include "../../burp.h"
2 #include "../../action.h"
3 #include "../../alloc.h"
4 #include "../../asfd.h"
5 #include "../../async.h"
6 #include "../../bu.h"
7 #include "../../cmd.h"
8 #include "../../cstat.h"
9 #include "../../forkchild.h"
10 #include "../../fsops.h"
11 #include "../../fzp.h"
12 #include "../../handy.h"
13 #include "../../iobuf.h"
14 #include "../../log.h"
15 #include "../../times.h"
16 #include "json_input.h"
17 #include "lline.h"
18 #include "sel.h"
19 #include "status_client_ncurses.h"
20 
21 #ifdef HAVE_NCURSES_H
22 #include <ncurses.h>
23 #elif HAVE_NCURSES_NCURSES_H
24 #include <ncurses/ncurses.h>
25 #endif
26 
27 // So that the sighandler can call endwin():
28 static enum action actg=ACTION_STATUS;
29 
30 #define LEFT_SPACE	3
31 #define TOP_SPACE	2
32 
33 static struct fzp *lfzp=NULL;
34 
35 #ifdef HAVE_NCURSES
print_line_ncurses(const char * string,int row,int col)36 static void print_line_ncurses(const char *string, int row, int col)
37 {
38 	int k=0;
39 	const char *cp=NULL;
40 	while(k<LEFT_SPACE) mvprintw(row+TOP_SPACE, k++, " ");
41 	for(cp=string; (*cp && k<col); cp++)
42 		mvprintw(row+TOP_SPACE, k++, "%c", *cp);
43 	while(k<col) mvprintw(row+TOP_SPACE, k++, " ");
44 }
45 #endif
46 
47 static struct asfd *stdout_asfd=NULL;
48 
print_line_stdout(const char * string)49 static void print_line_stdout(const char *string)
50 {
51 	int k=0;
52 	while(k<LEFT_SPACE)
53 	{
54 		stdout_asfd->write_str(stdout_asfd, CMD_GEN, " ");
55 		k++;
56 	}
57 	stdout_asfd->write_str(stdout_asfd, CMD_GEN, string);
58 	stdout_asfd->write_str(stdout_asfd, CMD_GEN, "\n");
59 }
60 
print_line(const char * string,int row,int col)61 static void print_line(const char *string, int row, int col)
62 {
63 #ifdef HAVE_NCURSES
64 	if(actg==ACTION_STATUS)
65 	{
66 		print_line_ncurses(string, row, col);
67 		return;
68 	}
69 #endif
70 	print_line_stdout(string);
71 }
72 
get_bu_str(struct bu * bu)73 static char *get_bu_str(struct bu *bu)
74 {
75 	static char ret[38];
76 	if(!bu) snprintf(ret, sizeof(ret), "%07d never", 0);
77 	else if(!bu->bno) snprintf(ret, sizeof(ret), "%s", bu->timestamp);
78 	else snprintf(ret, sizeof(ret), "%07" PRIu64 " %s",
79 		bu->bno, bu->timestamp);
80 	return ret;
81 }
82 
client_summary(struct cstat * cstat,int row,int col,int clientwidth)83 static void client_summary(struct cstat *cstat,
84 	int row, int col, int clientwidth)
85 {
86 	char msg[1024]="";
87 	char fmt[64]="";
88 	struct bu *cbu=NULL;
89 	snprintf(fmt, sizeof(fmt), "%%-%d.%ds %%9s %%s%%s",
90 		clientwidth, clientwidth);
91 
92 	// Find the current backup.
93 	cbu=bu_find_current(cstat->bu);
94 
95 	snprintf(msg, sizeof(msg), fmt, cstat->name, run_status_to_str(cstat),
96 		" last backup: ", get_bu_str(cbu));
97 
98 	if(*msg) print_line(msg, row, col);
99 }
100 
101 /* for the counters */
to_msg(char msg[],size_t s,const char * fmt,...)102 static void to_msg(char msg[], size_t s, const char *fmt, ...)
103 {
104 	va_list ap;
105 	va_start(ap, fmt);
106 	vsnprintf(msg, s, fmt, ap);
107 	va_end(ap);
108 }
109 
print_cntr_ent(const char * field,uint64_t a,uint64_t b,uint64_t c,uint64_t d,uint64_t e,int * x,int col)110 static void print_cntr_ent(const char *field,
111 	uint64_t a,
112 	uint64_t b,
113 	uint64_t c,
114 	uint64_t d,
115 	uint64_t e,
116 	int *x, int col)
117 {
118 	char msg[256]="";
119 	uint64_t t=a+b+c;
120 	if(!field || (!t && !d && !e)) return;
121 
122 /* FIX THIS.
123 	if(phase==STATUS_RESTORING
124 	  || phase==STATUS_VERIFYING)
125 	{
126 		to_msg(msg, sizeof(msg),
127 			"% 15s % 9s % 9" PRIu64 " % 9" PRIu64,
128 			field, "", t, e);
129 	}
130 	else
131 	{
132 */
133 		to_msg(msg, sizeof(msg),
134 			"% 15s % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 "",
135 			field, a, b, c, d, t, e);
136 //	}
137 	print_line(msg, (*x)++, col);
138 /* FIX THIS
139 	if(percent && e)
140 	{
141 	  uint64_t p;
142 	  p=(t*100)/e;
143 	  if(phase==STATUS_RESTORING
144 	    || phase==STATUS_VERIFYING)
145 	  {
146 	    to_msg(msg, sizeof(msg), "% 15s % 9s % 9" PRIu64 "%% % 9s",
147 		"", "", p, "");
148 	  }
149 	  else
150 	  {
151 	    to_msg(msg, sizeof(msg), "% 15s % 9s % 9s % 9s % 9s % 9" PRIu64 "%% % 9s",
152 		"", "", "", "", "", p, "");
153 	  print_line(msg, (*x)++, col);
154 	}
155 */
156 }
157 
table_header(int * x,int col)158 static void table_header(int *x, int col)
159 {
160 	char msg[256]="";
161 /* FIX THIS
162 	if(phase==STATUS_RESTORING
163 	  || phase==STATUS_VERIFYING)
164 	{
165 	  to_msg(msg, sizeof(msg), "% 15s % 9s % 9s % 9s",
166 	    "", "", "Attempted", "Expected");
167 	}
168 	else
169 	{
170 */
171 	  to_msg(msg, sizeof(msg), "% 15s % 9s % 9s % 9s % 9s % 9s % 9s",
172 	    "", "New", "Changed", "Unchanged", "Deleted", "Total", "Scanned");
173 //	}
174 	print_line(msg, (*x)++, col);
175 }
176 
177 /*
178 static void print_detail2(const char *field, uint64_t value1, const char *value2, int *x, int col)
179 {
180 	char msg[256]="";
181 	if(!field || !value1 || !value2 || !*value2) return;
182 	snprintf(msg, sizeof(msg), "%s: %" PRIu64 "%s", field, value1, value2);
183 	print_line(msg, (*x)++, col);
184 }
185 
186 static void print_detail3(const char *field, const char *value, int *x, int col)
187 {
188 	char msg[256]="";
189 	if(!field || !value || !*value) return;
190 	snprintf(msg, sizeof(msg), "%s: %s", field, value);
191 	print_line(msg, (*x)++, col);
192 }
193 */
194 /*
195 static void detail(const char *cntrclient, char status, char phase, const char *path, struct cntr *p1cntr, struct cntr *cntr, struct strlist *backups, int row, int col)
196 {
197 	int x=0;
198 	char msg[1024]="";
199 	print_line("", x++, col);
200 	table_header(phase, &x, col);
201 
202 	print_detail(phase, "Files",
203 				cntr->file,
204 				cntr->file_changed,
205 				cntr->file_same,
206 				cntr->file_deleted,
207 				p1cntr->file,
208 				&x, col, 0);
209 	print_detail(phase, "Encrypted files",
210 				cntr->enc,
211 				cntr->enc_changed,
212 				cntr->enc_same,
213 				cntr->enc_deleted,
214 				p1cntr->enc,
215 				&x, col, 0);
216 	print_detail(phase, "Meta data",
217 				cntr->meta,
218 				cntr->meta_changed,
219 				cntr->meta_same,
220 				cntr->meta_deleted,
221 				p1cntr->meta,
222 				&x, col, 0);
223 	print_detail(phase, "Encrypted meta data",
224 				cntr->encmeta,
225 				cntr->encmeta_changed,
226 				cntr->encmeta_same,
227 				cntr->encmeta_deleted,
228 				p1cntr->encmeta,
229 				&x, col, 0);
230 	print_detail(phase, "Directories",
231 				cntr->dir,
232 				cntr->dir_changed,
233 				cntr->dir_same,
234 				cntr->dir_deleted,
235 				p1cntr->dir,
236 				&x, col, 0);
237 	print_detail(phase, "Soft links",
238 				cntr->slink,
239 				cntr->slink_changed,
240 				cntr->slink_same,
241 				cntr->slink_deleted,
242 				p1cntr->slink,
243 				&x, col, 0);
244 	print_detail(phase, "Hard links",
245 				cntr->hlink,
246 				cntr->hlink_changed,
247 				cntr->hlink_same,
248 				cntr->hlink_deleted,
249 				p1cntr->hlink,
250 				&x, col, 0);
251 	print_detail(phase, "Special files",
252 				cntr->special,
253 				cntr->special_changed,
254 				cntr->special_same,
255 				cntr->special_deleted,
256 				p1cntr->special,
257 				&x, col, 0);
258 	print_detail(phase, "Total",
259 				cntr->gtotal,
260 				cntr->gtotal_changed,
261 				cntr->gtotal_same,
262 				cntr->gtotal_deleted,
263 				p1cntr->gtotal,
264 				&x, col, 1);
265 	print_line("", x++, col);
266 	print_detail(phase, "Warnings",
267 				cntr->warning, 0, 0, 0, 0,
268 				&x, col, 1);
269 
270 	if(p1cntr->byte)
271 	{
272 		tmp=bytes_to_human(p1cntr->byte);
273 		print_detail2("Bytes estimated", p1cntr->byte, tmp, &x, col);
274 	}
275 	if(cntr->byte)
276 	{
277 		const char *text=NULL;
278 		if(phase==STATUS_BACKUP) text="Bytes in backup";
279 		else if(phase==STATUS_RESTORING) text="Bytes attempted";
280 		else if(phase==STATUS_VERIFYING) text="Bytes checked";
281 		tmp=bytes_to_human(cntr->byte);
282 		if(text) print_detail2(text, cntr->byte, tmp, &x, col);
283 	}
284 	if(cntr->recvbyte)
285 	{
286 		const char *text=NULL;
287 		tmp=bytes_to_human(cntr->recvbyte);
288 		if(phase==STATUS_BACKUP) text="Bytes received";
289 		if(text) print_detail2(text, cntr->recvbyte, tmp, &x, col);
290 	}
291 	if(cntr->sentbyte)
292 	{
293 		const char *text=NULL;
294 		if(phase==STATUS_BACKUP) text="Bytes sent";
295 		else if(phase==STATUS_RESTORING) text="Bytes sent";
296 		tmp=bytes_to_human(cntr->sentbyte);
297 		print_detail2(text, cntr->sentbyte, tmp, &x, col);
298 	}
299 	if(p1cntr->start)
300 	{
301 		time_t now=0;
302 		time_t diff=0;
303 		now=time(NULL);
304 		diff=now-p1cntr->start;
305 
306 		print_detail3("Start time", getdatestr(p1cntr->start), &x,col);
307 		print_detail3("Time taken", time_taken(diff), &x, col);
308 
309 		if(diff>0)
310 		{
311 			uint64_t bytesleft=0;
312 			uint64_t byteswant=0;
313 			uint64_t bytesgot=0;
314 			float bytespersec=0;
315 			byteswant=p1cntr->byte;
316 			bytesgot=cntr->byte;
317 			bytespersec=(float)(bytesgot/diff);
318 			bytesleft=byteswant-bytesgot;
319 			if(bytespersec>0)
320 			{
321 				time_t timeleft=0;
322 				timeleft=(time_t)(bytesleft/bytespersec);
323 				print_detail3("Time left",
324 					time_taken(timeleft), &x, col);
325 			}
326 		}
327 	}
328 	if(path && *path)
329 	{
330 		char pathstr[256]="";
331 		snprintf(pathstr, sizeof(pathstr), "\n%s\n", path);
332 #ifdef HAVE_NCURSES
333 		if(actg==ACTION_STATUS)
334 		{
335 			printw("%s", pathstr);
336 			return;
337 		}
338 #endif
339 		stdout_asfd->write_str(stdout_asfd, CMD_GEN, pathstr);
340 	}
341 }
342 */
343 
344 #ifdef HAVE_NCURSES
screen_header_ncurses(const char * date,int l,int col)345 static void screen_header_ncurses(const char *date, int l, int col)
346 {
347 	char v[32]="";
348 	snprintf(v, sizeof(v), " %s monitor %s", PACKAGE_TARNAME, VERSION);
349 	print_line(v, 0-TOP_SPACE, col);
350 	mvprintw(0, col-l-1, date);
351 }
352 #endif
353 
screen_header_stdout(const char * date,int l,int col)354 static void screen_header_stdout(const char *date, int l, int col)
355 {
356 	size_t c=0;
357 	char spaces[512]="";
358 	char msg[64]="";
359 	snprintf(msg, sizeof(msg), " %s status", PACKAGE_TARNAME);
360 
361 	stdout_asfd->write_str(stdout_asfd, CMD_GEN, "\n");
362 	stdout_asfd->write_str(stdout_asfd, CMD_GEN, msg);
363 	for(c=0;
364 	  c<(col-strlen(msg)-l-1)
365 		&& c<sizeof(spaces)-1; c++)
366 			spaces[c]=' ';
367 	spaces[c]='\0';
368 	stdout_asfd->write_str(stdout_asfd, CMD_GEN, spaces);
369 	stdout_asfd->write_str(stdout_asfd, CMD_GEN, date);
370 	stdout_asfd->write_str(stdout_asfd, CMD_GEN, "\n\n");
371 }
372 
screen_header(int col)373 static void screen_header(int col)
374 {
375 	int l;
376 	const char *date=NULL;
377 #ifdef UTEST
378 	date="1977-10-02 00:10:20";
379 #else
380 	date=gettimenow();
381 #endif
382 	l=strlen(date);
383 #ifdef HAVE_NCURSES
384 	if(actg==ACTION_STATUS)
385 	{
386 		screen_header_ncurses(date, l, col);
387 		return;
388 	}
389 #endif
390 	screen_header_stdout(date, l, col);
391 }
392 
need_status(struct sel * sel)393 static int need_status(struct sel *sel)
394 {
395 	static time_t lasttime=0;
396 	time_t now=0;
397 	time_t diff=0;
398 
399 	if(sel->page==PAGE_VIEW_LOG && sel->llines)
400 		return 0;
401 
402 	// Only ask for an update every second.
403 	now=time(NULL);
404 	diff=now-lasttime;
405 	if(diff<1)
406 	{
407 		// In case they fiddled their clock back in time.
408 		if(diff<0) lasttime=now;
409 		return 0;
410 	}
411 	lasttime=now;
412 	return 1;
413 }
414 
logop_to_text(uint16_t logop)415 static const char *logop_to_text(uint16_t logop)
416 {
417 	switch(logop)
418 	{
419 		case BU_MANIFEST:	return "Manifest";
420 		case BU_LOG_BACKUP:	return "Backup log";
421 		case BU_LOG_RESTORE:	return "Restore log";
422 		case BU_LOG_VERIFY:	return "Verify log";
423 		case BU_STATS_BACKUP:	return "Backup stats";
424 		case BU_STATS_RESTORE:	return "Restore stats";
425 		case BU_STATS_VERIFY:	return "Verify stats";
426 		case BU_LIVE_COUNTERS:	return "Live counters";
427 		default: return "";
428 	}
429 }
430 
print_logs_list_line(struct sel * sel,uint16_t bit,int * x,int col)431 static void print_logs_list_line(struct sel *sel,
432 	uint16_t bit, int *x, int col)
433 {
434 	char msg[64]="";
435 	if(!sel->backup || !(sel->backup->flags & bit)) return;
436 	snprintf(msg, sizeof(msg), "%s%s",
437 		*x==3?"Browse: ":"        ", logop_to_text(bit));
438 	print_line(msg, (*x)++, col);
439 
440 	if(!sel->logop) sel->logop=bit;
441 #ifdef HAVE_NCURSES
442 	if(sel->logop==bit) mvprintw(*x+TOP_SPACE-1, 1, "*");
443 #endif
444 }
445 
print_client(struct sel * sel,int * x,int col)446 static void print_client(struct sel *sel, int *x, int col)
447 {
448 	char msg[1024]="";
449 	snprintf(msg, sizeof(msg), "Client: %s", sel->client->name);
450 //		sel->client->cntr->ent[CMD_FILE]->phase1,
451 //		sel->client->cntr->ent[CMD_FILE]->count);
452 	print_line(msg, (*x)++, col);
453 }
454 
client_and_status(struct sel * sel,int * x,int col)455 static void client_and_status(struct sel *sel, int *x, int col)
456 {
457 	char msg[1024]="";
458 	print_client(sel, x, col);
459 	snprintf(msg, sizeof(msg),
460 		"Status: %s", run_status_to_str(sel->client));
461 	print_line(msg, (*x)++, col);
462 }
463 
client_and_status_and_backup(struct sel * sel,int * x,int col)464 static void client_and_status_and_backup(struct sel *sel, int *x, int col)
465 {
466 	char msg[1024];
467 	client_and_status(sel, x, col);
468 	snprintf(msg, sizeof(msg), "Backup: %s", get_bu_str(sel->backup));
469 	print_line(msg, (*x)++, col);
470 }
471 
client_and_status_and_backup_and_log(struct sel * sel,int * x,int col)472 static void client_and_status_and_backup_and_log(struct sel *sel,
473 	int *x, int col)
474 {
475 	char msg[1024];
476 	client_and_status_and_backup(sel, x, col);
477 	snprintf(msg, sizeof(msg), "Browse: %s", logop_to_text(sel->logop));
478 	print_line(msg, (*x)++, col);
479 }
480 
481 #ifdef HAVE_NCURSES
selindex_from_cstat(struct sel * sel)482 static int selindex_from_cstat(struct sel *sel)
483 {
484 	int selindex=0;
485 	struct cstat *c;
486 	for(c=sel->clist; c; c=c->next)
487 	{
488 		selindex++;
489 		if(sel->client==c) break;
490 	}
491 	return selindex;
492 }
493 
selindex_from_bu(struct sel * sel)494 static int selindex_from_bu(struct sel *sel)
495 {
496 	int selindex=0;
497 	struct bu *b;
498 	for(b=sel->client->bu; b; b=b->next)
499 	{
500 		selindex++;
501 		if(sel->backup==b) break;
502 	}
503 	return selindex;
504 }
505 
selindex_from_lline(struct sel * sel)506 static int selindex_from_lline(struct sel *sel)
507 {
508 	int selindex=0;
509 	struct lline *l;
510 	for(l=sel->llines; l; l=l->next)
511 	{
512 		selindex++;
513 		if(sel->lline==l) break;
514 	}
515 	return selindex;
516 }
517 #endif
518 
print_logs_list(struct sel * sel,int * x,int col)519 static void print_logs_list(struct sel *sel, int *x, int col)
520 {
521 	print_logs_list_line(sel, BU_LIVE_COUNTERS, x, col);
522 	print_logs_list_line(sel, BU_MANIFEST, x, col);
523 	print_logs_list_line(sel, BU_LOG_BACKUP, x, col);
524 	print_logs_list_line(sel, BU_LOG_RESTORE, x, col);
525 	print_logs_list_line(sel, BU_LOG_VERIFY, x, col);
526 	print_logs_list_line(sel, BU_STATS_BACKUP, x, col);
527 	print_logs_list_line(sel, BU_STATS_RESTORE, x, col);
528 	print_logs_list_line(sel, BU_STATS_VERIFY, x, col);
529 }
530 
update_screen_clients(struct sel * sel,int * x,int col,int winmin,int winmax)531 static void update_screen_clients(struct sel *sel, int *x, int col,
532 	int winmin, int winmax)
533 {
534 #ifdef HAVE_NCURSES
535 	int s=0;
536 #endif
537 	struct cstat *c;
538 	int star_printed=0;
539 	int max_cname=23*((float)col/100);
540 #ifdef HAVE_NCURSES
541 	if(actg==ACTION_STATUS_SNAPSHOT)
542 #endif
543 	{
544 		size_t l;
545 		for(c=sel->clist; c; c=c->next)
546 			if((l=strlen(c->name))>(unsigned int)max_cname)
547 				max_cname=l;
548 	}
549 	for(c=sel->clist; c; c=c->next)
550 	{
551 #ifdef HAVE_NCURSES
552 		if(actg==ACTION_STATUS)
553 		{
554 			s++;
555 			if(s<winmin) continue;
556 			if(s>winmax) break;
557 		}
558 #endif
559 
560 		client_summary(c, (*x)++, col, max_cname);
561 
562 #ifdef HAVE_NCURSES
563 		if(actg==ACTION_STATUS && sel->client==c)
564 		{
565 			mvprintw((*x)+TOP_SPACE-1, 1, "*");
566 			star_printed=1;
567 		}
568 #endif
569 	}
570 	if(!star_printed) sel->client=sel->clist;
571 }
572 
get_extradesc(struct bu * b,struct cntr * cntrs)573 static char *get_extradesc(struct bu *b, struct cntr *cntrs)
574 {
575 	char *extradesc=NULL;
576 	struct cntr *cntr=NULL;
577 	if(b->flags & BU_CURRENT)
578 	{
579 		extradesc=strdup_w(" (current)", __func__);
580 	}
581 	else if(b->flags & BU_WORKING)
582 	{
583 		extradesc=strdup_w(" (working)", __func__);
584 	}
585 	else if(b->flags & BU_FINISHING)
586 	{
587 		extradesc=strdup_w(" (finishing)", __func__);
588 	}
589 	else
590 	{
591 		extradesc=strdup_w("", __func__);
592 	}
593 
594 	for(cntr=cntrs; cntr; cntr=cntr->next)
595 	{
596 		char phase[32]="";
597 		if(cntr->bno==b->bno)
598 		{
599 			snprintf(phase, sizeof(phase),
600 				" %s, pid: %d",
601 				cntr_status_to_str(cntr), cntr->pid);
602 			if(astrcat(&extradesc, phase, __func__))
603 				return NULL;
604 		}
605 	}
606 	return extradesc;
607 }
608 
update_screen_backups(struct sel * sel,int * x,int col,int winmin,int winmax)609 static int update_screen_backups(struct sel *sel, int *x, int col,
610 	int winmin, int winmax)
611 {
612 #ifdef HAVE_NCURSES
613 	int s=0;
614 #endif
615 	struct bu *b;
616 	char msg[1024]="";
617 	int star_printed=0;
618 	for(b=sel->client->bu; b; b=b->next)
619 	{
620 		char *extradesc=NULL;
621 #ifdef HAVE_NCURSES
622 		if(actg==ACTION_STATUS)
623 		{
624 			s++;
625 			if(s<winmin) continue;
626 			if(s>winmax) break;
627 		}
628 #endif
629 
630 		if(!(extradesc=get_extradesc(b, sel->client->cntrs)))
631 			return -1;
632 
633 		snprintf(msg, sizeof(msg), "%s %s%s",
634 				b==sel->client->bu?"Backup list:":
635 				"            ",
636 				get_bu_str(b),
637 				extradesc);
638 		free_w(&extradesc);
639 		print_line(msg, (*x)++, col);
640 #ifdef HAVE_NCURSES
641 		if(actg==ACTION_STATUS && sel->backup==b)
642 		{
643 			mvprintw((*x)+TOP_SPACE-1, 1, "*");
644 			star_printed=1;
645 		}
646 #endif
647 	}
648 	if(!star_printed) sel->backup=sel->client->bu;
649 	return 0;
650 }
651 
update_screen_live_counter_table(struct cntr_ent * e,int * x,int col)652 static void update_screen_live_counter_table(struct cntr_ent *e,
653 	int *x, int col)
654 {
655 	if(!(e->flags & CNTR_TABULATE)) return;
656 	print_cntr_ent(e->label,
657 		e->count,
658 		e->changed,
659 		e->same,
660 		e->deleted,
661 		e->phase1,
662 		x, col);
663 }
664 
update_screen_live_counter_single(struct cntr_ent * e,int * x,int col)665 static void update_screen_live_counter_single(struct cntr_ent *e,
666 	int *x, int col)
667 {
668 	char msg[128]="";
669 	const char *bytes_human="";
670 	if(!(e->flags & CNTR_SINGLE_FIELD)) return;
671 	if(!e->count) return;
672 	switch(e->cmd)
673 	{
674 		case CMD_TIMESTAMP:
675 		case CMD_TIMESTAMP_END:
676 			return;
677 		case CMD_BYTES_ESTIMATED:
678 		case CMD_BYTES:
679 		case CMD_BYTES_RECV:
680 		case CMD_BYTES_SENT:
681 			bytes_human=bytes_to_human(e->count);
682 			break;
683 		default:
684 			break;
685 	}
686 	snprintf(msg, sizeof(msg), "%19s: %12" PRIu64 " %s",
687 		e->label, e->count, bytes_human);
688 	print_line(msg, (*x)++, col);
689 }
690 
update_screen_live_counters(struct cntr * cntr,int * x,int col)691 static void update_screen_live_counters(struct cntr *cntr, int *x, int col)
692 {
693 	char msg[128]="";
694 	struct cntr_ent *e;
695 	time_t start=(time_t)cntr->ent[(uint8_t)CMD_TIMESTAMP]->count;
696 	time_t end=(time_t)cntr->ent[(uint8_t)CMD_TIMESTAMP_END]->count;
697 	struct cntr_ent *gtotal=cntr->ent[(uint8_t)CMD_GRAND_TOTAL];
698 
699 	print_line("", (*x)++, col);
700 	snprintf(msg, sizeof(msg), "       PID: %d (%s)",
701 		cntr->pid, cntr_status_to_str(cntr));
702 	print_line(msg, (*x)++, col);
703 	snprintf(msg, sizeof(msg), "Start time: %s", getdatestr(start));
704 	print_line(msg, (*x)++, col);
705 	snprintf(msg, sizeof(msg), "  End time: %s", getdatestr(end));
706 	print_line(msg, (*x)++, col);
707 	snprintf(msg, sizeof(msg), "Time taken: %s", time_taken(end-start));
708 	print_line(msg, (*x)++, col);
709 	table_header(x, col);
710 	for(e=cntr->list; e; e=e->next)
711 		update_screen_live_counter_table(e, x, col);
712 	print_line("", (*x)++, col);
713 
714 	if(gtotal->phase1)
715 	{
716 		snprintf(msg, sizeof(msg),
717 			"%19s: %" PRIu64 "%%", "Percentage complete",
718 			((gtotal->count+gtotal->same+gtotal->changed)*100)/gtotal->phase1);
719 		print_line(msg, (*x)++, col);
720 	}
721 	print_line("", (*x)++, col);
722 	for(e=cntr->list; e; e=e->next)
723 		update_screen_live_counter_single(e, x, col);
724 }
725 
update_screen_live_counters_w(struct sel * sel,int * x,int col)726 static void update_screen_live_counters_w(struct sel *sel, int *x, int col)
727 {
728 	struct cstat *client=sel->client;
729 	struct cntr *cntr=NULL;
730 	for(cntr=client->cntrs; cntr; cntr=cntr->next)
731 	{
732 		if(sel->backup
733 		  && sel->backup->bno==cntr->bno)
734 			update_screen_live_counters(cntr, x, col);
735 	}
736 }
737 
update_screen_view_log(struct sel * sel,int * x,int col,int winmin,int winmax)738 static void update_screen_view_log(struct sel *sel, int *x, int col,
739 	int winmin, int winmax)
740 {
741 #ifdef HAVE_NCURSES
742 	int s=0;
743 #endif
744 	int o=0;
745 	struct lline *l;
746 	const char *cp=NULL;
747 	int star_printed=0;
748 
749 	if(sel->client
750 	  && sel->backup
751 	  && (sel->logop & BU_LIVE_COUNTERS))
752 		return update_screen_live_counters_w(sel, x, col);
753 
754 	for(l=sel->llines; l; l=l->next)
755 	{
756 #ifdef HAVE_NCURSES
757 		if(actg==ACTION_STATUS)
758 		{
759 			s++;
760 			if(s<winmin) continue;
761 			if(s>winmax) break;
762 		}
763 #endif
764 
765 		// Allow them to scroll log lines left and right.
766 		for(cp=l->line, o=0; *cp && o<sel->offset; cp++, o++) { }
767 		print_line(cp, (*x)++, col);
768 
769 #ifdef HAVE_NCURSES
770 		if(actg==ACTION_STATUS && sel->lline==l)
771 		{
772 			mvprintw((*x)+TOP_SPACE-1, 1, "*");
773 			star_printed=1;
774 		}
775 #endif
776 	}
777 	if(!star_printed) sel->lline=sel->llines;
778 }
779 
update_screen(struct sel * sel)780 static int update_screen(struct sel *sel)
781 {
782 	int x=0;
783 	int row=24;
784 	int col=80;
785 #ifdef HAVE_NCURSES
786 	int selindex=0;
787 	static int selindex_last=0;
788 #endif
789 	static int winmin=0;
790 	static int winmax=0;
791 
792 	screen_header(col);
793 
794 	if(!sel->client) return 0;
795 
796 #ifdef HAVE_NCURSES
797 	if(actg==ACTION_STATUS)
798 	{
799 		getmaxyx(stdscr, row, col);
800 		// Unit tests give -1 for row and column.
801 		// Hack around it so that the unit tests still work.
802 		if(row<0)
803 			row=24;
804 		if(col<0)
805 			col=80;
806 		//if(!winmax) winmax=row;
807 		switch(sel->page)
808 		{
809 			case PAGE_CLIENT_LIST:
810 				selindex=selindex_from_cstat(sel);
811 				break;
812 			case PAGE_BACKUP_LIST:
813 				selindex=selindex_from_bu(sel);
814 				break;
815 			case PAGE_BACKUP_LOGS:
816 				break;
817 			case PAGE_VIEW_LOG:
818 				selindex=selindex_from_lline(sel);
819 				break;
820 		}
821 	}
822 #endif
823 	switch(sel->page)
824 	{
825 		case PAGE_CLIENT_LIST:
826 			break;
827 		case PAGE_BACKUP_LIST:
828 			client_and_status(sel, &x, col);
829 			break;
830 		case PAGE_BACKUP_LOGS:
831 			client_and_status_and_backup(sel, &x, col);
832 			break;
833 		case PAGE_VIEW_LOG:
834 			client_and_status_and_backup_and_log(sel, &x, col);
835 			break;
836 	}
837 
838 #ifdef HAVE_NCURSES
839 	if(actg==ACTION_STATUS)
840 	{
841 		// Adjust sliding window appropriately.
842 		if(selindex>selindex_last)
843 		{
844 			if(selindex>winmax-TOP_SPACE-1-x)
845 			{
846 				winmin+=selindex-selindex_last;
847 				winmax+=selindex-selindex_last;
848 			}
849 		}
850 		else if(selindex<selindex_last)
851 		{
852 			if(selindex<winmin)
853 			{
854 				winmin+=selindex-selindex_last;
855 				winmax+=selindex-selindex_last;
856 			}
857 		}
858 
859 		if(winmin==winmax)
860 		{
861 			winmin=0;
862 			winmax=row;
863 		}
864 		else if(winmin<0)
865 		{
866 			winmin=0;
867 			winmax=row;
868 		}
869 /*
870 		{
871 			char msg[64];
872 			snprintf(msg, sizeof(msg),
873 				"sel:%d si:%d min:%d max:%d %s\n",
874 				selindex, selindex_last, winmin, winmax,
875 				(selbu && *selbu && (*selbu)->prev)?
876 					(*selbu)->prev->timestamp:"");
877 			print_line(msg, -1, col);
878 		}
879 */
880 	}
881 #endif
882 
883 	switch(sel->page)
884 	{
885 		case PAGE_CLIENT_LIST:
886 			update_screen_clients(sel, &x, col, winmin, winmax);
887 			break;
888 		case PAGE_BACKUP_LIST:
889 			if(update_screen_backups(sel, &x, col, winmin, winmax))
890 				return -1;
891 			break;
892 		case PAGE_BACKUP_LOGS:
893 			print_logs_list(sel, &x, col);
894 			break;
895 		case PAGE_VIEW_LOG:
896 			update_screen_view_log(sel, &x, col, winmin, winmax);
897 			break;
898 	}
899 
900 #ifdef HAVE_NCURSES
901 	if(actg==ACTION_STATUS)
902 	{
903 		// Blank any remainder of the screen.
904 		for(; x<row; x++)
905 			print_line("", x, col);
906 		selindex_last=selindex;
907 	}
908 #endif
909 	return 0;
910 }
911 
request_status(struct asfd * asfd,const char * client,struct sel * sel)912 static int request_status(struct asfd *asfd,
913 	const char *client, struct sel *sel)
914 {
915 	char buf[256]="";
916 	switch(sel->page)
917 	{
918 		case PAGE_CLIENT_LIST:
919 			snprintf(buf, sizeof(buf), "c:\n");
920 			break;
921 		case PAGE_BACKUP_LIST:
922 			snprintf(buf, sizeof(buf), "c:%s\n", client);
923 			break;
924 		case PAGE_BACKUP_LOGS:
925 			if(sel->backup)
926 				snprintf(buf, sizeof(buf),
927 					"c:%s:b:%" PRIu64 "\n",
928 					client, sel->backup->bno);
929 			break;
930 		case PAGE_VIEW_LOG:
931 		{
932 			const char *lname=NULL;
933 			if(sel->logop & BU_LOG_BACKUP)
934 				lname="backup";
935 			else if(sel->logop & BU_LOG_RESTORE)
936 				lname="restore";
937 			else if(sel->logop & BU_LOG_VERIFY)
938 				lname="verify";
939 			else if(sel->logop & BU_MANIFEST)
940 				lname="manifest";
941 			else if(sel->logop & BU_STATS_RESTORE)
942 				lname="restore_stats";
943 			else if(sel->logop & BU_STATS_VERIFY)
944 				lname="verify_stats";
945 			else if(sel->logop & BU_STATS_BACKUP)
946 				lname="backup_stats";
947 			else if(sel->logop & BU_LIVE_COUNTERS)
948 			{
949 				// Hack so that it does not request the logs
950 				// for live counters.
951 				if(!sel->backup
952 				  || !sel->client
953 				  || !sel->client->cntrs)
954 					break;
955 				// Make sure a request is sent, so that the
956 				// counters update.
957 				snprintf(buf, sizeof(buf),
958 					"c:%s:b:%" PRIu64 "\n",
959 					client, sel->backup->bno);
960 				break;
961 			}
962 
963 			if(sel->backup && lname)
964 				snprintf(buf, sizeof(buf),
965 					"c:%s:b:%" PRIu64 ":l:%s\n",
966 					client, sel->backup->bno, lname);
967 			break;
968 		}
969 	}
970 /*
971 	if(confs->browsedir)
972 		snprintf(buf, sizeof(buf), "c:%s:b:%s:p:%s\n",
973 			client, confs->backup, confs->browsedir);
974 	else if(confs->browsefile)
975 		snprintf(buf, sizeof(buf), "c:%s:b:%s:f:%s\n",
976 			client, confs->backup, confs->browsefile);
977 */
978 	if(*buf)
979 	{
980 		if(lfzp) logp("request: %s\n", buf);
981 		if(asfd->write_str(asfd, CMD_GEN /* ignored */, buf)) return -1;
982 	}
983 	return 0;
984 }
985 
986 #ifdef HAVE_NCURSES
ncurses_free(void)987 static void ncurses_free(void)
988 {
989 	endwin();
990 }
991 #endif
992 
sighandler(int sig)993 static void sighandler(int sig)
994 {
995 #ifdef HAVE_NCURSES
996 	if(actg==ACTION_STATUS)
997 		ncurses_free();
998 #endif
999         logp("got signal: %d\n", sig);
1000 	if(sig==SIGPIPE) logp("Server may have too many active status clients.\n");
1001         logp("exiting\n");
1002         exit(1);
1003 }
1004 
setup_signals(void)1005 static void setup_signals(void)
1006 {
1007 	signal(SIGABRT, &sighandler);
1008 	signal(SIGTERM, &sighandler);
1009 	signal(SIGINT, &sighandler);
1010 	signal(SIGPIPE, &sighandler);
1011 }
1012 
1013 #ifdef HAVE_NCURSES
left(struct sel * sel)1014 static void left(struct sel *sel)
1015 {
1016 	switch(sel->page)
1017 	{
1018 		case PAGE_CLIENT_LIST:
1019 			break;
1020 		case PAGE_BACKUP_LIST:
1021 			sel->page=PAGE_CLIENT_LIST;
1022 			break;
1023 		case PAGE_BACKUP_LOGS:
1024 			sel->page=PAGE_BACKUP_LIST;
1025 			break;
1026 		case PAGE_VIEW_LOG:
1027 			if(sel->offset>0)
1028 			{
1029 				// Allow log lines to be scrolled left.
1030 				sel->offset--;
1031 				break;
1032 			}
1033 			sel->page=PAGE_BACKUP_LOGS;
1034 			llines_free(&sel->llines);
1035 			sel->lline=NULL;
1036 			break;
1037 	}
1038 }
1039 
right(struct sel * sel)1040 static void right(struct sel *sel)
1041 {
1042 	switch(sel->page)
1043 	{
1044 		case PAGE_CLIENT_LIST:
1045 			sel->page=PAGE_BACKUP_LIST;
1046 			break;
1047 		case PAGE_BACKUP_LIST:
1048 			sel->page=PAGE_BACKUP_LOGS;
1049 			break;
1050 		case PAGE_BACKUP_LOGS:
1051 			if(lfzp) logp("Option selected: 0x%04X\n", sel->logop);
1052 			sel->page=PAGE_VIEW_LOG;
1053 			break;
1054 		case PAGE_VIEW_LOG:
1055 			// Allow log lines to be scrolled right.
1056 			sel->offset++;
1057 			break;
1058 	}
1059 }
1060 
up_client(struct sel * sel)1061 static void up_client(struct sel *sel)
1062 {
1063 	if(sel->client && sel->client->prev)
1064 		sel->client=sel->client->prev;
1065 }
1066 
down_client(struct sel * sel)1067 static void down_client(struct sel *sel)
1068 {
1069 	if(sel->client && sel->client->next)
1070 		sel->client=sel->client->next;
1071 }
1072 
up_backup(struct sel * sel)1073 static void up_backup(struct sel *sel)
1074 {
1075 	if(sel->backup && sel->backup->prev)
1076 		sel->backup=sel->backup->prev;
1077 }
1078 
down_backup(struct sel * sel)1079 static void down_backup(struct sel *sel)
1080 {
1081 	if(sel->backup && sel->backup->next)
1082 		sel->backup=sel->backup->next;
1083 }
1084 
up_logs(struct sel * sel)1085 static void up_logs(struct sel *sel)
1086 {
1087 	int i=0;
1088 	uint16_t sh=sel->logop;
1089 	for(i=0; sh>BU_LIVE_COUNTERS && i<16; i++)
1090 	{
1091 		sh=sh>>1;
1092 		if(sh & sel->backup->flags)
1093 		{
1094 			sel->logop=sh;
1095 			break;
1096 		}
1097 	}
1098 }
1099 
down_logs(struct sel * sel)1100 static void down_logs(struct sel *sel)
1101 {
1102 	int i=0;
1103 	uint16_t sh=sel->logop;
1104 	for(i=0; sh && i<16; i++)
1105 	{
1106 		sh=sh<<1;
1107 		if(sh & sel->backup->flags)
1108 		{
1109 			sel->logop=sh;
1110 			break;
1111 		}
1112 	}
1113 }
1114 
up_view_log(struct sel * sel)1115 static void up_view_log(struct sel *sel)
1116 {
1117 	if(sel->lline && sel->lline->prev)
1118 		sel->lline=sel->lline->prev;
1119 }
1120 
down_view_log(struct sel * sel)1121 static void down_view_log(struct sel *sel)
1122 {
1123 	if(sel->lline && sel->lline->next)
1124 		sel->lline=sel->lline->next;
1125 }
1126 
up(struct sel * sel)1127 static void up(struct sel *sel)
1128 {
1129 	switch(sel->page)
1130 	{
1131 		case PAGE_CLIENT_LIST:
1132 			up_client(sel);
1133 			break;
1134 		case PAGE_BACKUP_LIST:
1135 			up_backup(sel);
1136 			break;
1137 		case PAGE_BACKUP_LOGS:
1138 			up_logs(sel);
1139 			break;
1140 		case PAGE_VIEW_LOG:
1141 			up_view_log(sel);
1142 			break;
1143 	}
1144 }
1145 
down(struct sel * sel)1146 static void down(struct sel *sel)
1147 {
1148 	switch(sel->page)
1149 	{
1150 		case PAGE_CLIENT_LIST:
1151 			down_client(sel);
1152 			break;
1153 		case PAGE_BACKUP_LIST:
1154 			down_backup(sel);
1155 			break;
1156 		case PAGE_BACKUP_LOGS:
1157 			down_logs(sel);
1158 			break;
1159 		case PAGE_VIEW_LOG:
1160 			down_view_log(sel);
1161 			break;
1162 	}
1163 }
1164 
page_up_client(struct sel * sel,int row)1165 static void page_up_client(struct sel *sel, int row)
1166 {
1167 	struct cstat *c;
1168 	for(c=sel->client; c; c=c->prev)
1169 	{
1170 		row--;
1171 		if(!row) break;
1172 	}
1173 	sel->client=c;
1174 }
1175 
page_down_client(struct sel * sel,int row)1176 static void page_down_client(struct sel *sel, int row)
1177 {
1178 	struct cstat *c;
1179 	for(c=sel->client; c; c=c->next)
1180 	{
1181 		row--;
1182 		if(!row) break;
1183 		if(!c->next) break;
1184 	}
1185 	sel->client=c;
1186 }
1187 
page_up_backup(struct sel * sel,int row)1188 static void page_up_backup(struct sel *sel, int row)
1189 {
1190 	struct bu *b;
1191 	for(b=sel->backup; b; b=b->prev)
1192 	{
1193 		row--;
1194 		if(!row) break;
1195 	}
1196 	sel->backup=b;
1197 }
1198 
page_down_backup(struct sel * sel,int row)1199 static void page_down_backup(struct sel *sel, int row)
1200 {
1201 	struct bu *b;
1202 	for(b=sel->backup; b; b=b->next)
1203 	{
1204 		row--;
1205 		if(!row) break;
1206 		if(!b->next) break;
1207 	}
1208 	sel->backup=b;
1209 }
1210 
page_up(struct sel * sel)1211 static void page_up(struct sel *sel)
1212 {
1213 	int row=getmaxy(stdscr);
1214 	switch(sel->page)
1215 	{
1216 		case PAGE_CLIENT_LIST:
1217 			page_up_client(sel, row);
1218 			break;
1219 		case PAGE_BACKUP_LIST:
1220 			page_up_backup(sel, row);
1221 			break;
1222 		case PAGE_BACKUP_LOGS:
1223 			break;
1224 		case PAGE_VIEW_LOG:
1225 			break;
1226 	}
1227 }
1228 
page_down(struct sel * sel)1229 static void page_down(struct sel *sel)
1230 {
1231 	int row=getmaxy(stdscr);
1232 	switch(sel->page)
1233 	{
1234 		case PAGE_CLIENT_LIST:
1235 			page_down_client(sel, row);
1236 			break;
1237 		case PAGE_BACKUP_LIST:
1238 			page_down_backup(sel, row);
1239 			break;
1240 		case PAGE_BACKUP_LOGS:
1241 			break;
1242 		case PAGE_VIEW_LOG:
1243 			break;
1244 	}
1245 }
1246 
parse_stdin_data(struct asfd * asfd,struct sel * sel)1247 static int parse_stdin_data(struct asfd *asfd, struct sel *sel)
1248 {
1249 	static int ch;
1250 	if(asfd->rbuf->len!=sizeof(ch))
1251 	{
1252 		logp("Unexpected input length in %s: %lu!=%zu\n",
1253 			__func__, (unsigned long)asfd->rbuf->len, sizeof(ch));
1254 		return -1;
1255 	}
1256 	memcpy(&ch, asfd->rbuf->buf, sizeof(ch));
1257 	switch(ch)
1258 	{
1259 		case 'q':
1260 		case 'Q':
1261 			return 1;
1262 		case KEY_UP:
1263 		case 'k':
1264 		case 'K':
1265 			up(sel);
1266 			break;
1267 		case KEY_DOWN:
1268 		case 'j':
1269 		case 'J':
1270 			down(sel);
1271 			break;
1272 		case KEY_LEFT:
1273 		case 'h':
1274 		case 'H':
1275 			left(sel);
1276 			break;
1277 		case KEY_RIGHT:
1278 		case 'l':
1279 		case 'L':
1280 		case KEY_ENTER:
1281 		case '\n':
1282 		case ' ':
1283 			right(sel);
1284 			break;
1285 		case KEY_PPAGE:
1286 			page_up(sel);
1287 			break;
1288 		case KEY_NPAGE:
1289 			page_down(sel);
1290 			break;
1291 		case -1:
1292 			logp("Error on stdin\n");
1293 			return -1;
1294 	}
1295 
1296 	return 0;
1297 }
1298 #endif
1299 
parse_data(struct asfd * asfd,struct sel * sel)1300 static int parse_data(struct asfd *asfd, struct sel *sel)
1301 {
1302 #ifdef HAVE_NCURSES
1303 	if(actg==ACTION_STATUS && asfd->streamtype==ASFD_STREAM_NCURSES_STDIN)
1304 		return parse_stdin_data(asfd, sel);
1305 #endif
1306 	switch(json_input(asfd, sel))
1307 	{
1308 		// 0 means carry on.
1309 		// 1 means it got to the end of the JSON statement.
1310 		// 2 means it got to the end but had warnings.
1311 		// Anything else means an error.
1312 		case 0: return 0;
1313 		case 1: return 0;
1314 		case 2:
1315 		{
1316 			// If we had a warning exit straight away. For example,
1317 			// if they specified '-C non-existent-client'.
1318 			return -1;
1319 		}
1320 		default: return -1;
1321 	}
1322 }
1323 
1324 #ifndef UTEST
1325 static
1326 #endif
status_client_ncurses_main_loop(struct async * as,struct asfd * so_asfd,struct sel * sel,const char * orig_client)1327 int status_client_ncurses_main_loop(struct async *as,
1328 	struct asfd *so_asfd, struct sel *sel,
1329 	const char *orig_client)
1330 {
1331 	int ret=-1;
1332 	char *client=NULL;
1333 	struct asfd *asfd=NULL;
1334 	struct asfd *sfd=NULL; // Server asfd.
1335 	int reqdone=0;
1336 	int client_count=-1;
1337 
1338 	if(!sel
1339 	  || !as
1340 	  || !(stdout_asfd=so_asfd)
1341 	  || !(sfd=as->asfd))
1342 	{
1343 		logp("parameters not set up correctly in %s\n", __func__);
1344 		goto error;
1345 	}
1346 
1347 	sel->page=PAGE_CLIENT_LIST;
1348 
1349 	if(orig_client)
1350 	{
1351 		client=strdup_w(orig_client, __func__);
1352 		sel->page=PAGE_BACKUP_LIST;
1353 	}
1354 
1355 	while(1)
1356 	{
1357 		if(need_status(sel) && !reqdone)
1358 		{
1359 			char *req=NULL;
1360 			if(sel->page>PAGE_CLIENT_LIST)
1361 			{
1362 				if(client)
1363 					req=client;
1364 				else if(sel->client)
1365 					req=sel->client->name;
1366 			}
1367 			if(request_status(sfd, req?req:"", sel))
1368 				goto error;
1369 
1370 			// We only want to start on the client the user gave to
1371 			// us. Freeing it will allow the user to browse other
1372 			// clients thereafter.
1373 			free_w(&client);
1374 
1375 			if(actg==ACTION_STATUS_SNAPSHOT)
1376 				reqdone=1;
1377 		}
1378 
1379 		if(as->read_write(as))
1380 		{
1381 			// FIX THIS - an exception is thrown when the console
1382 			// is resized.
1383 /*
1384 			if(sfd->want_to_remove)
1385 			{
1386 				sfd->want_to_remove=0;
1387 				continue;
1388 			}
1389 */
1390 			logp("Exiting main loop\n");
1391 			goto error;
1392 		}
1393 
1394 		for(asfd=as->asfd; asfd; asfd=asfd->next)
1395 		{
1396 			while(asfd->rbuf->buf)
1397 			{
1398 				switch(parse_data(asfd, sel))
1399 				{
1400 					case 0: break;
1401 					case 1: goto end;
1402 					default: goto error;
1403 				}
1404 				iobuf_free_content(asfd->rbuf);
1405 				if(asfd->parse_readbuf(asfd))
1406 					goto error;
1407 			}
1408 
1409 			// Select things if they are not already selected.
1410 			if(sel->client)
1411 			{
1412 				if(!sel->backup)
1413 					sel->backup=sel->client->bu;
1414 			}
1415 			else
1416 				sel->client=sel->clist;
1417 		}
1418 
1419 #ifdef HAVE_NCURSES
1420 		if(actg==ACTION_STATUS
1421 		  && update_screen(sel))
1422 			goto error;
1423 		refresh();
1424 #endif
1425 
1426 		if(actg==ACTION_STATUS_SNAPSHOT)
1427 		{
1428 			int new_count=cstat_count(sel->clist);
1429 			if(new_count==client_count
1430 		  	  && sel->client)
1431 			{
1432 				if(update_screen(sel))
1433 					goto error;
1434 				stdout_asfd->write_str(stdout_asfd,
1435 					CMD_GEN, "\n");
1436 				break;
1437 			}
1438 			client_count=new_count;
1439 		}
1440 	}
1441 
1442 end:
1443 	ret=0;
1444 error:
1445 	return ret;
1446 }
1447 
1448 #ifdef HAVE_NCURSES
ncurses_init(void)1449 static void ncurses_init(void)
1450 {
1451 	initscr();
1452 	start_color();
1453 	use_default_colors();
1454 	raw();
1455 	keypad(stdscr, TRUE);
1456 	noecho();
1457 	curs_set(0);
1458 	halfdelay(3);
1459 	//nodelay(stdscr, TRUE);
1460 }
1461 #endif
1462 
fork_monitor(int * csin,int * csout,struct conf ** confs)1463 static pid_t fork_monitor(int *csin, int *csout, struct conf **confs)
1464 {
1465 	int a=0;
1466 	char *args[12];
1467 	char procpath[32];
1468 	char buf[PATH_MAX];
1469 	char *monitor_exe;
1470 
1471 	monitor_exe=get_string(confs[OPT_MONITOR_EXE]);
1472 	snprintf(procpath, sizeof(procpath), "/proc/%d/exe", getpid());
1473 	if(monitor_exe && is_reg_lstat(monitor_exe)>0)
1474 		args[a++]=monitor_exe;
1475 	else if(!readlink_w(procpath, buf, sizeof(buf)))
1476 		args[a++]=(char *)buf;
1477 	else if(is_reg_lstat(prog_long)>0)
1478 		args[a++]=(char *)prog_long;
1479 	else
1480 	{
1481 		static char p[64]="";
1482 		snprintf(p, sizeof(p), "/usr/sbin/%s", PACKAGE_TARNAME);
1483 		logp("Using fallback monitor path: %s\n", p);
1484 		args[a++]=p;
1485 	}
1486 
1487 	args[a++]=(char *)"-c";
1488 	args[a++]=get_string(confs[OPT_CONFFILE]);
1489 	args[a++]=(char *)"-a";
1490 	args[a++]=(char *)"m";
1491 	args[a++]=NULL;
1492 
1493 	return forkchild_fd(csin, csout, NULL, args[0], args);
1494 }
1495 
status_client_ncurses_init(enum action act)1496 int status_client_ncurses_init(enum action act)
1497 {
1498 	actg=act;
1499 #ifndef HAVE_NCURSES
1500 	if(act==ACTION_STATUS)
1501 	{
1502 		printf("To use the live status monitor, you need to recompile with ncurses support.\n");
1503 		return -1;
1504 	}
1505 #endif
1506 	return 0;
1507 }
1508 
show_loglines(struct lline * llines,const char * prefix)1509 static void show_loglines(struct lline *llines, const char *prefix)
1510 {
1511 	struct lline *l;
1512 	for(l=llines; l; l=l->next)
1513 		logp("%s%s\n", prefix, l->line);
1514 }
1515 
status_client_ncurses(struct conf ** confs)1516 int status_client_ncurses(struct conf **confs)
1517 {
1518         int ret=-1;
1519 	int csin=-1;
1520 	int csout=-1;
1521 	pid_t childpid=-1;
1522 	struct async *as=NULL;
1523 	const char *monitor_logfile=get_string(confs[OPT_MONITOR_LOGFILE]);
1524 	struct asfd *so_asfd=NULL;
1525 	struct sel *sel=NULL;
1526 	struct lline *llines=NULL;
1527 	struct lline *wlines=NULL;
1528 
1529 	if(json_input_init())
1530 		goto end;
1531 
1532 	if(!(sel=sel_alloc()))
1533 		goto end;
1534 
1535 	setup_signals();
1536 
1537 	// Fork a burp child process that will contact the server over SSL.
1538 	// We will read and write from and to its stdout and stdin.
1539 	if((childpid=fork_monitor(&csin, &csout, confs))<0)
1540 		goto end;
1541 //printf("childpid: %d\n", childpid);
1542 
1543 	if(!(as=async_alloc())
1544 	  || as->init(as, 0)
1545 	  || !setup_asfd_linebuf_write(as, "monitor stdin", &csin)
1546 	  || !setup_asfd_linebuf_read(as, "monitor stdout", &csout))
1547 		goto end;
1548 //printf("ml: %s\n", monitor_logfile);
1549 #ifdef HAVE_NCURSES
1550 	if(actg==ACTION_STATUS)
1551 	{
1552 		if(!setup_asfd_ncurses_stdin(as))
1553 			goto end;
1554 		ncurses_init();
1555 	}
1556 #endif
1557 	if(!(so_asfd=setup_asfd_stdout(as)))
1558 		goto end;
1559 
1560 	if(monitor_logfile
1561 	  && !(lfzp=fzp_open(monitor_logfile, "wb")))
1562 		goto end;
1563 	log_fzp_set_direct(lfzp);
1564 
1565 	ret=status_client_ncurses_main_loop(as, so_asfd, sel,
1566 		get_string(confs[OPT_ORIG_CLIENT]));
1567 end:
1568 #ifdef HAVE_NCURSES
1569 	if(actg==ACTION_STATUS)
1570 		ncurses_free();
1571 #endif
1572 	llines=json_input_get_loglines();
1573 	wlines=json_input_get_warnings();
1574 	if(ret)
1575 	{
1576 		show_loglines(llines, "");
1577 		show_loglines(wlines, "WARNING: ");
1578 		logp("%s exiting with error: %d\n", __func__, ret);
1579 	}
1580 	json_input_clear_loglines();
1581 	json_input_clear_warnings();
1582 	json_input_free();
1583 	fzp_close(&lfzp);
1584 	async_asfd_free_all(&as);
1585 	close_fd(&csin);
1586 	close_fd(&csout);
1587 	sel_free(&sel);
1588 	return ret;
1589 }
1590