1ea37671dSMatthew Dillon /* 2ea37671dSMatthew Dillon * Copyright (c) 2019 The DragonFly Project. All rights reserved. 3ea37671dSMatthew Dillon * 4ea37671dSMatthew Dillon * This code is derived from software contributed to The DragonFly Project 5ea37671dSMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 6ea37671dSMatthew Dillon * 7ea37671dSMatthew Dillon * This code uses concepts and configuration based on 'synth', by 8ea37671dSMatthew Dillon * John R. Marino <draco@marino.st>, which was written in ada. 9ea37671dSMatthew Dillon * 10ea37671dSMatthew Dillon * Redistribution and use in source and binary forms, with or without 11ea37671dSMatthew Dillon * modification, are permitted provided that the following conditions 12ea37671dSMatthew Dillon * are met: 13ea37671dSMatthew Dillon * 14ea37671dSMatthew Dillon * 1. Redistributions of source code must retain the above copyright 15ea37671dSMatthew Dillon * notice, this list of conditions and the following disclaimer. 16ea37671dSMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 17ea37671dSMatthew Dillon * notice, this list of conditions and the following disclaimer in 18ea37671dSMatthew Dillon * the documentation and/or other materials provided with the 19ea37671dSMatthew Dillon * distribution. 20ea37671dSMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 21ea37671dSMatthew Dillon * contributors may be used to endorse or promote products derived 22ea37671dSMatthew Dillon * from this software without specific, prior written permission. 23ea37671dSMatthew Dillon * 24ea37671dSMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25ea37671dSMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26ea37671dSMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27ea37671dSMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28ea37671dSMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29ea37671dSMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 30ea37671dSMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31ea37671dSMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32ea37671dSMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33ea37671dSMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 34ea37671dSMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35ea37671dSMatthew Dillon * SUCH DAMAGE. 36ea37671dSMatthew Dillon */ 37ea37671dSMatthew Dillon #include "dsynth.h" 38ea37671dSMatthew Dillon 39ea37671dSMatthew Dillon #include <curses.h> 40ea37671dSMatthew Dillon 41ea37671dSMatthew Dillon /* 42ea37671dSMatthew Dillon * ncurses - LINES, COLS are the main things we care about 43ea37671dSMatthew Dillon */ 44ea37671dSMatthew Dillon static WINDOW *CWin; 45ea37671dSMatthew Dillon static WINDOW *CMon; 46ea37671dSMatthew Dillon static const char *Line0 = " Total - Built - Ignored - " 47ea37671dSMatthew Dillon "Load - Pkg/hour - "; 48ea37671dSMatthew Dillon static const char *Line1 = " Left - Failed - Skipped - " 49ea37671dSMatthew Dillon "Swap - Impulse - --:--:-- "; 50ea37671dSMatthew Dillon static const char *LineB = "===========================================" 51ea37671dSMatthew Dillon "===================================="; 52ea37671dSMatthew Dillon static const char *LineI = " ID Duration Build Phase Origin " 53ea37671dSMatthew Dillon " Lines"; 54ea37671dSMatthew Dillon 55ea37671dSMatthew Dillon static int LastReduce; 56*aac7a6d9SMatthew Dillon static monitorlog_t nclog; 57ea37671dSMatthew Dillon 58ea37671dSMatthew Dillon #define TOTAL_COL 7 59ea37671dSMatthew Dillon #define BUILT_COL 21 60ea37671dSMatthew Dillon #define IGNORED_COL 36 61ea37671dSMatthew Dillon #define LOAD_COL 48 62ea37671dSMatthew Dillon #define GPKGRATE_COL 64 63ea37671dSMatthew Dillon #define REDUCE_COL 71 64ea37671dSMatthew Dillon 65ea37671dSMatthew Dillon #define LEFT_COL 7 66ea37671dSMatthew Dillon #define FAILED_COL 21 67ea37671dSMatthew Dillon #define SKIPPED_COL 36 68ea37671dSMatthew Dillon #define SWAP_COL 48 69ea37671dSMatthew Dillon #define IMPULSE_COL 64 70ea37671dSMatthew Dillon #define TIME_COL 71 71ea37671dSMatthew Dillon 72ea37671dSMatthew Dillon #define ID_COL 1 73ea37671dSMatthew Dillon #define DURATION_COL 5 74ea37671dSMatthew Dillon #define BUILD_PHASE_COL 15 75ea37671dSMatthew Dillon #define ORIGIN_COL 32 76ea37671dSMatthew Dillon #define LINES_COL 73 77ea37671dSMatthew Dillon 78ea37671dSMatthew Dillon /* 79ea37671dSMatthew Dillon * The row that the worker list starts on, and the row that the log starts 80ea37671dSMatthew Dillon * on. 81ea37671dSMatthew Dillon */ 82ea37671dSMatthew Dillon #define WORKER_START 5 83ea37671dSMatthew Dillon #define LOG_START (WORKER_START + MaxWorkers + 1) 84ea37671dSMatthew Dillon 85ea37671dSMatthew Dillon static void NCursesReset(void); 86ea37671dSMatthew Dillon 87ea37671dSMatthew Dillon static void 88ea37671dSMatthew Dillon NCursesInit(void) 89ea37671dSMatthew Dillon { 90ea37671dSMatthew Dillon if (UseNCurses == 0) 91ea37671dSMatthew Dillon return; 92ea37671dSMatthew Dillon 93ea37671dSMatthew Dillon CWin = initscr(); 94ea37671dSMatthew Dillon NCursesReset(); 95ea37671dSMatthew Dillon 96ea37671dSMatthew Dillon intrflush(stdscr, FALSE); 97ea37671dSMatthew Dillon nonl(); 98ea37671dSMatthew Dillon noecho(); 99ea37671dSMatthew Dillon cbreak(); 100ea37671dSMatthew Dillon 101ea37671dSMatthew Dillon start_color(); 102ea37671dSMatthew Dillon use_default_colors(); 103ea37671dSMatthew Dillon init_pair(1, COLOR_RED, -1); 104ea37671dSMatthew Dillon init_pair(2, COLOR_GREEN, -1); 105ea37671dSMatthew Dillon init_pair(3, -1, -1); 106ea37671dSMatthew Dillon } 107ea37671dSMatthew Dillon 108ea37671dSMatthew Dillon static void 109ea37671dSMatthew Dillon NCursesReset(void) 110ea37671dSMatthew Dillon { 111ea37671dSMatthew Dillon int i; 112ea37671dSMatthew Dillon 113ea37671dSMatthew Dillon if (UseNCurses == 0) 114ea37671dSMatthew Dillon return; 115ea37671dSMatthew Dillon 116ea37671dSMatthew Dillon if (CMon) { 117ea37671dSMatthew Dillon delwin(CMon); 118ea37671dSMatthew Dillon CMon = NULL; 119ea37671dSMatthew Dillon } 120ea37671dSMatthew Dillon 121ea37671dSMatthew Dillon werase(CWin); 122ea37671dSMatthew Dillon curs_set(0); 123ea37671dSMatthew Dillon redrawwin(CWin); 124ea37671dSMatthew Dillon wrefresh(CWin); 125ea37671dSMatthew Dillon mvwprintw(CWin, 0, 0, "%s", Line0); 126ea37671dSMatthew Dillon mvwprintw(CWin, 1, 0, "%s", Line1); 127ea37671dSMatthew Dillon mvwprintw(CWin, 2, 0, "%s", LineB); 128ea37671dSMatthew Dillon mvwprintw(CWin, 3, 0, "%s", LineI); 129ea37671dSMatthew Dillon mvwprintw(CWin, 4, 0, "%s", LineB); 130ea37671dSMatthew Dillon 131ea37671dSMatthew Dillon for (i = 0; i < MaxWorkers; ++i) { 132ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, ID_COL, "%02d", i); 133ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, DURATION_COL, "--:--:--"); 134ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, BUILD_PHASE_COL, "Idle"); 135ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, ORIGIN_COL, "%39.39s", ""); 136ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, LINES_COL, "%6.6s", ""); 137ea37671dSMatthew Dillon } 138ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + MaxWorkers, 0, "%s", LineB); 139ea37671dSMatthew Dillon wrefresh(CWin); 140ea37671dSMatthew Dillon 141ea37671dSMatthew Dillon CMon = subwin(CWin, 0, 0, LOG_START, 0); 142ea37671dSMatthew Dillon scrollok(CMon, 1); 143*aac7a6d9SMatthew Dillon 144*aac7a6d9SMatthew Dillon bzero(&nclog, sizeof(nclog)); 145*aac7a6d9SMatthew Dillon nclog.fd = dlog00_fd(); 146ea37671dSMatthew Dillon nodelay(CMon, 1); 147ea37671dSMatthew Dillon 148ea37671dSMatthew Dillon LastReduce = -1; 149ea37671dSMatthew Dillon } 150ea37671dSMatthew Dillon 151ea37671dSMatthew Dillon static void 152ea37671dSMatthew Dillon NCursesUpdateTop(topinfo_t *info) 153ea37671dSMatthew Dillon { 154ea37671dSMatthew Dillon if (UseNCurses == 0) 155ea37671dSMatthew Dillon return; 156ea37671dSMatthew Dillon 157ea37671dSMatthew Dillon mvwprintw(CWin, 0, TOTAL_COL, "%-5d", info->total); 158ea37671dSMatthew Dillon mvwprintw(CWin, 0, BUILT_COL, "%-5d", info->successful); 159ea37671dSMatthew Dillon mvwprintw(CWin, 0, IGNORED_COL, "%-5d", info->ignored); 160ea37671dSMatthew Dillon if (info->dload[0] > 999.9) 161ea37671dSMatthew Dillon mvwprintw(CWin, 0, LOAD_COL, "%5.0f", info->dload[0]); 162ea37671dSMatthew Dillon else 163ea37671dSMatthew Dillon mvwprintw(CWin, 0, LOAD_COL, "%5.1f", info->dload[0]); 164ea37671dSMatthew Dillon mvwprintw(CWin, 0, GPKGRATE_COL, "%-5d", info->pkgrate); 165ea37671dSMatthew Dillon 166ea37671dSMatthew Dillon /* 167ea37671dSMatthew Dillon * If dynamic worker reduction is active include a field, 168ea37671dSMatthew Dillon * Otherwise blank the field. 169ea37671dSMatthew Dillon */ 170*aac7a6d9SMatthew Dillon if (LastReduce != info->dynmaxworkers) { 171*aac7a6d9SMatthew Dillon LastReduce = info->dynmaxworkers; 172ea37671dSMatthew Dillon if (MaxWorkers == LastReduce) 173ea37671dSMatthew Dillon mvwprintw(CWin, 0, REDUCE_COL, " "); 174ea37671dSMatthew Dillon else 175ea37671dSMatthew Dillon mvwprintw(CWin, 0, REDUCE_COL, "Limit %-2d", 176ea37671dSMatthew Dillon LastReduce); 177ea37671dSMatthew Dillon } 178ea37671dSMatthew Dillon 179ea37671dSMatthew Dillon mvwprintw(CWin, 1, LEFT_COL, "%-5d", info->remaining); 180ea37671dSMatthew Dillon mvwprintw(CWin, 1, FAILED_COL, "%-5d", info->failed); 181ea37671dSMatthew Dillon mvwprintw(CWin, 1, SKIPPED_COL, "%-5d", info->skipped); 182ea37671dSMatthew Dillon if (info->noswap) 183ea37671dSMatthew Dillon mvwprintw(CWin, 1, SWAP_COL, "- "); 184ea37671dSMatthew Dillon else 185ea37671dSMatthew Dillon mvwprintw(CWin, 1, SWAP_COL, "%5.1f", info->dswap); 186ea37671dSMatthew Dillon mvwprintw(CWin, 1, IMPULSE_COL, "%-5d", info->pkgimpulse); 187ea37671dSMatthew Dillon if (info->h > 99) 188ea37671dSMatthew Dillon mvwprintw(CWin, 1, TIME_COL-1, "%3d:%02d:%02d", 189ea37671dSMatthew Dillon info->h, info->m, info->s); 190ea37671dSMatthew Dillon else 191ea37671dSMatthew Dillon mvwprintw(CWin, 1, TIME_COL, "%02d:%02d:%02d", 192ea37671dSMatthew Dillon info->h, info->m, info->s); 193ea37671dSMatthew Dillon } 194ea37671dSMatthew Dillon 195ea37671dSMatthew Dillon static void 196ea37671dSMatthew Dillon NCursesUpdateLogs(void) 197ea37671dSMatthew Dillon { 198ea37671dSMatthew Dillon char *ptr; 199ea37671dSMatthew Dillon char c; 200ea37671dSMatthew Dillon ssize_t n; 201ea37671dSMatthew Dillon int w; 202ea37671dSMatthew Dillon 203ea37671dSMatthew Dillon if (UseNCurses == 0) 204ea37671dSMatthew Dillon return; 205ea37671dSMatthew Dillon 206ea37671dSMatthew Dillon for (;;) { 207*aac7a6d9SMatthew Dillon n = readlogline(&nclog, &ptr); 208ea37671dSMatthew Dillon if (n < 0) 209ea37671dSMatthew Dillon break; 210ea37671dSMatthew Dillon if (n == 0) 211ea37671dSMatthew Dillon continue; 212ea37671dSMatthew Dillon 213ea37671dSMatthew Dillon /* 214ea37671dSMatthew Dillon * Scroll down 215ea37671dSMatthew Dillon */ 216ea37671dSMatthew Dillon if (n > COLS) 217ea37671dSMatthew Dillon w = COLS; 218ea37671dSMatthew Dillon else 219ea37671dSMatthew Dillon w = n; 220ea37671dSMatthew Dillon c = ptr[w]; 221ea37671dSMatthew Dillon ptr[w] = 0; 222ea37671dSMatthew Dillon 223ea37671dSMatthew Dillon /* 224ea37671dSMatthew Dillon * Filter out these logs from the display (they remain in 225ea37671dSMatthew Dillon * the 00*.log file) to reduce clutter. 226ea37671dSMatthew Dillon */ 227*aac7a6d9SMatthew Dillon if (strncmp(ptr, "[XXX] Load=", 11) != 0) { 228ea37671dSMatthew Dillon /* 229ea37671dSMatthew Dillon * Output possibly colored log line 230ea37671dSMatthew Dillon */ 231ea37671dSMatthew Dillon wscrl(CMon, -1); 232ea37671dSMatthew Dillon if (strstr(ptr, "] SUCCESS ")) { 233ea37671dSMatthew Dillon wattrset(CMon, COLOR_PAIR(2)); 234ea37671dSMatthew Dillon } else if (strstr(ptr, "] FAILURE ")) { 235ea37671dSMatthew Dillon wattrset(CMon, COLOR_PAIR(1)); 236ea37671dSMatthew Dillon } 237ea37671dSMatthew Dillon mvwprintw(CMon, 0, 0, "%s", ptr); 238ea37671dSMatthew Dillon wattrset(CMon, COLOR_PAIR(3)); 239ea37671dSMatthew Dillon } 240*aac7a6d9SMatthew Dillon ptr[w] = c; 241*aac7a6d9SMatthew Dillon } 242ea37671dSMatthew Dillon } 243ea37671dSMatthew Dillon 244ea37671dSMatthew Dillon static void 245*aac7a6d9SMatthew Dillon NCursesUpdate(worker_t *work, const char *portdir) 246ea37671dSMatthew Dillon { 247ea37671dSMatthew Dillon const char *phase; 248ea37671dSMatthew Dillon const char *origin; 249ea37671dSMatthew Dillon time_t t; 250ea37671dSMatthew Dillon int i = work->index; 251ea37671dSMatthew Dillon int h; 252ea37671dSMatthew Dillon int m; 253ea37671dSMatthew Dillon int s; 254ea37671dSMatthew Dillon 255ea37671dSMatthew Dillon if (UseNCurses == 0) 256ea37671dSMatthew Dillon return; 257ea37671dSMatthew Dillon 258ea37671dSMatthew Dillon phase = "Unknown"; 259ea37671dSMatthew Dillon origin = ""; 260ea37671dSMatthew Dillon 261ea37671dSMatthew Dillon switch(work->state) { 262ea37671dSMatthew Dillon case WORKER_NONE: 263ea37671dSMatthew Dillon phase = "None"; 264ea37671dSMatthew Dillon /* fall through */ 265ea37671dSMatthew Dillon case WORKER_IDLE: 266ea37671dSMatthew Dillon if (work->state == WORKER_IDLE) 267ea37671dSMatthew Dillon phase = "Idle"; 268ea37671dSMatthew Dillon /* fall through */ 269ea37671dSMatthew Dillon case WORKER_FAILED: 270ea37671dSMatthew Dillon if (work->state == WORKER_FAILED) 271ea37671dSMatthew Dillon phase = "Failed"; 272ea37671dSMatthew Dillon /* fall through */ 273ea37671dSMatthew Dillon case WORKER_EXITING: 274ea37671dSMatthew Dillon if (work->state == WORKER_EXITING) 275ea37671dSMatthew Dillon phase = "Exiting"; 276ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, DURATION_COL, 277ea37671dSMatthew Dillon "--:--:--"); 278ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, BUILD_PHASE_COL, 279ea37671dSMatthew Dillon "%-16.16s", phase); 280ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, ORIGIN_COL, 281ea37671dSMatthew Dillon "%-39.39s", ""); 282ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, LINES_COL, 283ea37671dSMatthew Dillon "%-6.6s", ""); 284ea37671dSMatthew Dillon return; 285ea37671dSMatthew Dillon case WORKER_PENDING: 286ea37671dSMatthew Dillon phase = "Pending"; 287ea37671dSMatthew Dillon break; 288ea37671dSMatthew Dillon case WORKER_RUNNING: 289ea37671dSMatthew Dillon phase = "Running"; 290ea37671dSMatthew Dillon break; 291ea37671dSMatthew Dillon case WORKER_DONE: 292ea37671dSMatthew Dillon phase = "Done"; 293ea37671dSMatthew Dillon break; 294ea37671dSMatthew Dillon case WORKER_FROZEN: 295ea37671dSMatthew Dillon phase = "FROZEN"; 296ea37671dSMatthew Dillon break; 297ea37671dSMatthew Dillon default: 298ea37671dSMatthew Dillon break; 299ea37671dSMatthew Dillon } 300ea37671dSMatthew Dillon 301ea37671dSMatthew Dillon t = time(NULL) - work->start_time; 302ea37671dSMatthew Dillon s = t % 60; 303ea37671dSMatthew Dillon m = t / 60 % 60; 304ea37671dSMatthew Dillon h = t / 60 / 60; 305ea37671dSMatthew Dillon 306ea37671dSMatthew Dillon if (work->state == WORKER_RUNNING) 307ea37671dSMatthew Dillon phase = getphasestr(work->phase); 308ea37671dSMatthew Dillon 309*aac7a6d9SMatthew Dillon /* 310*aac7a6d9SMatthew Dillon * When called from the monitor frontend portdir has to be passed 311*aac7a6d9SMatthew Dillon * in directly because work->pkg is not mapped. 312*aac7a6d9SMatthew Dillon */ 313*aac7a6d9SMatthew Dillon if (portdir) 314*aac7a6d9SMatthew Dillon origin = portdir; 315*aac7a6d9SMatthew Dillon else if (work->pkg) 316ea37671dSMatthew Dillon origin = work->pkg->portdir; 317ea37671dSMatthew Dillon else 318ea37671dSMatthew Dillon origin = ""; 319ea37671dSMatthew Dillon 320ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, DURATION_COL, 321ea37671dSMatthew Dillon "%02d:%02d:%02d", h, m, s); 322ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, BUILD_PHASE_COL, 323ea37671dSMatthew Dillon "%-16.16s", phase); 324ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, ORIGIN_COL, 325ea37671dSMatthew Dillon "%-39.39s", origin); 326ea37671dSMatthew Dillon mvwprintw(CWin, WORKER_START + i, LINES_COL, 327ea37671dSMatthew Dillon "%6d", work->lines); 328ea37671dSMatthew Dillon } 329ea37671dSMatthew Dillon 330ea37671dSMatthew Dillon static void 331ea37671dSMatthew Dillon NCursesSync(void) 332ea37671dSMatthew Dillon { 333ea37671dSMatthew Dillon int c; 334ea37671dSMatthew Dillon 335ea37671dSMatthew Dillon if (UseNCurses == 0) 336ea37671dSMatthew Dillon return; 337ea37671dSMatthew Dillon 338ea37671dSMatthew Dillon while ((c = wgetch(CMon)) != ERR) { 339ea37671dSMatthew Dillon if (c == KEY_RESIZE) 340ea37671dSMatthew Dillon NCursesReset(); 341ea37671dSMatthew Dillon } 342ea37671dSMatthew Dillon wrefresh(CWin); 343ea37671dSMatthew Dillon wrefresh(CMon); 344ea37671dSMatthew Dillon } 345ea37671dSMatthew Dillon 346ea37671dSMatthew Dillon static void 347ea37671dSMatthew Dillon NCursesDone(void) 348ea37671dSMatthew Dillon { 349ea37671dSMatthew Dillon if (UseNCurses == 0) 350ea37671dSMatthew Dillon return; 351ea37671dSMatthew Dillon 352ea37671dSMatthew Dillon endwin(); 353ea37671dSMatthew Dillon } 354ea37671dSMatthew Dillon 355ea37671dSMatthew Dillon runstats_t NCursesRunStats = { 356ea37671dSMatthew Dillon .init = NCursesInit, 357ea37671dSMatthew Dillon .done = NCursesDone, 358ea37671dSMatthew Dillon .reset = NCursesReset, 359ea37671dSMatthew Dillon .update = NCursesUpdate, 360ea37671dSMatthew Dillon .updateTop = NCursesUpdateTop, 361ea37671dSMatthew Dillon .updateLogs = NCursesUpdateLogs, 362ea37671dSMatthew Dillon .sync = NCursesSync 363ea37671dSMatthew Dillon }; 364