xref: /netbsd-src/external/bsd/pkg_install/dist/lib/fexec.c (revision 1b2611421270f128b12bc59de682905e00d5645e)
1 /*	$NetBSD: fexec.c,v 1.4 2024/06/11 09:26:57 wiz Exp $	*/
2 
3 /*-
4  * Copyright (c) 2003 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Matthias Scheler.
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  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 
33 #if HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 #include <nbcompat.h>
37 #if HAVE_SYS_CDEFS_H
38 #include <sys/cdefs.h>
39 #endif
40 #if HAVE_SYS_TYPES_H
41 #include <sys/types.h>
42 #endif
43 #if HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46 #if HAVE_SYS_WAIT_H
47 #include <sys/wait.h>
48 #endif
49 
50 #if HAVE_ERR_H
51 #include <err.h>
52 #endif
53 #if HAVE_ERRNO_H
54 #include <errno.h>
55 #endif
56 #if HAVE_FCNTL_H
57 #include <fcntl.h>
58 #endif
59 #if HAVE_STDARG_H
60 #include <stdarg.h>
61 #endif
62 #if HAVE_STDLIB_H
63 #include <stdlib.h>
64 #endif
65 #if HAVE_UNISTD_H
66 #include <unistd.h>
67 #endif
68 
69 #include "lib.h"
70 
71 /*
72  * Newer macOS releases are not able to correctly handle vfork() when the
73  * underlying file is changed or removed, as is the case when upgrading
74  * pkg_install itself.  The manual pages suggest using posix_spawn()
75  * instead, which seems to work ok.
76  */
77 #if defined(__APPLE__) && \
78 	((__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__-0) >= 1050)
79 #define FEXEC_USE_POSIX_SPAWN	1
80 #else
81 #define FEXEC_USE_POSIX_SPAWN	0
82 #endif
83 
84 #if FEXEC_USE_POSIX_SPAWN
85 #include <spawn.h>
86 extern char **environ;
87 
88 #ifndef O_CLOEXEC
89 #define O_CLOEXEC	0
90 #endif
91 
92 #ifndef O_DIRECTORY
93 #define O_DIRECTORY	0
94 #endif
95 #endif
96 
97 __RCSID("$NetBSD: fexec.c,v 1.4 2024/06/11 09:26:57 wiz Exp $");
98 
99 static int	vfcexec(const char *, int, const char *, va_list);
100 
101 /*
102  * fork, then change current working directory to path and
103  * execute the command and arguments in the argv array.
104  * wait for the command to finish, then return the exit status.
105  *
106  * macOS uses posix_spawn() instead due to reasons explained above.
107  */
108 int
pfcexec(const char * path,const char * file,const char ** argv)109 pfcexec(const char *path, const char *file, const char **argv)
110 {
111 	pid_t			child;
112 	int			status;
113 
114 #if FEXEC_USE_POSIX_SPAWN
115 	int prevcwd;
116 
117 	if ((prevcwd = open(".", O_RDONLY|O_CLOEXEC|O_DIRECTORY)) < 0) {
118 		warn("open prevcwd failed");
119 		return -1;
120 	}
121 
122 	if ((path != NULL) && (chdir(path) < 0)) {
123 		warn("chdir %s failed", path);
124 		return -1;
125 	}
126 
127 	if (posix_spawn(&child, file, NULL, NULL, (char **)argv, environ) < 0) {
128 		warn("posix_spawn failed");
129 		return -1;
130 	}
131 
132 	if (fchdir(prevcwd) < 0) {
133 		warn("fchdir prevcwd failed");
134 		return -1;
135 	}
136 
137 	(void)close(prevcwd);
138 #else
139 	child = vfork();
140 	switch (child) {
141 	case 0:
142 		if ((path != NULL) && (chdir(path) < 0))
143 			_exit(127);
144 
145 		(void)execvp(file, __UNCONST(argv));
146 		_exit(127);
147 		/* NOTREACHED */
148 	case -1:
149 		return -1;
150 	}
151 #endif
152 
153 	while (waitpid(child, &status, 0) < 0) {
154 		if (errno != EINTR)
155 			return -1;
156 	}
157 
158 	if (!WIFEXITED(status))
159 		return -1;
160 
161 	return WEXITSTATUS(status);
162 }
163 
164 static int
vfcexec(const char * path,int skipempty,const char * arg,va_list ap)165 vfcexec(const char *path, int skipempty, const char *arg, va_list ap)
166 {
167 	const char **argv;
168 	size_t argv_size, argc;
169 	int retval;
170 
171 	argv_size = 16;
172 	argv = xcalloc(argv_size, sizeof(*argv));
173 
174 	argv[0] = arg;
175 	argc = 1;
176 
177 	do {
178 		if (argc == argv_size) {
179 			argv_size *= 2;
180 			argv = xrealloc(argv, argv_size * sizeof(*argv));
181 		}
182 		arg = va_arg(ap, const char *);
183 		if (skipempty && arg && strlen(arg) == 0)
184 		    continue;
185 		argv[argc++] = arg;
186 	} while (arg != NULL);
187 
188 	retval = pfcexec(path, argv[0], argv);
189 	free(argv);
190 	return retval;
191 }
192 
193 int
fexec(const char * arg,...)194 fexec(const char *arg, ...)
195 {
196 	va_list	ap;
197 	int	result;
198 
199 	va_start(ap, arg);
200 	result = vfcexec(NULL, 0, arg, ap);
201 	va_end(ap);
202 
203 	return result;
204 }
205 
206 int
fexec_skipempty(const char * arg,...)207 fexec_skipempty(const char *arg, ...)
208 {
209 	va_list	ap;
210 	int	result;
211 
212 	va_start(ap, arg);
213 	result = vfcexec(NULL, 1, arg, ap);
214 	va_end(ap);
215 
216 	return result;
217 }
218 
219 int
fcexec(const char * path,const char * arg,...)220 fcexec(const char *path, const char *arg, ...)
221 {
222 	va_list	ap;
223 	int	result;
224 
225 	va_start(ap, arg);
226 	result = vfcexec(path, 0, arg, ap);
227 	va_end(ap);
228 
229 	return result;
230 }
231