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' ||
73 snprintf(buf, sizeof(buf), "%s", reason);
95 HtmlSlots = calloc(sizeof(char *), MaxWorkers);
97 HtmlStart = time(NULL);
99 asprintf(&ReportPath, "%s/Report", LogsPath);
100 if (stat(ReportPath, &st) < 0 && mkdir(ReportPath, 0755) < 0)
101 dfatal("Unable to create %s", ReportPath);
102 for (i = 0; CopyFilesAry[i]; ++i) {
103 asprintf(&src, "%s/%s", SCRIPTPATH(SCRIPTDIR), CopyFilesAry[i]);
104 if (strcmp(CopyFilesAry[i], "progress.html") == 0) {
105 asprintf(&dst, "%s/index.html", ReportPath);
107 asprintf(&dst, "%s/%s", ReportPath, CopyFilesAry[i]);
114 asprintf(&src, "%s/summary.json", ReportPath);
120 strftime(KickOff_Buf, sizeof(KickOff_Buf),
121 " %d-%b-%Y %H:%M:%S %Z", &tmm);
123 dir = opendir(ReportPath);
125 dfatal("Unable to scan %s", ReportPath);
126 while ((den = readdir(dir)) != NULL) {
127 len = strlen(den->d_name);
129 strcmp(den->d_name + len - 13, "_history.json") == 0) {
130 asprintf(&src, "%s/%s", ReportPath, den->d_name);
149 for (i = 0; i < MaxWorkers; ++i) {
163 HtmlUpdate(worker_t *work, const char *portdir)
173 char elapsed_buf[32];
180 switch(work->state) {
185 if (work->state == WORKER_IDLE)
190 if (work->state == WORKER_FAILED)
194 if (work->state == WORKER_EXITING)
215 SNPRINTF(elapsed_buf, "%s", " --:--:--");
216 SNPRINTF(lines_buf, "%s", "");
219 t = time(NULL) - work->start_time;
224 SNPRINTF(elapsed_buf, "%3d:%02d:%02d", h, m, s);
226 SNPRINTF(elapsed_buf, " %02d:%02d:%02d", h, m, s);
228 if (work->state == WORKER_RUNNING)
229 phase = getphasestr(work->phase);
232 * When called from the monitor frontend portdir has to be
233 * passed in directly because work->pkg is not mapped.
238 origin = work->pkg->portdir;
242 SNPRINTF(lines_buf, "%ld", work->lines);
246 * Update the summary information
250 asprintf(&HtmlSlots[i],
253 " ,\"elapsed\":\"%s\"\n"
254 " ,\"phase\":\"%s\"\n"
255 " ,\"origin\":\"%s\"\n"
256 " ,\"lines\":\"%s\"\n"
267 HtmlUpdateTop(topinfo_t *info)
273 char elapsed_buf[32];
278 * Be sure to do the first update and final update, but otherwise
279 * only update every 10 seconds or so.
281 if (HtmlLast && (int)(time(NULL) - HtmlLast) < 10 && info->active)
283 HtmlLast = time(NULL);
286 SNPRINTF(elapsed_buf, "%3d:%02d:%02d",
287 info->h, info->m, info->s);
289 SNPRINTF(elapsed_buf, " %02d:%02d:%02d",
290 info->h, info->m, info->s);
294 SNPRINTF(swap_buf, "- ");
296 SNPRINTF(swap_buf, "%5.1f", info->dswap);
298 if (info->dload[0] > 999.9)
299 SNPRINTF(load_buf, "%5.0f", info->dload[0]);
301 SNPRINTF(load_buf, "%5.1f", info->dload[0]);
303 asprintf(&path, "%s/summary.json.new", ReportPath);
304 asprintf(&dst, "%s/summary.json", ReportPath);
305 fp = fopen(path, "we");
311 " \"profile\":\"%s\"\n"
312 " ,\"kickoff\":\"%s\"\n"
322 " ,\"elapsed\":\"%s\"\n"
325 " ,\"swapinfo\":\"%s\"\n"
326 " ,\"load\":\"%s\"\n"
330 HistNum, /* kfiles */
331 info->active, /* active */
333 info->total, /* queued */
334 info->successful, /* built */
335 info->failed, /* failed */
336 info->ignored, /* ignored */
337 info->skipped, /* skipped */
338 info->remaining, /* remaining */
339 elapsed_buf, /* elapsed */
340 info->pkgrate, /* pkghour */
341 info->pkgimpulse, /* impulse */
342 swap_buf, /* swapinfo */
348 for (i = 0; i < MaxWorkers; ++i) {
352 fwrite(HtmlSlots[i], 1,
353 strlen(HtmlSlots[i]), fp);
358 " ,\"elapsed\":\"Shutdown\"\n"
360 " ,\"origin\":\"\"\n"
385 HtmlUpdateCompletion(worker_t *work, int dlogid, pkg_t *pkg, const char *reason)
389 char elapsed_buf[64];
399 t = time(NULL) - work->start_time;
403 SNPRINTF(elapsed_buf, "%02d:%02d:%02d", h, m, s);
417 asprintf(&mreason, "%s:%s",
418 getphasestr(work->phase),
421 asprintf(&mreason, "unknown:%s", reason);
427 asprintf(&mreason, "%s:|:0", reason);
438 t = time(NULL) - HtmlStart;
444 * Cycle history file as appropriate, includes initial file handling.
448 asprintf(&path, "%s/%02d_history.json", ReportPath, HistNum);
449 if (stat(path, &st) < 0) {
450 fp = fopen(path, "we");
451 } else if (st.st_size > 50000) {
454 asprintf(&path, "%s/%02d_history.json", ReportPath, HistNum);
455 fp = fopen(path, "we");
457 fp = fopen(path, "r+e");
458 fseek(fp, 0, SEEK_END);
462 if (ftell(fp) == 0) {
465 fseek(fp, -2, SEEK_END);
470 " ,\"elapsed\":\"%02d:%02d:%02d\"\n"
471 " ,\"ID\":\"%02d\"\n"
472 " ,\"result\":\"%s\"\n"
473 " ,\"origin\":\"%s\"\n"
474 " ,\"info\":\"%s\"\n"
475 " ,\"duration\":\"%s\"\n"
478 ((ftell(fp) > 10) ? "," : ""),
501 runstats_t HtmlRunStats = {
505 .update = HtmlUpdate,
506 .updateTop = HtmlUpdateTop,
507 .updateLogs = HtmlUpdateLogs,
508 .updateCompletion = HtmlUpdateCompletion,