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