xref: /dragonfly/usr.bin/dsynth/monitor.c (revision 6a3cbbc2)
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(&copy, 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(&copy.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(&copy.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