xref: /dragonfly/usr.bin/dsynth/html.c (revision 1e79c7a7)
1ea37671dSMatthew Dillon /*
28b485838SMatthew Dillon  * Copyright (c) 2019-2020 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 
398b485838SMatthew Dillon #define SNPRINTF(buf, ctl, ...)         \
408b485838SMatthew Dillon 	snprintf((buf), sizeof(buf), ctl, ## __VA_ARGS__)
418b485838SMatthew Dillon 
428b485838SMatthew Dillon static char *ReportPath;
438b485838SMatthew Dillon static int HistNum;
448b485838SMatthew Dillon static int EntryNum;
458b485838SMatthew Dillon static char KickOff_Buf[64];
468b485838SMatthew Dillon 
478b485838SMatthew Dillon const char *CopyFilesAry[] = {
488b485838SMatthew Dillon 	"favicon.png",
498b485838SMatthew Dillon 	"progress.html",
508b485838SMatthew Dillon 	"progress.css",
518b485838SMatthew Dillon 	"progress.js",
528b485838SMatthew Dillon 	"dsynth.png",
538b485838SMatthew Dillon 	NULL
548b485838SMatthew Dillon };
558b485838SMatthew Dillon 
568b485838SMatthew Dillon char **HtmlSlots;
578b485838SMatthew Dillon time_t HtmlStart;
588b485838SMatthew Dillon time_t HtmlLast;
598b485838SMatthew Dillon 
608b485838SMatthew Dillon /*
618b485838SMatthew Dillon  * Get rid of stuff that might blow up the json output.
628b485838SMatthew Dillon  */
638b485838SMatthew Dillon static const char *
648b485838SMatthew Dillon dequote(const char *reason)
658b485838SMatthew Dillon {
66*1e79c7a7SMatthew Dillon 	static char *buf;
678b485838SMatthew Dillon 	int i;
688b485838SMatthew Dillon 
698b485838SMatthew Dillon 	for (i = 0; reason[i]; ++i) {
7032f84edaSMatthew Dillon 		if (reason[i] == '\"' || reason[i] == '\n' ||
7132f84edaSMatthew Dillon 		    reason[i] == '\\') {
728b485838SMatthew Dillon 			if (reason != buf) {
73*1e79c7a7SMatthew Dillon 				if (buf)
74*1e79c7a7SMatthew Dillon 					free(buf);
75*1e79c7a7SMatthew Dillon 				buf = strdup(reason);
768b485838SMatthew Dillon 				reason = buf;
778b485838SMatthew Dillon 			}
788b485838SMatthew Dillon 			buf[i] = ' ';
798b485838SMatthew Dillon 		}
808b485838SMatthew Dillon 	}
818b485838SMatthew Dillon 	return reason;
828b485838SMatthew Dillon }
838b485838SMatthew Dillon 
84ea37671dSMatthew Dillon static void
85ea37671dSMatthew Dillon HtmlInit(void)
86ea37671dSMatthew Dillon {
878b485838SMatthew Dillon 	struct dirent *den;
888b485838SMatthew Dillon 	DIR *dir;
898b485838SMatthew Dillon 	struct stat st;
908b485838SMatthew Dillon 	struct tm tmm;
918b485838SMatthew Dillon 	size_t len;
928b485838SMatthew Dillon 	char *src;
938b485838SMatthew Dillon 	char *dst;
948b485838SMatthew Dillon 	time_t t;
958b485838SMatthew Dillon 	int i;
968b485838SMatthew Dillon 
978b485838SMatthew Dillon 	HtmlSlots = calloc(sizeof(char *), MaxWorkers);
988b485838SMatthew Dillon 	HtmlLast = 0;
998b485838SMatthew Dillon 	HtmlStart = time(NULL);
1008b485838SMatthew Dillon 
1018b485838SMatthew Dillon 	asprintf(&ReportPath, "%s/Report", LogsPath);
1028b485838SMatthew Dillon 	if (stat(ReportPath, &st) < 0 && mkdir(ReportPath, 0755) < 0)
1038b485838SMatthew Dillon 		dfatal("Unable to create %s", ReportPath);
1048b485838SMatthew Dillon 	for (i = 0; CopyFilesAry[i]; ++i) {
1058b485838SMatthew Dillon 		asprintf(&src, "%s/%s", SCRIPTPATH(SCRIPTDIR), CopyFilesAry[i]);
1068b485838SMatthew Dillon 		if (strcmp(CopyFilesAry[i], "progress.html") == 0) {
1078b485838SMatthew Dillon 			asprintf(&dst, "%s/index.html", ReportPath);
1088b485838SMatthew Dillon 		} else {
1098b485838SMatthew Dillon 			asprintf(&dst, "%s/%s", ReportPath, CopyFilesAry[i]);
1108b485838SMatthew Dillon 		}
1118b485838SMatthew Dillon 		copyfile(src, dst);
1128b485838SMatthew Dillon 		free(src);
1138b485838SMatthew Dillon 		free(dst);
1148b485838SMatthew Dillon 	}
1158b485838SMatthew Dillon 
1168b485838SMatthew Dillon 	asprintf(&src, "%s/summary.json", ReportPath);
1178b485838SMatthew Dillon 	remove(src);
1188b485838SMatthew Dillon 	free(src);
1198b485838SMatthew Dillon 
1208b485838SMatthew Dillon 	t = time(NULL);
1218b485838SMatthew Dillon 	gmtime_r(&t, &tmm);
1228b485838SMatthew Dillon 	strftime(KickOff_Buf, sizeof(KickOff_Buf),
1238b485838SMatthew Dillon 		 " %d-%b-%Y %H:%M:%S %Z", &tmm);
1248b485838SMatthew Dillon 
1258b485838SMatthew Dillon 	dir = opendir(ReportPath);
1268b485838SMatthew Dillon 	if (dir == NULL)
1278b485838SMatthew Dillon 		dfatal("Unable to scan %s", ReportPath);
1288b485838SMatthew Dillon 	while ((den = readdir(dir)) != NULL) {
1298b485838SMatthew Dillon 		len = strlen(den->d_name);
1308b485838SMatthew Dillon 		if (len > 13 &&
1318b485838SMatthew Dillon 		    strcmp(den->d_name + len - 13, "_history.json") == 0) {
1328b485838SMatthew Dillon 			asprintf(&src, "%s/%s", ReportPath, den->d_name);
1338b485838SMatthew Dillon 			remove(src);
1348b485838SMatthew Dillon 			free(src);
1358b485838SMatthew Dillon 		}
1368b485838SMatthew Dillon 	}
1378b485838SMatthew Dillon 	closedir(dir);
1388b485838SMatthew Dillon 
1398b485838SMatthew Dillon 	/*
1408b485838SMatthew Dillon 	 * First history file
1418b485838SMatthew Dillon 	 */
1428b485838SMatthew Dillon 	HistNum = 0;
1438b485838SMatthew Dillon 	EntryNum = 1;
144ea37671dSMatthew Dillon }
145ea37671dSMatthew Dillon 
146ea37671dSMatthew Dillon static void
147ea37671dSMatthew Dillon HtmlDone(void)
148ea37671dSMatthew Dillon {
1498b485838SMatthew Dillon 	int i;
1508b485838SMatthew Dillon 
1518b485838SMatthew Dillon 	for (i = 0; i < MaxWorkers; ++i) {
1528b485838SMatthew Dillon 		if (HtmlSlots[i])
1538b485838SMatthew Dillon 			free(HtmlSlots[i]);
1548b485838SMatthew Dillon 	}
1558b485838SMatthew Dillon 	free(HtmlSlots);
1568b485838SMatthew Dillon 	HtmlSlots = NULL;
157ea37671dSMatthew Dillon }
158ea37671dSMatthew Dillon 
159ea37671dSMatthew Dillon static void
160ea37671dSMatthew Dillon HtmlReset(void)
161ea37671dSMatthew Dillon {
162ea37671dSMatthew Dillon }
163ea37671dSMatthew Dillon 
164ea37671dSMatthew Dillon static void
1658b485838SMatthew Dillon HtmlUpdate(worker_t *work, const char *portdir)
166ea37671dSMatthew Dillon {
1678b485838SMatthew Dillon 	const char *phase;
1688b485838SMatthew Dillon 	const char *origin;
1698b485838SMatthew Dillon 	time_t t;
1708b485838SMatthew Dillon 	int i = work->index;
1718b485838SMatthew Dillon 	int h;
1728b485838SMatthew Dillon 	int m;
1738b485838SMatthew Dillon 	int s;
1748b485838SMatthew Dillon 	int clear;
1758b485838SMatthew Dillon 	char elapsed_buf[32];
1768b485838SMatthew Dillon 	char lines_buf[32];
1778b485838SMatthew Dillon 
1788b485838SMatthew Dillon 	phase = "Unknown";
1798b485838SMatthew Dillon 	origin = "";
1808b485838SMatthew Dillon 	clear = 0;
1818b485838SMatthew Dillon 
1828b485838SMatthew Dillon 	switch(work->state) {
1838b485838SMatthew Dillon 	case WORKER_NONE:
1848b485838SMatthew Dillon 		phase = "None";
1858b485838SMatthew Dillon 		/* fall through */
1868b485838SMatthew Dillon 	case WORKER_IDLE:
1878b485838SMatthew Dillon 		if (work->state == WORKER_IDLE)
1888b485838SMatthew Dillon 			phase = "Idle";
1898b485838SMatthew Dillon 		clear = 1;
1908b485838SMatthew Dillon 		break;
1918b485838SMatthew Dillon 	case WORKER_FAILED:
1928b485838SMatthew Dillon 		if (work->state == WORKER_FAILED)
1938b485838SMatthew Dillon 			phase = "Failed";
1948b485838SMatthew Dillon 		/* fall through */
1958b485838SMatthew Dillon 	case WORKER_EXITING:
1968b485838SMatthew Dillon 		if (work->state == WORKER_EXITING)
1978b485838SMatthew Dillon 			phase = "Exiting";
1988b485838SMatthew Dillon 		return;
1998b485838SMatthew Dillon 		/* NOT REACHED */
2008b485838SMatthew Dillon 	case WORKER_PENDING:
2018b485838SMatthew Dillon 		phase = "Pending";
2028b485838SMatthew Dillon 		break;
2038b485838SMatthew Dillon 	case WORKER_RUNNING:
2048b485838SMatthew Dillon 		phase = "Running";
2058b485838SMatthew Dillon 		break;
2068b485838SMatthew Dillon 	case WORKER_DONE:
2078b485838SMatthew Dillon 		phase = "Done";
2088b485838SMatthew Dillon 		break;
2098b485838SMatthew Dillon 	case WORKER_FROZEN:
2108b485838SMatthew Dillon 		phase = "FROZEN";
2118b485838SMatthew Dillon 		break;
2128b485838SMatthew Dillon 	default:
2138b485838SMatthew Dillon 		break;
2148b485838SMatthew Dillon 	}
2158b485838SMatthew Dillon 
2168b485838SMatthew Dillon 	if (clear) {
2178b485838SMatthew Dillon 		SNPRINTF(elapsed_buf, "%s", " --:--:--");
2188b485838SMatthew Dillon 		SNPRINTF(lines_buf, "%s", "");
2198b485838SMatthew Dillon 		origin = "";
2208b485838SMatthew Dillon 	} else {
2218b485838SMatthew Dillon 		t = time(NULL) - work->start_time;
2228b485838SMatthew Dillon 		s = t % 60;
2238b485838SMatthew Dillon 		m = t / 60 % 60;
2248b485838SMatthew Dillon 		h = t / 60 / 60;
2258b485838SMatthew Dillon 		if (h > 99)
2268b485838SMatthew Dillon 			SNPRINTF(elapsed_buf, "%3d:%02d:%02d", h, m, s);
2278b485838SMatthew Dillon 		else
2288b485838SMatthew Dillon 			SNPRINTF(elapsed_buf, " %02d:%02d:%02d", h, m, s);
2298b485838SMatthew Dillon 
2308b485838SMatthew Dillon 		if (work->state == WORKER_RUNNING)
2318b485838SMatthew Dillon 			phase = getphasestr(work->phase);
2328b485838SMatthew Dillon 
2338b485838SMatthew Dillon 		/*
2348b485838SMatthew Dillon 		 * When called from the monitor frontend portdir has to be
2358b485838SMatthew Dillon 		 * passed in directly because work->pkg is not mapped.
2368b485838SMatthew Dillon 		 */
2378b485838SMatthew Dillon 		if (portdir)
2388b485838SMatthew Dillon 			origin = portdir;
2398b485838SMatthew Dillon 		else if (work->pkg)
2408b485838SMatthew Dillon 			origin = work->pkg->portdir;
2418b485838SMatthew Dillon 		else
2428b485838SMatthew Dillon 			origin = "";
2438b485838SMatthew Dillon 
2448b485838SMatthew Dillon 		SNPRINTF(lines_buf, "%ld", work->lines);
2458b485838SMatthew Dillon 	}
2468b485838SMatthew Dillon 
2478b485838SMatthew Dillon 	/*
2488b485838SMatthew Dillon 	 * Update the summary information
2498b485838SMatthew Dillon 	 */
2508b485838SMatthew Dillon 	if (HtmlSlots[i])
2518b485838SMatthew Dillon 		free(HtmlSlots[i]);
2528b485838SMatthew Dillon 	asprintf(&HtmlSlots[i],
2538b485838SMatthew Dillon 		 "  {\n"
2548b485838SMatthew Dillon 		 "     \"ID\":\"%02d\"\n"
2558b485838SMatthew Dillon 		 "     ,\"elapsed\":\"%s\"\n"
2568b485838SMatthew Dillon 		 "     ,\"phase\":\"%s\"\n"
2578b485838SMatthew Dillon 		 "     ,\"origin\":\"%s\"\n"
2588b485838SMatthew Dillon 		 "     ,\"lines\":\"%s\"\n"
2598b485838SMatthew Dillon 		 "  }\n",
2608b485838SMatthew Dillon 		 i,
2618b485838SMatthew Dillon 		 elapsed_buf,
2628b485838SMatthew Dillon 		 phase,
2638b485838SMatthew Dillon 		 origin,
2648b485838SMatthew Dillon 		 lines_buf
2658b485838SMatthew Dillon 	);
266ea37671dSMatthew Dillon }
267ea37671dSMatthew Dillon 
268ea37671dSMatthew Dillon static void
2698b485838SMatthew Dillon HtmlUpdateTop(topinfo_t *info)
270ea37671dSMatthew Dillon {
2718b485838SMatthew Dillon 	char *path;
2728b485838SMatthew Dillon 	char *dst;
2738b485838SMatthew Dillon 	FILE *fp;
2748b485838SMatthew Dillon 	int i;
2758b485838SMatthew Dillon 	char elapsed_buf[32];
2768b485838SMatthew Dillon 	char swap_buf[32];
2778b485838SMatthew Dillon 	char load_buf[32];
2788b485838SMatthew Dillon 
2798b485838SMatthew Dillon 	/*
2808b485838SMatthew Dillon 	 * Be sure to do the first update and final update, but otherwise
2818b485838SMatthew Dillon 	 * only update every 10 seconds or so.
2828b485838SMatthew Dillon 	 */
2838b485838SMatthew Dillon 	if (HtmlLast && (int)(time(NULL) - HtmlLast) < 10 && info->active)
2848b485838SMatthew Dillon 		return;
2858b485838SMatthew Dillon 	HtmlLast = time(NULL);
2868b485838SMatthew Dillon 
2878b485838SMatthew Dillon 	if (info->h > 99) {
2888b485838SMatthew Dillon 		SNPRINTF(elapsed_buf, "%3d:%02d:%02d",
2898b485838SMatthew Dillon 			 info->h, info->m, info->s);
2908b485838SMatthew Dillon 	} else {
2918b485838SMatthew Dillon 		SNPRINTF(elapsed_buf, " %02d:%02d:%02d",
2928b485838SMatthew Dillon 			 info->h, info->m, info->s);
2938b485838SMatthew Dillon 	}
2948b485838SMatthew Dillon 
2958b485838SMatthew Dillon 	if (info->noswap)
2968b485838SMatthew Dillon 		SNPRINTF(swap_buf, "-    ");
2978b485838SMatthew Dillon 	else
2988b485838SMatthew Dillon 		SNPRINTF(swap_buf, "%5.1f", info->dswap);
2998b485838SMatthew Dillon 
3008b485838SMatthew Dillon 	if (info->dload[0] > 999.9)
3018b485838SMatthew Dillon 		SNPRINTF(load_buf, "%5.0f", info->dload[0]);
3028b485838SMatthew Dillon 	else
3038b485838SMatthew Dillon 		SNPRINTF(load_buf, "%5.1f", info->dload[0]);
3048b485838SMatthew Dillon 
3058b485838SMatthew Dillon 	asprintf(&path, "%s/summary.json.new", ReportPath);
3068b485838SMatthew Dillon 	asprintf(&dst, "%s/summary.json", ReportPath);
3078b485838SMatthew Dillon 	fp = fopen(path, "we");
3088b485838SMatthew Dillon 	if (!fp)
3098b485838SMatthew Dillon 		ddassert(0);
3108b485838SMatthew Dillon 	if (fp) {
3118b485838SMatthew Dillon 		fprintf(fp,
3128b485838SMatthew Dillon 			"{\n"
3138b485838SMatthew Dillon 			"  \"profile\":\"%s\"\n"
3148b485838SMatthew Dillon 			"  ,\"kickoff\":\"%s\"\n"
3158b485838SMatthew Dillon 			"  ,\"kfiles\":%d\n"
3168b485838SMatthew Dillon 			"  ,\"active\":%d\n"
3178b485838SMatthew Dillon 			"  ,\"stats\":{\n"
3188b485838SMatthew Dillon 			"    \"queued\":%d\n"
3198b485838SMatthew Dillon 			"    ,\"built\":%d\n"
3208b485838SMatthew Dillon 			"    ,\"failed\":%d\n"
3218b485838SMatthew Dillon 			"    ,\"ignored\":%d\n"
3228b485838SMatthew Dillon 			"    ,\"skipped\":%d\n"
3238b485838SMatthew Dillon 			"    ,\"remains\":%d\n"
3248b485838SMatthew Dillon 			"    ,\"elapsed\":\"%s\"\n"
3258b485838SMatthew Dillon 			"    ,\"pkghour\":%d\n"
3268b485838SMatthew Dillon 			"    ,\"impulse\":%d\n"
3278b485838SMatthew Dillon 			"    ,\"swapinfo\":\"%s\"\n"
3288b485838SMatthew Dillon 			"    ,\"load\":\"%s\"\n"
3298b485838SMatthew Dillon 			"  }\n",
3308b485838SMatthew Dillon 			Profile,
3318b485838SMatthew Dillon 			KickOff_Buf,
3328b485838SMatthew Dillon 			HistNum,		/* kfiles */
3338b485838SMatthew Dillon 			info->active,		/* active */
3348b485838SMatthew Dillon 
3358b485838SMatthew Dillon 			info->total,		/* queued */
3368b485838SMatthew Dillon 			info->successful,	/* built */
3378b485838SMatthew Dillon 			info->failed,		/* failed */
3388b485838SMatthew Dillon 			info->ignored,		/* ignored */
3398b485838SMatthew Dillon 			info->skipped,		/* skipped */
3408b485838SMatthew Dillon 			info->remaining,	/* remaining */
3418b485838SMatthew Dillon 			elapsed_buf,		/* elapsed */
3428b485838SMatthew Dillon 			info->pkgrate,		/* pkghour */
3438b485838SMatthew Dillon 			info->pkgimpulse,	/* impulse */
3448b485838SMatthew Dillon 			swap_buf,		/* swapinfo */
3458b485838SMatthew Dillon 			load_buf		/* load */
3468b485838SMatthew Dillon 		);
3478b485838SMatthew Dillon 		fprintf(fp,
3488b485838SMatthew Dillon 			"  ,\"builders\":[\n"
3498b485838SMatthew Dillon 		);
3508b485838SMatthew Dillon 		for (i = 0; i < MaxWorkers; ++i) {
3518b485838SMatthew Dillon 			if (HtmlSlots[i]) {
3528b485838SMatthew Dillon 				if (i)
3538b485838SMatthew Dillon 					fprintf(fp, ",");
3548b485838SMatthew Dillon 				fwrite(HtmlSlots[i], 1,
3558b485838SMatthew Dillon 				       strlen(HtmlSlots[i]), fp);
3568b485838SMatthew Dillon 			} else {
3578b485838SMatthew Dillon 				fprintf(fp,
3588b485838SMatthew Dillon 					"   %s{\n"
3598b485838SMatthew Dillon 					"     \"ID\":\"%02d\"\n"
3608b485838SMatthew Dillon 					"     ,\"elapsed\":\"Shutdown\"\n"
3618b485838SMatthew Dillon 					"     ,\"phase\":\"\"\n"
3628b485838SMatthew Dillon 					"     ,\"origin\":\"\"\n"
3638b485838SMatthew Dillon 					"     ,\"lines\":\"\"\n"
3648b485838SMatthew Dillon 					"    }\n",
3658b485838SMatthew Dillon 					(i ? "," : ""),
3668b485838SMatthew Dillon 					i
3678b485838SMatthew Dillon 				);
3688b485838SMatthew Dillon 			}
3698b485838SMatthew Dillon 		}
3708b485838SMatthew Dillon 		fprintf(fp,
3718b485838SMatthew Dillon 			"  ]\n"
3728b485838SMatthew Dillon 			"}\n");
3738b485838SMatthew Dillon 		fflush(fp);
3748b485838SMatthew Dillon 		fclose(fp);
3758b485838SMatthew Dillon 	}
3768b485838SMatthew Dillon 	rename(path, dst);
3778b485838SMatthew Dillon 	free(path);
3788b485838SMatthew Dillon 	free(dst);
379ea37671dSMatthew Dillon }
380ea37671dSMatthew Dillon 
381ea37671dSMatthew Dillon static void
382ea37671dSMatthew Dillon HtmlUpdateLogs(void)
383ea37671dSMatthew Dillon {
384ea37671dSMatthew Dillon }
385ea37671dSMatthew Dillon 
386ea37671dSMatthew Dillon static void
3878b485838SMatthew Dillon HtmlUpdateCompletion(worker_t *work, int dlogid, pkg_t *pkg, const char *reason)
3888b485838SMatthew Dillon {
3898b485838SMatthew Dillon 	FILE *fp;
3908b485838SMatthew Dillon 	char *path;
3918b485838SMatthew Dillon 	char elapsed_buf[64];
3928b485838SMatthew Dillon 	struct stat st;
3938b485838SMatthew Dillon 	time_t t;
3948b485838SMatthew Dillon 	int s, m, h;
3958b485838SMatthew Dillon 	int slot;
3968b485838SMatthew Dillon 	const char *result;
3976f22c855SMatthew Dillon 	char *mreason;
3988b485838SMatthew Dillon 
3996f22c855SMatthew Dillon 	mreason = NULL;
4008b485838SMatthew Dillon 	if (work) {
4018b485838SMatthew Dillon 		t = time(NULL) - work->start_time;
4028b485838SMatthew Dillon 		s = t % 60;
4038b485838SMatthew Dillon 		m = t / 60 % 60;
4048b485838SMatthew Dillon 		h = t / 60 / 60;
4058b485838SMatthew Dillon 		SNPRINTF(elapsed_buf, "%02d:%02d:%02d", h, m, s);
4068b485838SMatthew Dillon 		slot = work->index;
4078b485838SMatthew Dillon 	} else {
4088b485838SMatthew Dillon 		slot = -1;
4098b485838SMatthew Dillon 		elapsed_buf[0] = 0;
4108b485838SMatthew Dillon 	}
4118b485838SMatthew Dillon 
4128b485838SMatthew Dillon 	switch(dlogid) {
4138b485838SMatthew Dillon 	case DLOG_SUCC:
4148b485838SMatthew Dillon 		result = "built";
4158b485838SMatthew Dillon 		break;
4168b485838SMatthew Dillon 	case DLOG_FAIL:
4178b485838SMatthew Dillon 		result = "failed";
4186f22c855SMatthew Dillon 		if (work) {
4196f22c855SMatthew Dillon 			asprintf(&mreason, "%s:%s",
4206f22c855SMatthew Dillon 				 getphasestr(work->phase),
4216f22c855SMatthew Dillon 				 reason);
4226f22c855SMatthew Dillon 		} else {
4236f22c855SMatthew Dillon 			asprintf(&mreason, "unknown:%s", reason);
4246f22c855SMatthew Dillon 		}
4256f22c855SMatthew Dillon 		reason = mreason;
4268b485838SMatthew Dillon 		break;
4278b485838SMatthew Dillon 	case DLOG_IGN:
4288b485838SMatthew Dillon 		result = "ignored";
4296f22c855SMatthew Dillon 		asprintf(&mreason, "%s:|:0", reason);
4306f22c855SMatthew Dillon 		reason = mreason;
4318b485838SMatthew Dillon 		break;
4328b485838SMatthew Dillon 	case DLOG_SKIP:
4338b485838SMatthew Dillon 		result = "skipped";
4348b485838SMatthew Dillon 		break;
4358b485838SMatthew Dillon 	default:
4368b485838SMatthew Dillon 		result = "Unknown";
4378b485838SMatthew Dillon 		break;
4388b485838SMatthew Dillon 	}
4398b485838SMatthew Dillon 
4408b485838SMatthew Dillon 	t = time(NULL) - HtmlStart;
4418b485838SMatthew Dillon 	s = t % 60;
4428b485838SMatthew Dillon 	m = t / 60 % 60;
4438b485838SMatthew Dillon 	h = t / 60 / 60;
4448b485838SMatthew Dillon 
4458b485838SMatthew Dillon 	/*
4468b485838SMatthew Dillon 	 * Cycle history file as appropriate, includes initial file handling.
4478b485838SMatthew Dillon 	 */
4488b485838SMatthew Dillon 	if (HistNum == 0)
4498b485838SMatthew Dillon 		HistNum = 1;
4508b485838SMatthew Dillon 	asprintf(&path, "%s/%02d_history.json", ReportPath, HistNum);
4518b485838SMatthew Dillon 	if (stat(path, &st) < 0) {
4528b485838SMatthew Dillon 		fp = fopen(path, "we");
4538b485838SMatthew Dillon 	} else if (st.st_size > 50000) {
4548b485838SMatthew Dillon 		++HistNum;
4558b485838SMatthew Dillon 		free(path);
4568b485838SMatthew Dillon 		asprintf(&path, "%s/%02d_history.json", ReportPath, HistNum);
4578b485838SMatthew Dillon 		fp = fopen(path, "we");
4588b485838SMatthew Dillon 	} else {
4598b485838SMatthew Dillon 		fp = fopen(path, "r+e");
4608b485838SMatthew Dillon 		fseek(fp, 0, SEEK_END);
4618b485838SMatthew Dillon 	}
4628b485838SMatthew Dillon 
4638b485838SMatthew Dillon 	if (fp) {
4648b485838SMatthew Dillon 		if (ftell(fp) == 0) {
4658b485838SMatthew Dillon 			fprintf(fp, "[\n");
4668b485838SMatthew Dillon 		} else {
4678b485838SMatthew Dillon 			fseek(fp, -2, SEEK_END);
4688b485838SMatthew Dillon 		}
4698b485838SMatthew Dillon 		fprintf(fp,
4708b485838SMatthew Dillon 			"  %s{\n"
4718b485838SMatthew Dillon 			"   \"entry\":%d\n"
4728b485838SMatthew Dillon 			"   ,\"elapsed\":\"%02d:%02d:%02d\"\n"
4738b485838SMatthew Dillon 			"   ,\"ID\":\"%02d\"\n"
4748b485838SMatthew Dillon 			"   ,\"result\":\"%s\"\n"
4758b485838SMatthew Dillon 			"   ,\"origin\":\"%s\"\n"
4766f22c855SMatthew Dillon 			"   ,\"info\":\"%s\"\n"
4778b485838SMatthew Dillon 			"   ,\"duration\":\"%s\"\n"
4788b485838SMatthew Dillon 			"  }\n"
4798b485838SMatthew Dillon 			"]\n",
4808b485838SMatthew Dillon 			((ftell(fp) > 10) ? "," : ""),
4818b485838SMatthew Dillon 			EntryNum,
4828b485838SMatthew Dillon 			h, m, s,
4838b485838SMatthew Dillon 			slot,
4848b485838SMatthew Dillon 			result,
4858b485838SMatthew Dillon 			pkg->portdir,
4868b485838SMatthew Dillon 			dequote(reason),
4878b485838SMatthew Dillon 			elapsed_buf
4888b485838SMatthew Dillon 		);
4898b485838SMatthew Dillon 		++EntryNum;
4908b485838SMatthew Dillon 		fclose(fp);
4918b485838SMatthew Dillon 
4928b485838SMatthew Dillon 	}
4938b485838SMatthew Dillon 	free(path);
4946f22c855SMatthew Dillon 	if (mreason)
4956f22c855SMatthew Dillon 		free(mreason);
4968b485838SMatthew Dillon }
4978b485838SMatthew Dillon 
4988b485838SMatthew Dillon static void
499ea37671dSMatthew Dillon HtmlSync(void)
500ea37671dSMatthew Dillon {
501ea37671dSMatthew Dillon }
502ea37671dSMatthew Dillon 
503ea37671dSMatthew Dillon runstats_t HtmlRunStats = {
504ea37671dSMatthew Dillon 	.init = HtmlInit,
505ea37671dSMatthew Dillon 	.done = HtmlDone,
506ea37671dSMatthew Dillon 	.reset = HtmlReset,
507ea37671dSMatthew Dillon 	.update = HtmlUpdate,
508ea37671dSMatthew Dillon 	.updateTop = HtmlUpdateTop,
509ea37671dSMatthew Dillon 	.updateLogs = HtmlUpdateLogs,
5108b485838SMatthew Dillon 	.updateCompletion = HtmlUpdateCompletion,
511ea37671dSMatthew Dillon 	.sync = HtmlSync
512ea37671dSMatthew Dillon };
513