Commit | Line | Data |
---|---|---|
d310bb81 C |
1 | /* which.c: check to see if a file is executable. |
2 | ||
3 | This function was originally written with Maarten Litmaath's which.c as | |
4 | a template, but was changed in order to accomodate the possibility of | |
5 | rc's running setuid or the possibility of executing files not in the | |
6 | primary group. Much of this file has been re-vamped by Paul Haahr. | |
7 | I re-re-vamped the functions that Paul supplied to correct minor bugs | |
8 | and to strip out unneeded functionality. | |
9 | */ | |
10 | ||
11 | #include <sys/types.h> | |
12 | #include <sys/stat.h> | |
13 | #include <sys/param.h> | |
14 | #include <errno.h> | |
15 | #include "rc.h" | |
16 | ||
17 | #define X_USR 0100 | |
18 | #define X_GRP 0010 | |
19 | #define X_OTH 0001 | |
20 | #define X_ALL (X_USR|X_GRP|X_OTH) | |
21 | ||
22 | extern int stat(const char *, struct stat *); | |
23 | ||
24 | static bool initialized = FALSE; | |
25 | static int uid, gid; | |
26 | ||
27 | static int ngroups, gidset[NGROUPS]; | |
28 | ||
29 | /* determine whether gid lies in gidset */ | |
30 | ||
31 | static int ingidset(int g) { | |
32 | int i; | |
33 | for (i = 0; i < ngroups; i++) | |
34 | if (g == gidset[i]) | |
35 | return 1; | |
36 | return 0; | |
37 | } | |
38 | ||
39 | /* | |
40 | A home-grown access/stat. Does the right thing for group-executable files. | |
41 | Returns a bool instead of this -1 nonsense. | |
42 | */ | |
43 | ||
44 | static bool rc_access(char *path, bool verbose) { | |
45 | struct stat st; | |
46 | int mask; | |
47 | if (stat(path, &st) != 0) { | |
48 | if (verbose) /* verbose flag only set for absolute pathname */ | |
49 | uerror(path); | |
50 | return FALSE; | |
51 | } | |
52 | if (uid == 0) | |
53 | mask = X_ALL; | |
54 | else if (uid == st.st_uid) | |
55 | mask = X_USR; | |
56 | else if (gid == st.st_gid || ingidset(st.st_gid)) | |
57 | mask = X_GRP; | |
58 | else | |
59 | mask = X_OTH; | |
60 | if (((st.st_mode & S_IFMT) == S_IFREG) && (st.st_mode & mask)) | |
61 | return TRUE; | |
62 | errno = EACCES; | |
63 | if (verbose) | |
64 | uerror(path); | |
65 | return FALSE; | |
66 | } | |
67 | ||
68 | /* return a full pathname by searching $path, and by checking the status of the file */ | |
69 | ||
70 | extern char *which(char *name, bool verbose) { | |
71 | static char *test = NULL; | |
72 | static size_t testlen = 0; | |
73 | List *path; | |
74 | int len; | |
75 | if (name == NULL) /* no filename? can happen with "> foo" as a command */ | |
76 | return NULL; | |
77 | if (!initialized) { | |
78 | initialized = TRUE; | |
79 | uid = geteuid(); | |
80 | gid = getegid(); | |
81 | ngroups = getgroups(NGROUPS, gidset); | |
82 | } | |
83 | if (isabsolute(name)) /* absolute pathname? */ | |
84 | return rc_access(name, verbose) ? name : NULL; | |
85 | len = strlen(name); | |
86 | for (path = varlookup("path"); path != NULL; path = path->n) { | |
87 | size_t need = strlen(path->w) + len + 2; /* one for null terminator, one for the '/' */ | |
88 | if (testlen < need) { | |
89 | efree(test); | |
90 | test = ealloc(testlen = need); | |
91 | } | |
92 | if (*path->w == '\0') { | |
93 | strcpy(test, name); | |
94 | } else { | |
95 | strcpy(test, path->w); | |
96 | if (!streq(test, "/")) /* "//" is special to POSIX */ | |
97 | strcat(test, "/"); | |
98 | strcat(test, name); | |
99 | } | |
100 | if (rc_access(test, FALSE)) | |
101 | return test; | |
102 | } | |
103 | if (verbose) | |
104 | fprint(2, "%s not found\n", name); | |
105 | return NULL; | |
106 | } |