NetBSD/lib/libc/gen/getgrent.c
2006-03-19 03:08:11 +00:00

1942 lines
45 KiB
C

/* $NetBSD: getgrent.c,v 1.59 2006/03/19 03:08:11 christos Exp $ */
/*-
* Copyright (c) 1999-2000, 2004-2005 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.59 2006/03/19 03:08:11 christos 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
#include "gr_private.h"
#ifdef __weak_alias
__weak_alias(endgrent,_endgrent)
__weak_alias(getgrent,_getgrent)
__weak_alias(getgrent_r,_getgrent_r)
__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
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
*/
int
__grstart_files(struct __grstate_files *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;
}
int
__grend_files(struct __grstate_files *state)
{
_DIAGASSERT(state != NULL);
if (state->fp) {
(void) fclose(state->fp);
state->fp = NULL;
}
return NS_SUCCESS;
}
/*
* __grscan_files
* 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
* or NS_NOTFOUND.
*/
int
__grscan_files(int *retval, struct group *grp, char *buffer, size_t buflen,
struct __grstate_files *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 = __grstart_files(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) { /* skip lines that are too big */
int ch;
while ((ch = getc(state->fp)) != '\n' && ch != EOF)
continue;
continue;
}
*ep = '\0'; /* clear trailing \n */
if (filebuf[0] == '+') /* skip compat line */
continue;
/* validate line */
if (! _gr_parse(filebuf, grp, buffer, buflen)) {
continue; /* skip bad lines */
}
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 && rv != NS_NOTFOUND)
*retval = errno;
return rv;
}
static struct __grstate_files _files_state;
/* storage for non _r functions */
static struct group _files_group;
static char _files_groupbuf[_GETGR_R_SIZE_MAX];
/*ARGSUSED*/
static int
_files_setgrent(void *nsrv, void *nscb, va_list ap)
{
_files_state.stayopen = 0;
return __grstart_files(&_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 = __grstart_files(&_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 __grend_files(&_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 = __grscan_files(&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_getgrent_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
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 **);
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
rv = __grscan_files(retval, grp, buffer, buflen,
&_files_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*result = grp;
else
*result = NULL;
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 = __grstart_files(&_files_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_files(&rerror, &_files_group,
_files_groupbuf, sizeof(_files_groupbuf),
&_files_state, 1, NULL, gid);
if (!_files_state.stayopen)
__grend_files(&_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 __grstate_files state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_files(retval, grp, buffer, buflen, &state, 1, NULL, gid);
__grend_files(&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 = __grstart_files(&_files_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_files(&rerror, &_files_group,
_files_groupbuf, sizeof(_files_groupbuf),
&_files_state, 1, name, 0);
if (!_files_state.stayopen)
__grend_files(&_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 __grstate_files state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_files(retval, grp, buffer, buflen, &state, 1, name, 0);
__grend_files(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
#ifdef HESIOD
/*
* dns methods
*/
int
__grstart_dns(struct __grstate_dns *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;
}
int
__grend_dns(struct __grstate_dns *state)
{
_DIAGASSERT(state != NULL);
state->num = 0;
if (state->context) {
hesiod_end(state->context);
state->context = NULL;
}
return NS_SUCCESS;
}
/*
* __grscan_dns
* Search Hesiod 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).
*/
int
__grscan_dns(int *retval, struct group *grp, char *buffer, size_t buflen,
struct __grstate_dns *state, int search, const char *name, gid_t gid)
{
const char **curzone;
char **hp, *ep;
int rv;
static const char *zones_gid_group[] = {
"gid",
"group",
NULL
};
static const char *zones_group[] = {
"group",
NULL
};
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
/* name is NULL to indicate searching for gid */
*retval = 0;
if (state->context == NULL) { /* only start if Hesiod not setup */
rv = __grstart_dns(state);
if (rv != NS_SUCCESS)
return rv;
}
next_dns_entry:
hp = NULL;
rv = NS_NOTFOUND;
if (! search) { /* find next entry */
if (state->num == -1) /* exhausted search */
return NS_NOTFOUND;
/* find group-NNN */
snprintf(buffer, buflen, "group-%u", state->num);
state->num++;
curzone = zones_group;
} else if (name) { /* find group name */
snprintf(buffer, buflen, "%s", name);
curzone = zones_group;
} else { /* find gid */
snprintf(buffer, buflen, "%u", (unsigned int)gid);
curzone = zones_gid_group;
}
for (; *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) {
if (! search)
state->num = -1;
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 */
if (! search) { /* just want this one */
rv = NS_SUCCESS;
} else if ((name && strcmp(name, grp->gr_name) == 0) ||
(!name && gid == grp->gr_gid)) { /* want specific */
rv = NS_SUCCESS;
}
} else { /* dodgy entry */
if (!search) { /* try again if ! searching */
hesiod_free_list(state->context, hp);
goto next_dns_entry;
}
}
dnsgrscan_out:
if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
*retval = errno;
if (hp)
hesiod_free_list(state->context, hp);
return rv;
}
static struct __grstate_dns _dns_state;
/* storage for non _r functions */
static struct group _dns_group;
static char _dns_groupbuf[_GETGR_R_SIZE_MAX];
/*ARGSUSED*/
static int
_dns_setgrent(void *nsrv, void *nscb, va_list ap)
{
_dns_state.stayopen = 0;
return __grstart_dns(&_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 = __grstart_dns(&_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 __grend_dns(&_dns_state);
}
/*ARGSUSED*/
static int
_dns_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 = __grscan_dns(&rerror, &_dns_group,
_dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*retval = &_dns_group;
return rv;
}
/*ARGSUSED*/
static int
_dns_getgrent_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
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 **);
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
rv = __grscan_dns(retval, grp, buffer, buflen,
&_dns_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*result = grp;
else
*result = NULL;
return rv;
}
/*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 = __grstart_dns(&_dns_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_dns(&rerror, &_dns_group,
_dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, 1, NULL, gid);
if (!_dns_state.stayopen)
__grend_dns(&_dns_state);
if (rv == NS_SUCCESS)
*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 __grstate_dns state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_dns(retval, grp, buffer, buflen, &state, 1, NULL, gid);
__grend_dns(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
/*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 = __grstart_dns(&_dns_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_dns(&rerror, &_dns_group,
_dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, 1, name, 0);
if (!_dns_state.stayopen)
__grend_dns(&_dns_state);
if (rv == NS_SUCCESS)
*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 __grstate_dns state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_dns(retval, grp, buffer, buflen, &state, 1, name, 0);
__grend_dns(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
#endif /* HESIOD */
#ifdef YP
/*
* nis methods
*/
int
__grstart_nis(struct __grstate_nis *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;
}
int
__grend_nis(struct __grstate_nis *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;
}
/*
* __grscan_nis
* Search NIS 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).
*/
int
__grscan_nis(int *retval, struct group *grp, char *buffer, size_t buflen,
struct __grstate_nis *state, int search, const char *name, gid_t gid)
{
const char *map;
char *key, *data;
int nisr, rv, keylen, datalen;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(state != NULL);
/* name is NULL to indicate searching for gid */
*retval = 0;
if (state->domain == NULL) { /* only start if NIS not setup */
rv = __grstart_nis(state);
if (rv != NS_SUCCESS)
return rv;
}
next_nis_entry:
key = NULL;
data = NULL;
rv = NS_SUCCESS;
if (! search) { /* find next entry */
if (state->done) /* exhausted search */
return NS_NOTFOUND;
map = "group.byname";
if (state->current) { /* already searching */
nisr = yp_next(state->domain, map,
state->current, state->currentlen,
&key, &keylen, &data, &datalen);
free(state->current);
state->current = NULL;
switch (nisr) {
case 0:
state->current = key;
state->currentlen = keylen;
key = NULL;
break;
case YPERR_NOMORE:
rv = NS_NOTFOUND;
state->done = 1;
break;
default:
rv = NS_UNAVAIL;
break;
}
} else { /* new search */
if (yp_first(state->domain, map,
&state->current, &state->currentlen,
&data, &datalen)) {
rv = NS_UNAVAIL;
}
}
} else { /* search for specific item */
if (name) { /* find group name */
snprintf(buffer, buflen, "%s", name);
map = "group.byname";
} else { /* find gid */
snprintf(buffer, buflen, "%u", (unsigned int)gid);
map = "group.bygid";
}
nisr = yp_match(state->domain, map, buffer, (int)strlen(buffer),
&data, &datalen);
switch (nisr) {
case 0:
break;
case YPERR_KEY:
rv = NS_NOTFOUND;
break;
default:
rv = NS_UNAVAIL;
break;
}
}
if (rv == NS_SUCCESS) { /* validate data */
data[datalen] = '\0'; /* clear trailing \n */
if (_gr_parse(data, grp, buffer, buflen)) {
if (! search) { /* just want this one */
rv = NS_SUCCESS;
} else if ((name && strcmp(name, grp->gr_name) == 0) ||
(!name && gid == grp->gr_gid)) {
/* want specific */
rv = NS_SUCCESS;
}
} else { /* dodgy entry */
if (!search) { /* try again if ! searching */
free(data);
goto next_nis_entry;
}
}
}
if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
*retval = errno;
if (key)
free(key);
if (data)
free(data);
return rv;
}
static struct __grstate_nis _nis_state;
/* storage for non _r functions */
static struct group _nis_group;
static char _nis_groupbuf[_GETGR_R_SIZE_MAX];
/*ARGSUSED*/
static int
_nis_setgrent(void *nsrv, void *nscb, va_list ap)
{
_nis_state.stayopen = 0;
return __grstart_nis(&_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 = __grstart_nis(&_nis_state);
*retval = (rv == NS_SUCCESS);
return rv;
}
/*ARGSUSED*/
static int
_nis_endgrent(void *nsrv, void *nscb, va_list ap)
{
return __grend_nis(&_nis_state);
}
/*ARGSUSED*/
static int
_nis_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 = __grscan_nis(&rerror, &_nis_group,
_nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*retval = &_nis_group;
return rv;
}
/*ARGSUSED*/
static int
_nis_getgrent_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
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 **);
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
rv = __grscan_nis(retval, grp, buffer, buflen,
&_nis_state, 0, NULL, 0);
if (rv == NS_SUCCESS)
*result = grp;
else
*result = NULL;
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 = __grstart_nis(&_nis_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_nis(&rerror, &_nis_group,
_nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, 1, NULL, gid);
if (!_nis_state.stayopen)
__grend_nis(&_nis_state);
if (rv == NS_SUCCESS)
*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 __grstate_nis state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_nis(retval, grp, buffer, buflen, &state, 1, NULL, gid);
__grend_nis(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
/*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 = __grstart_nis(&_nis_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_nis(&rerror, &_nis_group,
_nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, 1, name, 0);
if (!_nis_state.stayopen)
__grend_nis(&_nis_state);
if (rv == NS_SUCCESS)
*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 __grstate_nis state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_nis(retval, grp, buffer, buflen, &state, 1, name, 0);
__grend_nis(&state);
if (rv == NS_SUCCESS)
*result = grp;
return rv;
}
#endif /* YP */
#ifdef _GROUP_COMPAT
/*
* compat methods
*/
int
__grstart_compat(struct __grstate_compat *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;
}
int
__grend_compat(struct __grstate_compat *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;
}
/*
* __grbad_compat
* log an error if "files" or "compat" is specified in
* group_compat database
*/
/*ARGSUSED*/
int
__grbad_compat(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'",
(const char *)nscb);
}
warned = 1;
return NS_UNAVAIL;
}
/*
* __grscan_compat
* 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 or
* NS_NOTFOUND.
*
* searchfunc is invoked when a compat "+" lookup is required;
* searchcookie is passed as the first argument to searchfunc,
* the second argument is the group result.
* This should return NS_NOTFOUND when "no more groups" from compat src.
* If searchfunc is NULL then nsdispatch of getgrent is used.
* This is primarily intended for getgroupmembership(3)'s compat backend.
*/
int
__grscan_compat(int *retval, struct group *grp, char *buffer, size_t buflen,
struct __grstate_compat *state, int search, const char *name, gid_t gid,
int (*searchfunc)(void *, struct group **), void *searchcookie)
{
int rv;
char filebuf[_GETGR_R_SIZE_MAX], *ep;
static const ns_dtab compatentdtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_getgrent_r, NULL)
NS_NIS_CB(_nis_getgrent_r, NULL)
NS_COMPAT_CB(__grbad_compat, "compat")
{ 0 }
};
static const ns_dtab compatgiddtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_getgrgid_r, NULL)
NS_NIS_CB(_nis_getgrgid_r, NULL)
NS_COMPAT_CB(__grbad_compat, "compat")
{ 0 }
};
static const ns_dtab compatnamdtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_getgrnam_r, NULL)
NS_NIS_CB(_nis_getgrnam_r, NULL)
NS_COMPAT_CB(__grbad_compat, "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 = __grstart_compat(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 */
if (searchfunc) {
crv = searchfunc(searchcookie,
&cgrpres);
} else {
crv = nsdispatch(NULL, compatentdtab,
NSDB_GROUP_COMPAT, "getgrent_r",
__nsdefaultnis,
&cretval, &cgrp, filebuf,
sizeof(filebuf), &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) { /* skip lines that are too big */
int ch;
while ((ch = getc(state->fp)) != '\n' && ch != EOF)
continue;
continue;
}
*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)) {
continue; /* skip bad lines */
}
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 && rv != NS_NOTFOUND)
*retval = errno;
return rv;
}
static struct __grstate_compat _compat_state;
/* storage for non _r functions */
static struct group _compat_group;
static char _compat_groupbuf[_GETGR_R_SIZE_MAX];
/*ARGSUSED*/
static int
_compat_setgrent(void *nsrv, void *nscb, va_list ap)
{
static const ns_dtab dtab[] = {
NS_FILES_CB(__grbad_compat, "files")
NS_DNS_CB(_dns_setgrent, NULL)
NS_NIS_CB(_nis_setgrent, NULL)
NS_COMPAT_CB(__grbad_compat, "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 __grstart_compat(&_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(__grbad_compat, "files")
NS_DNS_CB(_dns_setgroupent, NULL)
NS_NIS_CB(_nis_setgroupent, NULL)
NS_COMPAT_CB(__grbad_compat, "compat")
{ 0 }
};
/* force group_compat setgroupent() */
(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgroupent",
__nsdefaultnis_forceall, &rv, stayopen);
_compat_state.stayopen = stayopen;
rv = __grstart_compat(&_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(__grbad_compat, "files")
NS_DNS_CB(_dns_endgrent, NULL)
NS_NIS_CB(_nis_endgrent, NULL)
NS_COMPAT_CB(__grbad_compat, "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 __grend_compat(&_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 = __grscan_compat(&rerror, &_compat_group,
_compat_groupbuf, sizeof(_compat_groupbuf),
&_compat_state, 0, NULL, 0, NULL, NULL);
if (rv == NS_SUCCESS)
*retval = &_compat_group;
return rv;
}
/*ARGSUSED*/
static int
_compat_getgrent_r(void *nsrv, void *nscb, va_list ap)
{
int *retval = va_arg(ap, int *);
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 **);
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
rv = __grscan_compat(retval, grp, buffer, buflen,
&_compat_state, 0, NULL, 0, NULL, NULL);
if (rv == NS_SUCCESS)
*result = grp;
else
*result = NULL;
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 = __grstart_compat(&_compat_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_compat(&rerror, &_compat_group,
_compat_groupbuf, sizeof(_compat_groupbuf),
&_compat_state, 1, NULL, gid, NULL, NULL);
if (!_compat_state.stayopen)
__grend_compat(&_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 __grstate_compat state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_compat(retval, grp, buffer, buflen, &state,
1, NULL, gid, NULL, NULL);
__grend_compat(&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 = __grstart_compat(&_compat_state);
if (rv != NS_SUCCESS)
return rv;
rv = __grscan_compat(&rerror, &_compat_group,
_compat_groupbuf, sizeof(_compat_groupbuf),
&_compat_state, 1, name, 0, NULL, NULL);
if (!_compat_state.stayopen)
__grend_compat(&_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 __grstate_compat state;
int rv;
_DIAGASSERT(retval != NULL);
_DIAGASSERT(grp != NULL);
_DIAGASSERT(buffer != NULL);
_DIAGASSERT(result != NULL);
*result = NULL;
memset(&state, 0, sizeof(state));
rv = __grscan_compat(retval, grp, buffer, buflen, &state,
1, name, 0, NULL, NULL);
__grend_compat(&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;
}
int
getgrent_r(struct group *grp, char *buffer, size_t buflen,
struct group **result)
{
int rv, retval;
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getgrent_r, NULL)
NS_DNS_CB(_dns_getgrent_r, NULL)
NS_NIS_CB(_nis_getgrent_r, NULL)
NS_COMPAT_CB(_compat_getgrent_r, NULL)
{ 0 }
};
mutex_lock(&__grmutex);
rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrent_r", __nsdefaultcompat,
&retval, grp, buffer, buflen, result);
mutex_unlock(&__grmutex);
switch (rv) {
case NS_SUCCESS:
case NS_NOTFOUND:
return 0;
default:
return retval;
}
}
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);
switch (rv) {
case NS_SUCCESS:
case NS_NOTFOUND:
return 0;
default:
return retval;
}
}
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);
switch (rv) {
case NS_SUCCESS:
case NS_NOTFOUND:
return 0;
default:
return retval;
}
}
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);
}