xref: /openbsd/usr.bin/which/which.c (revision bf198cc6)
1*bf198cc6Smillert /*	$OpenBSD: which.c,v 1.27 2019/01/25 00:19:27 millert Exp $	*/
271e895c5Smillert 
371e895c5Smillert /*
4*bf198cc6Smillert  * Copyright (c) 1997 Todd C. Miller <millert@openbsd.org>
571e895c5Smillert  *
606f01696Smillert  * Permission to use, copy, modify, and distribute this software for any
706f01696Smillert  * purpose with or without fee is hereby granted, provided that the above
806f01696Smillert  * copyright notice and this permission notice appear in all copies.
971e895c5Smillert  *
10328f1f07Smillert  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11328f1f07Smillert  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12328f1f07Smillert  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13328f1f07Smillert  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14328f1f07Smillert  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15328f1f07Smillert  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16328f1f07Smillert  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1771e895c5Smillert  */
1871e895c5Smillert 
1971e895c5Smillert #include <sys/stat.h>
203d908e8eSmillert #include <sys/sysctl.h>
2171e895c5Smillert 
2271e895c5Smillert #include <err.h>
2371e895c5Smillert #include <errno.h>
2484d7e2a2Sschwarze #include <limits.h>
254df91351Sguenther #include <paths.h>
2671e895c5Smillert #include <stdio.h>
2771e895c5Smillert #include <stdlib.h>
2871e895c5Smillert #include <string.h>
2971e895c5Smillert #include <unistd.h>
3071e895c5Smillert 
313d908e8eSmillert #define PROG_WHICH	1
323d908e8eSmillert #define PROG_WHEREIS	2
333d908e8eSmillert 
3471e895c5Smillert extern char *__progname;
3571e895c5Smillert 
36c72b5b24Smillert int findprog(char *, char *, int, int);
3784d7e2a2Sschwarze static void __dead usage(void);
3871e895c5Smillert 
3971e895c5Smillert /*
4071e895c5Smillert  * which(1) -- find an executable(s) in the user's path
413d908e8eSmillert  * whereis(1) -- find an executable(s) in the default user path
4271e895c5Smillert  *
4371e895c5Smillert  * Return values:
4471e895c5Smillert  *	0 - all executables found
4571e895c5Smillert  *	1 - some found, some not
4671e895c5Smillert  *	2 - none found
4771e895c5Smillert  */
4871e895c5Smillert 
4971e895c5Smillert int
main(int argc,char * argv[])501837a5caSderaadt main(int argc, char *argv[])
5171e895c5Smillert {
5271e895c5Smillert 	char *path;
533d908e8eSmillert 	size_t n;
54d27b3816Smillert 	int ch, allmatches = 0, notfound = 0, progmode = PROG_WHICH;
5571e895c5Smillert 
56347320ecSguenther 	while ((ch = getopt(argc, argv, "a")) != -1)
573d908e8eSmillert 		switch (ch) {
58d27b3816Smillert 		case 'a':
59d27b3816Smillert 			allmatches = 1;
60d27b3816Smillert 			break;
613d908e8eSmillert 		default:
623d908e8eSmillert 			usage();
633d908e8eSmillert 		}
64347320ecSguenther 	argc -= optind;
65347320ecSguenther 	argv += optind;
66347320ecSguenther 
67347320ecSguenther 	if (argc == 0)
68347320ecSguenther 		usage();
693d908e8eSmillert 
703d908e8eSmillert 	if (strcmp(__progname, "whereis") == 0) {
713d908e8eSmillert 		progmode = PROG_WHEREIS;
724df91351Sguenther 		path = _PATH_STDPATH;
733d908e8eSmillert 	} else {
7485c225b1Smillert 		if ((path = getenv("PATH")) == NULL || *path == '\0')
7585c225b1Smillert 			path = _PATH_DEFPATH;
763d908e8eSmillert 	}
7771e895c5Smillert 
7871e895c5Smillert 	/* To make access(2) do what we want */
7971e895c5Smillert 	if (setgid(getegid()))
80ef38576fSderaadt 		err(1, "Can't set gid to %u", getegid());
8171e895c5Smillert 	if (setuid(geteuid()))
82ef38576fSderaadt 		err(1, "Can't set uid to %u", geteuid());
8371e895c5Smillert 
841bc050f6Sderaadt 	if (pledge("stdio rpath", NULL) == -1)
85b4e5b6a2Sgsoares 		err(2, "pledge");
861bc050f6Sderaadt 
87347320ecSguenther 	for (n = 0; n < argc; n++)
88d27b3816Smillert 		if (findprog(argv[n], path, progmode, allmatches) == 0)
8971e895c5Smillert 			notfound++;
9071e895c5Smillert 
9184d7e2a2Sschwarze 	return ((notfound == 0) ? 0 : ((notfound == argc) ? 2 : 1));
9271e895c5Smillert }
9371e895c5Smillert 
9471e895c5Smillert int
findprog(char * prog,char * path,int progmode,int allmatches)9568c5532dSmillert findprog(char *prog, char *path, int progmode, int allmatches)
9671e895c5Smillert {
97b9fc9a72Sderaadt 	char *p, filename[PATH_MAX];
987fc79ef4Smillert 	int len, rval = 0;
9971e895c5Smillert 	struct stat sbuf;
1002efdc4fcSfgsch 	char *pathcpy;
10171e895c5Smillert 
10271e895c5Smillert 	/* Special case if prog contains '/' */
10371e895c5Smillert 	if (strchr(prog, '/')) {
10471e895c5Smillert 		if ((stat(prog, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
10571e895c5Smillert 		    access(prog, X_OK) == 0) {
10671e895c5Smillert 			(void)puts(prog);
10771e895c5Smillert 			return (1);
10871e895c5Smillert 		} else {
109f97c4ca6Ssobrado 			warnx("%s: Command not found.", prog);
11071e895c5Smillert 			return (0);
11171e895c5Smillert 		}
11271e895c5Smillert 	}
11371e895c5Smillert 
11471e895c5Smillert 	if ((path = strdup(path)) == NULL)
115f97c4ca6Ssobrado 		err(1, "strdup");
1162efdc4fcSfgsch 	pathcpy = path;
11771e895c5Smillert 
1182efdc4fcSfgsch 	while ((p = strsep(&pathcpy, ":")) != NULL) {
11971e895c5Smillert 		if (*p == '\0')
12071e895c5Smillert 			p = ".";
12171e895c5Smillert 
1227fc79ef4Smillert 		len = strlen(p);
1237fc79ef4Smillert 		while (len > 0 && p[len-1] == '/')
1247fc79ef4Smillert 			p[--len] = '\0';	/* strip trailing '/' */
12571e895c5Smillert 
1267fc79ef4Smillert 		len = snprintf(filename, sizeof(filename), "%s/%s", p, prog);
1277fc79ef4Smillert 		if (len < 0 || len >= sizeof(filename)) {
1285ad04d35Sguenther 			warnc(ENAMETOOLONG, "%s/%s", p, prog);
1294001bc03Smillert 			free(path);
13071e895c5Smillert 			return (0);
13171e895c5Smillert 		}
13271e895c5Smillert 		if ((stat(filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
13371e895c5Smillert 		    access(filename, X_OK) == 0) {
13471e895c5Smillert 			(void)puts(filename);
135d27b3816Smillert 			rval = 1;
1364001bc03Smillert 			if (!allmatches) {
1374001bc03Smillert 				free(path);
138d27b3816Smillert 				return (rval);
13971e895c5Smillert 			}
14071e895c5Smillert 		}
1414001bc03Smillert 	}
14271e895c5Smillert 	(void)free(path);
14371e895c5Smillert 
1443d908e8eSmillert 	/* whereis(1) is silent on failure. */
145d27b3816Smillert 	if (!rval && progmode != PROG_WHEREIS)
146f97c4ca6Ssobrado 		warnx("%s: Command not found.", prog);
147d27b3816Smillert 	return (rval);
14871e895c5Smillert }
14971e895c5Smillert 
15084d7e2a2Sschwarze static void __dead
usage(void)15168c5532dSmillert usage(void)
15271e895c5Smillert {
1534bdcd61aSsobrado 	(void)fprintf(stderr, "usage: %s [-a] name ...\n", __progname);
15471e895c5Smillert 	exit(1);
15571e895c5Smillert }
156