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