env.c revision 1.23.10.1 1 /* $NetBSD: env.c,v 1.23.10.1 2025/08/02 05:58:26 perseant Exp $ */
2 /*
3 * Copyright (c) 1988, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #ifndef lint
33 __COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\
34 The Regents of the University of California. All rights reserved.");
35 #endif /* not lint */
36
37 #ifndef lint
38 /*static char sccsid[] = "@(#)env.c 8.3 (Berkeley) 4/2/94";*/
39 __RCSID("$NetBSD: env.c,v 1.23.10.1 2025/08/02 05:58:26 perseant Exp $");
40 #endif /* not lint */
41
42 #include <sys/stat.h>
43
44 #include <err.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <locale.h>
50 #include <errno.h>
51 #include <paths.h>
52
53 static void usage(void) __dead;
54 static const char *path_find(const char *);
55
56 extern char **environ;
57
58 int
59 main(int argc, char **argv)
60 {
61 char **ep, term;
62 char *cleanenv[1];
63 int ch;
64
65 setprogname(*argv);
66 (void)setlocale(LC_ALL, "");
67
68 term = '\n';
69 while ((ch = getopt(argc, argv, "-0C:iu:")) != -1)
70 switch((char)ch) {
71 case '0':
72 term = '\0';
73 break;
74 case 'C':
75 if (chdir(optarg) == -1)
76 err(125, "chdir '%s'", optarg);
77 /* better to do this than have it be invalid */
78 (void)unsetenv("PWD");
79 break;
80 case '-': /* obsolete */
81 case 'i':
82 environ = cleanenv;
83 cleanenv[0] = NULL;
84 break;
85 case 'u':
86 if (unsetenv(optarg) == -1)
87 err(125, "unsetenv '%s'", optarg);
88 break;
89 case '?':
90 default:
91 usage();
92 }
93
94 for (argv += optind; *argv && strchr(*argv, '=') != NULL; ++argv)
95 if (putenv(*argv) == -1)
96 err(125, "putenv '%s'", *argv);
97
98 /*
99 * Allow an extra "--" to allow utility names to contain '=' chars
100 */
101 if (*argv && strcmp(*argv, "--") == 0)
102 argv++;
103
104 if (!*argv) {
105 /*
106 * No utility name is present, simply dump the environment
107 * to stdout, and we're done.
108 */
109 for (ep = environ; *ep; ep++)
110 (void)printf("%s%c", *ep, term);
111
112 (void)fflush(stdout);
113 if (ferror(stdout))
114 err(125, "write to standard output");
115
116 exit(0);
117 }
118
119 /*
120 * Run the utility, if this succeeds, it doesn't return,
121 * and we no longer exist! No need to check for errors,
122 * if we have the opportunity to do so, there must be one.
123 */
124 (void)execvp(*argv, argv);
125
126 /*
127 * Return 127 if the command to be run could not be found;
128 * or 126 if the command was found but could not be invoked.
129 *
130 * Working out which happened is hard, and impossible to
131 * truly get correct, but let's try.
132 *
133 * First we need to discover if the utility exists, that
134 * means duplicating much of the work of execvp() without
135 * the actual exec attempt.
136 */
137
138 if (path_find(*argv) == NULL) /* Could not be found */
139 err(127, "%s", *argv);
140
141 /*
142 * We could (should) free the return value from path_find()
143 * if it is not identical to *argv, but since all we are
144 * going to do is exit anyway, why bother?
145 */
146
147 /*
148 * The file does exist, so the "could not be found" is
149 * false, this must be the 126 exit case.
150 */
151
152 err(126, "%s", *argv);
153 /* NOTREACHED */
154 }
155
156 /*
157 * search for name in directories given in env var PATH
158 *
159 * return the location found, or none if there is none.
160 * (note this return value is either NULL, or == name,
161 * or is a pointer to malloc()'d memory)
162 *
163 * The value of errno on entry *must* be preserved.
164 */
165 static const char *
166 path_find(const char *name)
167 {
168 int e = errno;
169 struct stat sb;
170 const char *path;
171 const char *firstfound = NULL;
172
173 if (strchr(name, '/') != NULL) {
174 /*
175 * name contains a '/', no PATH search,
176 * just work out if it exists or not.
177 *
178 * nb: stat, not lstat, the thing must
179 * resolve to something we can exec(),
180 * not just a symlink.
181 */
182 if (stat(name, &sb) == -1) {
183 errno = e;
184 return NULL;
185 }
186 errno = e;
187 return name;
188 }
189
190 /* borrow the outline of this loop from execvp() */
191
192 path = getenv("PATH");
193 if (path == NULL)
194 path = _PATH_DEFPATH;
195
196 do {
197 const char *p;
198 char *pathname;
199 ptrdiff_t lp;
200
201 /* Find the end of this path element. */
202 for (p = path; *path != 0 && *path != ':'; path++)
203 continue;
204 /*
205 * It's a SHELL path -- double, leading and trailing colons
206 * mean the current directory.
207 */
208 if (p == path) {
209 p = ".";
210 lp = 1;
211 } else
212 lp = path - p;
213
214 if (asprintf(&pathname, "%.*s/%s", (int)lp, p, name) == -1) {
215 /*
216 * This is very unlikely, and usually means ENOMEM
217 * from malloc() - just give up, and say the file could
218 * not be found (which is more or less correct, given
219 * a slightly slanted view on what just happened).
220 */
221 errno = e;
222 return NULL;
223 }
224
225 if (stat(pathname, &sb) == -1) {
226 free(pathname);
227 continue;
228 }
229
230 if ((sb.st_mode & 0111) != 0) {
231 /*
232 * We located an existing file with
233 * the correct name in PATH, and it
234 * has (someone's) execute permission.
235 *
236 * Done.
237 */
238 errno = e;
239 return (const char *)pathname;
240 }
241
242 /*
243 * No execute permission, but a file is located.
244 * Continue looking for one which does have execute
245 * permission, which is how execvp() works,
246 * more or less. (Close enough for our purposes).
247 */
248
249 if (firstfound == NULL)
250 firstfound = pathname;
251 else
252 free(pathname);
253
254 } while (*path++ == ':'); /* Otherwise, *path was '\0' */
255
256 errno = e;
257 return firstfound;
258 }
259
260 static void
261 usage(void)
262 {
263 const char *me = getprogname();
264 int howwide = (int)strlen(me);
265
266 howwide += 7; /* "Usage: " */
267
268 (void)fprintf(stderr,
269 "\nUsage: %s %s \\\n%*s %s \\\n%*s %s\n",
270 me, "[-0i] [-C dir] [-u name] [--]",
271 howwide, "", "[name=value ...] [--]",
272 howwide, "", "[utility [arg ...] ]");
273
274 exit(125);
275 }
276