1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1988, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/types.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <login_cap.h> 37 #include <pwd.h> 38 #include <stdbool.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 #include "envopts.h" 45 46 extern char **environ; 47 48 int env_verbosity; 49 50 static void usage(void) __dead2; 51 52 /* 53 * Exit codes. 54 */ 55 #define EXIT_CANCELED 125 /* Internal error prior to exec attempt. */ 56 #define EXIT_CANNOT_INVOKE 126 /* Program located, but not usable. */ 57 #define EXIT_ENOENT 127 /* Could not find program to exec. */ 58 59 int 60 main(int argc, char **argv) 61 { 62 char *altpath, *altwd, **ep, *p, **parg, term; 63 char *cleanenv[1]; 64 char *login_class, *login_name; 65 struct passwd *pw; 66 login_cap_t *lc; 67 bool login_as_user; 68 uid_t uid; 69 int ch, want_clear; 70 int rtrn; 71 72 altpath = NULL; 73 altwd = NULL; 74 login_class = NULL; 75 login_name = NULL; 76 pw = NULL; 77 lc = NULL; 78 login_as_user = false; 79 want_clear = 0; 80 term = '\n'; 81 while ((ch = getopt(argc, argv, "-0C:iL:P:S:U:u:v")) != -1) 82 switch(ch) { 83 case '-': 84 case 'i': 85 want_clear = 1; 86 break; 87 case '0': 88 term = '\0'; 89 break; 90 case 'C': 91 altwd = optarg; 92 break; 93 case 'U': 94 login_as_user = true; 95 /* FALLTHROUGH */ 96 case 'L': 97 login_name = optarg; 98 break; 99 case 'P': 100 altpath = optarg; 101 break; 102 case 'S': 103 /* 104 * The -S option, for "split string on spaces, with 105 * support for some simple substitutions"... 106 */ 107 split_spaces(optarg, &optind, &argc, &argv); 108 break; 109 case 'u': 110 if (env_verbosity) 111 fprintf(stderr, "#env unset:\t%s\n", optarg); 112 rtrn = unsetenv(optarg); 113 if (rtrn == -1) 114 err(EXIT_FAILURE, "unsetenv %s", optarg); 115 break; 116 case 'v': 117 env_verbosity++; 118 if (env_verbosity > 1) 119 fprintf(stderr, "#env verbosity now at %d\n", 120 env_verbosity); 121 break; 122 case '?': 123 default: 124 usage(); 125 } 126 if (want_clear) { 127 environ = cleanenv; 128 cleanenv[0] = NULL; 129 if (env_verbosity) 130 fprintf(stderr, "#env clearing environ\n"); 131 } 132 if (login_name != NULL) { 133 login_class = strchr(login_name, '/'); 134 if (login_class) 135 *login_class++ = '\0'; 136 if (*login_name != '\0' && strcmp(login_name, "-") != 0) { 137 pw = getpwnam(login_name); 138 if (pw == NULL) { 139 char *endp = NULL; 140 errno = 0; 141 uid = strtoul(login_name, &endp, 10); 142 if (errno == 0 && *endp == '\0') 143 pw = getpwuid(uid); 144 } 145 if (pw == NULL) 146 errx(EXIT_FAILURE, "no such user: %s", login_name); 147 } 148 /* 149 * Note that it is safe for pw to be null here; the libutil 150 * code handles that, bypassing substitution of $ and using 151 * the class "default" if no class name is given either. 152 */ 153 if (login_class != NULL) { 154 lc = login_getclass(login_class); 155 if (lc == NULL) 156 errx(EXIT_FAILURE, "no such login class: %s", 157 login_class); 158 } else { 159 lc = login_getpwclass(pw); 160 if (lc == NULL) 161 errx(EXIT_FAILURE, "login_getpwclass failed"); 162 } 163 164 /* 165 * This is not done with setusercontext() because that will 166 * try and use ~/.login_conf even when we don't want it to. 167 */ 168 setclassenvironment(lc, pw, 1); 169 setclassenvironment(lc, pw, 0); 170 if (login_as_user) { 171 login_close(lc); 172 if ((lc = login_getuserclass(pw)) != NULL) { 173 setclassenvironment(lc, pw, 1); 174 setclassenvironment(lc, pw, 0); 175 } 176 } 177 endpwent(); 178 if (lc != NULL) 179 login_close(lc); 180 } 181 for (argv += optind; *argv && (p = strchr(*argv, '=')); ++argv) { 182 if (env_verbosity) 183 fprintf(stderr, "#env setenv:\t%s\n", *argv); 184 *p = '\0'; 185 rtrn = setenv(*argv, p + 1, 1); 186 *p = '='; 187 if (rtrn == -1) 188 err(EXIT_FAILURE, "setenv %s", *argv); 189 } 190 if (*argv) { 191 if (term == '\0') 192 errx(EXIT_CANCELED, "cannot specify command with -0"); 193 if (altwd && chdir(altwd) != 0) 194 err(EXIT_CANCELED, "cannot change directory to '%s'", 195 altwd); 196 if (altpath) 197 search_paths(altpath, argv); 198 if (env_verbosity) { 199 fprintf(stderr, "#env executing:\t%s\n", *argv); 200 for (parg = argv, argc = 0; *parg; parg++, argc++) 201 fprintf(stderr, "#env arg[%d]=\t'%s'\n", 202 argc, *parg); 203 if (env_verbosity > 1) 204 sleep(1); 205 } 206 execvp(*argv, argv); 207 err(errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE, 208 "%s", *argv); 209 } else { 210 if (altwd) 211 errx(EXIT_CANCELED, "must specify command with -C"); 212 if (altpath) 213 errx(EXIT_CANCELED, "must specify command with -P"); 214 } 215 for (ep = environ; *ep; ep++) 216 (void)printf("%s%c", *ep, term); 217 if (fflush(stdout) != 0) 218 err(1, "stdout"); 219 exit(0); 220 } 221 222 static void 223 usage(void) 224 { 225 (void)fprintf(stderr, 226 "usage: env [-0iv] [-C workdir] [-L|-U user[/class]] [-P utilpath] [-S string]\n" 227 " [-u name] [name=value ...] [utility [argument ...]]\n"); 228 exit(1); 229 } 230