BSD 4_4_Lite1 development
[unix-history] / usr / src / contrib / rc-1.4 / which.c
CommitLineData
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
22extern int stat(const char *, struct stat *);
23
24static bool initialized = FALSE;
25static int uid, gid;
26
27static int ngroups, gidset[NGROUPS];
28
29/* determine whether gid lies in gidset */
30
31static 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
44static 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
70extern 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}