xref: /openbsd/usr.bin/which/which.c (revision db3296cf)
1 /*	$OpenBSD: which.c,v 1.11 2003/06/17 21:56:26 millert 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 #ifndef lint
20 static const char rcsid[] = "$OpenBSD: which.c,v 1.11 2003/06/17 21:56:26 millert Exp $";
21 #endif /* not lint */
22 
23 #include <sys/param.h>
24 #include <sys/stat.h>
25 #include <sys/sysctl.h>
26 
27 #include <err.h>
28 #include <errno.h>
29 #include <locale.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 
35 #define PROG_WHICH	1
36 #define PROG_WHEREIS	2
37 
38 extern char *__progname;
39 
40 int findprog(char *, char *, int, int);
41 void usage(void);
42 
43 /*
44  * which(1) -- find an executable(s) in the user's path
45  * whereis(1) -- find an executable(s) in the default user path
46  *
47  * Return values:
48  *	0 - all executables found
49  *	1 - some found, some not
50  *	2 - none found
51  */
52 
53 int
54 main(int argc, char *argv[])
55 {
56 	char *path;
57 	size_t n;
58 	int ch, allmatches = 0, notfound = 0, progmode = PROG_WHICH;
59 
60 	(void)setlocale(LC_ALL, "");
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
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 
119 	/* Special case if prog contains '/' */
120 	if (strchr(prog, '/')) {
121 		if ((stat(prog, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
122 		    access(prog, X_OK) == 0) {
123 			(void)puts(prog);
124 			return(1);
125 		} else {
126 			(void)printf("%s: Command not found.\n", prog);
127 			return(0);
128 		}
129 	}
130 
131 	if ((path = strdup(path)) == NULL)
132 		errx(1, "Can't allocate memory.");
133 
134 	proglen = strlen(prog);
135 	while ((p = strsep(&path, ":")) != NULL) {
136 		if (*p == '\0')
137 			p = ".";
138 
139 		plen = strlen(p);
140 		while (p[plen-1] == '/')
141 			p[--plen] = '\0';	/* strip trailing '/' */
142 
143 		if (plen + 1 + proglen >= sizeof(filename)) {
144 			warnx("%s/%s: %s", p, prog, strerror(ENAMETOOLONG));
145 			return(0);
146 		}
147 
148 		snprintf(filename, sizeof(filename), "%s/%s", p, prog);
149 		if ((stat(filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
150 		    access(filename, X_OK) == 0) {
151 			(void)puts(filename);
152 			rval = 1;
153 			if (!allmatches)
154 				return(rval);
155 		}
156 	}
157 	(void)free(path);
158 
159 	/* whereis(1) is silent on failure. */
160 	if (!rval && progmode != PROG_WHEREIS)
161 		(void)printf("%s: Command not found.\n", prog);
162 	return(rval);
163 }
164 
165 void
166 usage(void)
167 {
168 	(void) fprintf(stderr, "Usage: %s [-a] name [...]\n", __progname);
169 	exit(1);
170 }
171