2 * Copyright (c) 2019-2020 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * This code uses concepts and configuration based on 'synth', by
8 * John R. Marino <draco@marino.st>, which was written in ada.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
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.
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
39 #define SNPRINTF(buf, ctl, ...) \
40 snprintf((buf), sizeof(buf), ctl, ## __VA_ARGS__)
42 static char *ReportPath;
45 static char KickOff_Buf[64];
47 const char *CopyFilesAry[] = {
61 * Get rid of stuff that might blow up the json output.
64 dequote(const char *reason)
69 for (i = 0; reason[i]; ++i) {
70 if (reason[i] == '\"' || reason[i] == '\n' ||
97 HtmlSlots = calloc(sizeof(char *), MaxWorkers);
99 HtmlStart = time(NULL);
101 asprintf(&ReportPath, "%s/Report", LogsPath);
102 if (stat(ReportPath, &st) < 0 && mkdir(ReportPath, 0755) < 0)
103 dfatal("Unable to create %s", ReportPath);
104 for (i = 0; CopyFilesAry[i]; ++i) {
105 asprintf(&src, "%s/%s", SCRIPTPATH(SCRIPTDIR), CopyFilesAry[i]);
106 if (strcmp(CopyFilesAry[i], "progress.html") == 0) {
107 asprintf(&dst, "%s/index.html", ReportPath);
109 asprintf(&dst, "%s/%s", ReportPath, CopyFilesAry[i]);
116 asprintf(&src, "%s/summary.json", ReportPath);
122 strftime(KickOff_Buf, sizeof(KickOff_Buf),
123 " %d-%b-%Y %H:%M:%S %Z", &tmm);
125 dir = opendir(ReportPath);
127 dfatal("Unable to scan %s", ReportPath);
128 while ((den = readdir(dir)) != NULL) {
129 len = strlen(den->d_name);
131 strcmp(den->d_name + len - 13, "_history.json") == 0) {
132 asprintf(&src, "%s/%s", ReportPath, den->d_name);
151 for (i = 0; i < MaxWorkers; ++i) {
165 HtmlUpdate(worker_t *work, const char *portdir)
175 char elapsed_buf[32];
182 switch(work->state) {
187 if (work->state == WORKER_IDLE)
192 if (work->state == WORKER_FAILED)
196 if (work->state == WORKER_EXITING)
217 SNPRINTF(elapsed_buf, "%s", " --:--:--");
218 SNPRINTF(lines_buf, "%s", "");
221 t = time(NULL) - work->start_time;
226 SNPRINTF(elapsed_buf, "%3d:%02d:%02d", h, m, s);
228 SNPRINTF(elapsed_buf, " %02d:%02d:%02d", h, m, s);
230 if (work->state == WORKER_RUNNING)
231 phase = getphasestr(work->phase);
234 * When called from the monitor frontend portdir has to be
235 * passed in directly because work->pkg is not mapped.
240 origin = work->pkg->portdir;
244 SNPRINTF(lines_buf, "%ld", work->lines);
248 * Update the summary information
252 asprintf(&HtmlSlots[i],
255 " ,\"elapsed\":\"%s\"\n"
256 " ,\"phase\":\"%s\"\n"
257 " ,\"origin\":\"%s\"\n"
258 " ,\"lines\":\"%s\"\n"
269 HtmlUpdateTop(topinfo_t *info)
275 char elapsed_buf[32];
280 * Be sure to do the first update and final update, but otherwise
281 * only update every 10 seconds or so.
283 if (HtmlLast && (int)(time(NULL) - HtmlLast) < 10 && info->active)
285 HtmlLast = time(NULL);
288 SNPRINTF(elapsed_buf, "%3d:%02d:%02d",
289 info->h, info->m, info->s);
291 SNPRINTF(elapsed_buf, " %02d:%02d:%02d",
292 info->h, info->m, info->s);
296 SNPRINTF(swap_buf, "- ");
298 SNPRINTF(swap_buf, "%5.1f", info->dswap);
300 if (info->dload[0] > 999.9)
301 SNPRINTF(load_buf, "%5.0f", info->dload[0]);
303 SNPRINTF(load_buf, "%5.1f", info->dload[0]);
305 asprintf(&path, "%s/summary.json.new", ReportPath);
306 asprintf(&dst, "%s/summary.json", ReportPath);
307 fp = fopen(path, "we");
313 " \"profile\":\"%s\"\n"
314 " ,\"kickoff\":\"%s\"\n"
324 " ,\"elapsed\":\"%s\"\n"
327 " ,\"swapinfo\":\"%s\"\n"
328 " ,\"load\":\"%s\"\n"
332 HistNum, /* kfiles */
333 info->active, /* active */
335 info->total, /* queued */
336 info->successful, /* built */
337 info->failed, /* failed */
338 info->ignored, /* ignored */
339 info->skipped, /* skipped */
340 info->remaining, /* remaining */
341 elapsed_buf, /* elapsed */
342 info->pkgrate, /* pkghour */
343 info->pkgimpulse, /* impulse */
344 swap_buf, /* swapinfo */
350 for (i = 0; i < MaxWorkers; ++i) {
354 fwrite(HtmlSlots[i], 1,
355 strlen(HtmlSlots[i]), fp);
360 " ,\"elapsed\":\"Shutdown\"\n"
362 " ,\"origin\":\"\"\n"
387 HtmlUpdateCompletion(worker_t *work, int dlogid, pkg_t *pkg,
388 const char *reason, const char *skipbuf)
392 char elapsed_buf[64];
402 else if (skipbuf[0] == ' ')
407 t = time(NULL) - work->start_time;
411 SNPRINTF(elapsed_buf, "%02d:%02d:%02d", h, m, s);
425 asprintf(&mreason, "%s:%s",
426 getphasestr(work->phase),
429 asprintf(&mreason, "unknown:%s", reason);
435 asprintf(&mreason, "%s:|:%s", reason, skipbuf);
446 t = time(NULL) - HtmlStart;
452 * Cycle history file as appropriate, includes initial file handling.
456 asprintf(&path, "%s/%02d_history.json", ReportPath, HistNum);
457 if (stat(path, &st) < 0) {
458 fp = fopen(path, "we");
459 } else if (st.st_size > 50000) {
462 asprintf(&path, "%s/%02d_history.json", ReportPath, HistNum);
463 fp = fopen(path, "we");
465 fp = fopen(path, "r+e");
466 fseek(fp, 0, SEEK_END);
470 if (ftell(fp) == 0) {
473 fseek(fp, -2, SEEK_END);
478 " ,\"elapsed\":\"%02d:%02d:%02d\"\n"
479 " ,\"ID\":\"%02d\"\n"
480 " ,\"result\":\"%s\"\n"
481 " ,\"origin\":\"%s\"\n"
482 " ,\"info\":\"%s\"\n"
483 " ,\"duration\":\"%s\"\n"
486 ((ftell(fp) > 10) ? "," : ""),
509 runstats_t HtmlRunStats = {
513 .update = HtmlUpdate,
514 .updateTop = HtmlUpdateTop,
515 .updateLogs = HtmlUpdateLogs,
516 .updateCompletion = HtmlUpdateCompletion,