1 /* $OpenBSD: which.c,v 1.4 1998/05/07 19:12:20 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com> 5 * 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Todd C. Miller. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 22 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 23 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 24 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 30 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 static char rcsid[] = "$OpenBSD: which.c,v 1.4 1998/05/07 19:12:20 deraadt Exp $"; 35 #endif /* not lint */ 36 37 #include <sys/param.h> 38 #include <sys/stat.h> 39 #include <sys/sysctl.h> 40 41 #include <err.h> 42 #include <errno.h> 43 #include <locale.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 49 #define PROG_WHICH 1 50 #define PROG_WHEREIS 2 51 52 extern char *__progname; 53 54 int findprog __P((char *, char *, int, int)); 55 void usage __P((void)); 56 57 /* 58 * which(1) -- find an executable(s) in the user's path 59 * whereis(1) -- find an executable(s) in the default user path 60 * 61 * Return values: 62 * 0 - all executables found 63 * 1 - some found, some not 64 * 2 - none found 65 */ 66 67 int 68 main(argc, argv) 69 int argc; 70 char **argv; 71 { 72 char *path; 73 size_t n; 74 int ch, allmatches = 0, notfound = 0, progmode = PROG_WHICH; 75 76 (void)setlocale(LC_ALL, ""); 77 78 if (argc == 1) 79 usage(); 80 81 /* Don't accept command args but check since old whereis(1) used to */ 82 while ((ch = getopt(argc, argv, "a")) != -1) { 83 switch (ch) { 84 case 'a': 85 allmatches = 1; 86 break; 87 default: 88 usage(); 89 } 90 } 91 92 /* 93 * which(1) uses user's $PATH. 94 * whereis(1) uses user.cs_path from sysctl(3). 95 */ 96 if (strcmp(__progname, "whereis") == 0) { 97 int mib[2]; 98 99 progmode = PROG_WHEREIS; 100 mib[0] = CTL_USER; 101 mib[1] = USER_CS_PATH; 102 if (sysctl(mib, 2, NULL, &n, NULL, 0) == -1) 103 err(1, "unable to get length of user.cs_path"); 104 if (n == 0) 105 errx(1, "user.cs_path was zero length!"); 106 if ((path = (char *)malloc(n)) == NULL) 107 errx(1, "can't allocate memory."); 108 if (sysctl(mib, 2, path, &n, NULL, 0) == -1) 109 err(1, "unable to get user.cs_path"); 110 } else { 111 if ((path = getenv("PATH")) == NULL) 112 err(1, "can't get $PATH from environment"); 113 } 114 115 /* To make access(2) do what we want */ 116 if (setgid(getegid())) 117 err(1, "Can't set gid to %u", getegid()); 118 if (setuid(geteuid())) 119 err(1, "Can't set uid to %u", geteuid()); 120 121 for (n = optind; n < argc; n++) 122 if (findprog(argv[n], path, progmode, allmatches) == 0) 123 notfound++; 124 125 exit((notfound == 0) ? 0 : ((notfound == argc - 1) ? 2 : 1)); 126 } 127 128 int 129 findprog(prog, path, progmode, allmatches) 130 char *prog; 131 char *path; 132 int progmode; 133 int allmatches; 134 { 135 char *p, filename[MAXPATHLEN]; 136 int proglen, plen, rval = 0; 137 struct stat sbuf; 138 139 /* Special case if prog contains '/' */ 140 if (strchr(prog, '/')) { 141 if ((stat(prog, &sbuf) == 0) && S_ISREG(sbuf.st_mode) && 142 access(prog, X_OK) == 0) { 143 (void)puts(prog); 144 return(1); 145 } else { 146 (void)printf("%s: Command not found.\n", prog); 147 return(0); 148 } 149 } 150 151 if ((path = strdup(path)) == NULL) 152 errx(1, "Can't allocate memory."); 153 154 proglen = strlen(prog); 155 while ((p = strsep(&path, ":")) != NULL) { 156 if (*p == '\0') 157 p = "."; 158 159 plen = strlen(p); 160 while (p[plen-1] == '/') 161 p[--plen] = '\0'; /* strip trailing '/' */ 162 163 if (plen + 1 + proglen >= sizeof(filename)) { 164 warnx("%s/%s: %s", p, prog, strerror(ENAMETOOLONG)); 165 return(0); 166 } 167 168 (void)strcpy(filename, p); 169 filename[plen] = '/'; 170 (void)strcpy(filename + plen + 1, prog); 171 if ((stat(filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) && 172 access(filename, X_OK) == 0) { 173 (void)puts(filename); 174 rval = 1; 175 if (!allmatches) 176 return(rval); 177 } 178 } 179 (void)free(path); 180 181 /* whereis(1) is silent on failure. */ 182 if (!rval && progmode != PROG_WHEREIS) 183 (void)printf("%s: Command not found.\n", prog); 184 return(rval); 185 } 186 187 void 188 usage() 189 { 190 (void) fprintf(stderr, "Usage: %s [-a] name [...]\n", __progname); 191 exit(1); 192 } 193