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