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