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