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