xref: /openbsd/usr.bin/which/which.c (revision f2dfb0a4)
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