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