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