Home | History | Annotate | Line # | Download | only in lib
      1 /*	$NetBSD: fexec.c,v 1.5 2025/05/09 13:26:38 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.5 2025/05/09 13:26:38 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
    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 err, prevcwd;
    116 	posix_spawn_file_actions_t act;
    117 
    118 	if ((prevcwd = open(".", O_RDONLY|O_CLOEXEC|O_DIRECTORY)) < 0) {
    119 		warn("open prevcwd failed");
    120 		return -1;
    121 	}
    122 
    123 	if ((path != NULL) && (chdir(path) < 0)) {
    124 		warn("chdir %s failed", path);
    125 		return -1;
    126 	}
    127 
    128 	if ((err = posix_spawn_file_actions_init(&act)) != 0) {
    129 		errno = err;
    130 		warn("failed to init posix_spawn");
    131 		return -1;
    132 	}
    133 
    134 	if (HideStdout) {
    135 		err = posix_spawn_file_actions_addopen(&act, STDOUT_FILENO,
    136 		    "/dev/null", O_WRONLY, 0);
    137 		if (err != 0) {
    138 			errno = err;
    139 			warn("failed to add posix_spawn action");
    140 			return -1;
    141 		}
    142 	}
    143 
    144 	if (posix_spawn(&child, file, &act, NULL, (char **)argv, environ) < 0) {
    145 		warn("posix_spawn failed");
    146 		return -1;
    147 	}
    148 
    149 	if (fchdir(prevcwd) < 0) {
    150 		warn("fchdir prevcwd failed");
    151 		return -1;
    152 	}
    153 
    154 	(void)close(prevcwd);
    155 #else
    156 	int devnull;
    157 	child = vfork();
    158 	switch (child) {
    159 	case 0:
    160 		devnull = open("/dev/null", O_WRONLY);
    161 
    162 		if ((path != NULL) && (chdir(path) < 0))
    163 			_exit(127);
    164 
    165 		if (HideStdout)
    166 			(void)dup2(devnull, STDOUT_FILENO);
    167 
    168 		(void)execvp(file, __UNCONST(argv));
    169 		_exit(127);
    170 		/* NOTREACHED */
    171 	case -1:
    172 		return -1;
    173 	}
    174 #endif
    175 
    176 	while (waitpid(child, &status, 0) < 0) {
    177 		if (errno != EINTR)
    178 			return -1;
    179 	}
    180 
    181 	if (!WIFEXITED(status))
    182 		return -1;
    183 
    184 	return WEXITSTATUS(status);
    185 }
    186 
    187 static int
    188 vfcexec(const char *path, int skipempty, const char *arg, va_list ap)
    189 {
    190 	const char **argv;
    191 	size_t argv_size, argc;
    192 	int retval;
    193 
    194 	argv_size = 16;
    195 	argv = xcalloc(argv_size, sizeof(*argv));
    196 
    197 	argv[0] = arg;
    198 	argc = 1;
    199 
    200 	do {
    201 		if (argc == argv_size) {
    202 			argv_size *= 2;
    203 			argv = xrealloc(argv, argv_size * sizeof(*argv));
    204 		}
    205 		arg = va_arg(ap, const char *);
    206 		if (skipempty && arg && strlen(arg) == 0)
    207 		    continue;
    208 		argv[argc++] = arg;
    209 	} while (arg != NULL);
    210 
    211 	retval = pfcexec(path, argv[0], argv);
    212 	free(argv);
    213 	return retval;
    214 }
    215 
    216 int
    217 fexec(const char *arg, ...)
    218 {
    219 	va_list	ap;
    220 	int	result;
    221 
    222 	va_start(ap, arg);
    223 	result = vfcexec(NULL, 0, arg, ap);
    224 	va_end(ap);
    225 
    226 	return result;
    227 }
    228 
    229 int
    230 fexec_skipempty(const char *arg, ...)
    231 {
    232 	va_list	ap;
    233 	int	result;
    234 
    235 	va_start(ap, arg);
    236 	result = vfcexec(NULL, 1, arg, ap);
    237 	va_end(ap);
    238 
    239 	return result;
    240 }
    241 
    242 int
    243 fcexec(const char *path, const char *arg, ...)
    244 {
    245 	va_list	ap;
    246 	int	result;
    247 
    248 	va_start(ap, arg);
    249 	result = vfcexec(path, 0, arg, ap);
    250 	va_end(ap);
    251 
    252 	return result;
    253 }
    254