1*806b2ba1Smartin /* $NetBSD: posix_spawnp.c,v 1.5 2024/11/11 06:49:31 martin Exp $ */ 219f52532Smartin 319f52532Smartin /*- 419f52532Smartin * Copyright (c) 2011 The NetBSD Foundation, Inc. 519f52532Smartin * All rights reserved. 619f52532Smartin * 719f52532Smartin * This code is derived from software contributed to The NetBSD Foundation 819f52532Smartin * by Martin Husemann <martin@NetBSD.org>. 919f52532Smartin * 1019f52532Smartin * Redistribution and use in source and binary forms, with or without 1119f52532Smartin * modification, are permitted provided that the following conditions 1219f52532Smartin * are met: 1319f52532Smartin * 1. Redistributions of source code must retain the above copyright 1419f52532Smartin * notice, this list of conditions and the following disclaimer. 1519f52532Smartin * 2. Redistributions in binary form must reproduce the above copyright 1619f52532Smartin * notice, this list of conditions and the following disclaimer in the 1719f52532Smartin * documentation and/or other materials provided with the distribution. 1819f52532Smartin * 1919f52532Smartin * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2019f52532Smartin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2119f52532Smartin * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2219f52532Smartin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2319f52532Smartin * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2419f52532Smartin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2519f52532Smartin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2619f52532Smartin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2719f52532Smartin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2819f52532Smartin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2919f52532Smartin * POSSIBILITY OF SUCH DAMAGE. 3019f52532Smartin */ 3119f52532Smartin 3219f52532Smartin #include <sys/cdefs.h> 3319f52532Smartin #if defined(LIBC_SCCS) && !defined(lint) 34*806b2ba1Smartin __RCSID("$NetBSD: posix_spawnp.c,v 1.5 2024/11/11 06:49:31 martin Exp $"); 3519f52532Smartin #endif /* LIBC_SCCS and not lint */ 3619f52532Smartin 37c5b83981Skamil #include "namespace.h" 38c5b83981Skamil 39872f7801Skre #include <assert.h> 4019f52532Smartin #include <errno.h> 41872f7801Skre #include <paths.h> 4219f52532Smartin #include <spawn.h> 43872f7801Skre #include <stdio.h> 44872f7801Skre #include <stdlib.h> 45872f7801Skre #include <string.h> 46872f7801Skre #include <unistd.h> 4719f52532Smartin 4819f52532Smartin 4919f52532Smartin int posix_spawnp(pid_t * __restrict pid, const char * __restrict file, 5019f52532Smartin const posix_spawn_file_actions_t *fa, 5119f52532Smartin const posix_spawnattr_t * __restrict sa, 52136a5cd5Smartin char * const *__restrict cav, char * const *__restrict env) 5319f52532Smartin { 54872f7801Skre char fpath[FILENAME_MAX]; 55872f7801Skre const char *path, *p; 56872f7801Skre size_t lp, ln; 57872f7801Skre int err; 58872f7801Skre 59872f7801Skre _DIAGASSERT(file != NULL); 6019f52532Smartin 61*806b2ba1Smartin /* "" is not a valid filename; check this before traversing PATH. */ 62*806b2ba1Smartin if (file[0] == '\0') 63*806b2ba1Smartin return ENOENT; 64*806b2ba1Smartin 6519f52532Smartin /* 66872f7801Skre * If there is a / in the name, fall straight through to posix_spawn(). 6719f52532Smartin */ 68872f7801Skre if (strchr(file, '/') != NULL) 6919f52532Smartin return posix_spawn(pid, file, fa, sa, cav, env); 7019f52532Smartin 71872f7801Skre /* Get the path we're searching. */ 72872f7801Skre if ((path = getenv("PATH")) == NULL) 73872f7801Skre path = _PATH_DEFPATH; 7419f52532Smartin 7519f52532Smartin /* 7619f52532Smartin * Find an executable image with the given name in the PATH 7719f52532Smartin */ 78872f7801Skre 79872f7801Skre ln = strlen(file); 80872f7801Skre err = 0; 81872f7801Skre do { 82872f7801Skre /* Find the end of this path element. */ 83872f7801Skre for (p = path; *path != 0 && *path != ':'; path++) 84872f7801Skre continue; 85872f7801Skre /* 86872f7801Skre * It's a SHELL path -- double, leading and trailing colons 87872f7801Skre * mean the current directory. 88872f7801Skre */ 89872f7801Skre if (p == path) { 90872f7801Skre p = "."; 91872f7801Skre lp = 1; 92872f7801Skre } else 93872f7801Skre lp = (size_t)(path - p); 94872f7801Skre 95872f7801Skre /* 96872f7801Skre * Once we gain chdir/fchdir file actions, this will need 97872f7801Skre * serious work, as we must treat "." relative to the 98872f7801Skre * target of the (final) chdir performed. 99872f7801Skre * 100872f7801Skre * Fortunately, that day is yet to come. 101872f7801Skre */ 102872f7801Skre 103872f7801Skre /* 104872f7801Skre * If the path is too long complain. This is a possible 105872f7801Skre * security issue; given a way to make the path too long 106872f7801Skre * the user may execute the wrong program. 107872f7801Skre */ 108872f7801Skre if (lp + ln + 2 > sizeof(fpath)) { 109872f7801Skre (void)write(STDERR_FILENO, "posix_spawnp: ", 14); 110872f7801Skre (void)write(STDERR_FILENO, p, lp); 111872f7801Skre (void)write(STDERR_FILENO, ": path too long\n", 16); 112872f7801Skre continue; 113872f7801Skre } 114872f7801Skre memcpy(fpath, p, lp); 115872f7801Skre fpath[lp] = '/'; 116872f7801Skre memcpy(fpath + lp + 1, file, ln); 117872f7801Skre fpath[lp + ln + 1] = '\0'; 118872f7801Skre 119872f7801Skre /* 120872f7801Skre * It would be nice (much better) to try posix_spawn() 121872f7801Skre * here, using the current fpath as the filename, but 122872f7801Skre * there's no guarantee that it is safe to execute it 123872f7801Skre * twice (the file actions may screw us) so that we 124872f7801Skre * cannot do. This test is weak, barely even adequate. 125872f7801Skre * but unless we are forced into making posix_spawmp() 126872f7801Skre * become a system call (with PATH as an arg, or an array 127872f7801Skre * of possible paths to try, based upon PATH and file) 128872f7801Skre * we really have no better method. 129872f7801Skre */ 13019f52532Smartin if (access(fpath, X_OK) == 0) 13119f52532Smartin break; 132872f7801Skre 133872f7801Skre if (err == 0) 134872f7801Skre err = errno; 135872f7801Skre 136872f7801Skre fpath[0] = '\0'; 137872f7801Skre 138872f7801Skre 139872f7801Skre } while (*path++ == ':'); /* Otherwise, *path was NUL */ 140872f7801Skre 141872f7801Skre if (fpath[0] == '\0') 142872f7801Skre return err; 14319f52532Smartin 14419f52532Smartin /* 14519f52532Smartin * Use posix_spawn() with the found binary 14619f52532Smartin */ 14719f52532Smartin return posix_spawn(pid, fpath, fa, sa, cav, env); 14819f52532Smartin } 149