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 2446 : for(cp=string; (*cp && k<col); cp++)
45 2446 : mvprintw(row+TOP_SPACE, k++, "%c", *cp);
46 22733 : 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)
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);
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 29 : else snprintf(ret, sizeof(ret), "%07" PRIu64 " %s",
82 : 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 0 : struct cntr_ent *ent_gtotal=
109 : cntr->ent[(uint8_t)CMD_GRAND_TOTAL];
110 :
111 0 : 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 0 : snprintf(f, sizeof(f),
117 : " %" PRIu64
118 : "/%" PRIu64
119 : " %" PRIu64
120 : "%%",
121 : t, ent_gtotal->phase1, p);
122 0 : if(cntr->byte)
123 0 : snprintf(b, sizeof(b), "%s",
124 : bytes_to_human(cntr->byte));
125 0 : snprintf(msg, sizeof(msg), fmt,
126 : cstat->name,
127 : run_status_to_str(cstat),
128 : 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 24 : snprintf(msg, sizeof(msg), fmt,
137 : cstat->name,
138 : run_status_to_str(cstat),
139 : " last backup: ",
140 : 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 0 : to_msg(msg, sizeof(msg),
180 : "% 15s % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 " % 9" PRIu64 "",
181 : 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 0 : to_msg(msg, sizeof(msg), "% 15s % 9s % 9s % 9s % 9s % 9s % 9s",
218 : "", "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 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 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 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, col);
430 30 : return;
431 : }
432 : #endif
433 0 : screen_header_stdout(date, l, 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 0 : 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 5 : snprintf(msg, sizeof(msg),
496 : "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 10 : snprintf(msg, sizeof(msg), "%s %s%s",
638 5 : b==sel->client->bu?"Backup list:":
639 : " ",
640 : get_bu_str(b),
641 : 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 0 : print_cntr_ent(e->label,
659 : e->count,
660 : e->changed,
661 : e->same,
662 : e->deleted,
663 : e->phase1,
664 : 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 0 : snprintf(msg, sizeof(msg), "%19s: %12" PRIu64 " %s",
689 : 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 0 : 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(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 0 : snprintf(buf, sizeof(buf),
910 : "c:%s:b:%" PRIu64 "\n",
911 : 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 0 : snprintf(buf, sizeof(buf),
939 : "c:%s:b:%" PRIu64 "\n",
940 : 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 0 : snprintf(buf, sizeof(buf),
953 : "c:%s:b:%" PRIu64 ":l:%s\n",
954 : 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(void)
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=getmaxy(stdscr);
1202 0 : switch(sel->page)
1203 : {
1204 : case PAGE_CLIENT_LIST:
1205 0 : page_up_client(sel, row);
1206 : break;
1207 : case PAGE_BACKUP_LIST:
1208 0 : page_up_backup(sel, row);
1209 : break;
1210 : case PAGE_BACKUP_LOGS:
1211 : break;
1212 : case PAGE_VIEW_LOG:
1213 : break;
1214 : }
1215 0 : }
1216 :
1217 0 : static void page_down(struct sel *sel)
1218 : {
1219 0 : int row=getmaxy(stdscr);
1220 0 : switch(sel->page)
1221 : {
1222 : case PAGE_CLIENT_LIST:
1223 0 : page_down_client(sel, row);
1224 : break;
1225 : case PAGE_BACKUP_LIST:
1226 0 : page_down_backup(sel, row);
1227 : break;
1228 : case PAGE_BACKUP_LOGS:
1229 : break;
1230 : case PAGE_VIEW_LOG:
1231 : break;
1232 : }
1233 0 : }
1234 :
1235 12 : static int parse_stdin_data(struct asfd *asfd, struct sel *sel)
1236 : {
1237 : static int ch;
1238 12 : if(asfd->rbuf->len!=sizeof(ch))
1239 : {
1240 1 : logp("Unexpected input length in %s: %lu!=%zu\n",
1241 : __func__, (unsigned long)asfd->rbuf->len, sizeof(ch));
1242 : return -1;
1243 : }
1244 11 : memcpy(&ch, asfd->rbuf->buf, sizeof(ch));
1245 11 : switch(ch)
1246 : {
1247 : case 'q':
1248 : case 'Q':
1249 : return 1;
1250 : case 't':
1251 : case 'T':
1252 0 : if(toggle) toggle=0;
1253 0 : else toggle=1;
1254 : break;
1255 : case KEY_UP:
1256 : case 'k':
1257 : case 'K':
1258 1 : up(sel);
1259 : break;
1260 : case KEY_DOWN:
1261 : case 'j':
1262 : case 'J':
1263 3 : down(sel);
1264 : break;
1265 : case KEY_LEFT:
1266 : case 'h':
1267 : case 'H':
1268 0 : left(sel);
1269 : break;
1270 : case KEY_RIGHT:
1271 : case 'l':
1272 : case 'L':
1273 : case KEY_ENTER:
1274 : case '\n':
1275 : case ' ':
1276 0 : right(sel);
1277 : break;
1278 : case KEY_PPAGE:
1279 0 : page_up(sel);
1280 : break;
1281 : case KEY_NPAGE:
1282 0 : page_down(sel);
1283 : break;
1284 : case -1:
1285 1 : logp("Error on stdin\n");
1286 : return -1;
1287 : }
1288 :
1289 : return 0;
1290 : }
1291 : #endif
1292 :
1293 33 : static int parse_data(struct asfd *asfd, struct sel *sel)
1294 : {
1295 : #ifdef HAVE_NCURSES
1296 21 : if(actg==ACTION_STATUS && asfd->streamtype==ASFD_STREAM_NCURSES_STDIN)
1297 12 : return parse_stdin_data(asfd, sel);
1298 : #endif
1299 9 : switch(json_input(asfd, sel))
1300 : {
1301 : // 0 means carry on.
1302 : // 1 means it got to the end of the JSON statement.
1303 : // Anything else means an error.
1304 : case 0: return 0;
1305 : case 1: return 0;
1306 1 : default: return -1;
1307 : }
1308 : }
1309 :
1310 : #ifndef UTEST
1311 : static
1312 : #endif
1313 13 : int status_client_ncurses_main_loop(struct async *as,
1314 62 : struct asfd *so_asfd, struct sel *sel,
1315 : const char *orig_client)
1316 : {
1317 13 : int ret=-1;
1318 13 : char *client=NULL;
1319 13 : struct asfd *asfd=NULL;
1320 13 : struct asfd *sfd=NULL; // Server asfd.
1321 13 : int reqdone=0;
1322 13 : int client_count=-1;
1323 :
1324 26 : if(!sel
1325 13 : || !as
1326 12 : || !(stdout_asfd=so_asfd)
1327 12 : || !(sfd=as->asfd))
1328 : {
1329 1 : logp("parameters not set up correctly in %s\n", __func__);
1330 1 : goto error;
1331 : }
1332 :
1333 12 : sel->page=PAGE_CLIENT_LIST;
1334 :
1335 12 : if(orig_client)
1336 : {
1337 1 : client=strdup_w(orig_client, __func__);
1338 1 : sel->page=PAGE_BACKUP_LIST;
1339 : }
1340 :
1341 : while(1)
1342 : {
1343 62 : if(need_status(sel) && !reqdone)
1344 : {
1345 12 : char *req=NULL;
1346 12 : if(sel->page>PAGE_CLIENT_LIST)
1347 : {
1348 1 : if(client)
1349 : req=client;
1350 0 : else if(sel->client)
1351 0 : req=sel->client->name;
1352 : }
1353 12 : if(request_status(sfd, req, sel))
1354 : goto error;
1355 :
1356 : // We only want to start on the client the user gave to
1357 : // us. Freeing it will allow the user to browse other
1358 : // clients thereafter.
1359 12 : free_w(&client);
1360 :
1361 12 : if(actg==ACTION_STATUS_SNAPSHOT)
1362 4 : reqdone=1;
1363 : }
1364 :
1365 31 : if(as->read_write(as))
1366 : {
1367 : // FIX THIS - an exception is thrown when the console
1368 : // is resized.
1369 : /*
1370 : if(sfd->want_to_remove)
1371 : {
1372 : sfd->want_to_remove=0;
1373 : continue;
1374 : }
1375 : */
1376 3 : logp("Exiting main loop\n");
1377 3 : goto error;
1378 : }
1379 :
1380 116 : for(asfd=as->asfd; asfd; asfd=asfd->next)
1381 : {
1382 109 : while(asfd->rbuf->buf)
1383 : {
1384 21 : switch(parse_data(asfd, sel))
1385 : {
1386 : case 0: break;
1387 : case 1: goto end;
1388 : default: goto error;
1389 : }
1390 12 : iobuf_free_content(asfd->rbuf);
1391 12 : if(asfd->parse_readbuf(asfd))
1392 : goto error;
1393 : }
1394 :
1395 : // Select things if they are not already selected.
1396 88 : if(sel->client)
1397 : {
1398 56 : if(!sel->backup)
1399 4 : sel->backup=sel->client->bu;
1400 : }
1401 : else
1402 32 : sel->client=sel->clist;
1403 : }
1404 :
1405 : #ifdef HAVE_NCURSES
1406 19 : if(actg==ACTION_STATUS
1407 15 : && update_screen(sel))
1408 : goto error;
1409 19 : refresh();
1410 : #endif
1411 :
1412 19 : if(actg==ACTION_STATUS_SNAPSHOT)
1413 : {
1414 4 : int new_count=cstat_count(sel->clist);
1415 4 : if(new_count==client_count
1416 2 : && sel->client)
1417 : {
1418 0 : if(update_screen(sel))
1419 : goto error;
1420 0 : stdout_asfd->write_str(stdout_asfd,
1421 : CMD_GEN, "\n");
1422 0 : break;
1423 : }
1424 : client_count=new_count;
1425 : }
1426 : }
1427 :
1428 : end:
1429 : ret=0;
1430 : error:
1431 13 : return ret;
1432 : }
1433 :
1434 : #ifdef HAVE_NCURSES
1435 0 : static void ncurses_init(void)
1436 : {
1437 0 : initscr();
1438 0 : start_color();
1439 0 : use_default_colors();
1440 0 : raw();
1441 0 : keypad(stdscr, TRUE);
1442 0 : noecho();
1443 0 : curs_set(0);
1444 0 : halfdelay(3);
1445 : //nodelay(stdscr, TRUE);
1446 0 : }
1447 : #endif
1448 :
1449 0 : static pid_t fork_monitor(int *csin, int *csout, struct conf **confs)
1450 : {
1451 0 : int a=0;
1452 : char *args[12];
1453 : char procpath[32];
1454 : char buf[PATH_MAX];
1455 :
1456 0 : snprintf(procpath, sizeof(procpath), "/proc/%d/exe", getpid());
1457 0 : if(!readlink_w(procpath, buf, sizeof(buf)))
1458 0 : args[a++]=(char *)buf;
1459 0 : else if(is_reg_lstat(prog_long)>0)
1460 0 : args[a++]=(char *)prog_long;
1461 : else
1462 0 : args[a++]=(char *)"/usr/sbin/burp";
1463 :
1464 0 : args[a++]=(char *)"-c";
1465 0 : args[a++]=get_string(confs[OPT_CONFFILE]);
1466 0 : args[a++]=(char *)"-a";
1467 0 : args[a++]=(char *)"m";
1468 0 : args[a++]=NULL;
1469 :
1470 0 : return forkchild_fd(csin, csout, NULL, args[0], args);
1471 : }
1472 :
1473 13 : int status_client_ncurses_init(enum action act)
1474 : {
1475 13 : actg=act;
1476 : #ifndef HAVE_NCURSES
1477 : if(act==ACTION_STATUS)
1478 : {
1479 : printf("To use the live status monitor, you need to recompile with ncurses support.\n");
1480 : return -1;
1481 : }
1482 : #endif
1483 13 : return 0;
1484 : }
1485 :
1486 : static void show_loglines(struct lline *llines)
1487 : {
1488 : struct lline *l;
1489 0 : for(l=llines; l; l=l->next)
1490 0 : logp("%s\n", l->line);
1491 : }
1492 :
1493 0 : int status_client_ncurses(struct conf **confs)
1494 : {
1495 0 : int ret=-1;
1496 0 : int csin=-1;
1497 0 : int csout=-1;
1498 0 : pid_t childpid=-1;
1499 0 : struct async *as=NULL;
1500 0 : const char *monitor_logfile=get_string(confs[OPT_MONITOR_LOGFILE]);
1501 0 : struct asfd *so_asfd=NULL;
1502 0 : struct sel *sel=NULL;
1503 0 : struct lline *llines=NULL;
1504 :
1505 0 : if(json_input_init())
1506 : goto end;
1507 :
1508 0 : if(!(sel=sel_alloc()))
1509 : goto end;
1510 :
1511 0 : setup_signals();
1512 :
1513 : // Fork a burp child process that will contact the server over SSL.
1514 : // We will read and write from and to its stdout and stdin.
1515 0 : if((childpid=fork_monitor(&csin, &csout, confs))<0)
1516 : goto end;
1517 : //printf("childpid: %d\n", childpid);
1518 :
1519 0 : if(!(as=async_alloc())
1520 0 : || as->init(as, 0)
1521 0 : || !setup_asfd_linebuf_write(as, "monitor stdin", &csin)
1522 0 : || !setup_asfd_linebuf_read(as, "monitor stdout", &csout))
1523 : goto end;
1524 : //printf("ml: %s\n", monitor_logfile);
1525 : #ifdef HAVE_NCURSES
1526 0 : if(actg==ACTION_STATUS)
1527 : {
1528 0 : if(!setup_asfd_ncurses_stdin(as))
1529 : goto end;
1530 0 : ncurses_init();
1531 : }
1532 : #endif
1533 0 : if(!(so_asfd=setup_asfd_stdout(as)))
1534 : goto end;
1535 :
1536 0 : if(monitor_logfile
1537 0 : && !(lfzp=fzp_open(monitor_logfile, "wb")))
1538 : goto end;
1539 0 : log_fzp_set_direct(lfzp);
1540 :
1541 0 : ret=status_client_ncurses_main_loop(as, so_asfd, sel,
1542 0 : get_string(confs[OPT_ORIG_CLIENT]));
1543 : end:
1544 : #ifdef HAVE_NCURSES
1545 0 : if(actg==ACTION_STATUS)
1546 : ncurses_free();
1547 : #endif
1548 0 : llines=json_input_get_loglines();
1549 0 : if(ret)
1550 : {
1551 : show_loglines(llines);
1552 0 : logp("%s exiting with error: %d\n", __func__, ret);
1553 : }
1554 0 : llines_free(&llines);
1555 0 : json_input_free();
1556 0 : fzp_close(&lfzp);
1557 0 : async_asfd_free_all(&as);
1558 0 : close_fd(&csin);
1559 0 : close_fd(&csout);
1560 0 : sel_free(&sel);
1561 0 : return ret;
1562 : }
|