Home | History | Annotate | Line # | Download | only in gen
getgrent.c revision 1.51
      1 /*	$NetBSD: getgrent.c,v 1.51 2004/10/29 06:32:08 lukem Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1999, 2000, 2004 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Luke Mewburn.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the NetBSD
     21  *	Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 /*
     40  * Copyright (c) 1989, 1993
     41  *	The Regents of the University of California.  All rights reserved.
     42  *
     43  * Redistribution and use in source and binary forms, with or without
     44  * modification, are permitted provided that the following conditions
     45  * are met:
     46  * 1. Redistributions of source code must retain the above copyright
     47  *    notice, this list of conditions and the following disclaimer.
     48  * 2. Redistributions in binary form must reproduce the above copyright
     49  *    notice, this list of conditions and the following disclaimer in the
     50  *    documentation and/or other materials provided with the distribution.
     51  * 3. Neither the name of the University nor the names of its contributors
     52  *    may be used to endorse or promote products derived from this software
     53  *    without specific prior written permission.
     54  *
     55  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     56  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     65  * SUCH DAMAGE.
     66  */
     67 
     68 /*
     69  * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved.
     70  *
     71  * Redistribution and use in source and binary forms, with or without
     72  * modification, are permitted provided that the following conditions
     73  * are met:
     74  * 1. Redistributions of source code must retain the above copyright
     75  *    notice, this list of conditions and the following disclaimer.
     76  * 2. Redistributions in binary form must reproduce the above copyright
     77  *    notice, this list of conditions and the following disclaimer in the
     78  *    documentation and/or other materials provided with the distribution.
     79  *
     80  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
     81  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     82  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     83  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
     84  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     85  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     86  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     87  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     88  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     89  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     90  * SUCH DAMAGE.
     91  */
     92 
     93 #include <sys/cdefs.h>
     94 #if defined(LIBC_SCCS) && !defined(lint)
     95 #if 0
     96 static char sccsid[] = "@(#)getgrent.c	8.2 (Berkeley) 3/21/94";
     97 #else
     98 __RCSID("$NetBSD: getgrent.c,v 1.51 2004/10/29 06:32:08 lukem Exp $");
     99 #endif
    100 #endif /* LIBC_SCCS and not lint */
    101 
    102 #include "namespace.h"
    103 #include "reentrant.h"
    104 
    105 #include <sys/param.h>
    106 
    107 #include <assert.h>
    108 #include <errno.h>
    109 #include <grp.h>
    110 #include <limits.h>
    111 #include <nsswitch.h>
    112 #include <stdarg.h>
    113 #include <stdio.h>
    114 #include <stdlib.h>
    115 #include <string.h>
    116 #include <syslog.h>
    117 
    118 #ifdef HESIOD
    119 #include <hesiod.h>
    120 #endif
    121 
    122 #ifdef YP
    123 #include <rpc/rpc.h>
    124 #include <rpcsvc/yp_prot.h>
    125 #include <rpcsvc/ypclnt.h>
    126 #endif
    127 
    128 #define _GROUP_COMPAT	/* "group" defaults to compat, so always provide it */
    129 
    130 #define	GETGR_R_SIZE_MAX	1024	/* XXXLUKEM: move to {grp,unistd}.h ? */
    131 
    132 #ifdef __weak_alias
    133 __weak_alias(endgrent,_endgrent)
    134 __weak_alias(getgrent,_getgrent)
    135 __weak_alias(getgrgid,_getgrgid)
    136 __weak_alias(getgrgid_r,_getgrgid_r)
    137 __weak_alias(getgrnam,_getgrnam)
    138 __weak_alias(getgrnam_r,_getgrnam_r)
    139 __weak_alias(setgrent,_setgrent)
    140 __weak_alias(setgroupent,_setgroupent)
    141 #endif
    142 
    143 #ifdef _REENTRANT
    144 static 	mutex_t			_grmutex = MUTEX_INITIALIZER;
    145 #endif
    146 
    147 static const ns_src defaultcompat[] = {
    148 	{ NSSRC_COMPAT,	NS_SUCCESS },
    149 	{ 0 }
    150 };
    151 
    152 static const ns_src defaultcompat_forceall[] = {
    153 	{ NSSRC_COMPAT,	NS_SUCCESS | NS_FORCEALL },
    154 	{ 0 }
    155 };
    156 
    157 static const ns_src defaultnis[] = {
    158 	{ NSSRC_NIS,	NS_SUCCESS },
    159 	{ 0 }
    160 };
    161 
    162 static const ns_src defaultnis_forceall[] = {
    163 	{ NSSRC_NIS,	NS_SUCCESS | NS_FORCEALL },
    164 	{ 0 }
    165 };
    166 
    167 
    168 /*
    169  * _gr_memfrombuf
    170  *	Obtain want bytes from buffer (of size buflen) and return a pointer
    171  *	to the available memory after adjusting buffer/buflen.
    172  *	Returns NULL if there is insufficient space.
    173  */
    174 static char *
    175 _gr_memfrombuf(size_t want, char **buffer, size_t *buflen)
    176 {
    177 	char	*rv;
    178 
    179 	if (want > *buflen) {
    180 		errno = ERANGE;
    181 		return NULL;
    182 	}
    183 	rv = *buffer;
    184 	*buffer += want;
    185 	*buflen -= want;
    186 	return rv;
    187 }
    188 
    189 /*
    190  * _gr_parse
    191  *	Parses entry as a line per group(5) (without the trailing \n)
    192  *	and fills in grp with corresponding values; memory for strings
    193  *	and arrays will be allocated from buf (of size buflen).
    194  *	Returns 1 if parsed successfully, 0 on parse failure.
    195  */
    196 static int
    197 _gr_parse(const char *entry, struct group *grp, char *buf, size_t buflen)
    198 {
    199 	unsigned long	id;
    200 	const char	*bp;
    201 	char		*ep;
    202 	size_t		count;
    203 	int		memc;
    204 
    205 	_DIAGASSERT(entry != NULL);
    206 	_DIAGASSERT(grp != NULL);
    207 	_DIAGASSERT(buf != NULL);
    208 
    209 #define COPYTOBUF(to) \
    210 	do { \
    211 		(to) = _gr_memfrombuf(count+1, &buf, &buflen); \
    212 		if ((to) == NULL) \
    213 			return 0; \
    214 		memmove((to), entry, count); \
    215 		to[count] = '\0'; \
    216 	} while (0)	/* LINTED */
    217 
    218 #if 0
    219 	if (*entry == '+')			/* fail on compat `+' token */
    220 		return 0;
    221 #endif
    222 
    223 	count = strcspn(entry, ":");		/* parse gr_name */
    224 	if (entry[count] == '\0')
    225 		return 0;
    226 	COPYTOBUF(grp->gr_name);
    227 	entry += count + 1;
    228 
    229 	count = strcspn(entry, ":");		/* parse gr_passwd */
    230 	if (entry[count] == '\0')
    231 		return 0;
    232 	COPYTOBUF(grp->gr_passwd);
    233 	entry += count + 1;
    234 
    235 	count = strcspn(entry, ":");		/* parse gr_gid */
    236 	if (entry[count] == '\0')
    237 		return 0;
    238 	id = strtoul(entry, &ep, 10);
    239 	if (id > GID_MAX || *ep != ':')
    240 		return 0;
    241 	grp->gr_gid = (gid_t)id;
    242 	entry += count + 1;
    243 
    244 	memc = 1;				/* for final NULL */
    245 	if (*entry != '\0')
    246 		memc++;				/* for first item */
    247 	for (bp = entry; *bp != '\0'; bp++) {
    248 		if (*bp == ',')
    249 			memc++;
    250 	}
    251 				/* grab ALIGNed char **gr_mem from buf */
    252 	ep = _gr_memfrombuf(memc * sizeof(char *) + ALIGNBYTES, &buf, &buflen);
    253 	grp->gr_mem = (char **)ALIGN(ep);
    254 	if (grp->gr_mem == NULL)
    255 		return 0;
    256 
    257 	for (memc = 0; *entry != '\0'; memc++) {
    258 		count = strcspn(entry, ",");	/* parse member */
    259 		COPYTOBUF(grp->gr_mem[memc]);
    260 		entry += count;
    261 		if (*entry == ',')
    262 			entry++;
    263 	}
    264 
    265 #undef COPYTOBUF
    266 
    267 	grp->gr_mem[memc] = NULL;
    268 	return 1;
    269 }
    270 
    271 /*
    272  * _gr_copy
    273  *	Copy the contents of fromgrp to grp; memory for strings
    274  *	and arrays will be allocated from buf (of size buflen).
    275  *	Returns 1 if copied successfully, 0 on copy failure.
    276  *	NOTE: fromgrp must not use buf for its own pointers.
    277  */
    278 static int
    279 _gr_copy(struct group *fromgrp, struct group *grp, char *buf, size_t buflen)
    280 {
    281 	char	*ep;
    282 	int	memc;
    283 
    284 	_DIAGASSERT(fromgrp != NULL);
    285 	_DIAGASSERT(grp != NULL);
    286 	_DIAGASSERT(buf != NULL);
    287 
    288 #define COPYSTR(to, from) \
    289 	do { \
    290 		size_t count = strlen((from)); \
    291 		(to) = _gr_memfrombuf(count+1, &buf, &buflen); \
    292 		if ((to) == NULL) \
    293 			return 0; \
    294 		memmove((to), (from), count); \
    295 		to[count] = '\0'; \
    296 	} while (0)	/* LINTED */
    297 
    298 	COPYSTR(grp->gr_name, fromgrp->gr_name);
    299 	COPYSTR(grp->gr_passwd, fromgrp->gr_passwd);
    300 	grp->gr_gid = fromgrp->gr_gid;
    301 
    302 	for (memc = 0; fromgrp->gr_mem[memc]; memc++)
    303 		continue;
    304 	memc++;					/* for final NULL */
    305 
    306 				/* grab ALIGNed char **gr_mem from buf */
    307 	ep = _gr_memfrombuf(memc * sizeof(char *) + ALIGNBYTES, &buf, &buflen);
    308 	grp->gr_mem = (char **)ALIGN(ep);
    309 	if (grp->gr_mem == NULL)
    310 		return 0;
    311 
    312 	for (memc = 0; fromgrp->gr_mem[memc]; memc++) {
    313 		COPYSTR(grp->gr_mem[memc], fromgrp->gr_mem[memc]);
    314 	}
    315 
    316 #undef COPYSTR
    317 
    318 	grp->gr_mem[memc] = NULL;
    319 	return 1;
    320 }
    321 
    322 
    323 		/*
    324 		 *	files methods
    325 		 */
    326 
    327 	/* state shared between files methods */
    328 struct files_state {
    329 	int	 stayopen;	/* see getgroupent(3) */
    330 	FILE	*fp;		/* groups file handle */
    331 };
    332 
    333 static struct files_state	_files_state;
    334 					/* storage for non _r functions */
    335 static struct group		_files_group;
    336 static char			_files_groupbuf[GETGR_R_SIZE_MAX];
    337 
    338 static int
    339 _files_start(struct files_state *state)
    340 {
    341 
    342 	_DIAGASSERT(state != NULL);
    343 
    344 	if (state->fp == NULL) {
    345 		state->fp = fopen(_PATH_GROUP, "r");
    346 		if (state->fp == NULL)
    347 			return NS_UNAVAIL;
    348 	} else {
    349 		rewind(state->fp);
    350 	}
    351 	return NS_SUCCESS;
    352 }
    353 
    354 static int
    355 _files_end(struct files_state *state)
    356 {
    357 
    358 	_DIAGASSERT(state != NULL);
    359 
    360 	if (state->fp) {
    361 		(void) fclose(state->fp);
    362 		state->fp = NULL;
    363 	}
    364 	return NS_SUCCESS;
    365 }
    366 
    367 /*
    368  * _files_grscan
    369  *	Scan state->fp for the next desired entry.
    370  *	If search is zero, return the next entry.
    371  *	If search is non-zero, look for a specific name (if name != NULL),
    372  *	or a specific gid (if name == NULL).
    373  *	Sets *retval to the errno if the result is not NS_SUCCESS.
    374  */
    375 static int
    376 _files_grscan(int *retval, struct group *grp, char *buffer, size_t buflen,
    377 	struct files_state *state, int search, const char *name, gid_t gid)
    378 {
    379 	int	rv;
    380 	char	filebuf[GETGR_R_SIZE_MAX], *ep;
    381 
    382 	_DIAGASSERT(retval != NULL);
    383 	_DIAGASSERT(grp != NULL);
    384 	_DIAGASSERT(buffer != NULL);
    385 	_DIAGASSERT(state != NULL);
    386 	/* name is NULL to indicate searching for gid */
    387 
    388 	*retval = 0;
    389 
    390 	if (state->fp == NULL) {	/* only start if file not open yet */
    391 		rv = _files_start(state);
    392 		if (rv != NS_SUCCESS)
    393 			goto filesgrscan_out;
    394 	}
    395 
    396 	rv = NS_NOTFOUND;
    397 
    398 							/* scan line by line */
    399 	while (fgets(filebuf, sizeof(filebuf), state->fp) != NULL) {
    400 		ep = strchr(filebuf, '\n');
    401 		if (ep == NULL) {	/* fail on lines that are too big */
    402 			int ch;
    403 
    404 			while ((ch = getc(state->fp)) != '\n' && ch != EOF)
    405 				continue;
    406 			rv = NS_UNAVAIL;
    407 			break;
    408 		}
    409 		*ep = '\0';				/* clear trailing \n */
    410 
    411 		if (filebuf[0] == '+')			/* skip compat line */
    412 			continue;
    413 
    414 							/* validate line */
    415 		if (! _gr_parse(filebuf, grp, buffer, buflen)) {
    416 			rv = NS_UNAVAIL;
    417 			break;
    418 		}
    419 		if (! search) {				/* just want this one */
    420 			rv = NS_SUCCESS;
    421 			break;
    422 		}
    423 							/* want specific */
    424 		if ((name && strcmp(name, grp->gr_name) == 0) ||
    425 		    (!name && gid == grp->gr_gid)) {
    426 			rv = NS_SUCCESS;
    427 			break;
    428 		}
    429 	}
    430 
    431  filesgrscan_out:
    432 	if (rv != NS_SUCCESS)
    433 		*retval = errno;
    434 	return rv;
    435 }
    436 
    437 /*ARGSUSED*/
    438 static int
    439 _files_setgrent(void *nsrv, void *nscb, va_list ap)
    440 {
    441 
    442 	_files_state.stayopen = 0;
    443 	return _files_start(&_files_state);
    444 }
    445 
    446 /*ARGSUSED*/
    447 static int
    448 _files_setgroupent(void *nsrv, void *nscb, va_list ap)
    449 {
    450 	int	*retval		= va_arg(ap, int *);
    451 	int	 stayopen	= va_arg(ap, int);
    452 
    453 	int	rv;
    454 
    455 	_files_state.stayopen = stayopen;
    456 	rv = _files_start(&_files_state);
    457 	*retval = (rv == NS_SUCCESS);
    458 	return rv;
    459 }
    460 
    461 /*ARGSUSED*/
    462 static int
    463 _files_endgrent(void *nsrv, void *nscb, va_list ap)
    464 {
    465 
    466 	_files_state.stayopen = 0;
    467 	return _files_end(&_files_state);
    468 }
    469 
    470 /*ARGSUSED*/
    471 static int
    472 _files_getgrent(void *nsrv, void *nscb, va_list ap)
    473 {
    474 	struct group	**retval = va_arg(ap, struct group **);
    475 
    476 	int	rv, rerror;
    477 
    478 	_DIAGASSERT(retval != NULL);
    479 
    480 	*retval = NULL;
    481 	rv = _files_grscan(&rerror, &_files_group,
    482 	    _files_groupbuf, sizeof(_files_groupbuf),
    483 	    &_files_state, 0, NULL, 0);
    484 	if (rv == NS_SUCCESS)
    485 		*retval = &_files_group;
    486 	return rv;
    487 }
    488 
    489 /*ARGSUSED*/
    490 static int
    491 _files_getgrgid(void *nsrv, void *nscb, va_list ap)
    492 {
    493 	struct group	**retval = va_arg(ap, struct group **);
    494 	gid_t		 gid	= va_arg(ap, gid_t);
    495 
    496 	int	rv, rerror;
    497 
    498 	_DIAGASSERT(retval != NULL);
    499 
    500 	*retval = NULL;
    501 	rv = _files_start(&_files_state);
    502 	if (rv != NS_SUCCESS)
    503 		return rv;
    504 	rv = _files_grscan(&rerror, &_files_group,
    505 	    _files_groupbuf, sizeof(_files_groupbuf),
    506 	    &_files_state, 1, NULL, gid);
    507 	if (!_files_state.stayopen)
    508 		_files_end(&_files_state);
    509 	if (rv == NS_SUCCESS)
    510 		*retval = &_files_group;
    511 	return rv;
    512 }
    513 
    514 /*ARGSUSED*/
    515 static int
    516 _files_getgrgid_r(void *nsrv, void *nscb, va_list ap)
    517 {
    518 	int		*retval	= va_arg(ap, int *);
    519 	gid_t		 gid	= va_arg(ap, gid_t);
    520 	struct group	*grp	= va_arg(ap, struct group *);
    521 	char		*buffer	= va_arg(ap, char *);
    522 	size_t		 buflen	= va_arg(ap, size_t);
    523 	struct group   **result	= va_arg(ap, struct group **);
    524 
    525 	struct files_state state;
    526 	int	rv;
    527 
    528 	_DIAGASSERT(retval != NULL);
    529 	_DIAGASSERT(grp != NULL);
    530 	_DIAGASSERT(buffer != NULL);
    531 	_DIAGASSERT(result != NULL);
    532 
    533 	*result = NULL;
    534 	memset(&state, 0, sizeof(state));
    535 	rv = _files_grscan(retval, grp, buffer, buflen, &state, 1, NULL, gid);
    536 	_files_end(&state);
    537 	if (rv == NS_SUCCESS)
    538 		*result = grp;
    539 	return rv;
    540 }
    541 
    542 /*ARGSUSED*/
    543 static int
    544 _files_getgrnam(void *nsrv, void *nscb, va_list ap)
    545 {
    546 	struct group	**retval = va_arg(ap, struct group **);
    547 	const char	*name	= va_arg(ap, const char *);
    548 
    549 	int	rv, rerror;
    550 
    551 	_DIAGASSERT(retval != NULL);
    552 
    553 	*retval = NULL;
    554 	rv = _files_start(&_files_state);
    555 	if (rv != NS_SUCCESS)
    556 		return rv;
    557 	rv = _files_grscan(&rerror, &_files_group,
    558 	    _files_groupbuf, sizeof(_files_groupbuf),
    559 	    &_files_state, 1, name, 0);
    560 	if (!_files_state.stayopen)
    561 		_files_end(&_files_state);
    562 	if (rv == NS_SUCCESS)
    563 		*retval = &_files_group;
    564 	return rv;
    565 }
    566 
    567 /*ARGSUSED*/
    568 static int
    569 _files_getgrnam_r(void *nsrv, void *nscb, va_list ap)
    570 {
    571 	int		*retval	= va_arg(ap, int *);
    572 	const char	*name	= va_arg(ap, const char *);
    573 	struct group	*grp	= va_arg(ap, struct group *);
    574 	char		*buffer	= va_arg(ap, char *);
    575 	size_t		 buflen	= va_arg(ap, size_t);
    576 	struct group   **result	= va_arg(ap, struct group **);
    577 
    578 	struct files_state state;
    579 	int	rv;
    580 
    581 	_DIAGASSERT(retval != NULL);
    582 	_DIAGASSERT(grp != NULL);
    583 	_DIAGASSERT(buffer != NULL);
    584 	_DIAGASSERT(result != NULL);
    585 
    586 	*result = NULL;
    587 	memset(&state, 0, sizeof(state));
    588 	rv = _files_grscan(retval, grp, buffer, buflen, &state, 1, name, 0);
    589 	_files_end(&state);
    590 	if (rv == NS_SUCCESS)
    591 		*result = grp;
    592 	return rv;
    593 }
    594 
    595 
    596 #ifdef HESIOD
    597 		/*
    598 		 *	dns methods
    599 		 */
    600 
    601 	/* state shared between dns methods */
    602 struct dns_state {
    603 	int	 stayopen;		/* see getgroupent(3) */
    604 	void	*context;		/* Hesiod context */
    605 	int	 num;			/* group index, -1 if no more */
    606 };
    607 
    608 static struct dns_state		_dns_state;
    609 					/* storage for non _r functions */
    610 static struct group		_dns_group;
    611 static char			_dns_groupbuf[GETGR_R_SIZE_MAX];
    612 
    613 static int
    614 _dns_start(struct dns_state *state)
    615 {
    616 
    617 	_DIAGASSERT(state != NULL);
    618 
    619 	state->num = 0;
    620 	if (state->context == NULL) {			/* setup Hesiod */
    621 		if (hesiod_init(&state->context) == -1)
    622 			return NS_UNAVAIL;
    623 	}
    624 
    625 	return NS_SUCCESS;
    626 }
    627 
    628 static int
    629 _dns_end(struct dns_state *state)
    630 {
    631 
    632 	_DIAGASSERT(state != NULL);
    633 
    634 	state->num = 0;
    635 	if (state->context) {
    636 		hesiod_end(state->context);
    637 		state->context = NULL;
    638 	}
    639 	return NS_SUCCESS;
    640 }
    641 
    642 /*
    643  * _dns_grscan
    644  *	Look for the Hesiod name provided in buffer in the NULL-terminated
    645  *	list of zones,
    646  *	and decode into grp/buffer/buflen.
    647  */
    648 static int
    649 _dns_grscan(int *retval, struct group *grp, char *buffer, size_t buflen,
    650 	struct dns_state *state, const char **zones)
    651 {
    652 	const char	**curzone;
    653 	char		**hp, *ep;
    654 	int		rv;
    655 
    656 	_DIAGASSERT(retval != NULL);
    657 	_DIAGASSERT(grp != NULL);
    658 	_DIAGASSERT(buffer != NULL);
    659 	_DIAGASSERT(state != NULL);
    660 	_DIAGASSERT(zones != NULL);
    661 
    662 	*retval = 0;
    663 
    664 	if (state->context == NULL) {	/* only start if Hesiod not setup */
    665 		rv = _dns_start(state);
    666 		if (rv != NS_SUCCESS)
    667 			return rv;
    668 	}
    669 
    670 	hp = NULL;
    671 	rv = NS_NOTFOUND;
    672 
    673 	for (curzone = zones; *curzone; curzone++) {	/* search zones */
    674 		hp = hesiod_resolve(state->context, buffer, *curzone);
    675 		if (hp != NULL)
    676 			break;
    677 		if (errno != ENOENT) {
    678 			rv = NS_UNAVAIL;
    679 			goto dnsgrscan_out;
    680 		}
    681 	}
    682 	if (*curzone == NULL)
    683 		goto dnsgrscan_out;
    684 
    685 	if ((ep = strchr(hp[0], '\n')) != NULL)
    686 		*ep = '\0';				/* clear trailing \n */
    687 	if (_gr_parse(hp[0], grp, buffer, buflen))	/* validate line */
    688 		rv = NS_SUCCESS;
    689 	else
    690 		rv = NS_UNAVAIL;
    691 
    692  dnsgrscan_out:
    693 	if (rv != NS_SUCCESS)
    694 		*retval = errno;
    695 	if (hp)
    696 		hesiod_free_list(state->context, hp);
    697 	return rv;
    698 }
    699 
    700 /*ARGSUSED*/
    701 static int
    702 _dns_setgrent(void *nsrv, void *nscb, va_list ap)
    703 {
    704 
    705 	_dns_state.stayopen = 0;
    706 	return _dns_start(&_dns_state);
    707 }
    708 
    709 /*ARGSUSED*/
    710 static int
    711 _dns_setgroupent(void *nsrv, void *nscb, va_list ap)
    712 {
    713 	int	*retval		= va_arg(ap, int *);
    714 	int	 stayopen	= va_arg(ap, int);
    715 
    716 	int	rv;
    717 
    718 	_dns_state.stayopen = stayopen;
    719 	rv = _dns_start(&_dns_state);
    720 	*retval = (rv == NS_SUCCESS);
    721 	return rv;
    722 }
    723 
    724 /*ARGSUSED*/
    725 static int
    726 _dns_endgrent(void *nsrv, void *nscb, va_list ap)
    727 {
    728 
    729 	_dns_state.stayopen = 0;
    730 	return _dns_end(&_dns_state);
    731 }
    732 
    733 /*ARGSUSED*/
    734 static int
    735 _dns_getgrent(void *nsrv, void *nscb, va_list ap)
    736 {
    737 	struct group	**retval = va_arg(ap, struct group **);
    738 
    739 	char	**hp, *ep;
    740 	int	  rv;
    741 
    742 	_DIAGASSERT(retval != NULL);
    743 
    744 	*retval = NULL;
    745 
    746 	if (_dns_state.num == -1)			/* exhausted search */
    747 		return NS_NOTFOUND;
    748 
    749 	if (_dns_state.context == NULL) {
    750 			/* only start if Hesiod not setup */
    751 		rv = _dns_start(&_dns_state);
    752 		if (rv != NS_SUCCESS)
    753 			return rv;
    754 	}
    755 
    756 	hp = NULL;
    757 	rv = NS_NOTFOUND;
    758 
    759 							/* find group-NNN */
    760 	snprintf(_dns_groupbuf, sizeof(_dns_groupbuf),
    761 	    "group-%u", _dns_state.num);
    762 	_dns_state.num++;
    763 
    764 	hp = hesiod_resolve(_dns_state.context, _dns_groupbuf, "group");
    765 	if (hp == NULL) {
    766 		if (errno == ENOENT)
    767 			_dns_state.num = -1;
    768 		else
    769 			rv = NS_UNAVAIL;
    770 	} else {
    771 		if ((ep = strchr(hp[0], '\n')) != NULL)
    772 			*ep = '\0';			/* clear trailing \n */
    773 							/* validate line */
    774 		if (_gr_parse(hp[0], &_dns_group,
    775 		    _dns_groupbuf, sizeof(_dns_groupbuf)))
    776 			rv = NS_SUCCESS;
    777 		else
    778 			rv = NS_UNAVAIL;
    779 	}
    780 
    781 	if (hp)
    782 		hesiod_free_list(_dns_state.context, hp);
    783 	if (rv == NS_SUCCESS)
    784 		*retval = &_dns_group;
    785 	return rv;
    786 }
    787 
    788 static const char *_dns_gid_zones[] = {
    789 	"gid",
    790 	"group",
    791 	NULL
    792 };
    793 
    794 /*ARGSUSED*/
    795 static int
    796 _dns_getgrgid(void *nsrv, void *nscb, va_list ap)
    797 {
    798 	struct group	**retval = va_arg(ap, struct group **);
    799 	gid_t		 gid	= va_arg(ap, gid_t);
    800 
    801 	int	rv, rerror;
    802 
    803 	_DIAGASSERT(retval != NULL);
    804 
    805 	*retval = NULL;
    806 	rv = _dns_start(&_dns_state);
    807 	if (rv != NS_SUCCESS)
    808 		return rv;
    809 	snprintf(_dns_groupbuf, sizeof(_dns_groupbuf), "%u", (unsigned int)gid);
    810 	rv = _dns_grscan(&rerror, &_dns_group,
    811 	    _dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, _dns_gid_zones);
    812 	if (!_dns_state.stayopen)
    813 		_dns_end(&_dns_state);
    814 	if (rv == NS_SUCCESS && gid == _dns_group.gr_gid)
    815 		*retval = &_dns_group;
    816 	return rv;
    817 }
    818 
    819 /*ARGSUSED*/
    820 static int
    821 _dns_getgrgid_r(void *nsrv, void *nscb, va_list ap)
    822 {
    823 	int		*retval	= va_arg(ap, int *);
    824 	gid_t		 gid	= va_arg(ap, gid_t);
    825 	struct group	*grp	= va_arg(ap, struct group *);
    826 	char		*buffer	= va_arg(ap, char *);
    827 	size_t		 buflen	= va_arg(ap, size_t);
    828 	struct group   **result	= va_arg(ap, struct group **);
    829 
    830 	struct dns_state state;
    831 	int	rv;
    832 
    833 	_DIAGASSERT(retval != NULL);
    834 	_DIAGASSERT(grp != NULL);
    835 	_DIAGASSERT(buffer != NULL);
    836 	_DIAGASSERT(result != NULL);
    837 
    838 	*result = NULL;
    839 	memset(&state, 0, sizeof(state));
    840 	snprintf(buffer, buflen, "%u", (unsigned int)gid);
    841 	rv = _dns_grscan(retval, grp, buffer, buflen, &state, _dns_gid_zones);
    842 	_dns_end(&state);
    843 	if (rv != NS_SUCCESS)
    844 		return rv;
    845 	if (gid == grp->gr_gid) {
    846 		*result = grp;
    847 		return NS_SUCCESS;
    848 	} else
    849 		return NS_NOTFOUND;
    850 }
    851 
    852 static const char *_dns_nam_zones[] = {
    853 	"group",
    854 	NULL
    855 };
    856 
    857 /*ARGSUSED*/
    858 static int
    859 _dns_getgrnam(void *nsrv, void *nscb, va_list ap)
    860 {
    861 	struct group	**retval = va_arg(ap, struct group **);
    862 	const char	*name	= va_arg(ap, const char *);
    863 
    864 	int	rv, rerror;
    865 
    866 	_DIAGASSERT(retval != NULL);
    867 
    868 	*retval = NULL;
    869 	rv = _dns_start(&_dns_state);
    870 	if (rv != NS_SUCCESS)
    871 		return rv;
    872 	snprintf(_dns_groupbuf, sizeof(_dns_groupbuf), "%s", name);
    873 	rv = _dns_grscan(&rerror, &_dns_group,
    874 	    _dns_groupbuf, sizeof(_dns_groupbuf), &_dns_state, _dns_nam_zones);
    875 	if (!_dns_state.stayopen)
    876 		_dns_end(&_dns_state);
    877 	if (rv == NS_SUCCESS && strcmp(name, _dns_group.gr_name) == 0)
    878 		*retval = &_dns_group;
    879 	return rv;
    880 }
    881 
    882 /*ARGSUSED*/
    883 static int
    884 _dns_getgrnam_r(void *nsrv, void *nscb, va_list ap)
    885 {
    886 	int		*retval	= va_arg(ap, int *);
    887 	const char	*name	= va_arg(ap, const char *);
    888 	struct group	*grp	= va_arg(ap, struct group *);
    889 	char		*buffer	= va_arg(ap, char *);
    890 	size_t		 buflen	= va_arg(ap, size_t);
    891 	struct group   **result	= va_arg(ap, struct group **);
    892 
    893 	struct dns_state state;
    894 	int	rv;
    895 
    896 	_DIAGASSERT(retval != NULL);
    897 	_DIAGASSERT(grp != NULL);
    898 	_DIAGASSERT(buffer != NULL);
    899 	_DIAGASSERT(result != NULL);
    900 
    901 	*result = NULL;
    902 	memset(&state, 0, sizeof(state));
    903 	snprintf(buffer, buflen, "%s", name);
    904 	rv = _dns_grscan(retval, grp, buffer, buflen, &state, _dns_nam_zones);
    905 	_dns_end(&state);
    906 	if (rv != NS_SUCCESS)
    907 		return rv;
    908 	if (strcmp(name, grp->gr_name) == 0) {
    909 		*result = grp;
    910 		return NS_SUCCESS;
    911 	} else
    912 		return NS_NOTFOUND;
    913 }
    914 
    915 #endif /* HESIOD */
    916 
    917 
    918 #ifdef YP
    919 		/*
    920 		 *	nis methods
    921 		 */
    922 	/* state shared between nis methods */
    923 struct nis_state {
    924 	int	 stayopen;		/* see getgroupent(3) */
    925 	char	*domain;		/* NIS domain */
    926 	int	 done;			/* non-zero if search exhausted */
    927 	char	*current;		/* current first/next match */
    928 	int	 currentlen;		/* length of _nis_current */
    929 };
    930 
    931 static struct nis_state		_nis_state;
    932 					/* storage for non _r functions */
    933 static struct group		_nis_group;
    934 static char			_nis_groupbuf[GETGR_R_SIZE_MAX];
    935 
    936 static int
    937 _nis_start(struct nis_state *state)
    938 {
    939 
    940 	_DIAGASSERT(state != NULL);
    941 
    942 	state->done = 0;
    943 	if (state->current) {
    944 		free(state->current);
    945 		state->current = NULL;
    946 	}
    947 	if (state->domain == NULL) {			/* setup NIS */
    948 		switch (yp_get_default_domain(&state->domain)) {
    949 		case 0:
    950 			break;
    951 		case YPERR_RESRC:
    952 			return NS_TRYAGAIN;
    953 		default:
    954 			return NS_UNAVAIL;
    955 		}
    956 	}
    957 	return NS_SUCCESS;
    958 }
    959 
    960 static int
    961 _nis_end(struct nis_state *state)
    962 {
    963 
    964 	_DIAGASSERT(state != NULL);
    965 
    966 	if (state->domain) {
    967 		state->domain = NULL;
    968 	}
    969 	state->done = 0;
    970 	if (state->current) {
    971 		free(state->current);
    972 		state->current = NULL;
    973 	}
    974 	return NS_SUCCESS;
    975 }
    976 
    977 /*
    978  * _nis_grscan
    979  *	Look for the yp key provided in buffer from map,
    980  *	and decode into grp/buffer/buflen.
    981  */
    982 static int
    983 _nis_grscan(int *retval, struct group *grp, char *buffer, size_t buflen,
    984 	struct nis_state *state, const char *map)
    985 {
    986 	char	*data;
    987 	int	nisr, rv, datalen;
    988 
    989 	_DIAGASSERT(retval != NULL);
    990 	_DIAGASSERT(grp != NULL);
    991 	_DIAGASSERT(buffer != NULL);
    992 	_DIAGASSERT(state != NULL);
    993 	_DIAGASSERT(map != NULL);
    994 
    995 	*retval = 0;
    996 
    997 	if (state->domain == NULL) {	/* only start if NIS not setup */
    998 		rv = _nis_start(state);
    999 		if (rv != NS_SUCCESS)
   1000 			return rv;
   1001 	}
   1002 
   1003 	data = NULL;
   1004 	rv = NS_NOTFOUND;
   1005 
   1006 							/* search map */
   1007 	nisr = yp_match(state->domain, map, buffer, (int)strlen(buffer),
   1008 	    &data, &datalen);
   1009 	switch (nisr) {
   1010 	case 0:
   1011 		data[datalen] = '\0';			/* clear trailing \n */
   1012 		if (_gr_parse(data, grp, buffer, buflen))
   1013 			rv = NS_SUCCESS;		/* validate line */
   1014 		else
   1015 			rv = NS_UNAVAIL;
   1016 		break;
   1017 	case YPERR_KEY:
   1018 		break;
   1019 	default:
   1020 		rv = NS_UNAVAIL;
   1021 		break;
   1022 	}
   1023 
   1024 	if (rv != NS_SUCCESS)
   1025 		*retval = errno;
   1026 	if (data)
   1027 		free(data);
   1028 	return rv;
   1029 }
   1030 
   1031 /*ARGSUSED*/
   1032 static int
   1033 _nis_setgrent(void *nsrv, void *nscb, va_list ap)
   1034 {
   1035 
   1036 	_nis_state.stayopen = 0;
   1037 	return _nis_start(&_nis_state);
   1038 }
   1039 
   1040 /*ARGSUSED*/
   1041 static int
   1042 _nis_setgroupent(void *nsrv, void *nscb, va_list ap)
   1043 {
   1044 	int	*retval		= va_arg(ap, int *);
   1045 	int	 stayopen	= va_arg(ap, int);
   1046 
   1047 	int	rv;
   1048 
   1049 	_nis_state.stayopen = stayopen;
   1050 	rv = _nis_start(&_nis_state);
   1051 	*retval = (rv == NS_SUCCESS);
   1052 	return rv;
   1053 }
   1054 
   1055 /*ARGSUSED*/
   1056 static int
   1057 _nis_endgrent(void *nsrv, void *nscb, va_list ap)
   1058 {
   1059 
   1060 	return _nis_end(&_nis_state);
   1061 }
   1062 
   1063 
   1064 /*ARGSUSED*/
   1065 static int
   1066 _nis_getgrent(void *nsrv, void *nscb, va_list ap)
   1067 {
   1068 	struct group	**retval = va_arg(ap, struct group **);
   1069 
   1070 	char	*key, *data;
   1071 	int	keylen, datalen, rv, nisr;
   1072 
   1073 	_DIAGASSERT(retval != NULL);
   1074 
   1075 	*retval = NULL;
   1076 
   1077 	if (_nis_state.done)				/* exhausted search */
   1078 		return NS_NOTFOUND;
   1079 	if (_nis_state.domain == NULL) {
   1080 					/* only start if NIS not setup */
   1081 		rv = _nis_start(&_nis_state);
   1082 		if (rv != NS_SUCCESS)
   1083 			return rv;
   1084 	}
   1085 
   1086 	key = NULL;
   1087 	data = NULL;
   1088 	rv = NS_NOTFOUND;
   1089 
   1090 	if (_nis_state.current) {			/* already searching */
   1091 		nisr = yp_next(_nis_state.domain, "group.byname",
   1092 		    _nis_state.current, _nis_state.currentlen,
   1093 		    &key, &keylen, &data, &datalen);
   1094 		free(_nis_state.current);
   1095 		_nis_state.current = NULL;
   1096 		switch (nisr) {
   1097 		case 0:
   1098 			_nis_state.current = key;
   1099 			_nis_state.currentlen = keylen;
   1100 			key = NULL;
   1101 			break;
   1102 		case YPERR_NOMORE:
   1103 			_nis_state.done = 1;
   1104 			goto nisent_out;
   1105 		default:
   1106 			rv = NS_UNAVAIL;
   1107 			goto nisent_out;
   1108 		}
   1109 	} else {					/* new search */
   1110 		if (yp_first(_nis_state.domain, "group.byname",
   1111 		    &_nis_state.current, &_nis_state.currentlen,
   1112 		    &data, &datalen)) {
   1113 			rv = NS_UNAVAIL;
   1114 			goto nisent_out;
   1115 		}
   1116 	}
   1117 
   1118 	data[datalen] = '\0';				/* clear trailing \n */
   1119 							/* validate line */
   1120 	if (_gr_parse(data, &_nis_group, _nis_groupbuf, sizeof(_nis_groupbuf)))
   1121 		rv = NS_SUCCESS;
   1122 	else
   1123 		rv = NS_UNAVAIL;
   1124 
   1125  nisent_out:
   1126 	if (key)
   1127 		free(key);
   1128 	if (data)
   1129 		free(data);
   1130 	if (rv == NS_SUCCESS)
   1131 		*retval = &_nis_group;
   1132 	return rv;
   1133 }
   1134 
   1135 /*ARGSUSED*/
   1136 static int
   1137 _nis_getgrgid(void *nsrv, void *nscb, va_list ap)
   1138 {
   1139 	struct group	**retval = va_arg(ap, struct group **);
   1140 	gid_t		 gid	= va_arg(ap, gid_t);
   1141 
   1142 	int	rv, rerror;
   1143 
   1144 	_DIAGASSERT(retval != NULL);
   1145 
   1146 	*retval = NULL;
   1147 	rv = _nis_start(&_nis_state);
   1148 	if (rv != NS_SUCCESS)
   1149 		return rv;
   1150 	snprintf(_nis_groupbuf, sizeof(_nis_groupbuf), "%u", (unsigned int)gid);
   1151 	rv = _nis_grscan(&rerror, &_nis_group,
   1152 	    _nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, "group.bygid");
   1153 	if (!_nis_state.stayopen)
   1154 		_nis_end(&_nis_state);
   1155 	if (rv == NS_SUCCESS && gid == _nis_group.gr_gid)
   1156 		*retval = &_nis_group;
   1157 	return rv;
   1158 }
   1159 
   1160 /*ARGSUSED*/
   1161 static int
   1162 _nis_getgrgid_r(void *nsrv, void *nscb, va_list ap)
   1163 {
   1164 	int		*retval	= va_arg(ap, int *);
   1165 	gid_t		 gid	= va_arg(ap, gid_t);
   1166 	struct group	*grp	= va_arg(ap, struct group *);
   1167 	char		*buffer	= va_arg(ap, char *);
   1168 	size_t		 buflen	= va_arg(ap, size_t);
   1169 	struct group   **result	= va_arg(ap, struct group **);
   1170 
   1171 	struct nis_state state;
   1172 	int	rv;
   1173 
   1174 	_DIAGASSERT(retval != NULL);
   1175 	_DIAGASSERT(grp != NULL);
   1176 	_DIAGASSERT(buffer != NULL);
   1177 	_DIAGASSERT(result != NULL);
   1178 
   1179 	*result = NULL;
   1180 	memset(&state, 0, sizeof(state));
   1181 	snprintf(buffer, buflen, "%u", (unsigned int)gid);
   1182 	rv = _nis_grscan(retval, grp, buffer, buflen, &state, "group.bygid");
   1183 	_nis_end(&state);
   1184 	if (rv != NS_SUCCESS)
   1185 		return rv;
   1186 	if (gid == grp->gr_gid) {
   1187 		*result = grp;
   1188 		return NS_SUCCESS;
   1189 	} else
   1190 		return NS_NOTFOUND;
   1191 }
   1192 
   1193 /*ARGSUSED*/
   1194 static int
   1195 _nis_getgrnam(void *nsrv, void *nscb, va_list ap)
   1196 {
   1197 	struct group	**retval = va_arg(ap, struct group **);
   1198 	const char	*name	= va_arg(ap, const char *);
   1199 
   1200 	int	rv, rerror;
   1201 
   1202 	_DIAGASSERT(retval != NULL);
   1203 
   1204 	*retval = NULL;
   1205 	rv = _nis_start(&_nis_state);
   1206 	if (rv != NS_SUCCESS)
   1207 		return rv;
   1208 	snprintf(_nis_groupbuf, sizeof(_nis_groupbuf), "%s", name);
   1209 	rv = _nis_grscan(&rerror, &_nis_group,
   1210 	    _nis_groupbuf, sizeof(_nis_groupbuf), &_nis_state, "group.byname");
   1211 	if (!_nis_state.stayopen)
   1212 		_nis_end(&_nis_state);
   1213 	if (rv == NS_SUCCESS && strcmp(name, _nis_group.gr_name) == 0)
   1214 		*retval = &_nis_group;
   1215 	return rv;
   1216 }
   1217 
   1218 /*ARGSUSED*/
   1219 static int
   1220 _nis_getgrnam_r(void *nsrv, void *nscb, va_list ap)
   1221 {
   1222 	int		*retval	= va_arg(ap, int *);
   1223 	const char	*name	= va_arg(ap, const char *);
   1224 	struct group	*grp	= va_arg(ap, struct group *);
   1225 	char		*buffer	= va_arg(ap, char *);
   1226 	size_t		 buflen	= va_arg(ap, size_t);
   1227 	struct group   **result	= va_arg(ap, struct group **);
   1228 
   1229 	struct nis_state state;
   1230 	int	rv;
   1231 
   1232 	_DIAGASSERT(retval != NULL);
   1233 	_DIAGASSERT(grp != NULL);
   1234 	_DIAGASSERT(buffer != NULL);
   1235 	_DIAGASSERT(result != NULL);
   1236 
   1237 	*result = NULL;
   1238 	snprintf(buffer, buflen, "%s", name);
   1239 	memset(&state, 0, sizeof(state));
   1240 	rv = _nis_grscan(retval, grp, buffer, buflen, &state, "group.byname");
   1241 	_nis_end(&state);
   1242 	if (rv != NS_SUCCESS)
   1243 		return rv;
   1244 	if (strcmp(name, grp->gr_name) == 0) {
   1245 		*result = grp;
   1246 		return NS_SUCCESS;
   1247 	} else
   1248 		return NS_NOTFOUND;
   1249 }
   1250 
   1251 #endif /* YP */
   1252 
   1253 
   1254 #ifdef _GROUP_COMPAT
   1255 		/*
   1256 		 *	compat methods
   1257 		 */
   1258 
   1259 	/* state shared between compat methods */
   1260 struct compat_state {
   1261 	int	 stayopen;		/* see getgroupent(3) */
   1262 	FILE	*fp;			/* file handle */
   1263 	char	*name;			/* NULL if reading file, */
   1264 					/* "" if compat "+", */
   1265 					/* name if compat "+name" */
   1266 };
   1267 
   1268 static struct compat_state	_compat_state;
   1269 					/* storage for non _r functions */
   1270 static struct group		_compat_group;
   1271 static char			_compat_groupbuf[GETGR_R_SIZE_MAX];
   1272 
   1273 static int
   1274 _compat_start(struct compat_state *state)
   1275 {
   1276 
   1277 	_DIAGASSERT(state != NULL);
   1278 
   1279 	if (state->fp == NULL) {
   1280 		state->fp = fopen(_PATH_GROUP, "r");
   1281 		if (state->fp == NULL)
   1282 			return NS_UNAVAIL;
   1283 	} else {
   1284 		rewind(state->fp);
   1285 	}
   1286 	return NS_SUCCESS;
   1287 }
   1288 
   1289 static int
   1290 _compat_end(struct compat_state *state)
   1291 {
   1292 
   1293 	_DIAGASSERT(state != NULL);
   1294 
   1295 	if (state->name) {
   1296 		free(state->name);
   1297 		state->name = NULL;
   1298 	}
   1299 	if (state->fp) {
   1300 		(void) fclose(state->fp);
   1301 		state->fp = NULL;
   1302 	}
   1303 	return NS_SUCCESS;
   1304 }
   1305 
   1306 
   1307 /*
   1308  * _compat_grbad
   1309  *	log an error if "files" or "compat" is specified in
   1310  *	group_compat database
   1311  */
   1312 /*ARGSUSED*/
   1313 static int
   1314 _compat_grbad(void *nsrv, void *nscb, va_list ap)
   1315 {
   1316 	static int warned;
   1317 
   1318 	_DIAGASSERT(cb_data != NULL);
   1319 
   1320 	if (!warned) {
   1321 		syslog(LOG_ERR,
   1322 			"nsswitch.conf group_compat database can't use '%s'",
   1323 			(char *)nscb);
   1324 	}
   1325 	warned = 1;
   1326 	return NS_UNAVAIL;
   1327 }
   1328 
   1329 /*
   1330  * _compat_grscan
   1331  *	Scan state->fp for the next desired entry.
   1332  *	If search is zero, return the next entry.
   1333  *	If search is non-zero, look for a specific name (if name != NULL),
   1334  *	or a specific gid (if name == NULL).
   1335  *	Sets *retval to the errno if the result is not NS_SUCCESS.
   1336  */
   1337 static int
   1338 _compat_grscan(int *retval, struct group *grp, char *buffer, size_t buflen,
   1339 	struct compat_state *state, int search, const char *name, gid_t gid)
   1340 {
   1341 	int		rv;
   1342 	char		filebuf[GETGR_R_SIZE_MAX], *ep;
   1343 
   1344 	static const ns_dtab compatentdtab[] = {
   1345 		NS_FILES_CB(_compat_grbad, "files")
   1346 		NS_DNS_CB(_dns_getgrent, NULL)
   1347 		NS_NIS_CB(_nis_getgrent, NULL)
   1348 		NS_COMPAT_CB(_compat_grbad, "compat")
   1349 		{ 0 }
   1350 	};
   1351 	static const ns_dtab compatgiddtab[] = {
   1352 		NS_FILES_CB(_compat_grbad, "files")
   1353 		NS_DNS_CB(_dns_getgrgid_r, NULL)
   1354 		NS_NIS_CB(_nis_getgrgid_r, NULL)
   1355 		NS_COMPAT_CB(_compat_grbad, "compat")
   1356 		{ 0 }
   1357 	};
   1358 	static const ns_dtab compatnamdtab[] = {
   1359 		NS_FILES_CB(_compat_grbad, "files")
   1360 		NS_DNS_CB(_dns_getgrnam_r, NULL)
   1361 		NS_NIS_CB(_nis_getgrnam_r, NULL)
   1362 		NS_COMPAT_CB(_compat_grbad, "compat")
   1363 		{ 0 }
   1364 	};
   1365 
   1366 	_DIAGASSERT(retval != NULL);
   1367 	_DIAGASSERT(grp != NULL);
   1368 	_DIAGASSERT(buffer != NULL);
   1369 	_DIAGASSERT(state != NULL);
   1370 	/* name is NULL to indicate searching for gid */
   1371 
   1372 	*retval = 0;
   1373 
   1374 	if (state->fp == NULL) {	/* only start if file not open yet */
   1375 		rv = _compat_start(state);
   1376 		if (rv != NS_SUCCESS)
   1377 			goto compatgrscan_out;
   1378 	}
   1379 	rv = NS_NOTFOUND;
   1380 
   1381 	for (;;) {					/* loop through file */
   1382 		if (state->name != NULL) {
   1383 					/* processing compat entry */
   1384 			int		crv, cretval;
   1385 			struct group	cgrp, *cgrpres;
   1386 
   1387 			if (state->name[0]) {		/* specific +group: */
   1388 				crv = nsdispatch(NULL, compatnamdtab,
   1389 				    NSDB_GROUP_COMPAT, "getgrnam_r", defaultnis,
   1390 				    &cretval, state->name,
   1391 				    &cgrp, filebuf, sizeof(filebuf), &cgrpres);
   1392 				free(state->name);	/* (only check 1 grp) */
   1393 				state->name = NULL;
   1394 			} else if (!search) {		/* any group */
   1395 	/* XXXLUKEM: need to implement and use getgrent_r() */
   1396 				crv = nsdispatch(NULL, compatentdtab,
   1397 				    NSDB_GROUP_COMPAT, "getgrent", defaultnis,
   1398 				    &cgrpres);
   1399 			} else if (name) {		/* specific group */
   1400 				crv = nsdispatch(NULL, compatnamdtab,
   1401 				    NSDB_GROUP_COMPAT, "getgrnam_r", defaultnis,
   1402 				    &cretval, name,
   1403 				    &cgrp, filebuf, sizeof(filebuf), &cgrpres);
   1404 			} else {			/* specific gid */
   1405 				crv = nsdispatch(NULL, compatgiddtab,
   1406 				    NSDB_GROUP_COMPAT, "getgrgid_r", defaultnis,
   1407 				    &cretval, gid,
   1408 				    &cgrp, filebuf, sizeof(filebuf), &cgrpres);
   1409 			}
   1410 			if (crv != NS_SUCCESS) {	/* not found */
   1411 				free(state->name);
   1412 				state->name = NULL;
   1413 				continue;		/* try next line */
   1414 			}
   1415 			if (!_gr_copy(cgrpres, grp, buffer, buflen)) {
   1416 				rv = NS_UNAVAIL;
   1417 				break;
   1418 			}
   1419 			goto compatgrscan_cmpgrp;	/* skip to grp test */
   1420 		}
   1421 
   1422 							/* get next file line */
   1423 		if (fgets(filebuf, sizeof(filebuf), state->fp) == NULL)
   1424 			break;
   1425 
   1426 		ep = strchr(filebuf, '\n');
   1427 		if (ep == NULL) {	/* fail on lines that are too big */
   1428 			int ch;
   1429 
   1430 			while ((ch = getc(state->fp)) != '\n' && ch != EOF)
   1431 				continue;
   1432 			rv = NS_UNAVAIL;
   1433 			break;
   1434 		}
   1435 		*ep = '\0';				/* clear trailing \n */
   1436 
   1437 		if (filebuf[0] == '+') {		/* parse compat line */
   1438 			if (state->name)
   1439 				free(state->name);
   1440 			state->name = NULL;
   1441 			switch(filebuf[1]) {
   1442 			case ':':
   1443 			case '\0':
   1444 				state->name = strdup("");
   1445 				break;
   1446 			default:
   1447 				ep = strchr(filebuf + 1, ':');
   1448 				if (ep == NULL)
   1449 					break;
   1450 				*ep = '\0';
   1451 				state->name = strdup(filebuf + 1);
   1452 				break;
   1453 			}
   1454 			if (state->name == NULL) {
   1455 				rv = NS_UNAVAIL;
   1456 				break;
   1457 			}
   1458 			continue;
   1459 		}
   1460 
   1461 							/* validate line */
   1462 		if (! _gr_parse(filebuf, grp, buffer, buflen)) {
   1463 			rv = NS_UNAVAIL;
   1464 			break;
   1465 		}
   1466 
   1467  compatgrscan_cmpgrp:
   1468 		if (! search) {				/* just want this one */
   1469 			rv = NS_SUCCESS;
   1470 			break;
   1471 		}
   1472 							/* want specific */
   1473 		if ((name && strcmp(name, grp->gr_name) == 0) ||
   1474 		    (!name && gid == grp->gr_gid)) {
   1475 			rv = NS_SUCCESS;
   1476 			break;
   1477 		}
   1478 
   1479 	}
   1480 
   1481  compatgrscan_out:
   1482 	if (rv != NS_SUCCESS)
   1483 		*retval = errno;
   1484 	return rv;
   1485 }
   1486 
   1487 /*ARGSUSED*/
   1488 static int
   1489 _compat_setgrent(void *nsrv, void *nscb, va_list ap)
   1490 {
   1491 	static const ns_dtab dtab[] = {
   1492 		NS_FILES_CB(_compat_grbad, "files")
   1493 		NS_DNS_CB(_dns_setgrent, NULL)
   1494 		NS_NIS_CB(_nis_setgrent, NULL)
   1495 		NS_COMPAT_CB(_compat_grbad, "compat")
   1496 		{ 0 }
   1497 	};
   1498 
   1499 					/* force group_compat setgrent() */
   1500 	(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgrent",
   1501 	    defaultnis_forceall);
   1502 
   1503 					/* reset state, keep fp open */
   1504 	_compat_state.stayopen = 0;
   1505 	return _compat_start(&_compat_state);
   1506 }
   1507 
   1508 /*ARGSUSED*/
   1509 static int
   1510 _compat_setgroupent(void *nsrv, void *nscb, va_list ap)
   1511 {
   1512 	int	*retval		= va_arg(ap, int *);
   1513 	int	 stayopen	= va_arg(ap, int);
   1514 
   1515 	int	rv;
   1516 
   1517 	static const ns_dtab dtab[] = {
   1518 		NS_FILES_CB(_compat_grbad, "files")
   1519 		NS_DNS_CB(_dns_setgroupent, NULL)
   1520 		NS_NIS_CB(_nis_setgroupent, NULL)
   1521 		NS_COMPAT_CB(_compat_grbad, "compat")
   1522 		{ 0 }
   1523 	};
   1524 
   1525 					/* force group_compat setgroupent() */
   1526 	(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "setgroupent",
   1527 	    defaultnis_forceall, &rv, stayopen);
   1528 
   1529 	_compat_state.stayopen = stayopen;
   1530 	rv = _compat_start(&_compat_state);
   1531 	*retval = (rv == NS_SUCCESS);
   1532 	return rv;
   1533 }
   1534 
   1535 /*ARGSUSED*/
   1536 static int
   1537 _compat_endgrent(void *nsrv, void *nscb, va_list ap)
   1538 {
   1539 	static const ns_dtab dtab[] = {
   1540 		NS_FILES_CB(_compat_grbad, "files")
   1541 		NS_DNS_CB(_dns_endgrent, NULL)
   1542 		NS_NIS_CB(_nis_endgrent, NULL)
   1543 		NS_COMPAT_CB(_compat_grbad, "compat")
   1544 		{ 0 }
   1545 	};
   1546 
   1547 					/* force group_compat endgrent() */
   1548 	(void) nsdispatch(NULL, dtab, NSDB_GROUP_COMPAT, "endgrent",
   1549 	    defaultnis_forceall);
   1550 
   1551 					/* reset state, close fp */
   1552 	_compat_state.stayopen = 0;
   1553 	return _compat_end(&_compat_state);
   1554 }
   1555 
   1556 /*ARGSUSED*/
   1557 static int
   1558 _compat_getgrent(void *nsrv, void *nscb, va_list ap)
   1559 {
   1560 	struct group	**retval = va_arg(ap, struct group **);
   1561 
   1562 	int	rv, rerror;
   1563 
   1564 	_DIAGASSERT(retval != NULL);
   1565 
   1566 	*retval = NULL;
   1567 	rv = _compat_grscan(&rerror, &_compat_group,
   1568 	    _compat_groupbuf, sizeof(_compat_groupbuf),
   1569 	    &_compat_state, 0, NULL, 0);
   1570 	if (rv == NS_SUCCESS)
   1571 		*retval = &_compat_group;
   1572 	return rv;
   1573 }
   1574 
   1575 /*ARGSUSED*/
   1576 static int
   1577 _compat_getgrgid(void *nsrv, void *nscb, va_list ap)
   1578 {
   1579 	struct group	**retval = va_arg(ap, struct group **);
   1580 	gid_t		 gid	= va_arg(ap, gid_t);
   1581 
   1582 	int	rv, rerror;
   1583 
   1584 	_DIAGASSERT(retval != NULL);
   1585 
   1586 	*retval = NULL;
   1587 	rv = _compat_start(&_compat_state);
   1588 	if (rv != NS_SUCCESS)
   1589 		return rv;
   1590 	rv = _compat_grscan(&rerror, &_compat_group,
   1591 	    _compat_groupbuf, sizeof(_compat_groupbuf),
   1592 	    &_compat_state, 1, NULL, gid);
   1593 	if (!_compat_state.stayopen)
   1594 		_compat_end(&_compat_state);
   1595 	if (rv == NS_SUCCESS)
   1596 		*retval = &_compat_group;
   1597 	return rv;
   1598 }
   1599 
   1600 /*ARGSUSED*/
   1601 static int
   1602 _compat_getgrgid_r(void *nsrv, void *nscb, va_list ap)
   1603 {
   1604 	int		*retval	= va_arg(ap, int *);
   1605 	gid_t		 gid	= va_arg(ap, gid_t);
   1606 	struct group	*grp	= va_arg(ap, struct group *);
   1607 	char		*buffer	= va_arg(ap, char *);
   1608 	size_t		 buflen	= va_arg(ap, size_t);
   1609 	struct group   **result	= va_arg(ap, struct group **);
   1610 
   1611 	struct compat_state	state;
   1612 	int		rv;
   1613 
   1614 	_DIAGASSERT(retval != NULL);
   1615 	_DIAGASSERT(grp != NULL);
   1616 	_DIAGASSERT(buffer != NULL);
   1617 	_DIAGASSERT(result != NULL);
   1618 
   1619 	*result = NULL;
   1620 	memset(&state, 0, sizeof(state));
   1621 	rv = _compat_grscan(retval, grp, buffer, buflen, &state, 1, NULL, gid);
   1622 	_compat_end(&state);
   1623 	if (rv == NS_SUCCESS)
   1624 		*result = grp;
   1625 	return rv;
   1626 }
   1627 
   1628 /*ARGSUSED*/
   1629 static int
   1630 _compat_getgrnam(void *nsrv, void *nscb, va_list ap)
   1631 {
   1632 	struct group	**retval = va_arg(ap, struct group **);
   1633 	const char	*name	= va_arg(ap, const char *);
   1634 
   1635 	int	rv, rerror;
   1636 
   1637 	_DIAGASSERT(retval != NULL);
   1638 
   1639 	*retval = NULL;
   1640 	rv = _compat_start(&_compat_state);
   1641 	if (rv != NS_SUCCESS)
   1642 		return rv;
   1643 	rv = _compat_grscan(&rerror, &_compat_group,
   1644 	    _compat_groupbuf, sizeof(_compat_groupbuf),
   1645 	    &_compat_state, 1, name, 0);
   1646 	if (!_compat_state.stayopen)
   1647 		_compat_end(&_compat_state);
   1648 	if (rv == NS_SUCCESS)
   1649 		*retval = &_compat_group;
   1650 	return rv;
   1651 }
   1652 
   1653 /*ARGSUSED*/
   1654 static int
   1655 _compat_getgrnam_r(void *nsrv, void *nscb, va_list ap)
   1656 {
   1657 	int		*retval	= va_arg(ap, int *);
   1658 	const char	*name	= va_arg(ap, const char *);
   1659 	struct group	*grp	= va_arg(ap, struct group *);
   1660 	char		*buffer	= va_arg(ap, char *);
   1661 	size_t		 buflen	= va_arg(ap, size_t);
   1662 	struct group   **result	= va_arg(ap, struct group **);
   1663 
   1664 	struct compat_state	state;
   1665 	int		rv;
   1666 
   1667 	_DIAGASSERT(retval != NULL);
   1668 	_DIAGASSERT(grp != NULL);
   1669 	_DIAGASSERT(buffer != NULL);
   1670 	_DIAGASSERT(result != NULL);
   1671 
   1672 	*result = NULL;
   1673 	memset(&state, 0, sizeof(state));
   1674 	rv = _compat_grscan(retval, grp, buffer, buflen, &state, 1, name, 0);
   1675 	_compat_end(&state);
   1676 	if (rv == NS_SUCCESS)
   1677 		*result = grp;
   1678 	return rv;
   1679 }
   1680 
   1681 #endif	/* _GROUP_COMPAT */
   1682 
   1683 
   1684 		/*
   1685 		 *	public functions
   1686 		 */
   1687 
   1688 struct group *
   1689 getgrent(void)
   1690 {
   1691 	int		rv;
   1692 	struct group	*retval;
   1693 
   1694 	static const ns_dtab dtab[] = {
   1695 		NS_FILES_CB(_files_getgrent, NULL)
   1696 		NS_DNS_CB(_dns_getgrent, NULL)
   1697 		NS_NIS_CB(_nis_getgrent, NULL)
   1698 		NS_COMPAT_CB(_compat_getgrent, NULL)
   1699 		{ 0 }
   1700 	};
   1701 
   1702 	mutex_lock(&_grmutex);
   1703 	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrent", defaultcompat,
   1704 	    &retval);
   1705 	mutex_unlock(&_grmutex);
   1706 	return (rv == NS_SUCCESS) ? retval : NULL;
   1707 }
   1708 
   1709 struct group *
   1710 getgrgid(gid_t gid)
   1711 {
   1712 	int		rv;
   1713 	struct group	*retval;
   1714 
   1715 	static const ns_dtab dtab[] = {
   1716 		NS_FILES_CB(_files_getgrgid, NULL)
   1717 		NS_DNS_CB(_dns_getgrgid, NULL)
   1718 		NS_NIS_CB(_nis_getgrgid, NULL)
   1719 		NS_COMPAT_CB(_compat_getgrgid, NULL)
   1720 		{ 0 }
   1721 	};
   1722 
   1723 	mutex_lock(&_grmutex);
   1724 	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrgid", defaultcompat,
   1725 	    &retval, gid);
   1726 	mutex_unlock(&_grmutex);
   1727 	return (rv == NS_SUCCESS) ? retval : NULL;
   1728 }
   1729 
   1730 int
   1731 getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t buflen,
   1732 	struct group **result)
   1733 {
   1734 	int	rv, retval;
   1735 
   1736 	static const ns_dtab dtab[] = {
   1737 		NS_FILES_CB(_files_getgrgid_r, NULL)
   1738 		NS_DNS_CB(_dns_getgrgid_r, NULL)
   1739 		NS_NIS_CB(_nis_getgrgid_r, NULL)
   1740 		NS_COMPAT_CB(_compat_getgrgid_r, NULL)
   1741 		{ 0 }
   1742 	};
   1743 
   1744 	_DIAGASSERT(grp != NULL);
   1745 	_DIAGASSERT(buffer != NULL);
   1746 	_DIAGASSERT(result != NULL);
   1747 
   1748 	*result = NULL;
   1749 	retval = 0;
   1750 	mutex_lock(&_grmutex);
   1751 	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrgid_r", defaultcompat,
   1752 	    &retval, gid, grp, buffer, buflen, result);
   1753 	mutex_unlock(&_grmutex);
   1754 	return (rv == NS_SUCCESS) ? 0 : retval ? retval : ENOENT;
   1755 }
   1756 
   1757 struct group *
   1758 getgrnam(const char *name)
   1759 {
   1760 	int		rv;
   1761 	struct group	*retval;
   1762 
   1763 	static const ns_dtab dtab[] = {
   1764 		NS_FILES_CB(_files_getgrnam, NULL)
   1765 		NS_DNS_CB(_dns_getgrnam, NULL)
   1766 		NS_NIS_CB(_nis_getgrnam, NULL)
   1767 		NS_COMPAT_CB(_compat_getgrnam, NULL)
   1768 		{ 0 }
   1769 	};
   1770 
   1771 	mutex_lock(&_grmutex);
   1772 	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrnam", defaultcompat,
   1773 	    &retval, name);
   1774 	mutex_unlock(&_grmutex);
   1775 	return (rv == NS_SUCCESS) ? retval : NULL;
   1776 }
   1777 
   1778 int
   1779 getgrnam_r(const char *name, struct group *grp, char *buffer, size_t buflen,
   1780 	struct group **result)
   1781 {
   1782 	int	rv, retval;
   1783 
   1784 	static const ns_dtab dtab[] = {
   1785 		NS_FILES_CB(_files_getgrnam_r, NULL)
   1786 		NS_DNS_CB(_dns_getgrnam_r, NULL)
   1787 		NS_NIS_CB(_nis_getgrnam_r, NULL)
   1788 		NS_COMPAT_CB(_compat_getgrnam_r, NULL)
   1789 		{ 0 }
   1790 	};
   1791 
   1792 	_DIAGASSERT(name != NULL);
   1793 	_DIAGASSERT(grp != NULL);
   1794 	_DIAGASSERT(buffer != NULL);
   1795 	_DIAGASSERT(result != NULL);
   1796 
   1797 	*result = NULL;
   1798 	retval = 0;
   1799 	mutex_lock(&_grmutex);
   1800 	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "getgrnam_r", defaultcompat,
   1801 	    &retval, name, grp, buffer, buflen, result);
   1802 	mutex_unlock(&_grmutex);
   1803 	return (rv == NS_SUCCESS) ? 0 : retval ? retval : ENOENT;
   1804 }
   1805 
   1806 void
   1807 endgrent(void)
   1808 {
   1809 	static const ns_dtab dtab[] = {
   1810 		NS_FILES_CB(_files_endgrent, NULL)
   1811 		NS_DNS_CB(_dns_endgrent, NULL)
   1812 		NS_NIS_CB(_nis_endgrent, NULL)
   1813 		NS_COMPAT_CB(_compat_endgrent, NULL)
   1814 		{ 0 }
   1815 	};
   1816 
   1817 	mutex_lock(&_grmutex);
   1818 					/* force all endgrent() methods */
   1819 	(void) nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent",
   1820 	    defaultcompat_forceall);
   1821 	mutex_unlock(&_grmutex);
   1822 }
   1823 
   1824 int
   1825 setgroupent(int stayopen)
   1826 {
   1827 	static const ns_dtab dtab[] = {
   1828 		NS_FILES_CB(_files_setgroupent, NULL)
   1829 		NS_DNS_CB(_dns_setgroupent, NULL)
   1830 		NS_NIS_CB(_nis_setgroupent, NULL)
   1831 		NS_COMPAT_CB(_compat_setgroupent, NULL)
   1832 		{ 0 }
   1833 	};
   1834 	int	rv, retval;
   1835 
   1836 	mutex_lock(&_grmutex);
   1837 					/* force all setgroupent() methods */
   1838 	rv = nsdispatch(NULL, dtab, NSDB_GROUP, "setgroupent",
   1839 	    defaultcompat_forceall, &retval, stayopen);
   1840 	mutex_unlock(&_grmutex);
   1841 	return (rv == NS_SUCCESS) ? retval : 0;
   1842 }
   1843 
   1844 void
   1845 setgrent(void)
   1846 {
   1847 	static const ns_dtab dtab[] = {
   1848 		NS_FILES_CB(_files_setgrent, NULL)
   1849 		NS_DNS_CB(_dns_setgrent, NULL)
   1850 		NS_NIS_CB(_nis_setgrent, NULL)
   1851 		NS_COMPAT_CB(_compat_setgrent, NULL)
   1852 		{ 0 }
   1853 	};
   1854 
   1855 	mutex_lock(&_grmutex);
   1856 					/* force all setgrent() methods */
   1857 	(void) nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent",
   1858 	    defaultcompat_forceall);
   1859 	mutex_unlock(&_grmutex);
   1860 }
   1861