rc

[fork] interactive rc shell
git clone https://hhvn.uk/rc
git clone git://hhvn.uk/rc
Log | Files | Refs | README | LICENSE

which.c (3350B)


      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 accommodate 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 "rc.h"
     12 
     13 #include <ctype.h>
     14 #include <errno.h>
     15 #include <sys/stat.h>
     16 
     17 #include "getgroups.h"
     18 
     19 #define X_USR 0100
     20 #define X_GRP 0010
     21 #define X_OTH 0001
     22 #define X_ALL (X_USR|X_GRP|X_OTH)
     23 
     24 static bool initialized = FALSE;
     25 static uid_t uid;
     26 static gid_t gid;
     27 
     28 #if HAVE_GETGROUPS
     29 static int ngroups;
     30 static GETGROUPS_T *gidset;
     31 
     32 /* determine whether gid lies in gidset */
     33 
     34 static int ingidset(gid_t g) {
     35 	int i;
     36 	for (i = 0; i < ngroups; ++i)
     37 		if (g == gidset[i])
     38 			return 1;
     39 	return 0;
     40 }
     41 #else
     42 #define ingidset(g) (FALSE)
     43 #endif
     44 
     45 /*
     46    A home-grown access/stat. Does the right thing for group-executable files.
     47    Returns a bool instead of this -1 nonsense.
     48 */
     49 
     50 bool rc_access(char *path, bool verbose, struct stat *stp) {
     51 	int mask;
     52 	if (stat(path, stp) != 0) {
     53 		if (verbose) /* verbose flag only set for absolute pathname */
     54 			uerror(path);
     55 		return FALSE;
     56 	}
     57 	if (uid == 0)
     58 		mask = X_ALL;
     59 	else if (uid == stp->st_uid)
     60 		mask = X_USR;
     61 	else if (gid == stp->st_gid || ingidset(stp->st_gid))
     62 		mask = X_GRP;
     63 	else
     64 		mask = X_OTH;
     65 	if (((stp->st_mode & S_IFMT) == S_IFREG) && (stp->st_mode & mask))
     66 		return TRUE;
     67 	errno = EACCES;
     68 	if (verbose)
     69 		uerror(path);
     70 	return FALSE;
     71 }
     72 
     73 /* replace non-printing characters with question marks in a freshly
     74  * allocated string */
     75 static char *protect(char *in) {
     76 	int l = strlen(in);
     77 	char *out = ealloc(l + 1);
     78 	int i;
     79 
     80 	for (i = 0; i < l; ++i)
     81 		out[i] = isprint(in[i]) ? in[i] : '?';
     82 	out[i] = '\0';
     83 	return out;
     84 }
     85 
     86 /* return a full pathname by searching $path, and by checking the status of the file */
     87 
     88 extern char *which(char *name, bool verbose) {
     89 	static char *test = NULL;
     90 	static size_t testlen = 0;
     91 	List *path;
     92 	int len;
     93 	struct stat st;
     94 	if (name == NULL)	/* no filename? can happen with "> foo" as a command */
     95 		return NULL;
     96 	if (!initialized) {
     97 		initialized = TRUE;
     98 		uid = geteuid();
     99 		gid = getegid();
    100 #if HAVE_GETGROUPS
    101 #if HAVE_POSIX_GETGROUPS
    102 		ngroups = getgroups(0, (GETGROUPS_T *)0);
    103 		if (ngroups < 0) {
    104 			uerror("getgroups");
    105 			rc_exit(1);
    106 		}
    107 #else
    108 		ngroups = NGROUPS;
    109 #endif
    110 		if (ngroups) {	
    111 			gidset = ealloc(ngroups * sizeof(GETGROUPS_T));
    112 			getgroups(ngroups, gidset);
    113 		}
    114 #endif
    115 	}
    116 	if (isabsolute(name)) /* absolute pathname? */
    117 		return rc_access(name, verbose, &st) ? name : NULL;
    118 	len = strlen(name);
    119 	for (path = varlookup("path"); path != NULL; path = path->n) {
    120 		size_t need = strlen(path->w) + len + 2; /* one for null terminator, one for the '/' */
    121 		if (testlen < need) {
    122 			efree(test);
    123 			test = ealloc(testlen = need);
    124 		}
    125 		if (*path->w == '\0') {
    126 			strcpy(test, name);
    127 		} else {
    128 			strcpy(test, path->w);
    129 			if (!streq(test, "/")) /* "//" is special to POSIX */
    130 				strcat(test, "/");
    131 			strcat(test, name);
    132 		}
    133 		if (rc_access(test, FALSE, &st))
    134 			return test;
    135 	}
    136 	if (verbose) {
    137 		char *n = protect(name);
    138 		fprint(2, RC "cannot find `%s'\n", n);
    139 		efree(n);
    140 	}
    141 	return NULL;
    142 }