1 /* $OpenBSD: which.c,v 1.27 2019/01/25 00:19:27 millert Exp $ */
2
3 /*
4 * Copyright (c) 1997 Todd C. Miller <millert@openbsd.org>
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/stat.h>
20 #include <sys/sysctl.h>
21
22 #include <err.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <paths.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #define PROG_WHICH 1
32 #define PROG_WHEREIS 2
33
34 extern char *__progname;
35
36 int findprog(char *, char *, int, int);
37 static void __dead usage(void);
38
39 /*
40 * which(1) -- find an executable(s) in the user's path
41 * whereis(1) -- find an executable(s) in the default user path
42 *
43 * Return values:
44 * 0 - all executables found
45 * 1 - some found, some not
46 * 2 - none found
47 */
48
49 int
main(int argc,char * argv[])50 main(int argc, char *argv[])
51 {
52 char *path;
53 size_t n;
54 int ch, allmatches = 0, notfound = 0, progmode = PROG_WHICH;
55
56 while ((ch = getopt(argc, argv, "a")) != -1)
57 switch (ch) {
58 case 'a':
59 allmatches = 1;
60 break;
61 default:
62 usage();
63 }
64 argc -= optind;
65 argv += optind;
66
67 if (argc == 0)
68 usage();
69
70 if (strcmp(__progname, "whereis") == 0) {
71 progmode = PROG_WHEREIS;
72 path = _PATH_STDPATH;
73 } else {
74 if ((path = getenv("PATH")) == NULL || *path == '\0')
75 path = _PATH_DEFPATH;
76 }
77
78 /* To make access(2) do what we want */
79 if (setgid(getegid()))
80 err(1, "Can't set gid to %u", getegid());
81 if (setuid(geteuid()))
82 err(1, "Can't set uid to %u", geteuid());
83
84 if (pledge("stdio rpath", NULL) == -1)
85 err(2, "pledge");
86
87 for (n = 0; n < argc; n++)
88 if (findprog(argv[n], path, progmode, allmatches) == 0)
89 notfound++;
90
91 return ((notfound == 0) ? 0 : ((notfound == argc) ? 2 : 1));
92 }
93
94 int
findprog(char * prog,char * path,int progmode,int allmatches)95 findprog(char *prog, char *path, int progmode, int allmatches)
96 {
97 char *p, filename[PATH_MAX];
98 int len, 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 while ((p = strsep(&pathcpy, ":")) != NULL) {
119 if (*p == '\0')
120 p = ".";
121
122 len = strlen(p);
123 while (len > 0 && p[len-1] == '/')
124 p[--len] = '\0'; /* strip trailing '/' */
125
126 len = snprintf(filename, sizeof(filename), "%s/%s", p, prog);
127 if (len < 0 || len >= sizeof(filename)) {
128 warnc(ENAMETOOLONG, "%s/%s", p, prog);
129 free(path);
130 return (0);
131 }
132 if ((stat(filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) &&
133 access(filename, X_OK) == 0) {
134 (void)puts(filename);
135 rval = 1;
136 if (!allmatches) {
137 free(path);
138 return (rval);
139 }
140 }
141 }
142 (void)free(path);
143
144 /* whereis(1) is silent on failure. */
145 if (!rval && progmode != PROG_WHEREIS)
146 warnx("%s: Command not found.", prog);
147 return (rval);
148 }
149
150 static void __dead
usage(void)151 usage(void)
152 {
153 (void)fprintf(stderr, "usage: %s [-a] name ...\n", __progname);
154 exit(1);
155 }
156