1 /* 2 * Copyright (c) 2019 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * This code uses concepts and configuration based on 'synth', by 8 * John R. Marino <draco@marino.st>, which was written in ada. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 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 19 * distribution. 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. 23 * 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 35 * SUCH DAMAGE. 36 */ 37 #include "dsynth.h" 38 #include <sys/mman.h> 39 40 typedef struct MonitorFile { 41 char version[32]; 42 int32_t maxworkers; 43 int32_t maxjobs; 44 topinfo_t info; 45 46 char packagespath[128]; 47 char repositorypath[128]; 48 char optionspath[128]; 49 char distfilespath[128]; 50 char buildbase[128]; 51 char logspath[128]; 52 char systempath[128]; 53 char profile[64]; 54 55 struct { 56 worker_t work; 57 char portdir[128]; 58 } slots[MAXWORKERS]; 59 } monitor_file_t; 60 61 #define SNPRINTF(buf, ctl, ...) \ 62 snprintf((buf), sizeof(buf), ctl, ## __VA_ARGS__) 63 64 static int StatsFd = -1; 65 static int LockFd = -1; 66 static monitor_file_t *RStats; 67 68 static void 69 MonitorInit(void) 70 { 71 struct stat st; 72 73 mkdir(StatsBase, 0755); 74 if (stat(StatsBase, &st) < 0) 75 dfatal_errno("Cannot create %s"); 76 if (st.st_uid && st.st_uid != getuid()) 77 dfatal("%s not owned by current uid", StatsBase); 78 StatsFd = open(StatsFilePath, O_RDWR|O_CREAT|O_CLOEXEC, 0644); 79 if (StatsFd < 0) 80 dfatal_errno("Cannot create %s", StatsFilePath); 81 ftruncate(StatsFd, sizeof(monitor_file_t)); 82 83 LockFd = open(StatsLockPath, O_RDWR|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 84 0666); 85 if (LockFd < 0) 86 dfatal_errno("Cannot create %s", StatsLockPath); 87 88 RStats = mmap(NULL, sizeof(monitor_file_t), 89 PROT_READ|PROT_WRITE, MAP_SHARED, 90 StatsFd, 0); 91 dassert_errno(RStats != MAP_FAILED, "Unable to mmap %s", StatsFilePath); 92 } 93 94 static void 95 MonitorDone(void) 96 { 97 if (RStats) { 98 if (RStats != MAP_FAILED) 99 munmap(RStats, sizeof(monitor_file_t)); 100 RStats = NULL; 101 } 102 if (StatsFd >= 0) { 103 close(StatsFd); 104 StatsFd = -1; 105 } 106 if (LockFd >= 0) { 107 flock(LockFd, LOCK_UN); 108 close(LockFd); 109 LockFd = -1; 110 } 111 } 112 113 static void 114 MonitorReset(void) 115 { 116 monitor_file_t *rs; 117 118 rs = RStats; 119 flock(LockFd, LOCK_UN); /* may fail */ 120 121 bzero(rs, sizeof(*rs)); 122 123 SNPRINTF(rs->version, "%s", DSYNTH_VERSION); 124 rs->maxworkers = MaxWorkers; 125 rs->maxjobs = MaxJobs; 126 127 SNPRINTF(rs->packagespath, "%s", PackagesPath); 128 SNPRINTF(rs->repositorypath, "%s", RepositoryPath); 129 SNPRINTF(rs->optionspath, "%s", OptionsPath); 130 SNPRINTF(rs->distfilespath, "%s", DistFilesPath); 131 SNPRINTF(rs->buildbase, "%s", BuildBase); 132 SNPRINTF(rs->logspath, "%s", LogsPath); 133 SNPRINTF(rs->systempath, "%s", SystemPath); 134 SNPRINTF(rs->profile, "%s", Profile); 135 136 flock(LockFd, LOCK_EX); /* ready */ 137 } 138 139 static void 140 MonitorUpdate(worker_t *work, const char *dummy __unused) 141 { 142 monitor_file_t *rs; 143 worker_t copy; 144 int i = work->index; 145 146 rs = RStats; 147 148 copy = *work; 149 copy.pkg = NULL; /* safety */ 150 if (work->pkg) 151 SNPRINTF(rs->slots[i].portdir, "%s", work->pkg->portdir); 152 else 153 SNPRINTF(rs->slots[i].portdir, "%s", ""); 154 rs->slots[i].work = copy; 155 } 156 157 static void 158 MonitorUpdateTop(topinfo_t *info) 159 { 160 monitor_file_t *rs; 161 162 rs = RStats; 163 164 rs->info = *info; 165 } 166 167 static void 168 MonitorUpdateLogs(void) 169 { 170 } 171 172 static void 173 MonitorSync(void) 174 { 175 } 176 177 runstats_t MonitorRunStats = { 178 .init = MonitorInit, 179 .done = MonitorDone, 180 .reset = MonitorReset, 181 .update = MonitorUpdate, 182 .updateTop = MonitorUpdateTop, 183 .updateLogs = MonitorUpdateLogs, 184 .sync = MonitorSync 185 }; 186 187 /**************************************************************************** 188 * MONITOR DIRECTIVE FRONTEND * 189 * (independently executed dsynth app) 190 **************************************************************************** 191 * 192 * Access the monitor file and display available information 193 * 194 * lkfile may be NULL 195 */ 196 void 197 MonitorDirective(const char *datfile, const char *lkfile) 198 { 199 monitor_file_t *rs; 200 monitor_file_t copy; 201 int i; 202 int running; 203 204 bzero(©, sizeof(copy)); 205 206 StatsFd = open(datfile, O_RDONLY); 207 if (StatsFd < 0) { 208 printf("dsynth is not running\n"); 209 exit(1); 210 } 211 if (lkfile) { 212 LockFd = open(lkfile, O_RDWR); 213 if (LockFd < 0) { 214 printf("dsynth is not running\n"); 215 exit(1); 216 } 217 } 218 219 rs = mmap(NULL, sizeof(monitor_file_t), 220 PROT_READ, MAP_SHARED, 221 StatsFd, 0); 222 dassert_errno(rs != MAP_FAILED, "Cannot mmap \"%s\"", datfile); 223 224 /* 225 * Expect flock() to fail, if it doesn't then dsynth i 226 * not running. 227 */ 228 if (flock(LockFd, LOCK_SH|LOCK_NB) == 0) { 229 flock(LockFd, LOCK_UN); 230 printf("dsynth is not running\n"); 231 exit(1); 232 } 233 234 /* 235 * Display setup 236 */ 237 NCursesRunStats.init(); 238 NCursesRunStats.reset(); 239 240 /* 241 * Run until done. When we detect completion we still give the 242 * body one more chance at the logs so the final log lines are 243 * properly displayed before exiting. 244 */ 245 running = 1; 246 while (running) { 247 /* 248 * Expect flock() to fail, if it doesn't then dsynth i 249 * not running. 250 */ 251 if (LockFd >= 0 && flock(LockFd, LOCK_SH|LOCK_NB) == 0) { 252 flock(LockFd, LOCK_UN); 253 running = 0; 254 } 255 if (rs->version[0]) 256 NCursesRunStats.updateTop(&rs->info); 257 for (i = 0; rs->version[0] && i < rs->maxworkers; ++i) { 258 /* 259 if (bcmp(©.slots[i].work, 260 &rs->slots[i].work, 261 sizeof(copy.slots[i].work)) != 0) { 262 */ 263 { 264 /* 265 * A slot changed 266 */ 267 copy.slots[i].work = rs->slots[i].work; 268 NCursesRunStats.update(©.slots[i].work, 269 rs->slots[i].portdir); 270 } 271 } 272 if (LockFd >= 0) 273 NCursesRunStats.updateLogs(); 274 NCursesRunStats.sync(); 275 usleep(500000); 276 } 277 NCursesRunStats.done(); 278 printf("dsynth exited\n"); 279 } 280