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