xref: /dragonfly/usr.bin/dsynth/subs.c (revision 086b156c)
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 
38 #include "dsynth.h"
39 
40 buildenv_t *BuildEnv;
41 static buildenv_t **BuildEnvTail = &BuildEnv;
42 
43 extern char **environ;
44 
45 __dead2 void
46 _dfatal(const char *file __unused, int line __unused, const char *func,
47 	int do_errno, const char *ctl, ...)
48 {
49 	va_list va;
50 
51 	fprintf(stderr, "%s: ", func);
52 	va_start(va, ctl);
53 	vfprintf(stderr, ctl, va);
54 	va_end(va);
55 	if (do_errno & 1)
56 		fprintf(stderr, ": %s", strerror(errno));
57 	fprintf(stderr, "\n");
58 	fflush(stderr);
59 
60 	if (do_errno & 2)
61 		kill(getpid(), SIGQUIT);
62 	exit(1);
63 }
64 
65 void
66 _ddprintf(int tab, const char *ctl, ...)
67 {
68 	va_list va;
69 
70 	if (tab)
71 		printf("%*.*s", tab, tab, "");
72 	va_start(va, ctl);
73 	vfprintf(stdout, ctl, va);
74 	va_end(va);
75 }
76 
77 char *
78 strdup_or_null(char *str)
79 {
80 	if (str && str[0])
81 		return(strdup(str));
82 	return NULL;
83 }
84 
85 static const char *DLogNames[] = {
86 	"00_last_results.log",
87 	"01_success_list.log",
88 	"02_failure_list.log",
89 	"03_ignored_list.log",
90 	"04_skipped_list.log",
91 	"05_abnormal_command_output.log",
92 	"06_obsolete_packages.log"
93 };
94 
95 static int DLogFd[DLOG_COUNT];
96 static pthread_mutex_t DLogFdMutex;
97 
98 #define arysize(ary)	(sizeof((ary)) / sizeof((ary)[0]))
99 
100 static int
101 dlogfd(int which, int modes)
102 {
103 	char *path;
104 	int fd;
105 
106 	which &= DLOG_MASK;
107 	if ((fd = DLogFd[which]) > 0)
108 		return fd;
109 	pthread_mutex_lock(&DLogFdMutex);
110 	if ((fd = DLogFd[which]) <= 0) {
111 		asprintf(&path, "%s/%s", LogsPath, DLogNames[which]);
112 		fd = open(path, modes, 0666);
113 		DLogFd[which] = fd;
114 		free(path);
115 	}
116 	pthread_mutex_unlock(&DLogFdMutex);
117 
118 	return fd;
119 }
120 
121 
122 void
123 dlogreset(void)
124 {
125 	int i;
126 
127 	ddassert(DLOG_COUNT == arysize(DLogNames));
128 	for (i = 0; i < DLOG_COUNT; ++i) {
129 		if (DLogFd[i] > 0) {
130 			close(DLogFd[i]);
131 			DLogFd[i] = -1;
132 		}
133 		(void)dlogfd(i, O_RDWR|O_CREAT|O_TRUNC|O_APPEND);
134 	}
135 }
136 
137 void
138 _dlog(int which, const char *ctl, ...)
139 {
140 	va_list va;
141 	char *buf;
142 	char *ptr;
143 	size_t len;
144 	int fd;
145 	int filter;
146 
147 	filter = which;
148 	which &= DLOG_MASK;
149 
150 	ddassert((uint)which < DLOG_COUNT);
151 	va_start(va, ctl);
152 	vasprintf(&buf, ctl, va);
153 	va_end(va);
154 	len = strlen(buf);
155 
156 	/*
157 	 * The special sequence ## right-justfies the text after the ##.
158 	 *
159 	 * NOTE: Alignment test includes \n so use 80 instead of 79 to
160 	 *	 leave one char unused on a 80-column terminal.
161 	 */
162 	if ((ptr = strstr(buf, "##")) != NULL) {
163 		size_t l2;
164 		size_t l1;
165 		char *b2;
166 		int spc;
167 
168 		l1 = (int)(ptr - buf);
169 		l2 = len - l1 - 2;
170 		if (l1 <= 80 - l2) {
171 			spc = 80 - l1 - l2;
172 			buf[l1] = 0;
173 			asprintf(&b2, "%s%*.*s%s",
174 				 buf, spc, spc, "", ptr + 2);
175 		} else {
176 			buf[l1] = 0;
177 			asprintf(&b2, "%s%s", buf, ptr + 2);
178 		}
179 		len = strlen(b2);
180 		free(buf);
181 		buf = b2;
182 	}
183 
184 	/*
185 	 * All logs also go to log 00.
186 	 */
187 	if (which != DLOG_ALL) {
188 		fd = dlogfd(DLOG_ALL, O_RDWR|O_CREAT|O_APPEND);
189 		if (fd > 0)
190 			write(fd, buf, len);
191 	}
192 
193 	/*
194 	 * Nominal log target
195 	 */
196 	fd = dlogfd(which, O_RDWR|O_CREAT|O_APPEND);
197 	write(fd, buf, len);
198 
199 	/*
200 	 * If ncurses is not being used, all log output also goes
201 	 * to stdout, unless filtered.
202 	 */
203 	if (UseNCurses == 0 && (filter & DLOG_FILTER) == 0) {
204 		if (ColorOpt) {
205 			if (filter & DLOG_GRN)
206 				write(1, "\x1b[0;32m", 7);
207 			if (filter & DLOG_RED)
208 				write(1, "\x1b[0;31m", 7);
209 		}
210 		write(1, buf, len);
211 		if (ColorOpt && (filter & (DLOG_GRN|DLOG_RED))) {
212 			write(1, "\x1b[0;39m", 7);
213 		}
214 	}
215 	free(buf);
216 }
217 
218 int
219 dlog00_fd(void)
220 {
221 	return(dlogfd(DLOG_ALL, O_RDWR|O_CREAT|O_APPEND));
222 }
223 
224 /*
225  * Bulk and Build environment control.  These routines are only called
226  * unthreaded or when dsynth threads are idle.
227  */
228 void
229 addbuildenv(const char *label, const char *data, int type)
230 {
231 	buildenv_t *env;
232 
233 	env = calloc(1, sizeof(*env));
234 	env->a1 = strdup(label);
235 	env->a2 = strdup(data);
236 	env->label = env->a1;
237 	env->data = env->a2;
238 	env->type = type;
239 	*BuildEnvTail = env;
240 	BuildEnvTail = &env->next;
241 }
242 
243 void
244 delbuildenv(const char *label)
245 {
246 	buildenv_t **envp;
247 	buildenv_t *env;
248 
249 	envp = &BuildEnv;
250 	while ((env = *envp) != NULL) {
251 		if (strcmp(env->label, label) == 0) {
252 			*envp = env->next;
253 			if (env->a1)
254 				free(env->a1);
255 			if (env->a2)
256 				free(env->a2);
257 			free(env);
258 		} else {
259 			envp = &env->next;
260 		}
261 	}
262 	BuildEnvTail = envp;
263 }
264 
265 void
266 freestrp(char **strp)
267 {
268 	if (*strp) {
269 		free(*strp);
270 		*strp = NULL;
271 	}
272 }
273 
274 void
275 dupstrp(char **strp)
276 {
277 	if (*strp)
278 		*strp = strdup(*strp);
279 }
280 
281 int
282 ipcreadmsg(int fd, wmsg_t *msg)
283 {
284 	size_t res;
285 	ssize_t r;
286 	char *ptr;
287 
288 	res = sizeof(*msg);
289 	ptr = (char *)(void *)msg;
290 	while (res) {
291 		r = read(fd, ptr, res);
292 		if (r <= 0) {
293 			if (errno == EINTR)
294 				continue;
295 			return -1;
296 		}
297 		res -= (size_t)r;
298 		ptr += r;
299 	}
300 	return 0;
301 }
302 
303 int
304 ipcwritemsg(int fd, wmsg_t *msg)
305 {
306 	size_t res;
307 	ssize_t r;
308 	char *ptr;
309 
310 	res = sizeof(*msg);
311 	ptr = (char *)(void *)msg;
312 	while (res) {
313 		r = write(fd, ptr, res);
314 		if (r < 0) {
315 			if (errno == EINTR)
316 				continue;
317 			return -1;
318 		}
319 		res -= (size_t)r;
320 		ptr += r;
321 	}
322 	return 0;
323 }
324 
325 int
326 askyn(const char *ctl, ...)
327 {
328 	va_list va;
329 	char buf[256];
330 	int res = 0;
331 
332 	if (YesOpt)
333 		return 1;
334 
335 	for (;;) {
336 		va_start(va, ctl);
337 		vprintf(ctl, va);
338 		va_end(va);
339 		fflush(stdout);
340 		if (fgets(buf, sizeof(buf), stdin) == NULL)
341 			break;
342 		if (buf[0] == 'y' || buf[0] == 'Y') {
343 			res = 1;
344 			break;
345 		}
346 		if (buf[0] == 'n' || buf[0] == 'N') {
347 			res = 0;
348 			break;
349 		}
350 		printf("Please type y/n\n");
351 	}
352 	return res;
353 }
354 
355 /*
356  * Get swap% used 0.0-1.0.
357  *
358  * NOTE: This routine is intended to return quickly.
359  *
360  * NOTE: swap_cache (caching for hard drives) is persistent and should
361  *	 not be counted for our purposes.
362  */
363 double
364 getswappct(int *noswapp)
365 {
366 	long swap_size = 0;
367 	long swap_anon = 0;
368 	long swap_cache __unused = 0;
369 	size_t len;
370 	double dswap;
371 
372 	len = sizeof(swap_size);
373 	sysctlbyname("vm.swap_size", &swap_size, &len, NULL, 0);
374 	len = sizeof(swap_size);
375 	sysctlbyname("vm.swap_anon_use", &swap_anon, &len, NULL, 0);
376 	len = sizeof(swap_size);
377 	sysctlbyname("vm.swap_cache_use", &swap_cache, &len, NULL, 0);
378 	if (swap_size) {
379 		dswap = (double)(swap_anon /*+swap_cache*/) / (double)swap_size;
380 		*noswapp = 0;
381 	} else {
382 		dswap = 0.0;
383 		*noswapp = 1;
384 	}
385 	return dswap;
386 }
387 
388 /*
389  * dexec_open()/fgets/dexec_close()
390  *
391  * Similar to popen() but directly exec()s the argument list (cav[0] must
392  * be an absolute path).
393  *
394  * If xenv is non-NULL its an array of local buildenv_t's to be used.
395  * The array is terminated with a NULL xenv->label.
396  *
397  * If with_env is non-zero the configured environment is included.
398  *
399  * If with_mvars is non-zero the make environment is passed as VAR=DATA
400  * elements on the command line.
401  */
402 FILE *
403 dexec_open(const char **cav, int cac, pid_t *pidp, buildenv_t *xenv,
404 	   int with_env, int with_mvars)
405 {
406 	buildenv_t *benv;
407 	const char **cenv;
408 	char *allocary[MAXCAC*2];
409 	int env_basei;
410 	int envi;
411 	int alloci;
412 	int nullfd;
413 	int fds[2];
414 	pid_t pid;
415 	FILE *fp;
416 
417 	env_basei = 0;
418 	while (environ[env_basei])
419 		++env_basei;
420 	cenv = calloc(env_basei + MAXCAC, sizeof(char *));
421 	env_basei = 0;
422 	for (envi = 0; envi < env_basei; ++envi)
423 		cenv[envi] = environ[envi];
424 
425 	alloci = 0;
426 	for (benv = BuildEnv; benv; benv = benv->next) {
427 		if (with_mvars &&
428 		    (benv->type & BENV_CMDMASK) == BENV_MAKECONF) {
429 			asprintf(&allocary[alloci], "%s=%s",
430 				 benv->label, benv->data);
431 			cav[cac++] = allocary[alloci];
432 			++alloci;
433 		}
434 		if (with_env &&
435 		    (benv->type & BENV_PKGLIST) &&
436 		    (benv->type & BENV_CMDMASK) == BENV_ENVIRONMENT) {
437 			asprintf(&allocary[alloci], "%s=%s",
438 				 benv->label, benv->data);
439 			cenv[envi++] = allocary[alloci];
440 			++alloci;
441 		}
442 		ddassert(cac < MAXCAC && envi - env_basei < MAXCAC);
443 	}
444 
445 	/*
446 	 * Extra environment specific to this particular dexec
447 	 */
448 	while (xenv && xenv->label) {
449 		asprintf(&allocary[alloci], "%s=%s",
450 			 xenv->label, xenv->data);
451 		cenv[envi++] = allocary[alloci];
452 		++alloci;
453 		++xenv;
454 	}
455 
456 	cav[cac] = NULL;
457 	cenv[envi] = NULL;
458 
459 	if (pipe(fds) < 0)
460 		dfatal_errno("pipe");
461 	nullfd = open("/dev/null", O_RDWR);
462 	if (nullfd < 0)
463 		dfatal_errno("open(\"/dev/null\")");
464 
465 	/*
466 	 * We have to be very careful using vfork(), do only the bare
467 	 * minimum necessary in the child to set it up and exec it.
468 	 */
469 	pid = vfork();
470 	if (pid == 0) {
471 #if 0
472 		int i;
473 		printf("%s", cav[0]);
474 		for (i = 0; cav[i]; ++i)
475 			printf(" %s", cav[i]);
476 		printf("\n");
477 		printf("ENV: ");
478 		for (i = 0; cenv[i]; ++i)
479 			printf(" %s", cenv[i]);
480 #endif
481 
482 		if (fds[1] != 1) {
483 			dup2(fds[1], 1);
484 			close(fds[1]);
485 		}
486 		close(fds[0]);		/* safety */
487 		dup2(nullfd, 0);	/* no questions! */
488 		closefrom(3);		/* be nice */
489 
490 		execve(cav[0], (void *)cav, (void *)cenv);
491 		write(2, "EXEC FAILURE\n", 13);
492 		_exit(1);
493 	}
494 	close(nullfd);
495 	close(fds[1]);
496 	if (pid < 0) {
497 		close(fds[0]);
498 		dfatal_errno("vfork failed");
499 	}
500 	fp = fdopen(fds[0], "r");
501 	*pidp = pid;
502 
503 	while (--alloci >= 0)
504 		free(allocary[alloci]);
505 	free(cenv);
506 
507 	return fp;
508 }
509 
510 int
511 dexec_close(FILE *fp, pid_t pid)
512 {
513 	pid_t rpid;
514 	int status;
515 
516 	fclose(fp);
517 	while ((rpid = waitpid(pid, &status, 0)) != pid) {
518 		if (rpid < 0) {
519 			if (errno == EINTR)
520 				continue;
521 			return 1;
522 		}
523 	}
524 	return (WEXITSTATUS(status));
525 }
526 
527 const char *
528 getphasestr(worker_phase_t phaseid)
529 {
530 	const char *phase;
531 
532 	switch(phaseid) {
533 	case PHASE_PENDING:
534 		phase = "pending";
535 		break;
536 	case PHASE_INSTALL_PKGS:
537 		phase = "install-pkgs";
538 		break;
539 	case PHASE_CHECK_SANITY:
540 		phase = "check-sanity";
541 		break;
542 	case PHASE_PKG_DEPENDS:
543 		phase = "pkg-depends";
544 		break;
545 	case PHASE_FETCH_DEPENDS:
546 		phase = "fetch-depends";
547 		break;
548 	case PHASE_FETCH:
549 		phase = "fetch";
550 		break;
551 	case PHASE_CHECKSUM:
552 		phase = "checksum";
553 		break;
554 	case PHASE_EXTRACT_DEPENDS:
555 		phase = "extract-depends";
556 		break;
557 	case PHASE_EXTRACT:
558 		phase = "extract";
559 		break;
560 	case PHASE_PATCH_DEPENDS:
561 		phase = "patch-depends";
562 		break;
563 	case PHASE_PATCH:
564 		phase = "patch";
565 		break;
566 	case PHASE_BUILD_DEPENDS:
567 		phase = "build-depends";
568 		break;
569 	case PHASE_LIB_DEPENDS:
570 		phase = "lib-depends";
571 		break;
572 	case PHASE_CONFIGURE:
573 		phase = "configure";
574 		break;
575 	case PHASE_BUILD:
576 		phase = "build";
577 		break;
578 	case PHASE_RUN_DEPENDS:
579 		phase = "run-depends";
580 		break;
581 	case PHASE_STAGE:
582 		phase = "stage";
583 		break;
584 	case PHASE_TEST:
585 		phase = "test";
586 		break;
587 	case PHASE_CHECK_PLIST:
588 		phase = "check-plist";
589 		break;
590 	case PHASE_PACKAGE:
591 		phase = "package";
592 		break;
593 	case PHASE_INSTALL_MTREE:
594 		phase = "install-mtree";
595 		break;
596 	case PHASE_INSTALL:
597 		phase = "install";
598 		break;
599 	case PHASE_DEINSTALL:
600 		phase = "deinstall";
601 		break;
602 	default:
603 		phase = "Run-Unknown";
604 		break;
605 	}
606 	return phase;
607 }
608