1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2024 Oxide Computer Company
26 */
27
28 /* Copyright (c) 1988 AT&T */
29 /* All Rights Reserved */
30
31 /*
32 * execlp(name, arg,...,0) (like execl, but does path search)
33 * execvp(name, argv) (like execv, but does path search)
34 * execvpe(name, argv, envp) (like execve, but does path search)
35 */
36
37 #pragma weak _execlp = execlp
38 #pragma weak _execvp = execvp
39
40 #include "lint.h"
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <alloca.h>
45 #include <errno.h>
46 #include <limits.h>
47 #include <stdarg.h>
48 #include <stdlib.h>
49 #include <paths.h>
50
51 static const char *execat(const char *, const char *, char *);
52
53 extern int __xpg4; /* defined in xpg4.c; 0 if not xpg4-compiled program */
54
55 /*VARARGS1*/
56 int
execlp(const char * name,const char * arg0,...)57 execlp(const char *name, const char *arg0, ...)
58 {
59 char **argp;
60 va_list args;
61 char **argvec;
62 int err;
63 int nargs = 0;
64 char *nextarg;
65
66 /*
67 * count the number of arguments in the variable argument list
68 * and allocate an argument vector for them on the stack,
69 * adding space for a terminating null pointer at the end
70 * and one additional space for argv[0] which is no longer
71 * counted by the varargs loop.
72 */
73
74 va_start(args, arg0);
75
76 while (va_arg(args, char *) != (char *)0)
77 nargs++;
78
79 va_end(args);
80
81 /*
82 * load the arguments in the variable argument list
83 * into the argument vector and add the terminating null pointer
84 */
85
86 va_start(args, arg0);
87 /* workaround for bugid 1242839 */
88 argvec = alloca((size_t)((nargs + 2) * sizeof (char *)));
89 nextarg = va_arg(args, char *);
90 argp = argvec;
91 *argp++ = (char *)arg0;
92 while (nargs-- && nextarg != (char *)0) {
93 *argp = nextarg;
94 argp++;
95 nextarg = va_arg(args, char *);
96 }
97 va_end(args);
98 *argp = (char *)0;
99
100 /*
101 * call execvp()
102 */
103
104 err = execvp(name, argvec);
105 return (err);
106 }
107
108 static int
execvpe_int(const char * name,char * const * argv,char * const * envp,boolean_t use_env)109 execvpe_int(const char *name, char *const *argv, char *const *envp,
110 boolean_t use_env)
111 {
112 const char *pathstr;
113 char fname[PATH_MAX+2];
114 char *newargs[256];
115 int i;
116 const char *cp;
117 unsigned etxtbsy = 1;
118 int eacces = 0;
119
120 if (*name == '\0') {
121 errno = ENOENT;
122 return (-1);
123 }
124 if ((pathstr = getenv("PATH")) == NULL) {
125 /*
126 * XPG4: pathstr is equivalent to CSPATH, except that
127 * :/usr/sbin is appended when root, and pathstr must end
128 * with a colon when not root. Keep these paths in sync
129 * with CSPATH in confstr.c. Note that pathstr must end
130 * with a colon when not root so that when name doesn't
131 * contain '/', the last call to execat() will result in an
132 * attempt to execv name from the current directory.
133 */
134 if (geteuid() == 0 || getuid() == 0) {
135 if (__xpg4 == 0) { /* not XPG4 */
136 pathstr = "/usr/sbin:/usr/ccs/bin:/usr/bin";
137 } else { /* XPG4 (CSPATH + /usr/sbin) */
138 pathstr = "/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:"
139 "/opt/SUNWspro/bin:/usr/sbin";
140 }
141 } else {
142 if (__xpg4 == 0) { /* not XPG4 */
143 pathstr = "/usr/ccs/bin:/usr/bin:";
144 } else { /* XPG4 (CSPATH) */
145 pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
146 "/usr/bin:/opt/SUNWspro/bin:";
147 }
148 }
149 }
150 cp = strchr(name, '/')? (const char *)"": pathstr;
151
152 do {
153 cp = execat(cp, name, fname);
154 retry:
155 /*
156 * 4025035 and 4038378
157 * if a filename begins with a "-" prepend "./" so that
158 * the shell can't interpret it as an option
159 */
160 if (*fname == '-') {
161 size_t size = strlen(fname) + 1;
162 if ((size + 2) > sizeof (fname)) {
163 errno = E2BIG;
164 return (-1);
165 }
166 (void) memmove(fname + 2, fname, size);
167 fname[0] = '.';
168 fname[1] = '/';
169 }
170 if (use_env) {
171 (void) execve(fname, argv, envp);
172 } else {
173 (void) execv(fname, argv);
174 }
175 switch (errno) {
176 case ENOEXEC:
177 newargs[0] = "sh";
178 newargs[1] = fname;
179 for (i = 1; (newargs[i + 1] = argv[i]) != NULL; ++i) {
180 if (i >= 254) {
181 errno = E2BIG;
182 return (-1);
183 }
184 }
185 if (use_env) {
186 (void) execve(_PATH_BSHELL, newargs, envp);
187 } else {
188 (void) execv(_PATH_BSHELL, newargs);
189 }
190 return (-1);
191 case ETXTBSY:
192 if (++etxtbsy > 5)
193 return (-1);
194 (void) sleep(etxtbsy);
195 goto retry;
196 case EACCES:
197 ++eacces;
198 break;
199 case ENOMEM:
200 case E2BIG:
201 case EFAULT:
202 return (-1);
203 }
204 } while (cp);
205 if (eacces)
206 errno = EACCES;
207 return (-1);
208 }
209
210 int
execvp(const char * file,char * const * argv)211 execvp(const char *file, char *const *argv)
212 {
213 return (execvpe_int(file, argv, NULL, B_FALSE));
214 }
215
216 int
execvpe(const char * file,char * const * argv,char * const * envp)217 execvpe(const char *file, char *const *argv, char *const *envp)
218 {
219 return (execvpe_int(file, argv, envp, B_TRUE));
220 }
221
222 static const char *
execat(const char * s1,const char * s2,char * si)223 execat(const char *s1, const char *s2, char *si)
224 {
225 char *s;
226 int cnt = PATH_MAX + 1; /* number of characters in s2 */
227
228 s = si;
229 while (*s1 && *s1 != ':') {
230 if (cnt > 0) {
231 *s++ = *s1++;
232 cnt--;
233 } else
234 s1++;
235 }
236 if (si != s && cnt > 0) {
237 *s++ = '/';
238 cnt--;
239 }
240 while (*s2 && cnt > 0) {
241 *s++ = *s2++;
242 cnt--;
243 }
244 *s = '\0';
245 return (*s1 ? ++s1: 0);
246 }
247