xref: /openbsd-src/usr.bin/which/which.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: which.c,v 1.19 2014/05/20 01:25:23 guenther 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 <paths.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #define PROG_WHICH	1
33 #define PROG_WHEREIS	2
34 
35 extern char *__progname;
36 
37 int findprog(char *, char *, int, int);
38 __dead void usage(void);
39 
40 /*
41  * which(1) -- find an executable(s) in the user's path
42  * whereis(1) -- find an executable(s) in the default user path
43  *
44  * Return values:
45  *	0 - all executables found
46  *	1 - some found, some not
47  *	2 - none found
48  */
49 
50 int
51 main(int argc, char *argv[])
52 {
53 	char *path;
54 	size_t n;
55 	int ch, allmatches = 0, notfound = 0, progmode = PROG_WHICH;
56 
57 	(void)setlocale(LC_ALL, "");
58 
59 	while ((ch = getopt(argc, argv, "a")) != -1)
60 		switch (ch) {
61 		case 'a':
62 			allmatches = 1;
63 			break;
64 		default:
65 			usage();
66 		}
67 	argc -= optind;
68 	argv += optind;
69 
70 	if (argc == 0)
71 		usage();
72 
73 	if (strcmp(__progname, "whereis") == 0) {
74 		progmode = PROG_WHEREIS;
75 		path = _PATH_STDPATH;
76 	} else {
77 		if ((path = getenv("PATH")) == NULL)
78 			err(1, "can't get $PATH from environment");
79 	}
80 
81 	/* To make access(2) do what we want */
82 	if (setgid(getegid()))
83 		err(1, "Can't set gid to %u", getegid());
84 	if (setuid(geteuid()))
85 		err(1, "Can't set uid to %u", geteuid());
86 
87 	for (n = 0; n < argc; n++)
88 		if (findprog(argv[n], path, progmode, allmatches) == 0)
89 			notfound++;
90 
91 	exit((notfound == 0) ? 0 : ((notfound == argc) ? 2 : 1));
92 }
93 
94 int
95 findprog(char *prog, char *path, int progmode, int allmatches)
96 {
97 	char *p, filename[MAXPATHLEN];
98 	int proglen, plen, rval = 0;
99 	struct stat sbuf;
100 	char *pathcpy;
101 
102 	/* Special case if prog contains '/' */
103 	if (strchr(prog, '/')) {
104 		if ((stat(prog, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
105 		    access(prog, X_OK) == 0) {
106 			(void)puts(prog);
107 			return (1);
108 		} else {
109 			warnx("%s: Command not found.", prog);
110 			return (0);
111 		}
112 	}
113 
114 	if ((path = strdup(path)) == NULL)
115 		err(1, "strdup");
116 	pathcpy = path;
117 
118 	proglen = strlen(prog);
119 	while ((p = strsep(&pathcpy, ":")) != NULL) {
120 		if (*p == '\0')
121 			p = ".";
122 
123 		plen = strlen(p);
124 		while (p[plen-1] == '/')
125 			p[--plen] = '\0';	/* strip trailing '/' */
126 
127 		if (plen + 1 + proglen >= sizeof(filename)) {
128 			warnc(ENAMETOOLONG, "%s/%s", p, prog);
129 			free(path);
130 			return (0);
131 		}
132 
133 		snprintf(filename, sizeof(filename), "%s/%s", p, prog);
134 		if ((stat(filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
135 		    access(filename, X_OK) == 0) {
136 			(void)puts(filename);
137 			rval = 1;
138 			if (!allmatches) {
139 				free(path);
140 				return (rval);
141 			}
142 		}
143 	}
144 	(void)free(path);
145 
146 	/* whereis(1) is silent on failure. */
147 	if (!rval && progmode != PROG_WHEREIS)
148 		warnx("%s: Command not found.", prog);
149 	return (rval);
150 }
151 
152 __dead void
153 usage(void)
154 {
155 	(void)fprintf(stderr, "usage: %s [-a] name ...\n", __progname);
156 	exit(1);
157 }
158