dsynth - Add manual page, change configuration directory, more
[dragonfly.git] / usr.bin / dsynth / gui.c
1 /*
2  * Copyright (c) 2019 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * This code uses concepts and configuration based on 'synth', by
8  * John R. Marino <draco@marino.st>, which was written in ada.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  * 3. Neither the name of The DragonFly Project nor the names of its
21  *    contributors may be used to endorse or promote products derived
22  *    from this software without specific, prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
28  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 #include "dsynth.h"
38
39 #include <curses.h>
40
41 /*
42  * ncurses - LINES, COLS are the main things we care about
43  */
44 static WINDOW *CWin;
45 static const char *Line0 = " Total -       Built -      Ignored -      "
46                            "Load -      Pkg/hour -               ";
47 static const char *Line1 = "  Left -      Failed -      Skipped -      "
48                            "Swap -       Impulse -      --:--:-- ";
49 static const char *LineB = "==========================================="
50                            "====================================";
51 static const char *LineI = " ID  Duration  Build Phase      Origin     "
52                            "                               Lines";
53
54 static time_t GuiStartTime;
55 static int LastReduce;
56
57 #define TOTAL_COL       7
58 #define BUILT_COL       21
59 #define IGNORED_COL     36
60 #define LOAD_COL        48
61 #define GPKGRATE_COL    64
62 #define REDUCE_COL      71
63
64 #define LEFT_COL        7
65 #define FAILED_COL      21
66 #define SKIPPED_COL     36
67 #define SWAP_COL        48
68 #define IMPULSE_COL     64
69 #define TIME_COL        71
70
71 #define ID_COL          1
72 #define DURATION_COL    5
73 #define BUILD_PHASE_COL 15
74 #define ORIGIN_COL      32
75 #define LINES_COL       73
76
77 void
78 GuiInit(void)
79 {
80         CWin = initscr();
81         GuiReset();
82         GuiStartTime = time(NULL);
83 }
84
85 void
86 GuiReset(void)
87 {
88         int i;
89
90         werase(CWin);
91         curs_set(0);
92         redrawwin(CWin);
93         wrefresh(CWin);
94         mvwprintw(CWin, 0, 0, "%s", Line0);
95         mvwprintw(CWin, 1, 0, "%s", Line1);
96         mvwprintw(CWin, 2, 0, "%s", LineB);
97         mvwprintw(CWin, 3, 0, "%s", LineI);
98         mvwprintw(CWin, 4, 0, "%s", LineB);
99
100         for (i = 0; i < MaxWorkers; ++i) {
101                 mvwprintw(CWin, 5 + i, ID_COL, "%02d", i);
102                 mvwprintw(CWin, 5 + i, DURATION_COL, "--:--:--");
103                 mvwprintw(CWin, 5 + i, BUILD_PHASE_COL, "Idle");
104                 mvwprintw(CWin, 5 + i, ORIGIN_COL, "%39.39s", "");
105                 mvwprintw(CWin, 5 + i, LINES_COL, "%6.6s", "");
106         }
107         wrefresh(CWin);
108         LastReduce = -1;
109 }
110
111 #define RHISTSIZE       600     /* 10 minutes */
112 #define ONEHOUR         (60 * 60)
113
114 void
115 GuiUpdateTop(void)
116 {
117         static int rate_history[RHISTSIZE];
118         static u_int last_ti;
119         u_int ti;
120         int h;
121         int m;
122         int s;
123         int pkgrate;
124         int pkgimpulse;
125         double dload[3];
126         double dswap;
127         int noswap;
128         time_t t;
129
130         /*
131          * Time
132          */
133
134         t = time(NULL) - GuiStartTime;
135         s = t % 60;
136         m = t / 60 % 60;
137         h = t / 60 / 60;
138
139         /*
140          * Load and swap
141          */
142         getloadavg(dload, 3);
143         dswap = getswappct(&noswap) * 100.0;
144
145         /*
146          * Rate and 10-minute impulse
147          */
148         if (t > 20)
149                 pkgrate = (BuildSuccessCount + BuildFailCount) * ONEHOUR / t;
150         else
151                 pkgrate = 0;
152         ti = (u_int)((unsigned long)t % RHISTSIZE);
153         rate_history[ti] = BuildSuccessCount + BuildFailCount;
154 #if 0
155         dlog(DLOG_ALL, "ti[%3d] = %d\n", ti, rate_history[ti]);
156 #endif
157         while (last_ti != ti) {
158                 rate_history[last_ti] = rate_history[ti];
159                 last_ti = (last_ti + 1) % RHISTSIZE;
160         }
161
162         if (t < 20) {
163                 pkgimpulse = 0;
164         } else if (t < RHISTSIZE) {
165                 pkgimpulse = rate_history[ti] -
166                              rate_history[(ti - t) % RHISTSIZE];
167                 pkgimpulse = pkgimpulse * ONEHOUR / t;
168         } else {
169                 pkgimpulse = rate_history[ti] -
170                              rate_history[(ti + 1) % RHISTSIZE];
171                 pkgimpulse = pkgimpulse * ONEHOUR / RHISTSIZE;
172 #if 0
173                 dlog(DLOG_ALL, "pkgimpulse %d - %d -> %d\n",
174                      rate_history[ti],
175                      rate_history[(ti + 1) % RHISTSIZE],
176                      pkgimpulse);
177 #endif
178         }
179
180         mvwprintw(CWin, 0, TOTAL_COL, "%-5d", BuildTotal);
181         mvwprintw(CWin, 0, BUILT_COL, "%-5d", BuildSuccessCount);
182         mvwprintw(CWin, 0, IGNORED_COL, "%-5d", -1);
183         if (dload[0] > 999.9)
184                 mvwprintw(CWin, 0, LOAD_COL, "%5.0f", dload[0]);
185         else
186                 mvwprintw(CWin, 0, LOAD_COL, "%5.1f", dload[0]);
187         mvwprintw(CWin, 0, GPKGRATE_COL, "%-5d", pkgrate);
188
189         /*
190          * If dynamic worker reduction is active include a field,
191          * Otherwise blank the field.
192          */
193         if (LastReduce != DynamicMaxWorkers) {
194                 LastReduce = DynamicMaxWorkers;
195                 if (MaxWorkers == LastReduce)
196                         mvwprintw(CWin, 0, REDUCE_COL, "        ");
197                 else
198                         mvwprintw(CWin, 0, REDUCE_COL, "Limit %-2d",
199                                   LastReduce);
200         }
201
202         mvwprintw(CWin, 1, LEFT_COL, "%-4d", BuildTotal - BuildCount);
203         mvwprintw(CWin, 1, FAILED_COL, "%-4d", BuildFailCount);
204         mvwprintw(CWin, 1, SKIPPED_COL, "%-4d", BuildSkipCount);
205         if (noswap)
206                 mvwprintw(CWin, 1, SWAP_COL, "-   ");
207         else
208                 mvwprintw(CWin, 1, SWAP_COL, "%5.1f", dswap);
209         mvwprintw(CWin, 1, IMPULSE_COL, "%-5d", pkgimpulse);
210         if (h > 99)
211                 mvwprintw(CWin, 1, TIME_COL-1, "%3d:%02d:%02d", h, m, s);
212         else
213                 mvwprintw(CWin, 1, TIME_COL, "%02d:%02d:%02d", h, m, s);
214 }
215
216 void
217 GuiUpdate(worker_t *work)
218 {
219         const char *phase;
220         const char *origin;
221         time_t t;
222         int i = work->index;
223         int h;
224         int m;
225         int s;
226
227         phase = "Unknown";
228         origin = "";
229
230         switch(work->state) {
231         case WORKER_NONE:
232                 phase = "None";
233                 /* fall through */
234         case WORKER_IDLE:
235                 if (work->state == WORKER_IDLE)
236                         phase = "Idle";
237                 /* fall through */
238         case WORKER_FAILED:
239                 if (work->state == WORKER_FAILED)
240                         phase = "Failed";
241                 /* fall through */
242         case WORKER_EXITING:
243                 if (work->state == WORKER_EXITING)
244                         phase = "Exiting";
245                 mvwprintw(CWin, 5 + i, DURATION_COL, "--:--:--");
246                 mvwprintw(CWin, 5 + i, BUILD_PHASE_COL, "%-16.16s", phase);
247                 mvwprintw(CWin, 5 + i, ORIGIN_COL, "%-39.39s", "");
248                 mvwprintw(CWin, 5 + i, LINES_COL, "%-6.6s", "");
249                 return;
250         case WORKER_PENDING:
251                 phase = "Pending";
252                 break;
253         case WORKER_RUNNING:
254                 phase = "Running";
255                 break;
256         case WORKER_DONE:
257                 phase = "Done";
258                 break;
259         case WORKER_FROZEN:
260                 phase = "FROZEN";
261                 break;
262         default:
263                 break;
264         }
265
266         t = time(NULL) - work->start_time;
267         s = t % 60;
268         m = t / 60 % 60;
269         h = t / 60 / 60;
270
271         if (work->state == WORKER_RUNNING) {
272                 switch(work->phase) {
273                 case PHASE_PENDING:
274                         phase = "pending";
275                         break;
276                 case PHASE_INSTALL_PKGS:
277                         phase = "install-pkgs";
278                         break;
279                 case PHASE_CHECK_SANITY:
280                         phase = "check-sanity";
281                         break;
282                 case PHASE_PKG_DEPENDS:
283                         phase = "pkg-depends";
284                         break;
285                 case PHASE_FETCH_DEPENDS:
286                         phase = "fetch-depends";
287                         break;
288                 case PHASE_FETCH:
289                         phase = "fetch";
290                         break;
291                 case PHASE_CHECKSUM:
292                         phase = "checksum";
293                         break;
294                 case PHASE_EXTRACT_DEPENDS:
295                         phase = "extract-depends";
296                         break;
297                 case PHASE_EXTRACT:
298                         phase = "extract";
299                         break;
300                 case PHASE_PATCH_DEPENDS:
301                         phase = "patch-depends";
302                         break;
303                 case PHASE_PATCH:
304                         phase = "patch";
305                         break;
306                 case PHASE_BUILD_DEPENDS:
307                         phase = "build-depends";
308                         break;
309                 case PHASE_LIB_DEPENDS:
310                         phase = "lib-depends";
311                         break;
312                 case PHASE_CONFIGURE:
313                         phase = "configure";
314                         break;
315                 case PHASE_BUILD:
316                         phase = "build";
317                         break;
318                 case PHASE_RUN_DEPENDS:
319                         phase = "run-depends";
320                         break;
321                 case PHASE_STAGE:
322                         phase = "stage";
323                         break;
324                 case PHASE_TEST:
325                         phase = "test";
326                         break;
327                 case PHASE_CHECK_PLIST:
328                         phase = "check-plist";
329                         break;
330                 case PHASE_PACKAGE:
331                         phase = "package";
332                         break;
333                 case PHASE_INSTALL_MTREE:
334                         phase = "install-mtree";
335                         break;
336                 case PHASE_INSTALL:
337                         phase = "install";
338                         break;
339                 case PHASE_DEINSTALL:
340                         phase = "deinstall";
341                         break;
342                 default:
343                         phase = "Run-Unknown";
344                         break;
345                 }
346         }
347
348         if (work->pkg)
349                 origin = work->pkg->portdir;
350         else
351                 origin = "";
352
353         mvwprintw(CWin, 5 + i, DURATION_COL, "%02d:%02d:%02d", h, m, s);
354         mvwprintw(CWin, 5 + i, BUILD_PHASE_COL, "%-16.16s", phase);
355         mvwprintw(CWin, 5 + i, ORIGIN_COL, "%-39.39s", origin);
356         mvwprintw(CWin, 5 + i, LINES_COL, "%6d", work->lines);
357 }
358
359 void
360 GuiSync(void)
361 {
362         wrefresh(CWin);
363 }
364
365 void
366 GuiDone(void)
367 {
368         endwin();
369 }