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
MonitorInit(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
MonitorDone(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
MonitorReset(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
MonitorUpdate(worker_t * work,const char * dummy __unused)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
MonitorUpdateTop(topinfo_t * info)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
MonitorUpdateLogs(void)168 MonitorUpdateLogs(void)
169 {
170 }
171
172 static void
MonitorSync(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
MonitorDirective(const char * datfile,const char * lkfile)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