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 static const char *CopyFilesAry
[] = {
56 static char **HtmlSlots
;
57 static time_t HtmlStart
;
58 static time_t HtmlLast
;
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"
325 " ,\"elapsed\":\"%s\"\n"
328 " ,\"swapinfo\":\"%s\"\n"
329 " ,\"load\":\"%s\"\n"
333 HistNum
, /* kfiles */
334 info
->active
, /* active */
336 info
->total
, /* queued */
337 info
->successful
, /* built */
338 info
->failed
, /* failed */
339 info
->ignored
, /* ignored */
340 info
->skipped
, /* skipped */
341 info
->remaining
, /* remaining */
342 info
->meta
, /* meta-nodes */
343 elapsed_buf
, /* elapsed */
344 info
->pkgrate
, /* pkghour */
345 info
->pkgimpulse
, /* impulse */
346 swap_buf
, /* swapinfo */
352 for (i
= 0; i
< MaxWorkers
; ++i
) {
356 fwrite(HtmlSlots
[i
], 1,
357 strlen(HtmlSlots
[i
]), fp
);
362 " ,\"elapsed\":\"Shutdown\"\n"
364 " ,\"origin\":\"\"\n"
389 HtmlUpdateCompletion(worker_t
*work
, int dlogid
, pkg_t
*pkg
,
390 const char *reason
, const char *skipbuf
)
394 char elapsed_buf
[64];
404 else if (skipbuf
[0] == ' ')
409 t
= time(NULL
) - work
->start_time
;
413 SNPRINTF(elapsed_buf
, "%02d:%02d:%02d", h
, m
, s
);
422 if (pkg
->flags
& PKGF_DUMMY
)
430 asprintf(&mreason
, "%s:%s",
431 getphasestr(work
->phase
),
434 asprintf(&mreason
, "unknown:%s", reason
);
440 asprintf(&mreason
, "%s:|:%s", reason
, skipbuf
);
451 t
= time(NULL
) - HtmlStart
;
457 * Cycle history file as appropriate, includes initial file handling.
461 asprintf(&path
, "%s/%02d_history.json", ReportPath
, HistNum
);
462 if (stat(path
, &st
) < 0) {
463 fp
= fopen(path
, "we");
464 } else if (st
.st_size
> 50000) {
467 asprintf(&path
, "%s/%02d_history.json", ReportPath
, HistNum
);
468 fp
= fopen(path
, "we");
470 fp
= fopen(path
, "r+e");
471 fseek(fp
, 0, SEEK_END
);
475 if (ftell(fp
) == 0) {
478 fseek(fp
, -2, SEEK_END
);
483 " ,\"elapsed\":\"%02d:%02d:%02d\"\n"
484 " ,\"ID\":\"%02d\"\n"
485 " ,\"result\":\"%s\"\n"
486 " ,\"origin\":\"%s\"\n"
487 " ,\"info\":\"%s\"\n"
488 " ,\"duration\":\"%s\"\n"
491 ((ftell(fp
) > 10) ? "," : ""),
514 runstats_t HtmlRunStats
= {
518 .update
= HtmlUpdate
,
519 .updateTop
= HtmlUpdateTop
,
520 .updateLogs
= HtmlUpdateLogs
,
521 .updateCompletion
= HtmlUpdateCompletion
,