#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <grp.h>
#include <errno.h>
#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp.h>
#include <rpcsvc/ypclnt.h>
#include "ypinternal.h"
#include "ypexclude.h"
#endif
#include "thread_private.h"
_THREAD_PRIVATE_KEY(gr_storage);
static struct group_storage {
#define MAXGRP 200
char *members[MAXGRP];
#define MAXLINELENGTH 1024
char line[MAXLINELENGTH];
} gr_storage;
#define GETGR_R_SIZE_MAX _GR_BUF_LEN
_THREAD_PRIVATE_KEY(gr);
static FILE *_gr_fp;
static struct group _gr_group;
static int _gr_stayopen;
static int grscan(int, gid_t, const char *, struct group *, struct group_storage *,
int *);
static int start_gr(void);
static void endgrent_basic(void);
static struct group *getgrnam_gs(const char *, struct group *,
struct group_storage *);
static struct group *getgrgid_gs(gid_t, struct group *,
struct group_storage *);
#ifdef YP
static struct _ypexclude *__ypexhead = NULL;
static int __ypmode = 0;
static char *__ypcurrent, *__ypdomain;
static int __ypcurrentlen;
#endif
struct group *
_getgrent_yp(int *foundyp)
{
struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL);
struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
gr_storage, NULL);
_THREAD_PRIVATE_MUTEX_LOCK(gr);
if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL, p_gr, gs, foundyp))
p_gr = NULL;
_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
return (p_gr);
}
struct group *
getgrent(void)
{
return (_getgrent_yp(NULL));
}
static struct group *
getgrnam_gs(const char *name, struct group *p_gr, struct group_storage *gs)
{
int rval;
_THREAD_PRIVATE_MUTEX_LOCK(gr);
if (!start_gr())
rval = 0;
else {
rval = grscan(1, 0, name, p_gr, gs, NULL);
if (!_gr_stayopen)
endgrent_basic();
}
_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
return(rval ? p_gr : NULL);
}
struct group *
getgrnam(const char *name)
{
struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr,_gr_group,NULL);
struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
gr_storage, NULL);
return getgrnam_gs(name, p_gr, gs);
}
int
getgrnam_r(const char *name, struct group *grp, char *buffer,
size_t bufsize, struct group **result)
{
int errnosave;
int ret;
if (bufsize < GETGR_R_SIZE_MAX)
return ERANGE;
errnosave = errno;
errno = 0;
*result = getgrnam_gs(name, grp, (struct group_storage *)buffer);
if (*result == NULL)
ret = errno;
else
ret = 0;
errno = errnosave;
return ret;
}
DEF_WEAK(getgrnam_r);
static struct group *
getgrgid_gs(gid_t gid, struct group *p_gr, struct group_storage *gs)
{
int rval;
_THREAD_PRIVATE_MUTEX_LOCK(gr);
if (!start_gr())
rval = 0;
else {
rval = grscan(1, gid, NULL, p_gr, gs, NULL);
if (!_gr_stayopen)
endgrent_basic();
}
_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
return(rval ? p_gr : NULL);
}
struct group *
getgrgid(gid_t gid)
{
struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL);
struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage,
gr_storage, NULL);
return getgrgid_gs(gid, p_gr, gs);
}
int
getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
struct group **result)
{
int errnosave;
int ret;
if (bufsize < GETGR_R_SIZE_MAX)
return ERANGE;
errnosave = errno;
errno = 0;
*result = getgrgid_gs(gid, grp, (struct group_storage *)buffer);
if (*result == NULL)
ret = errno;
else
ret = 0;
errno = errnosave;
return ret;
}
DEF_WEAK(getgrgid_r);
static int
start_gr(void)
{
int fd;
if (_gr_fp) {
rewind(_gr_fp);
#ifdef YP
__ypmode = 0;
free(__ypcurrent);
__ypcurrent = NULL;
if (__ypexhead)
__ypexclude_free(&__ypexhead);
__ypexhead = NULL;
#endif
return(1);
}
fd = __pledge_open(_PATH_GROUP, O_RDONLY|O_CLOEXEC);
if (fd == -1)
return 0;
if ((_gr_fp = fdopen(fd, "r")) == NULL)
close(fd);
return (_gr_fp ? 1 : 0);
}
void
setgrent(void)
{
int saved_errno;
saved_errno = errno;
setgroupent(0);
errno = saved_errno;
}
DEF_WEAK(setgrent);
int
setgroupent(int stayopen)
{
int retval;
_THREAD_PRIVATE_MUTEX_LOCK(gr);
if (!start_gr())
retval = 0;
else {
_gr_stayopen = stayopen;
retval = 1;
}
_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
return (retval);
}
DEF_WEAK(setgroupent);
static
void
endgrent_basic(void)
{
int saved_errno;
if (_gr_fp) {
saved_errno = errno;
fclose(_gr_fp);
_gr_fp = NULL;
#ifdef YP
__ypmode = 0;
free(__ypcurrent);
__ypcurrent = NULL;
if (__ypexhead)
__ypexclude_free(&__ypexhead);
__ypexhead = NULL;
#endif
errno = saved_errno;
}
}
void
endgrent(void)
{
_THREAD_PRIVATE_MUTEX_LOCK(gr);
endgrent_basic();
_THREAD_PRIVATE_MUTEX_UNLOCK(gr);
}
DEF_WEAK(endgrent);
static int
grscan(int search, gid_t gid, const char *name, struct group *p_gr,
struct group_storage *gs, int *foundyp)
{
char *cp, **m;
char *bp, *endp;
u_long ul;
#ifdef YP
char *key, *data;
int keylen, datalen;
int r;
#endif
char **members;
char *line;
int saved_errno;
if (gs == NULL)
return 0;
members = gs->members;
line = gs->line;
saved_errno = errno;
for (;;) {
#ifdef YP
if (__ypmode) {
if (__ypcurrent) {
r = yp_next(__ypdomain, "group.byname",
__ypcurrent, __ypcurrentlen,
&key, &keylen, &data, &datalen);
free(__ypcurrent);
__ypcurrent = key;
__ypcurrentlen = keylen;
} else {
r = yp_first(__ypdomain, "group.byname",
&__ypcurrent, &__ypcurrentlen,
&data, &datalen);
}
if (r) {
__ypmode = 0;
__ypcurrent = NULL;
if (r == YPERR_NOMORE)
continue;
else
return 0;
}
bcopy(data, line, datalen);
free(data);
line[datalen] = '\0';
bp = line;
goto parse;
}
#endif
if (!fgets(line, sizeof(gs->line), _gr_fp)) {
if (feof(_gr_fp) && !ferror(_gr_fp))
errno = saved_errno;
return 0;
}
bp = line;
if (!strchr(line, '\n')) {
int ch;
while ((ch = getc_unlocked(_gr_fp)) != '\n' &&
ch != EOF)
;
continue;
}
#ifdef YP
if (line[0] == '+' || line[0] == '-') {
if (!__ypdomain)
yp_get_default_domain(&__ypdomain);
}
if (line[0] == '+') {
switch (line[1]) {
case ':':
case '\0':
case '\n':
if (foundyp) {
*foundyp = 1;
errno = saved_errno;
return 0;
}
if (!search) {
__ypmode = 1;
continue;
}
if (name) {
r = yp_match(__ypdomain,
"group.byname", name, strlen(name),
&data, &datalen);
} else {
char buf[20];
snprintf(buf, sizeof buf, "%u", gid);
r = yp_match(__ypdomain, "group.bygid",
buf, strlen(buf), &data, &datalen);
}
switch (r) {
case 0:
break;
case YPERR_KEY:
continue;
default:
return 0;
}
bcopy(data, line, datalen);
free(data);
line[datalen] = '\0';
bp = line;
p_gr->gr_name = strsep(&bp, ":\n");
if (__ypexclude_is(&__ypexhead, p_gr->gr_name))
continue;
p_gr->gr_passwd = strsep(&bp, ":\n");
if (!(cp = strsep(&bp, ":\n")))
continue;
if (name) {
ul = strtoul(cp, &endp, 10);
if (*endp != '\0' || endp == cp ||
ul >= GID_MAX)
continue;
p_gr->gr_gid = ul;
} else
p_gr->gr_gid = gid;
goto found_it;
default:
bp = strsep(&bp, ":\n") + 1;
if ((search && name && strcmp(bp, name)) ||
__ypexclude_is(&__ypexhead, bp))
continue;
r = yp_match(__ypdomain, "group.byname",
bp, strlen(bp), &data, &datalen);
switch (r) {
case 0:
break;
case YPERR_KEY:
continue;
default:
return 0;
}
bcopy(data, line, datalen);
free(data);
line[datalen] = '\0';
bp = line;
}
} else if (line[0] == '-') {
if (__ypexclude_add(&__ypexhead,
strsep(&line, ":\n") + 1))
return 0;
if (foundyp) {
*foundyp = -1;
errno = saved_errno;
return 0;
}
continue;
}
parse:
#endif
p_gr->gr_name = strsep(&bp, ":\n");
if (search && name && strcmp(p_gr->gr_name, name))
continue;
#ifdef YP
if (__ypmode && __ypexclude_is(&__ypexhead, p_gr->gr_name))
continue;
#endif
p_gr->gr_passwd = strsep(&bp, ":\n");
if (!(cp = strsep(&bp, ":\n")))
continue;
ul = strtoul(cp, &endp, 10);
if (endp == cp || *endp != '\0' || ul >= GID_MAX)
continue;
p_gr->gr_gid = ul;
if (search && name == NULL && p_gr->gr_gid != gid)
continue;
#ifdef YP
found_it:
#endif
cp = NULL;
if (bp == NULL)
continue;
for (m = p_gr->gr_mem = members;; bp++) {
if (m == &members[MAXGRP - 1])
break;
if (*bp == ',') {
if (cp) {
*bp = '\0';
*m++ = cp;
cp = NULL;
}
} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
if (cp) {
*bp = '\0';
*m++ = cp;
}
break;
} else if (cp == NULL)
cp = bp;
}
*m = NULL;
errno = saved_errno;
return 1;
}
}