/* * pwsearch(name, uid, pwline) * search for name (if non null) or uid in password. return ptr to passwd structure. if pwline is non null return whole password line. */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <pwd.h> #define PASSWD "/etc/passwd" #define PWLINE 500 #define CHUNK 64 /* allocation of hint table -- must be a power of two */ static struct hint { long hint; char *name; short uid; } *htab; static int htabsize = 0; static char buf[PWLINE]; static FILE *pwf; static time_t pwtime; struct passwd *pwdecode(); char *malloc(), *realloc(), *strcpy(); static int buildtable(); struct passwd * pwsearch(name, uid, pwline) char *name, *pwline; { register int min, max, mid; register struct passwd *pw; int ntries = 0; struct stat sbuf; again: if (ntries>1) return(NULL); if (pwf) fstat(fileno(pwf), &sbuf); if (pwf==NULL || ntries || pwtime < sbuf.st_ctime) { buildtable(); pwtime = sbuf.st_ctime; } ntries++; if (name) { /* binary search for name */ min = 0; max = htabsize - 1; while (min <= max) { register n; mid = (min + max) >> 1; n = strcmp (name, htab[mid].name); if (n == 0) break; if (n < 0) max = mid - 1; else min = mid + 1; } if (min > max) goto again; } else { /* linear search for uid */ for (mid = 0; mid <htabsize; mid++) if (htab[mid].uid == uid) break; if (mid == htabsize) goto again; } /* * We have a hint. Seek to the given point in the file. * If it's not the very beginning, ensure it is the start * of a line by peeking at the preceding character. */ if (htab[mid].hint) { fseek(pwf, htab[mid].hint-1, 0); if (getc(pwf) != '\n') goto again; } else fseek(pwf, htab[mid].hint, 0); if (fgets(buf, PWLINE, pwf) == NULL) goto again; if (pwline) strcpy(pwline, buf); pw = pwdecode(buf); if (name) { if (strcmp(name, pw->pw_name)) goto again; } else if (uid != pw->pw_uid) goto again; return(pw); } /* comparison function for call to qsort */ static int comp (p, q) struct hint *p, *q; { return(strcmp (p->name, q->name)); } static int buildtable() { long where; register int i; /* free the old hint table */ for (i = 0; i < htabsize; i++) { if (htab[i].name) { (void)free((char *)htab[i].name); htab[i].name = NULL; } } if (htab) { (void)free((char *)htab); htab = NULL; } htabsize = 0; if (pwf) fclose(pwf); pwf = fopen(PASSWD, "r"); if (pwf == NULL) return; while (where = ftell(pwf), fgets(buf, PWLINE, pwf) != NULL) { register struct passwd *pw = pwdecode(buf); register char *p = malloc(strlen(pw->pw_name) + 1); if (p == NULL) return; /* time to expand the hint table? */ if ((htabsize & (CHUNK - 1)) == 0) { unsigned s = (htabsize + CHUNK) * sizeof(struct hint); if (htab) htab = (struct hint *) realloc((char *)htab, s); else htab = (struct hint *) malloc(s); if (htab == NULL) { htabsize = 0; return; } } htab[htabsize].name = strcpy(p, pw->pw_name); htab[htabsize].uid = pw->pw_uid; htab[htabsize].hint = where; htabsize++; } qsort((char *) htab, htabsize, sizeof (struct hint), comp); } pwclose() { if (pwf) fclose(pwf); }