xref: /dflybsd-src/usr.bin/dsynth/html.c (revision 8b485838508f8af4d3f70bdac23a16ab1ea4304c)
1ea37671dSMatthew Dillon /*
2*8b485838SMatthew 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 
39*8b485838SMatthew Dillon #define SNPRINTF(buf, ctl, ...)         \
40*8b485838SMatthew Dillon 	snprintf((buf), sizeof(buf), ctl, ## __VA_ARGS__)
41*8b485838SMatthew Dillon 
42*8b485838SMatthew Dillon static char *ReportPath;
43*8b485838SMatthew Dillon static int HistNum;
44*8b485838SMatthew Dillon static int EntryNum;
45*8b485838SMatthew Dillon static char KickOff_Buf[64];
46*8b485838SMatthew Dillon 
47*8b485838SMatthew Dillon const char *CopyFilesAry[] = {
48*8b485838SMatthew Dillon 	"favicon.png",
49*8b485838SMatthew Dillon 	"progress.html",
50*8b485838SMatthew Dillon 	"progress.css",
51*8b485838SMatthew Dillon 	"progress.js",
52*8b485838SMatthew Dillon 	"dsynth.png",
53*8b485838SMatthew Dillon 	NULL
54*8b485838SMatthew Dillon };
55*8b485838SMatthew Dillon 
56*8b485838SMatthew Dillon char **HtmlSlots;
57*8b485838SMatthew Dillon time_t HtmlStart;
58*8b485838SMatthew Dillon time_t HtmlLast;
59*8b485838SMatthew Dillon 
60*8b485838SMatthew Dillon /*
61*8b485838SMatthew Dillon  * Get rid of stuff that might blow up the json output.
62*8b485838SMatthew Dillon  */
63*8b485838SMatthew Dillon static const char *
64*8b485838SMatthew Dillon dequote(const char *reason)
65*8b485838SMatthew Dillon {
66*8b485838SMatthew Dillon 	char buf[256];
67*8b485838SMatthew Dillon 	int i;
68*8b485838SMatthew Dillon 
69*8b485838SMatthew Dillon 	for (i = 0; reason[i]; ++i) {
70*8b485838SMatthew Dillon 		if (reason[i] == '\"' || reason[i] == '\n') {
71*8b485838SMatthew Dillon 			if (reason != buf) {
72*8b485838SMatthew Dillon 				snprintf(buf, sizeof(buf), "%s", reason);
73*8b485838SMatthew Dillon 				reason = buf;
74*8b485838SMatthew Dillon 			}
75*8b485838SMatthew Dillon 			buf[i] = ' ';
76*8b485838SMatthew Dillon 		}
77*8b485838SMatthew Dillon 	}
78*8b485838SMatthew Dillon 	return reason;
79*8b485838SMatthew Dillon }
80*8b485838SMatthew Dillon 
81ea37671dSMatthew Dillon static void
82ea37671dSMatthew Dillon HtmlInit(void)
83ea37671dSMatthew Dillon {
84*8b485838SMatthew Dillon 	struct dirent *den;
85*8b485838SMatthew Dillon 	DIR *dir;
86*8b485838SMatthew Dillon 	struct stat st;
87*8b485838SMatthew Dillon 	struct tm tmm;
88*8b485838SMatthew Dillon 	size_t len;
89*8b485838SMatthew Dillon 	char *src;
90*8b485838SMatthew Dillon 	char *dst;
91*8b485838SMatthew Dillon 	time_t t;
92*8b485838SMatthew Dillon 	int i;
93*8b485838SMatthew Dillon 
94*8b485838SMatthew Dillon 	HtmlSlots = calloc(sizeof(char *), MaxWorkers);
95*8b485838SMatthew Dillon 	HtmlLast = 0;
96*8b485838SMatthew Dillon 	HtmlStart = time(NULL);
97*8b485838SMatthew Dillon 
98*8b485838SMatthew Dillon 	asprintf(&ReportPath, "%s/Report", LogsPath);
99*8b485838SMatthew Dillon 	if (stat(ReportPath, &st) < 0 && mkdir(ReportPath, 0755) < 0)
100*8b485838SMatthew Dillon 		dfatal("Unable to create %s", ReportPath);
101*8b485838SMatthew Dillon 	for (i = 0; CopyFilesAry[i]; ++i) {
102*8b485838SMatthew Dillon 		asprintf(&src, "%s/%s", SCRIPTPATH(SCRIPTDIR), CopyFilesAry[i]);
103*8b485838SMatthew Dillon 		if (strcmp(CopyFilesAry[i], "progress.html") == 0) {
104*8b485838SMatthew Dillon 			asprintf(&dst, "%s/index.html", ReportPath);
105*8b485838SMatthew Dillon 		} else {
106*8b485838SMatthew Dillon 			asprintf(&dst, "%s/%s", ReportPath, CopyFilesAry[i]);
107*8b485838SMatthew Dillon 		}
108*8b485838SMatthew Dillon 		copyfile(src, dst);
109*8b485838SMatthew Dillon 		free(src);
110*8b485838SMatthew Dillon 		free(dst);
111*8b485838SMatthew Dillon 	}
112*8b485838SMatthew Dillon 
113*8b485838SMatthew Dillon 	asprintf(&src, "%s/summary.json", ReportPath);
114*8b485838SMatthew Dillon 	remove(src);
115*8b485838SMatthew Dillon 	free(src);
116*8b485838SMatthew Dillon 
117*8b485838SMatthew Dillon 	t = time(NULL);
118*8b485838SMatthew Dillon 	gmtime_r(&t, &tmm);
119*8b485838SMatthew Dillon 	strftime(KickOff_Buf, sizeof(KickOff_Buf),
120*8b485838SMatthew Dillon 		 " %d-%b-%Y %H:%M:%S %Z", &tmm);
121*8b485838SMatthew Dillon 
122*8b485838SMatthew Dillon 	dir = opendir(ReportPath);
123*8b485838SMatthew Dillon 	if (dir == NULL)
124*8b485838SMatthew Dillon 		dfatal("Unable to scan %s", ReportPath);
125*8b485838SMatthew Dillon 	while ((den = readdir(dir)) != NULL) {
126*8b485838SMatthew Dillon 		len = strlen(den->d_name);
127*8b485838SMatthew Dillon 		if (len > 13 &&
128*8b485838SMatthew Dillon 		    strcmp(den->d_name + len - 13, "_history.json") == 0) {
129*8b485838SMatthew Dillon 			asprintf(&src, "%s/%s", ReportPath, den->d_name);
130*8b485838SMatthew Dillon 			remove(src);
131*8b485838SMatthew Dillon 			free(src);
132*8b485838SMatthew Dillon 		}
133*8b485838SMatthew Dillon 	}
134*8b485838SMatthew Dillon 	closedir(dir);
135*8b485838SMatthew Dillon 
136*8b485838SMatthew Dillon 	/*
137*8b485838SMatthew Dillon 	 * First history file
138*8b485838SMatthew Dillon 	 */
139*8b485838SMatthew Dillon 	HistNum = 0;
140*8b485838SMatthew Dillon 	EntryNum = 1;
141ea37671dSMatthew Dillon }
142ea37671dSMatthew Dillon 
143ea37671dSMatthew Dillon static void
144ea37671dSMatthew Dillon HtmlDone(void)
145ea37671dSMatthew Dillon {
146*8b485838SMatthew Dillon 	int i;
147*8b485838SMatthew Dillon 
148*8b485838SMatthew Dillon 	for (i = 0; i < MaxWorkers; ++i) {
149*8b485838SMatthew Dillon 		if (HtmlSlots[i])
150*8b485838SMatthew Dillon 			free(HtmlSlots[i]);
151*8b485838SMatthew Dillon 	}
152*8b485838SMatthew Dillon 	free(HtmlSlots);
153*8b485838SMatthew Dillon 	HtmlSlots = NULL;
154ea37671dSMatthew Dillon }
155ea37671dSMatthew Dillon 
156ea37671dSMatthew Dillon static void
157ea37671dSMatthew Dillon HtmlReset(void)
158ea37671dSMatthew Dillon {
159ea37671dSMatthew Dillon }
160ea37671dSMatthew Dillon 
161ea37671dSMatthew Dillon static void
162*8b485838SMatthew Dillon HtmlUpdate(worker_t *work, const char *portdir)
163ea37671dSMatthew Dillon {
164*8b485838SMatthew Dillon 	const char *phase;
165*8b485838SMatthew Dillon 	const char *origin;
166*8b485838SMatthew Dillon 	time_t t;
167*8b485838SMatthew Dillon 	int i = work->index;
168*8b485838SMatthew Dillon 	int h;
169*8b485838SMatthew Dillon 	int m;
170*8b485838SMatthew Dillon 	int s;
171*8b485838SMatthew Dillon 	int clear;
172*8b485838SMatthew Dillon 	char elapsed_buf[32];
173*8b485838SMatthew Dillon 	char lines_buf[32];
174*8b485838SMatthew Dillon 
175*8b485838SMatthew Dillon 	phase = "Unknown";
176*8b485838SMatthew Dillon 	origin = "";
177*8b485838SMatthew Dillon 	clear = 0;
178*8b485838SMatthew Dillon 
179*8b485838SMatthew Dillon 	switch(work->state) {
180*8b485838SMatthew Dillon 	case WORKER_NONE:
181*8b485838SMatthew Dillon 		phase = "None";
182*8b485838SMatthew Dillon 		/* fall through */
183*8b485838SMatthew Dillon 	case WORKER_IDLE:
184*8b485838SMatthew Dillon 		if (work->state == WORKER_IDLE)
185*8b485838SMatthew Dillon 			phase = "Idle";
186*8b485838SMatthew Dillon 		clear = 1;
187*8b485838SMatthew Dillon 		break;
188*8b485838SMatthew Dillon 	case WORKER_FAILED:
189*8b485838SMatthew Dillon 		if (work->state == WORKER_FAILED)
190*8b485838SMatthew Dillon 			phase = "Failed";
191*8b485838SMatthew Dillon 		/* fall through */
192*8b485838SMatthew Dillon 	case WORKER_EXITING:
193*8b485838SMatthew Dillon 		if (work->state == WORKER_EXITING)
194*8b485838SMatthew Dillon 			phase = "Exiting";
195*8b485838SMatthew Dillon 		return;
196*8b485838SMatthew Dillon 		/* NOT REACHED */
197*8b485838SMatthew Dillon 	case WORKER_PENDING:
198*8b485838SMatthew Dillon 		phase = "Pending";
199*8b485838SMatthew Dillon 		break;
200*8b485838SMatthew Dillon 	case WORKER_RUNNING:
201*8b485838SMatthew Dillon 		phase = "Running";
202*8b485838SMatthew Dillon 		break;
203*8b485838SMatthew Dillon 	case WORKER_DONE:
204*8b485838SMatthew Dillon 		phase = "Done";
205*8b485838SMatthew Dillon 		break;
206*8b485838SMatthew Dillon 	case WORKER_FROZEN:
207*8b485838SMatthew Dillon 		phase = "FROZEN";
208*8b485838SMatthew Dillon 		break;
209*8b485838SMatthew Dillon 	default:
210*8b485838SMatthew Dillon 		break;
211*8b485838SMatthew Dillon 	}
212*8b485838SMatthew Dillon 
213*8b485838SMatthew Dillon 	if (clear) {
214*8b485838SMatthew Dillon 		SNPRINTF(elapsed_buf, "%s", " --:--:--");
215*8b485838SMatthew Dillon 		SNPRINTF(lines_buf, "%s", "");
216*8b485838SMatthew Dillon 		origin = "";
217*8b485838SMatthew Dillon 	} else {
218*8b485838SMatthew Dillon 		t = time(NULL) - work->start_time;
219*8b485838SMatthew Dillon 		s = t % 60;
220*8b485838SMatthew Dillon 		m = t / 60 % 60;
221*8b485838SMatthew Dillon 		h = t / 60 / 60;
222*8b485838SMatthew Dillon 		if (h > 99)
223*8b485838SMatthew Dillon 			SNPRINTF(elapsed_buf, "%3d:%02d:%02d", h, m, s);
224*8b485838SMatthew Dillon 		else
225*8b485838SMatthew Dillon 			SNPRINTF(elapsed_buf, " %02d:%02d:%02d", h, m, s);
226*8b485838SMatthew Dillon 
227*8b485838SMatthew Dillon 		if (work->state == WORKER_RUNNING)
228*8b485838SMatthew Dillon 			phase = getphasestr(work->phase);
229*8b485838SMatthew Dillon 
230*8b485838SMatthew Dillon 		/*
231*8b485838SMatthew Dillon 		 * When called from the monitor frontend portdir has to be
232*8b485838SMatthew Dillon 		 * passed in directly because work->pkg is not mapped.
233*8b485838SMatthew Dillon 		 */
234*8b485838SMatthew Dillon 		if (portdir)
235*8b485838SMatthew Dillon 			origin = portdir;
236*8b485838SMatthew Dillon 		else if (work->pkg)
237*8b485838SMatthew Dillon 			origin = work->pkg->portdir;
238*8b485838SMatthew Dillon 		else
239*8b485838SMatthew Dillon 			origin = "";
240*8b485838SMatthew Dillon 
241*8b485838SMatthew Dillon 		SNPRINTF(lines_buf, "%ld", work->lines);
242*8b485838SMatthew Dillon 	}
243*8b485838SMatthew Dillon 
244*8b485838SMatthew Dillon 	/*
245*8b485838SMatthew Dillon 	 * Update the summary information
246*8b485838SMatthew Dillon 	 */
247*8b485838SMatthew Dillon 	if (HtmlSlots[i])
248*8b485838SMatthew Dillon 		free(HtmlSlots[i]);
249*8b485838SMatthew Dillon 	asprintf(&HtmlSlots[i],
250*8b485838SMatthew Dillon 		 "  {\n"
251*8b485838SMatthew Dillon 		 "     \"ID\":\"%02d\"\n"
252*8b485838SMatthew Dillon 		 "     ,\"elapsed\":\"%s\"\n"
253*8b485838SMatthew Dillon 		 "     ,\"phase\":\"%s\"\n"
254*8b485838SMatthew Dillon 		 "     ,\"origin\":\"%s\"\n"
255*8b485838SMatthew Dillon 		 "     ,\"lines\":\"%s\"\n"
256*8b485838SMatthew Dillon 		 "  }\n",
257*8b485838SMatthew Dillon 		 i,
258*8b485838SMatthew Dillon 		 elapsed_buf,
259*8b485838SMatthew Dillon 		 phase,
260*8b485838SMatthew Dillon 		 origin,
261*8b485838SMatthew Dillon 		 lines_buf
262*8b485838SMatthew Dillon 	);
263ea37671dSMatthew Dillon }
264ea37671dSMatthew Dillon 
265ea37671dSMatthew Dillon static void
266*8b485838SMatthew Dillon HtmlUpdateTop(topinfo_t *info)
267ea37671dSMatthew Dillon {
268*8b485838SMatthew Dillon 	char *path;
269*8b485838SMatthew Dillon 	char *dst;
270*8b485838SMatthew Dillon 	FILE *fp;
271*8b485838SMatthew Dillon 	int i;
272*8b485838SMatthew Dillon 	char elapsed_buf[32];
273*8b485838SMatthew Dillon 	char swap_buf[32];
274*8b485838SMatthew Dillon 	char load_buf[32];
275*8b485838SMatthew Dillon 
276*8b485838SMatthew Dillon 	/*
277*8b485838SMatthew Dillon 	 * Be sure to do the first update and final update, but otherwise
278*8b485838SMatthew Dillon 	 * only update every 10 seconds or so.
279*8b485838SMatthew Dillon 	 */
280*8b485838SMatthew Dillon 	if (HtmlLast && (int)(time(NULL) - HtmlLast) < 10 && info->active)
281*8b485838SMatthew Dillon 		return;
282*8b485838SMatthew Dillon 	HtmlLast = time(NULL);
283*8b485838SMatthew Dillon 
284*8b485838SMatthew Dillon 	if (info->h > 99) {
285*8b485838SMatthew Dillon 		SNPRINTF(elapsed_buf, "%3d:%02d:%02d",
286*8b485838SMatthew Dillon 			 info->h, info->m, info->s);
287*8b485838SMatthew Dillon 	} else {
288*8b485838SMatthew Dillon 		SNPRINTF(elapsed_buf, " %02d:%02d:%02d",
289*8b485838SMatthew Dillon 			 info->h, info->m, info->s);
290*8b485838SMatthew Dillon 	}
291*8b485838SMatthew Dillon 
292*8b485838SMatthew Dillon 	if (info->noswap)
293*8b485838SMatthew Dillon 		SNPRINTF(swap_buf, "-    ");
294*8b485838SMatthew Dillon 	else
295*8b485838SMatthew Dillon 		SNPRINTF(swap_buf, "%5.1f", info->dswap);
296*8b485838SMatthew Dillon 
297*8b485838SMatthew Dillon 	if (info->dload[0] > 999.9)
298*8b485838SMatthew Dillon 		SNPRINTF(load_buf, "%5.0f", info->dload[0]);
299*8b485838SMatthew Dillon 	else
300*8b485838SMatthew Dillon 		SNPRINTF(load_buf, "%5.1f", info->dload[0]);
301*8b485838SMatthew Dillon 
302*8b485838SMatthew Dillon 	asprintf(&path, "%s/summary.json.new", ReportPath);
303*8b485838SMatthew Dillon 	asprintf(&dst, "%s/summary.json", ReportPath);
304*8b485838SMatthew Dillon 	fp = fopen(path, "we");
305*8b485838SMatthew Dillon 	if (!fp)
306*8b485838SMatthew Dillon 		ddassert(0);
307*8b485838SMatthew Dillon 	if (fp) {
308*8b485838SMatthew Dillon 		fprintf(fp,
309*8b485838SMatthew Dillon 			"{\n"
310*8b485838SMatthew Dillon 			"  \"profile\":\"%s\"\n"
311*8b485838SMatthew Dillon 			"  ,\"kickoff\":\"%s\"\n"
312*8b485838SMatthew Dillon 			"  ,\"kfiles\":%d\n"
313*8b485838SMatthew Dillon 			"  ,\"active\":%d\n"
314*8b485838SMatthew Dillon 			"  ,\"stats\":{\n"
315*8b485838SMatthew Dillon 			"    \"queued\":%d\n"
316*8b485838SMatthew Dillon 			"    ,\"built\":%d\n"
317*8b485838SMatthew Dillon 			"    ,\"failed\":%d\n"
318*8b485838SMatthew Dillon 			"    ,\"ignored\":%d\n"
319*8b485838SMatthew Dillon 			"    ,\"skipped\":%d\n"
320*8b485838SMatthew Dillon 			"    ,\"remains\":%d\n"
321*8b485838SMatthew Dillon 			"    ,\"elapsed\":\"%s\"\n"
322*8b485838SMatthew Dillon 			"    ,\"pkghour\":%d\n"
323*8b485838SMatthew Dillon 			"    ,\"impulse\":%d\n"
324*8b485838SMatthew Dillon 			"    ,\"swapinfo\":\"%s\"\n"
325*8b485838SMatthew Dillon 			"    ,\"load\":\"%s\"\n"
326*8b485838SMatthew Dillon 			"  }\n",
327*8b485838SMatthew Dillon 			Profile,
328*8b485838SMatthew Dillon 			KickOff_Buf,
329*8b485838SMatthew Dillon 			HistNum,		/* kfiles */
330*8b485838SMatthew Dillon 			info->active,		/* active */
331*8b485838SMatthew Dillon 
332*8b485838SMatthew Dillon 			info->total,		/* queued */
333*8b485838SMatthew Dillon 			info->successful,	/* built */
334*8b485838SMatthew Dillon 			info->failed,		/* failed */
335*8b485838SMatthew Dillon 			info->ignored,		/* ignored */
336*8b485838SMatthew Dillon 			info->skipped,		/* skipped */
337*8b485838SMatthew Dillon 			info->remaining,	/* remaining */
338*8b485838SMatthew Dillon 			elapsed_buf,		/* elapsed */
339*8b485838SMatthew Dillon 			info->pkgrate,		/* pkghour */
340*8b485838SMatthew Dillon 			info->pkgimpulse,	/* impulse */
341*8b485838SMatthew Dillon 			swap_buf,		/* swapinfo */
342*8b485838SMatthew Dillon 			load_buf		/* load */
343*8b485838SMatthew Dillon 		);
344*8b485838SMatthew Dillon 		fprintf(fp,
345*8b485838SMatthew Dillon 			"  ,\"builders\":[\n"
346*8b485838SMatthew Dillon 		);
347*8b485838SMatthew Dillon 		for (i = 0; i < MaxWorkers; ++i) {
348*8b485838SMatthew Dillon 			if (HtmlSlots[i]) {
349*8b485838SMatthew Dillon 				if (i)
350*8b485838SMatthew Dillon 					fprintf(fp, ",");
351*8b485838SMatthew Dillon 				fwrite(HtmlSlots[i], 1,
352*8b485838SMatthew Dillon 				       strlen(HtmlSlots[i]), fp);
353*8b485838SMatthew Dillon 			} else {
354*8b485838SMatthew Dillon 				fprintf(fp,
355*8b485838SMatthew Dillon 					"   %s{\n"
356*8b485838SMatthew Dillon 					"     \"ID\":\"%02d\"\n"
357*8b485838SMatthew Dillon 					"     ,\"elapsed\":\"Shutdown\"\n"
358*8b485838SMatthew Dillon 					"     ,\"phase\":\"\"\n"
359*8b485838SMatthew Dillon 					"     ,\"origin\":\"\"\n"
360*8b485838SMatthew Dillon 					"     ,\"lines\":\"\"\n"
361*8b485838SMatthew Dillon 					"    }\n",
362*8b485838SMatthew Dillon 					(i ? "," : ""),
363*8b485838SMatthew Dillon 					i
364*8b485838SMatthew Dillon 				);
365*8b485838SMatthew Dillon 			}
366*8b485838SMatthew Dillon 		}
367*8b485838SMatthew Dillon 		fprintf(fp,
368*8b485838SMatthew Dillon 			"  ]\n"
369*8b485838SMatthew Dillon 			"}\n");
370*8b485838SMatthew Dillon 		fflush(fp);
371*8b485838SMatthew Dillon 		fclose(fp);
372*8b485838SMatthew Dillon 	}
373*8b485838SMatthew Dillon 	rename(path, dst);
374*8b485838SMatthew Dillon 	free(path);
375*8b485838SMatthew Dillon 	free(dst);
376ea37671dSMatthew Dillon }
377ea37671dSMatthew Dillon 
378ea37671dSMatthew Dillon static void
379ea37671dSMatthew Dillon HtmlUpdateLogs(void)
380ea37671dSMatthew Dillon {
381ea37671dSMatthew Dillon }
382ea37671dSMatthew Dillon 
383ea37671dSMatthew Dillon static void
384*8b485838SMatthew Dillon HtmlUpdateCompletion(worker_t *work, int dlogid, pkg_t *pkg, const char *reason)
385*8b485838SMatthew Dillon {
386*8b485838SMatthew Dillon 	FILE *fp;
387*8b485838SMatthew Dillon 	char *path;
388*8b485838SMatthew Dillon 	char elapsed_buf[64];
389*8b485838SMatthew Dillon 	struct stat st;
390*8b485838SMatthew Dillon 	time_t t;
391*8b485838SMatthew Dillon 	int s, m, h;
392*8b485838SMatthew Dillon 	int slot;
393*8b485838SMatthew Dillon 	const char *result;
394*8b485838SMatthew Dillon 
395*8b485838SMatthew Dillon 	if (work) {
396*8b485838SMatthew Dillon 		t = time(NULL) - work->start_time;
397*8b485838SMatthew Dillon 		s = t % 60;
398*8b485838SMatthew Dillon 		m = t / 60 % 60;
399*8b485838SMatthew Dillon 		h = t / 60 / 60;
400*8b485838SMatthew Dillon 		SNPRINTF(elapsed_buf, "%02d:%02d:%02d", h, m, s);
401*8b485838SMatthew Dillon 		slot = work->index;
402*8b485838SMatthew Dillon 	} else {
403*8b485838SMatthew Dillon 		slot = -1;
404*8b485838SMatthew Dillon 		elapsed_buf[0] = 0;
405*8b485838SMatthew Dillon 	}
406*8b485838SMatthew Dillon 
407*8b485838SMatthew Dillon 	switch(dlogid) {
408*8b485838SMatthew Dillon 	case DLOG_SUCC:
409*8b485838SMatthew Dillon 		result = "built";
410*8b485838SMatthew Dillon 		break;
411*8b485838SMatthew Dillon 	case DLOG_FAIL:
412*8b485838SMatthew Dillon 		result = "failed";
413*8b485838SMatthew Dillon 		break;
414*8b485838SMatthew Dillon 	case DLOG_IGN:
415*8b485838SMatthew Dillon 		result = "ignored";
416*8b485838SMatthew Dillon 		break;
417*8b485838SMatthew Dillon 	case DLOG_SKIP:
418*8b485838SMatthew Dillon 		result = "skipped";
419*8b485838SMatthew Dillon 		break;
420*8b485838SMatthew Dillon 	default:
421*8b485838SMatthew Dillon 		result = "Unknown";
422*8b485838SMatthew Dillon 		break;
423*8b485838SMatthew Dillon 	}
424*8b485838SMatthew Dillon 
425*8b485838SMatthew Dillon 	t = time(NULL) - HtmlStart;
426*8b485838SMatthew Dillon 	s = t % 60;
427*8b485838SMatthew Dillon 	m = t / 60 % 60;
428*8b485838SMatthew Dillon 	h = t / 60 / 60;
429*8b485838SMatthew Dillon 
430*8b485838SMatthew Dillon 	/*
431*8b485838SMatthew Dillon 	 * Cycle history file as appropriate, includes initial file handling.
432*8b485838SMatthew Dillon 	 */
433*8b485838SMatthew Dillon 	if (HistNum == 0)
434*8b485838SMatthew Dillon 		HistNum = 1;
435*8b485838SMatthew Dillon 	asprintf(&path, "%s/%02d_history.json", ReportPath, HistNum);
436*8b485838SMatthew Dillon 	if (stat(path, &st) < 0) {
437*8b485838SMatthew Dillon 		fp = fopen(path, "we");
438*8b485838SMatthew Dillon 	} else if (st.st_size > 50000) {
439*8b485838SMatthew Dillon 		++HistNum;
440*8b485838SMatthew Dillon 		free(path);
441*8b485838SMatthew Dillon 		asprintf(&path, "%s/%02d_history.json", ReportPath, HistNum);
442*8b485838SMatthew Dillon 		fp = fopen(path, "we");
443*8b485838SMatthew Dillon 	} else {
444*8b485838SMatthew Dillon 		fp = fopen(path, "r+e");
445*8b485838SMatthew Dillon 		fseek(fp, 0, SEEK_END);
446*8b485838SMatthew Dillon 	}
447*8b485838SMatthew Dillon 
448*8b485838SMatthew Dillon 	if (fp) {
449*8b485838SMatthew Dillon 		if (ftell(fp) == 0) {
450*8b485838SMatthew Dillon 			fprintf(fp, "[\n");
451*8b485838SMatthew Dillon 		} else {
452*8b485838SMatthew Dillon 			fseek(fp, -2, SEEK_END);
453*8b485838SMatthew Dillon 		}
454*8b485838SMatthew Dillon 		fprintf(fp,
455*8b485838SMatthew Dillon 			"  %s{\n"
456*8b485838SMatthew Dillon 			"   \"entry\":%d\n"
457*8b485838SMatthew Dillon 			"   ,\"elapsed\":\"%02d:%02d:%02d\"\n"
458*8b485838SMatthew Dillon 			"   ,\"ID\":\"%02d\"\n"
459*8b485838SMatthew Dillon 			"   ,\"result\":\"%s\"\n"
460*8b485838SMatthew Dillon 			"   ,\"origin\":\"%s\"\n"
461*8b485838SMatthew Dillon 			"   ,\"info\":\"%s%s%s\"\n"
462*8b485838SMatthew Dillon 			"   ,\"duration\":\"%s\"\n"
463*8b485838SMatthew Dillon 			"  }\n"
464*8b485838SMatthew Dillon 			"]\n",
465*8b485838SMatthew Dillon 			((ftell(fp) > 10) ? "," : ""),
466*8b485838SMatthew Dillon 			EntryNum,
467*8b485838SMatthew Dillon 			h, m, s,
468*8b485838SMatthew Dillon 			slot,
469*8b485838SMatthew Dillon 			result,
470*8b485838SMatthew Dillon 			pkg->portdir,
471*8b485838SMatthew Dillon 			(work ? getphasestr(work->phase) : ""),
472*8b485838SMatthew Dillon 			(work ? ":" : ""),
473*8b485838SMatthew Dillon 			dequote(reason),
474*8b485838SMatthew Dillon 			elapsed_buf
475*8b485838SMatthew Dillon 		);
476*8b485838SMatthew Dillon 		++EntryNum;
477*8b485838SMatthew Dillon 		fclose(fp);
478*8b485838SMatthew Dillon 
479*8b485838SMatthew Dillon 	}
480*8b485838SMatthew Dillon 	free(path);
481*8b485838SMatthew Dillon }
482*8b485838SMatthew Dillon 
483*8b485838SMatthew Dillon static void
484ea37671dSMatthew Dillon HtmlSync(void)
485ea37671dSMatthew Dillon {
486ea37671dSMatthew Dillon }
487ea37671dSMatthew Dillon 
488ea37671dSMatthew Dillon runstats_t HtmlRunStats = {
489ea37671dSMatthew Dillon 	.init = HtmlInit,
490ea37671dSMatthew Dillon 	.done = HtmlDone,
491ea37671dSMatthew Dillon 	.reset = HtmlReset,
492ea37671dSMatthew Dillon 	.update = HtmlUpdate,
493ea37671dSMatthew Dillon 	.updateTop = HtmlUpdateTop,
494ea37671dSMatthew Dillon 	.updateLogs = HtmlUpdateLogs,
495*8b485838SMatthew Dillon 	.updateCompletion = HtmlUpdateCompletion,
496ea37671dSMatthew Dillon 	.sync = HtmlSync
497ea37671dSMatthew Dillon };
498