NetBSD/lib/libc/gen/getgrent.c

1842 lines
42 KiB
C

/* $NetBSD: getgrent.c,v 1.53 2004/11/10 12:57:32 lukem Exp $ */
/*-
* Copyright (c) 1999, 2000, 2004 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Luke Mewburn.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Portions Copyright (c) 1994, Jason Downs. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94";
#else
__RCSID("$NetBSD: getgrent.c,v 1.53 2004/11/10 12:57:32 lukem Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include "reentrant.h"
#include <sys/param.h>
#include <assert.h>
#include <errno.h>
#include <grp.h>
#include <limits.h>
#include <nsswitch.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#ifdef HESIOD
#include <hesiod.h>
#endif
#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#endif
#define _GROUP_COMPAT /* "group" defaults to compat, so always provide it */
#ifdef __weak_alias
__weak_alias(endgrent,_endgrent)
__weak_alias(getgrent,_getgrent)
__weak_alias(getgrgid,_getgrgid)
__weak_alias(getgrgid_r,_getgrgid_r)
__weak_alias(getgrnam,_getgrnam)
__weak_alias(getgrnam_r,_getgrnam_r)
__weak_alias(setgrent,_setgrent)
__weak_alias(setgroupent,_setgroupent)
#endif
#ifdef _REENTRANT
static mutex_t _grmutex = MUTEX_INITIALIZER;
#endif
/*
* _gr_memfrombuf
* Obtain want bytes from buffer (of size buflen) and return a pointer
* to the available memory after adjusting buffer/buflen.
* Returns NULL if there is insufficient space.
*/
static char *
_gr_memfrombuf(size_t want, char **buffer, size_t *buflen)
{
char *rv;
if (want > *buflen) {
errno = ERANGE;
return NULL;
}
rv = *buffer;
*buffer += want;
*buflen -= want;
return rv;
}
/*
* _gr_parse
* Parses entry as a line per group(5) (without the trailing \n)
* and fills in grp with corresponding values; memory for strings
* and arrays will be allocated from buf (of size buflen).
* Returns 1 if parsed successfully, 0 on parse failure.
*/
static int
_gr_parse(const char *entry, struct group *grp, char *buf, size_t buflen)
{
unsigned long id;
const char *bp;
char *ep;
size_t count;
int memc;
_DIAGASSERT(entry != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buf != NULL);
#define COPYTOBUF(to) \
do { \
(to) = _gr_memfrombuf(count+1, &buf, &buflen); \
if ((to) == NULL) \
return 0; \
memmove((to), entry, count); \
to[count] = '\0'; \
} while (0) /* LINTED */
#if 0
if (*entry == '+') /* fail on compat `+' token */
return 0;
#endif
count = strcspn(entry, ":"); /* parse gr_name */
if (entry[count] == '\0')
return 0;
COPYTOBUF(grp->gr_name);
entry += count + 1;
count = strcspn(entry, ":"); /* parse gr_passwd */
if (entry[count] == '\0')
return 0;
COPYTOBUF(grp->gr_passwd);
entry += count + 1;
count = strcspn(entry, ":"); /* parse gr_gid */
if (entry[count] == '\0')
return 0;
id = strtoul(entry, &ep, 10);
if (id > GID_MAX || *ep != ':')
return 0;
grp->gr_gid = (gid_t)id;
entry += count + 1;
memc = 1; /* for final NULL */
if (*entry != '\0')
memc++; /* for first item */
for (bp = entry; *bp != '\0'; bp++) {
if (*bp == ',')
memc++;
}
/* grab ALIGNed char **gr_mem from buf */
ep = _gr_memfrombuf(memc * sizeof(char *) + ALIGNBYTES, &buf, &buflen);
grp->gr_mem = (char **)ALIGN(ep);
if (grp->gr_mem == NULL)
return 0;
for (memc = 0; *entry != '\0'; memc++) {
count = strcspn(entry, ","); /* parse member */
COPYTOBUF(grp->gr_mem[memc]);
entry += count;
if (*entry == ',')
entry++;
}
#undef COPYTOBUF
grp->gr_mem[memc] = NULL;
return 1;
}
/*
* _gr_copy
* Copy the contents of fromgrp to grp; memory for strings
* and arrays will be allocated from buf (of size buflen).
* Returns 1 if copied successfully, 0 on copy failure.
* NOTE: fromgrp must not use buf for its own pointers.
*/
static int
_gr_copy(struct group *fromgrp, struct group *grp, char *buf, size_t buflen)
{
char *ep;
int memc;
_DIAGASSERT(fromgrp != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buf != NULL);
#define COPYSTR(to, from) \
do { \
size_t count = strlen((from)); \
(to) = _gr_memfrombuf(count+1, &buf, &buflen); \
if ((to) == NULL) \
return 0; \
memmove((to), (from), count); \
to[count] = '\0'; \
} while (0) /* LINTED */
COPYSTR(grp->gr_name, fromgrp->gr_name);
COPYSTR(grp->gr_passwd, fromgrp->gr_passwd);
grp->gr_gid = fromgrp->gr_gid;
for (memc = 0; fromgrp->gr_mem[memc]; memc++)
continue;
memc++; /* for final NULL */
/* grab ALIGNed char **gr_mem from buf */
ep = _gr_memfrombuf(memc * sizeof(char *) + ALIGNBYTES, &buf, &buflen);
grp->gr_mem = (char **)ALIGN(ep);
if (grp->gr_mem == NULL)
return 0;
for (memc = 0; fromgrp->gr_mem[memc]; memc++) {
COPYSTR(grp->gr_mem[memc], fromgrp->gr_mem[memc]);
}
#undef COPYSTR
grp->gr_mem[memc] = NULL;
return 1;
}
/*
* files methods
*/
/* state shared between files methods */
struct files_state {
int stayopen; /* see getgroupent(3) */
FILE *fp; /* groups file handle */
};
static struct files_state _files_state;
/* storage for non _r functions */
static struct group _files_group;
static char _files_groupbuf[_GETGR_R_SIZE_MAX];
static int
_files_start(struct files_state *state)
{
_DIAGASSERT(state != NULL);
if (state->fp == NULL) {
state->fp = fopen(_PATH_GROUP, "r");
if (state->fp == NULL)
return NS_UNAVAIL;
} else {
rewind(state->fp);
}
return NS_SUCCESS;
}
static int
_files_end(struct files_state *state)
{
_DIAGASSERT(state != NULL);
if (state->fp) {
(void) fclose(state->fp);
state->fp = NULL;
}
return NS_SUCCESS;
}
/*
* _files_grscan
* Scan state->fp for the next desired entry.
* If search is zero, return the next entry.
* If search is non-zero, look for a specific name (if name != NULL),
* or a specific gid (if name == NULL).
* Sets *retval to the errno if the result is not NS_SUCCESS.
*/
static int
_files_grscan(int *retval, struct group *grp, char *buffer, size_t buflen,
struct files_state *state, int search, const char *name, gid_t gid)
{
int rv;
char filebuf[_GETGR_R_SIZE_MAX], *ep;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
/* name is NULL to indicate searching for gid */
*retval = 0;
if (state->fp == NULL) { /* only start if file not open yet */
rv = _files_start(state);
if (rv != NS_SUCCESS)
goto filesgrscan_out;
}
rv = NS_NOTFOUND;
/* scan line by line */
while (fgets(filebuf, sizeof(filebuf), state->fp) != NULL) {
ep = strchr(filebuf, '\n');
if (ep == NULL) { /* fail on lines that are too big */
int ch;
while ((ch = getc(state->fp)) != '\n' && ch != EOF)
continue;
rv = NS_UNAVAIL;
break;
}
*ep = '\0'; /* clear trailing \n */
if (filebuf[0] == '+') /* skip compat line */
continue;
/* validate line */
if (! _gr_parse(filebuf, grp, buffer, buflen)) {
rv = NS_UNAVAIL;
break;
}
if (! search) { /* just want this one */
rv = NS_SUCCESS;
break;
}
/* want specific */
if ((name && strcmp(name, grp->gr_name) == 0) ||
(!name && gid == grp->gr_gid)) {
rv = NS_SUCCESS;
break;
}
}
filesgrscan_out:
if (rv != NS_SUCCESS)
*retval = errno;
return rv;
}
/*ARGSUSED*/
static int
_files_setgrent(void *nsrv, void *nscb, va_list ap)
{
_files_state.stayopen = 0;
return _files_start(&_files_state);
}
/*ARGSUSED*/
static int
_files_setgroupent(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
int stayopen = va_arg(ap, int);
int rv;
_files_state.stayopen = stayopen;
rv = _files_start(&_files_state);
*retval = (rv == NS_SUCCESS);
return rv;
}
/*ARGSUSED*/
static int
_files_endgrent(void *nsrv, void *nscb, va_list ap)
{
_files_state.stayopen = 0;
return _files_end(&_files_state);
}
/*ARGSUSED*/
static int
_files_getgrent(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _files_grscan(&rerror, &_files_group,
_files_groupbuf, sizeof(_files_groupbuf),
&_files_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*retval = &_files_group;
return rv;
}
/*ARGSUSED*/
static int
_files_getgrgid(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
gid_t gid = va_arg(ap, gid_t);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _files_start(&_files_state);
if (rv != NS_SUCCESS)
return rv;
rv = _files_grscan(&rerror, &_files_group,
_files_groupbuf, sizeof(_files_groupbuf),
&_files_state, 1, NULL, gid);
if (!_files_state.stayopen)
_files_end(&_files_state);
if (rv == NS_SUCCESS)
*retval = &_files_group;
return rv;
}
/*ARGSUSED*/
static int
_files_getgrgid_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
gid_t gid = va_arg(ap, gid_t);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct files_state state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = _files_grscan(retval, grp, buffer, buflen, &state, 1, NULL, gid);
_files_end(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
/*ARGSUSED*/
static int
_files_getgrnam(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
const char *name = va_arg(ap, const char *);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _files_start(&_files_state);
if (rv != NS_SUCCESS)
return rv;
rv = _files_grscan(&rerror, &_files_group,
_files_groupbuf, sizeof(_files_groupbuf),
&_files_state, 1, name, 0);
if (!_files_state.stayopen)
_files_end(&_files_state);
if (rv == NS_SUCCESS)
*retval = &_files_group;
return rv;
}
/*ARGSUSED*/
static int
_files_getgrnam_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
const char *name = va_arg(ap, const char *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct files_state state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = _files_grscan(retval, grp, buffer, buflen, &state, 1, name, 0);
_files_end(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
#ifdef HESIOD
/*
* dns methods
*/
/* state shared between dns methods */
struct dns_state {
int stayopen; /* see getgroupent(3) */
void *context; /* Hesiod context */
int num; /* group index, -1 if no more */
};
static struct dns_state _dns_state;
/* storage for non _r functions */
static struct group _dns_group;
static char _dns_groupbuf[_GETGR_R_SIZE_MAX];
static int
_dns_start(struct dns_state *state)
{
_DIAGASSERT(state != NULL);
state->num = 0;
if (state->context == NULL) { /* setup Hesiod */
if (hesiod_init(&state->context) == -1)
return NS_UNAVAIL;
}
return NS_SUCCESS;
}
static int
_dns_end(struct dns_state *state)
{
_DIAGASSERT(state != NULL);
state->num = 0;
if (state->context) {
hesiod_end(state->context);
state->context = NULL;
}
return NS_SUCCESS;
}
/*
* _dns_grscan
* Look for the Hesiod name provided in buffer in the NULL-terminated
* list of zones,
* and decode into grp/buffer/buflen.
*/
static int
_dns_grscan(int *retval, struct group *grp, char *buffer, size_t buflen,
struct dns_state *state, const char **zones)
{
const char **curzone;
char **hp, *ep;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
_DIAGASSERT(zones != NULL);
*retval = 0;
if (state->context == NULL) { /* only start if Hesiod not setup */
rv = _dns_start(state);
if (rv != NS_SUCCESS)
return rv;
}
hp = NULL;
rv = NS_NOTFOUND;
for (curzone = zones; *curzone; curzone++) { /* search zones */
hp = hesiod_resolve(state->context, buffer, *curzone);
if (hp != NULL)
break;
if (errno != ENOENT) {
rv = NS_UNAVAIL;
goto dnsgrscan_out;
}
}
if (*curzone == NULL)
goto dnsgrscan_out;
if ((ep = strchr(hp[0], '\n')) != NULL)
*ep = '\0'; /* clear trailing \n */
if (_gr_parse(hp[0], grp, buffer, buflen)) /* validate line */
rv = NS_SUCCESS;
else
rv = NS_UNAVAIL;
dnsgrscan_out:
if (rv != NS_SUCCESS)
*retval = errno;
if (hp)
hesiod_free_list(state->context, hp);
return rv;
}
/*ARGSUSED*/
static int
_dns_setgrent(void *nsrv, void *nscb, va_list ap)
{
_dns_state.stayopen = 0;
return _dns_start(&_dns_state);
}
/*ARGSUSED*/
static int
_dns_setgroupent(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
int stayopen = va_arg(ap, int);
int rv;
_dns_state.stayopen = stayopen;
rv = _dns_start(&_dns_state);
*retval = (rv == NS_SUCCESS);
return rv;
}
/*ARGSUSED*/
static int
_dns_endgrent(void *nsrv, void *nscb, va_list ap)
{
_dns_state.stayopen = 0;
return _dns_end(&_dns_state);
}
/*ARGSUSED*/
static int
_dns_getgrent(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
char **hp, *ep;
int rv;
_DIAGASSERT(retval != NULL);
*retval = NULL;
if (_dns_state.num == -1) /* exhausted search */
return NS_NOTFOUND;
if (_dns_state.context == NULL) {
/* only start if Hesiod not setup */
rv = _dns_start(&_dns_state);
if (rv != NS_SUCCESS)
return rv;
}
hp = NULL;
rv = NS_NOTFOUND;
/* find group-NNN */
snprintf(_dns_groupbuf, sizeof(_dns_groupbuf),
"group-%u", _dns_state.num);
_dns_state.num++;
hp = hesiod_resolve(_dns_state.context, _dns_groupbuf, "group");
if (hp == NULL) {
if (errno == ENOENT)
_dns_state.num = -1;
else
rv = NS_UNAVAIL;
} else {
if ((ep = strchr(hp[0], '\n')) != NULL)
*ep = '\0'; /* clear trailing \n */
/* validate line */
if (_gr_parse(hp[0], &_dns_group,
_dns_groupbuf, sizeof(_dns_groupbuf)))
rv = NS_SUCCESS;
else
rv = NS_UNAVAIL;
}
if (hp)
hesiod_free_list(_dns_state.context, hp);
if (rv == NS_SUCCESS)
*retval = &_dns_group;
return rv;
}
static const char *_dns_gid_zones[] = {
"gid",
"group",
NULL
};
/*ARGSUSED*/
static int
_dns_getgrgid(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
gid_t gid = va_arg(ap, gid_t);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _dns_start(&_dns_state);
if (rv != NS_SUCCESS)
return rv;
snprintf(_dns_groupbuf, sizeof(_dns_groupbuf), "%u", (unsigned int)gid);
rv = _dns_grscan(&rerror, &_dns_group,
_dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, _dns_gid_zones);
if (!_dns_state.stayopen)
_dns_end(&_dns_state);
if (rv == NS_SUCCESS && gid == _dns_group.gr_gid)
*retval = &_dns_group;
return rv;
}
/*ARGSUSED*/
static int
_dns_getgrgid_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
gid_t gid = va_arg(ap, gid_t);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct dns_state state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
snprintf(buffer, buflen, "%u", (unsigned int)gid);
rv = _dns_grscan(retval, grp, buffer, buflen, &state, _dns_gid_zones);
_dns_end(&state);
if (rv != NS_SUCCESS)
return rv;
if (gid == grp->gr_gid) {
*result = grp;
return NS_SUCCESS;
} else
return NS_NOTFOUND;
}
static const char *_dns_nam_zones[] = {
"group",
NULL
};
/*ARGSUSED*/
static int
_dns_getgrnam(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
const char *name = va_arg(ap, const char *);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _dns_start(&_dns_state);
if (rv != NS_SUCCESS)
return rv;
snprintf(_dns_groupbuf, sizeof(_dns_groupbuf), "%s", name);
rv = _dns_grscan(&rerror, &_dns_group,
_dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, _dns_nam_zones);
if (!_dns_state.stayopen)
_dns_end(&_dns_state);
if (rv == NS_SUCCESS && strcmp(name, _dns_group.gr_name) == 0)
*retval = &_dns_group;
return rv;
}
/*ARGSUSED*/
static int
_dns_getgrnam_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
const char *name = va_arg(ap, const char *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct dns_state state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
snprintf(buffer, buflen, "%s", name);
rv = _dns_grscan(retval, grp, buffer, buflen, &state, _dns_nam_zones);
_dns_end(&state);
if (rv != NS_SUCCESS)
return rv;
if (strcmp(name, grp->gr_name) == 0) {
*result = grp;
return NS_SUCCESS;
} else
return NS_NOTFOUND;
}
#endif /* HESIOD */
#ifdef YP
/*
* nis methods
*/
/* state shared between nis methods */
struct nis_state {
int stayopen; /* see getgroupent(3) */
char *domain; /* NIS domain */
int done; /* non-zero if search exhausted */
char *current; /* current first/next match */
int currentlen; /* length of _nis_current */
};
static struct nis_state _nis_state;
/* storage for non _r functions */
static struct group _nis_group;
static char _nis_groupbuf[_GETGR_R_SIZE_MAX];
static int
_nis_start(struct nis_state *state)
{
_DIAGASSERT(state != NULL);
state->done = 0;
if (state->current) {
free(state->current);
state->current = NULL;
}
if (state->domain == NULL) { /* setup NIS */
switch (yp_get_default_domain(&state->domain)) {
case 0:
break;
case YPERR_RESRC:
return NS_TRYAGAIN;
default:
return NS_UNAVAIL;
}
}
return NS_SUCCESS;
}
static int
_nis_end(struct nis_state *state)
{
_DIAGASSERT(state != NULL);
if (state->domain) {
state->domain = NULL;
}
state->done = 0;
if (state->current) {
free(state->current);
state->current = NULL;
}
return NS_SUCCESS;
}
/*
* _nis_grscan
* Look for the yp key provided in buffer from map,
* and decode into grp/buffer/buflen.
*/
static int
_nis_grscan(int *retval, struct group *grp, char *buffer, size_t buflen,
struct nis_state *state, const char *map)
{
char *data;
int nisr, rv, datalen;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
_DIAGASSERT(map != NULL);
*retval = 0;
if (state->domain == NULL) { /* only start if NIS not setup */
rv = _nis_start(state);
if (rv != NS_SUCCESS)
return rv;
}
data = NULL;
rv = NS_NOTFOUND;
/* search map */
nisr = yp_match(state->domain, map, buffer, (int)strlen(buffer),
&data, &datalen);
switch (nisr) {
case 0:
data[datalen] = '\0'; /* clear trailing \n */
if (_gr_parse(data, grp, buffer, buflen))
rv = NS_SUCCESS; /* validate line */
else
rv = NS_UNAVAIL;
break;
case YPERR_KEY:
break;
default:
rv = NS_UNAVAIL;
break;
}
if (rv != NS_SUCCESS)
*retval = errno;
if (data)
free(data);
return rv;
}
/*ARGSUSED*/
static int
_nis_setgrent(void *nsrv, void *nscb, va_list ap)
{
_nis_state.stayopen = 0;
return _nis_start(&_nis_state);
}
/*ARGSUSED*/
static int
_nis_setgroupent(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
int stayopen = va_arg(ap, int);
int rv;
_nis_state.stayopen = stayopen;
rv = _nis_start(&_nis_state);
*retval = (rv == NS_SUCCESS);
return rv;
}
/*ARGSUSED*/
static int
_nis_endgrent(void *nsrv, void *nscb, va_list ap)
{
return _nis_end(&_nis_state);
}
/*ARGSUSED*/
static int
_nis_getgrent(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
char *key, *data;
int keylen, datalen, rv, nisr;
_DIAGASSERT(retval != NULL);
*retval = NULL;
if (_nis_state.done) /* exhausted search */
return NS_NOTFOUND;
if (_nis_state.domain == NULL) {
/* only start if NIS not setup */
rv = _nis_start(&_nis_state);
if (rv != NS_SUCCESS)
return rv;
}
key = NULL;
data = NULL;
rv = NS_NOTFOUND;
if (_nis_state.current) { /* already searching */
nisr = yp_next(_nis_state.domain, "group.byname",
_nis_state.current, _nis_state.currentlen,
&key, &keylen, &data, &datalen);
free(_nis_state.current);
_nis_state.current = NULL;
switch (nisr) {
case 0:
_nis_state.current = key;
_nis_state.currentlen = keylen;
key = NULL;
break;
case YPERR_NOMORE:
_nis_state.done = 1;
goto nisent_out;
default:
rv = NS_UNAVAIL;
goto nisent_out;
}
} else { /* new search */
if (yp_first(_nis_state.domain, "group.byname",
&_nis_state.current, &_nis_state.currentlen,
&data, &datalen)) {
rv = NS_UNAVAIL;
goto nisent_out;
}
}
data[datalen] = '\0'; /* clear trailing \n */
/* validate line */
if (_gr_parse(data, &_nis_group, _nis_groupbuf, sizeof(_nis_groupbuf)))
rv = NS_SUCCESS;
else
rv = NS_UNAVAIL;
nisent_out:
if (key)
free(key);
if (data)
free(data);
if (rv == NS_SUCCESS)
*retval = &_nis_group;
return rv;
}
/*ARGSUSED*/
static int
_nis_getgrgid(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
gid_t gid = va_arg(ap, gid_t);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _nis_start(&_nis_state);
if (rv != NS_SUCCESS)
return rv;
snprintf(_nis_groupbuf, sizeof(_nis_groupbuf), "%u", (unsigned int)gid);
rv = _nis_grscan(&rerror, &_nis_group,
_nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, "group.bygid");
if (!_nis_state.stayopen)
_nis_end(&_nis_state);
if (rv == NS_SUCCESS && gid == _nis_group.gr_gid)
*retval = &_nis_group;
return rv;
}
/*ARGSUSED*/
static int
_nis_getgrgid_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
gid_t gid = va_arg(ap, gid_t);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct nis_state state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
snprintf(buffer, buflen, "%u", (unsigned int)gid);
rv = _nis_grscan(retval, grp, buffer, buflen, &state, "group.bygid");
_nis_end(&state);
if (rv != NS_SUCCESS)
return rv;
if (gid == grp->gr_gid) {
*result = grp;
return NS_SUCCESS;
} else
return NS_NOTFOUND;
}
/*ARGSUSED*/
static int
_nis_getgrnam(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
const char *name = va_arg(ap, const char *);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _nis_start(&_nis_state);
if (rv != NS_SUCCESS)
return rv;
snprintf(_nis_groupbuf, sizeof(_nis_groupbuf), "%s", name);
rv = _nis_grscan(&rerror, &_nis_group,
_nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, "group.byname");
if (!_nis_state.stayopen)
_nis_end(&_nis_state);
if (rv == NS_SUCCESS && strcmp(name, _nis_group.gr_name) == 0)
*retval = &_nis_group;
return rv;
}
/*ARGSUSED*/
static int
_nis_getgrnam_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
const char *name = va_arg(ap, const char *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct nis_state state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
snprintf(buffer, buflen, "%s", name);
memset(&state, 0, sizeof(state));
rv = _nis_grscan(retval, grp, buffer, buflen, &state, "group.byname");
_nis_end(&state);
if (rv != NS_SUCCESS)
return rv;
if (strcmp(name, grp->gr_name) == 0) {
*result = grp;
return NS_SUCCESS;
} else
return NS_NOTFOUND;
}
#endif /* YP */
#ifdef _GROUP_COMPAT
/*
* compat methods
*/
/* state shared between compat methods */
struct compat_state {
int stayopen; /* see getgroupent(3) */
FILE *fp; /* file handle */
char *name; /* NULL if reading file, */
/* "" if compat "+", */
/* name if compat "+name" */
};
static struct compat_state _compat_state;
/* storage for non _r functions */
static struct group _compat_group;
static char _compat_groupbuf[_GETGR_R_SIZE_MAX];
static int
_compat_start(struct compat_state *state)
{
_DIAGASSERT(state != NULL);
if (state->fp == NULL) {
state->fp = fopen(_PATH_GROUP, "r");
if (state->fp == NULL)
return NS_UNAVAIL;
} else {
rewind(state->fp);
}
return NS_SUCCESS;
}
static int
_compat_end(struct compat_state *state)
{
_DIAGASSERT(state != NULL);
if (state->name) {
free(state->name);
state->name = NULL;
}
if (state->fp) {
(void) fclose(state->fp);
state->fp = NULL;
}
return NS_SUCCESS;
}
/*
* _compat_grbad
* log an error if "files" or "compat" is specified in
* group_compat database
*/
/*ARGSUSED*/
static int
_compat_grbad(void *nsrv, void *nscb, va_list ap)
{
static int warned;
_DIAGASSERT(cb_data != NULL);
if (!warned) {
syslog(LOG_ERR,
"nsswitch.conf group_compat database can't use '%s'",
(char *)nscb);
}
warned = 1;
return NS_UNAVAIL;
}
/*
* _compat_grscan
* Scan state->fp for the next desired entry.
* If search is zero, return the next entry.
* If search is non-zero, look for a specific name (if name != NULL),
* or a specific gid (if name == NULL).
* Sets *retval to the errno if the result is not NS_SUCCESS.
*/
static int
_compat_grscan(int *retval, struct group *grp, char *buffer, size_t buflen,
struct compat_state *state, int search, const char *name, gid_t gid)
{
int rv;
char filebuf[_GETGR_R_SIZE_MAX], *ep;
static const ns_dtab compatentdtab[] = {
NS_FILES_CB(_compat_grbad, "files")
NS_DNS_CB(_dns_getgrent, NULL)
NS_NIS_CB(_nis_getgrent, NULL)
NS_COMPAT_CB(_compat_grbad, "compat")
{ 0 }
};
static const ns_dtab compatgiddtab[] = {
NS_FILES_CB(_compat_grbad, "files")
NS_DNS_CB(_dns_getgrgid_r, NULL)
NS_NIS_CB(_nis_getgrgid_r, NULL)
NS_COMPAT_CB(_compat_grbad, "compat")
{ 0 }
};
static const ns_dtab compatnamdtab[] = {
NS_FILES_CB(_compat_grbad, "files")
NS_DNS_CB(_dns_getgrnam_r, NULL)
NS_NIS_CB(_nis_getgrnam_r, NULL)
NS_COMPAT_CB(_compat_grbad, "compat")
{ 0 }
};
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
/* name is NULL to indicate searching for gid */
*retval = 0;
if (state->fp == NULL) { /* only start if file not open yet */
rv = _compat_start(state);
if (rv != NS_SUCCESS)
goto compatgrscan_out;
}
rv = NS_NOTFOUND;
for (;;) { /* loop through file */
if (state->name != NULL) {
/* processing compat entry */
int crv, cretval;
struct group cgrp, *cgrpres;
if (state->name[0]) { /* specific +group: */
crv = nsdispatch(NULL, compatnamdtab,
NSDB_GROUP_COMPAT, "getgrnam_r",
__nsdefaultnis,
&cretval, state->name,
&cgrp, filebuf, sizeof(filebuf), &cgrpres);
free(state->name); /* (only check 1 grp) */
state->name = NULL;
} else if (!search) { /* any group */
/* XXXLUKEM: need to implement and use getgrent_r() */
crv = nsdispatch(NULL, compatentdtab,
NSDB_GROUP_COMPAT, "getgrent",
__nsdefaultnis,
&cgrpres);
} else if (name) { /* specific group */
crv = nsdispatch(NULL, compatnamdtab,
NSDB_GROUP_COMPAT, "getgrnam_r",
__nsdefaultnis,
&cretval, name,
&cgrp, filebuf, sizeof(filebuf), &cgrpres);
} else { /* specific gid */
crv = nsdispatch(NULL, compatgiddtab,
NSDB_GROUP_COMPAT, "getgrgid_r",
__nsdefaultnis,
&cretval, gid,
&cgrp, filebuf, sizeof(filebuf), &cgrpres);
}
if (crv != NS_SUCCESS) { /* not found */
free(state->name);
state->name = NULL;
continue; /* try next line */
}
if (!_gr_copy(cgrpres, grp, buffer, buflen)) {
rv = NS_UNAVAIL;
break;
}
goto compatgrscan_cmpgrp; /* skip to grp test */
}
/* get next file line */
if (fgets(filebuf, sizeof(filebuf), state->fp) == NULL)
break;
ep = strchr(filebuf, '\n');
if (ep == NULL) { /* fail on lines that are too big */
int ch;
while ((ch = getc(state->fp)) != '\n' && ch != EOF)
continue;
rv = NS_UNAVAIL;
break;
}
*ep = '\0'; /* clear trailing \n */
if (filebuf[0] == '+') { /* parse compat line */
if (state->name)
free(state->name);
state->name = NULL;
switch(filebuf[1]) {
case ':':
case '\0':
state->name = strdup("");
break;
default:
ep = strchr(filebuf + 1, ':');
if (ep == NULL)
break;
*ep = '\0';
state->name = strdup(filebuf + 1);
break;
}
if (state->name == NULL) {
rv = NS_UNAVAIL;
break;
}
continue;
}
/* validate line */
if (! _gr_parse(filebuf, grp, buffer, buflen)) {
rv = NS_UNAVAIL;
break;
}
compatgrscan_cmpgrp:
if (! search) { /* just want this one */
rv = NS_SUCCESS;
break;
}
/* want specific */
if ((name && strcmp(name, grp->gr_name) == 0) ||
(!name && gid == grp->gr_gid)) {
rv = NS_SUCCESS;
break;
}
}
compatgrscan_out:
if (rv != NS_SUCCESS)
*retval = errno;
return rv;
}
/*ARGSUSED*/
static int
_compat_setgrent(void *nsrv, void *nscb, va_list ap)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(_compat_grbad, "files")
NS_DNS_CB(_dns_setgrent, NULL)
NS_NIS_CB(_nis_setgrent, NULL)
NS_COMPAT_CB(_compat_grbad, "compat")
{ 0 }
};
/* force group_compat setgrent() */
(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
__nsdefaultnis_forceall);
/* reset state, keep fp open */
_compat_state.stayopen = 0;
return _compat_start(&_compat_state);
}
/*ARGSUSED*/
static int
_compat_setgroupent(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
int stayopen = va_arg(ap, int);
int rv;
static const ns_dtab dtab[] = {
NS_FILES_CB(_compat_grbad, "files")
NS_DNS_CB(_dns_setgroupent, NULL)
NS_NIS_CB(_nis_setgroupent, NULL)
NS_COMPAT_CB(_compat_grbad, "compat")
{ 0 }
};
/* force group_compat setgroupent() */
(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgroupent",
__nsdefaultnis_forceall, &rv, stayopen);
_compat_state.stayopen = stayopen;
rv = _compat_start(&_compat_state);
*retval = (rv == NS_SUCCESS);
return rv;
}
/*ARGSUSED*/
static int
_compat_endgrent(void *nsrv, void *nscb, va_list ap)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(_compat_grbad, "files")
NS_DNS_CB(_dns_endgrent, NULL)
NS_NIS_CB(_nis_endgrent, NULL)
NS_COMPAT_CB(_compat_grbad, "compat")
{ 0 }
};
/* force group_compat endgrent() */
(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
__nsdefaultnis_forceall);
/* reset state, close fp */
_compat_state.stayopen = 0;
return _compat_end(&_compat_state);
}
/*ARGSUSED*/
static int
_compat_getgrent(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _compat_grscan(&rerror, &_compat_group,
_compat_groupbuf, sizeof(_compat_groupbuf),
&_compat_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*retval = &_compat_group;
return rv;
}
/*ARGSUSED*/
static int
_compat_getgrgid(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
gid_t gid = va_arg(ap, gid_t);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _compat_start(&_compat_state);
if (rv != NS_SUCCESS)
return rv;
rv = _compat_grscan(&rerror, &_compat_group,
_compat_groupbuf, sizeof(_compat_groupbuf),
&_compat_state, 1, NULL, gid);
if (!_compat_state.stayopen)
_compat_end(&_compat_state);
if (rv == NS_SUCCESS)
*retval = &_compat_group;
return rv;
}
/*ARGSUSED*/
static int
_compat_getgrgid_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
gid_t gid = va_arg(ap, gid_t);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct compat_state state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = _compat_grscan(retval, grp, buffer, buflen, &state, 1, NULL, gid);
_compat_end(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
/*ARGSUSED*/
static int
_compat_getgrnam(void *nsrv, void *nscb, va_list ap)
{
struct group **retval = va_arg(ap, struct group **);
const char *name = va_arg(ap, const char *);
int rv, rerror;
_DIAGASSERT(retval != NULL);
*retval = NULL;
rv = _compat_start(&_compat_state);
if (rv != NS_SUCCESS)
return rv;
rv = _compat_grscan(&rerror, &_compat_group,
_compat_groupbuf, sizeof(_compat_groupbuf),
&_compat_state, 1, name, 0);
if (!_compat_state.stayopen)
_compat_end(&_compat_state);
if (rv == NS_SUCCESS)
*retval = &_compat_group;
return rv;
}
/*ARGSUSED*/
static int
_compat_getgrnam_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
const char *name = va_arg(ap, const char *);
struct group *grp = va_arg(ap, struct group *);
char *buffer = va_arg(ap, char *);
size_t buflen = va_arg(ap, size_t);
struct group **result = va_arg(ap, struct group **);
struct compat_state state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = _compat_grscan(retval, grp, buffer, buflen, &state, 1, name, 0);
_compat_end(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
#endif /* _GROUP_COMPAT */
/*
* public functions
*/
struct group *
getgrent(void)
{
int rv;
struct group *retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrent, NULL)
NS_DNS_CB(_dns_getgrent, NULL)
NS_NIS_CB(_nis_getgrent, NULL)
NS_COMPAT_CB(_compat_getgrent, NULL)
{ 0 }
};
mutex_lock(&_grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrent", __nsdefaultcompat,
&retval);
mutex_unlock(&_grmutex);
return (rv == NS_SUCCESS) ? retval : NULL;
}
struct group *
getgrgid(gid_t gid)
{
int rv;
struct group *retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrgid, NULL)
NS_DNS_CB(_dns_getgrgid, NULL)
NS_NIS_CB(_nis_getgrgid, NULL)
NS_COMPAT_CB(_compat_getgrgid, NULL)
{ 0 }
};
mutex_lock(&_grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrgid", __nsdefaultcompat,
&retval, gid);
mutex_unlock(&_grmutex);
return (rv == NS_SUCCESS) ? retval : NULL;
}
int
getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t buflen,
struct group **result)
{
int rv, retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrgid_r, NULL)
NS_DNS_CB(_dns_getgrgid_r, NULL)
NS_NIS_CB(_nis_getgrgid_r, NULL)
NS_COMPAT_CB(_compat_getgrgid_r, NULL)
{ 0 }
};
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
retval = 0;
mutex_lock(&_grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrgid_r", __nsdefaultcompat,
&retval, gid, grp, buffer, buflen, result);
mutex_unlock(&_grmutex);
return (rv == NS_SUCCESS) ? 0 : retval ? retval : ENOENT;
}
struct group *
getgrnam(const char *name)
{
int rv;
struct group *retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrnam, NULL)
NS_DNS_CB(_dns_getgrnam, NULL)
NS_NIS_CB(_nis_getgrnam, NULL)
NS_COMPAT_CB(_compat_getgrnam, NULL)
{ 0 }
};
mutex_lock(&_grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrnam", __nsdefaultcompat,
&retval, name);
mutex_unlock(&_grmutex);
return (rv == NS_SUCCESS) ? retval : NULL;
}
int
getgrnam_r(const char *name, struct group *grp, char *buffer, size_t buflen,
struct group **result)
{
int rv, retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrnam_r, NULL)
NS_DNS_CB(_dns_getgrnam_r, NULL)
NS_NIS_CB(_nis_getgrnam_r, NULL)
NS_COMPAT_CB(_compat_getgrnam_r, NULL)
{ 0 }
};
_DIAGASSERT(name != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
retval = 0;
mutex_lock(&_grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrnam_r", __nsdefaultcompat,
&retval, name, grp, buffer, buflen, result);
mutex_unlock(&_grmutex);
return (rv == NS_SUCCESS) ? 0 : retval ? retval : ENOENT;
}
void
endgrent(void)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_endgrent, NULL)
NS_DNS_CB(_dns_endgrent, NULL)
NS_NIS_CB(_nis_endgrent, NULL)
NS_COMPAT_CB(_compat_endgrent, NULL)
{ 0 }
};
mutex_lock(&_grmutex);
/* force all endgrent() methods */
(void) nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent",
__nsdefaultcompat_forceall);
mutex_unlock(&_grmutex);
}
int
setgroupent(int stayopen)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_setgroupent, NULL)
NS_DNS_CB(_dns_setgroupent, NULL)
NS_NIS_CB(_nis_setgroupent, NULL)
NS_COMPAT_CB(_compat_setgroupent, NULL)
{ 0 }
};
int rv, retval;
mutex_lock(&_grmutex);
/* force all setgroupent() methods */
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "setgroupent",
__nsdefaultcompat_forceall, &retval, stayopen);
mutex_unlock(&_grmutex);
return (rv == NS_SUCCESS) ? retval : 0;
}
void
setgrent(void)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_setgrent, NULL)
NS_DNS_CB(_dns_setgrent, NULL)
NS_NIS_CB(_nis_setgrent, NULL)
NS_COMPAT_CB(_compat_setgrent, NULL)
{ 0 }
};
mutex_lock(&_grmutex);
/* force all setgrent() methods */
(void) nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent",
__nsdefaultcompat_forceall);
mutex_unlock(&_grmutex);
}