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