1 /* $OpenBSD: which.c,v 1.13 2004/09/24 19:45:27 fgsch Exp $ */
2
3 /*
4 * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/stat.h>
21 #include <sys/sysctl.h>
22
23 #include <err.h>
24 #include <errno.h>
25 #include <locale.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #define PROG_WHICH 1
32 #define PROG_WHEREIS 2
33
34 __RCSID("$MirOS: src/usr.bin/which/which.c,v 1.2 2007/07/05 23:09:45 tg Exp $");
35
36 extern char *__progname;
37
38 int findprog(char *, char *, int, int);
39 __dead void usage(void);
40
41 /*
42 * which(1) -- find an executable(s) in the user's path
43 * whereis(1) -- find an executable(s) in the default user path
44 *
45 * Return values:
46 * 0 - all executables found
47 * 1 - some found, some not
48 * 2 - none found
49 */
50
51 int
main(int argc,char * argv[])52 main(int argc, char *argv[])
53 {
54 char *path;
55 size_t n;
56 int ch, allmatches = 0, notfound = 0, progmode = PROG_WHICH;
57
58 #ifndef __MirBSD__
59 (void)setlocale(LC_ALL, "");
60 #endif
61
62 if (argc == 1)
63 usage();
64
65 /* Don't accept command args but check since old whereis(1) used to */
66 while ((ch = getopt(argc, argv, "a")) != -1) {
67 switch (ch) {
68 case 'a':
69 allmatches = 1;
70 break;
71 default:
72 usage();
73 }
74 }
75
76 /*
77 * which(1) uses user's $PATH.
78 * whereis(1) uses user.cs_path from sysctl(3).
79 */
80 if (strcmp(__progname, "whereis") == 0) {
81 int mib[2];
82
83 progmode = PROG_WHEREIS;
84 mib[0] = CTL_USER;
85 mib[1] = USER_CS_PATH;
86 if (sysctl(mib, 2, NULL, &n, NULL, 0) == -1)
87 err(1, "unable to get length of user.cs_path");
88 if (n == 0)
89 errx(1, "user.cs_path was zero length!");
90 if ((path = (char *)malloc(n)) == NULL)
91 errx(1, "can't allocate memory.");
92 if (sysctl(mib, 2, path, &n, NULL, 0) == -1)
93 err(1, "unable to get user.cs_path");
94 } else {
95 if ((path = getenv("PATH")) == NULL)
96 err(1, "can't get $PATH from environment");
97 }
98
99 /* To make access(2) do what we want */
100 if (setgid(getegid()))
101 err(1, "Can't set gid to %u", getegid());
102 if (setuid(geteuid()))
103 err(1, "Can't set uid to %u", geteuid());
104
105 for (n = optind; n < argc; n++)
106 if (findprog(argv[n], path, progmode, allmatches) == 0)
107 notfound++;
108
109 exit((notfound == 0) ? 0 : ((notfound == argc - 1) ? 2 : 1));
110 }
111
112 int
findprog(char * prog,char * path,int progmode,int allmatches)113 findprog(char *prog, char *path, int progmode, int allmatches)
114 {
115 char *p, filename[MAXPATHLEN];
116 int proglen, plen, rval = 0;
117 struct stat sbuf;
118 char *pathcpy;
119
120 /* Special case if prog contains '/' */
121 if (strchr(prog, '/')) {
122 if ((stat(prog, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
123 access(prog, X_OK) == 0) {
124 (void)puts(prog);
125 return (1);
126 } else {
127 (void)printf("%s: Command not found.\n", prog);
128 return (0);
129 }
130 }
131
132 if ((path = strdup(path)) == NULL)
133 errx(1, "Can't allocate memory.");
134 pathcpy = path;
135
136 proglen = strlen(prog);
137 while ((p = strsep(&pathcpy, ":")) != NULL) {
138 if (*p == '\0')
139 p = ".";
140
141 plen = strlen(p);
142 while (p[plen-1] == '/')
143 p[--plen] = '\0'; /* strip trailing '/' */
144
145 if (plen + 1 + proglen >= sizeof(filename)) {
146 warnx("%s/%s: %s", p, prog, strerror(ENAMETOOLONG));
147 free(path);
148 return (0);
149 }
150
151 snprintf(filename, sizeof(filename), "%s/%s", p, prog);
152 if ((stat(filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
153 access(filename, X_OK) == 0) {
154 (void)puts(filename);
155 rval = 1;
156 if (!allmatches) {
157 free(path);
158 return (rval);
159 }
160 }
161 }
162 (void)free(path);
163
164 /* whereis(1) is silent on failure. */
165 if (!rval && progmode != PROG_WHEREIS)
166 (void)printf("%s: Command not found.\n", prog);
167 return (rval);
168 }
169
170 __dead void
usage(void)171 usage(void)
172 {
173 (void) fprintf(stderr, "Usage: %s [-a] name [...]\n", __progname);
174 exit(1);
175 }
176