/* $NetBSD: env.c,v 1.25 2025/02/09 14:25:26 kre Exp $ */ /* * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\ The Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint /*static char sccsid[] = "@(#)env.c 8.3 (Berkeley) 4/2/94";*/ __RCSID("$NetBSD: env.c,v 1.25 2025/02/09 14:25:26 kre Exp $"); #endif /* not lint */ #include #include #include #include #include #include #include #include #include static void usage(void) __dead; static const char *path_find(const char *); extern char **environ; int main(int argc, char **argv) { char **ep, term; char *cleanenv[1]; int ch; setprogname(*argv); (void)setlocale(LC_ALL, ""); term = '\n'; while ((ch = getopt(argc, argv, "-0C:iu:")) != -1) switch((char)ch) { case '0': term = '\0'; break; case 'C': if (chdir(optarg) == -1) err(125, "chdir '%s'", optarg); /* better to do this than have it be invalid */ (void)unsetenv("PWD"); break; case '-': /* obsolete */ case 'i': environ = cleanenv; cleanenv[0] = NULL; break; case 'u': if (unsetenv(optarg) == -1) err(125, "unsetenv '%s'", optarg); break; case '?': default: usage(); } for (argv += optind; *argv && strchr(*argv, '=') != NULL; ++argv) if (putenv(*argv) == -1) err(125, "putenv '%s'", *argv); /* * Allow an extra "--" to allow utility names to contain '=' chars */ if (*argv && strcmp(*argv, "--") == 0) argv++; if (!*argv) { /* * No utility name is present, simply dump the environment * to stdout, and we're done. */ for (ep = environ; *ep; ep++) (void)printf("%s%c", *ep, term); (void)fflush(stdout); if (ferror(stdout)) err(125, "write to standard output"); exit(0); } /* * Run the utility, if this succeeds, it doesn't return, * and we no longer exist! No need to check for errors, * if we have the opportunity to do so, there must be one. */ (void)execvp(*argv, argv); /* * Return 127 if the command to be run could not be found; * or 126 if the command was found but could not be invoked. * * Working out which happened is hard, and impossible to * truly get correct, but let's try. * * First we need to discover if the utility exists, that * means duplicating much of the work of execvp() without * the actual exec attempt. */ if (path_find(*argv) == NULL) /* Could not be found */ err(127, "%s", *argv); /* * We could (should) free the return value from path_find() * if it is not identical to *argv, but since all we are * going to do is exit anyway, why bother? */ /* * The file does exist, so the "could not be found" is * false, this must be the 126 exit case. */ err(126, "%s", *argv); /* NOTREACHED */ } /* * search for name in directories given in env var PATH * * return the location found, or none if there is none. * (note this return value is either NULL, or == name, * or is a pointer to malloc()'d memory) * * The value of errno on entry *must* be preserved. */ static const char * path_find(const char *name) { int e = errno; struct stat sb; const char *path; const char *firstfound = NULL; if (strchr(name, '/') != NULL) { /* * name contains a '/', no PATH search, * just work out if it exists or not. * * nb: stat, not lstat, the thing must * resolve to something we can exec(), * not just a symlink. */ if (stat(name, &sb) == -1) { errno = e; return NULL; } errno = e; return name; } /* borrow the outline of this loop from execvp() */ path = getenv("PATH"); if (path == NULL) path = _PATH_DEFPATH; do { const char *p; char *pathname; ptrdiff_t lp; /* Find the end of this path element. */ for (p = path; *path != 0 && *path != ':'; path++) continue; /* * It's a SHELL path -- double, leading and trailing colons * mean the current directory. */ if (p == path) { p = "."; lp = 1; } else lp = path - p; if (asprintf(&pathname, "%.*s/%s", (int)lp, p, name) == -1) { /* * This is very unlikely, and usually means ENOMEM * from malloc() - just give up, and say the file could * not be found (which is more or less correct, given * a slightly slanted view on what just happened). */ errno = e; return NULL; } if (stat(pathname, &sb) == -1) { free(pathname); continue; } if ((sb.st_mode & 0111) != 0) { /* * We located an existing file with * the correct name in PATH, and it * has (someone's) execute permission. * * Done. */ errno = e; return (const char *)pathname; } /* * No execute permission, but a file is located. * Continue looking for one which does have execute * permission, which is how execvp() works, * more or less. (Close enough for our purposes). */ if (firstfound == NULL) firstfound = pathname; else free(pathname); } while (*path++ == ':'); /* Otherwise, *path was '\0' */ errno = e; return firstfound; } static void usage(void) { const char *me = getprogname(); int howwide = (int)strlen(me); howwide += 7; /* "Usage: " */ (void)fprintf(stderr, "\nUsage: %s %s \\\n%*s %s \\\n%*s %s\n", me, "[-0i] [-C dir] [-u name] [--]", howwide, "", "[name=value ...] [--]", howwide, "", "[utility [arg ...] ]"); exit(125); }