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