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