"Fossies" - the Fresh Open Source Software Archive

Member "burp-2.3.6/src/client/monitor/status_client_ncurses.c" (28 Apr 2019, 33067 Bytes) of package /linux/privat/burp-2.3.6.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "status_client_ncurses.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.2.2_vs_2.2.6.

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