xref: /illumos-gate/usr/src/lib/libc/port/gen/nss_common.c (revision 44bac77bf8165ebe38afb85dda247b928d88edf8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Shared code used by the name-service-switch frontends (e.g. getpwnam_r())
31  */
32 
33 #pragma weak nss_delete = _nss_delete
34 #pragma weak nss_endent = _nss_endent
35 #pragma weak nss_getent = _nss_getent
36 #pragma weak nss_search = _nss_search
37 #pragma weak nss_setent = _nss_setent
38 
39 #include "synonyms.h"
40 #include <mtlib.h>
41 #include <dlfcn.h>
42 #include <atomic.h>
43 
44 #define	__NSS_PRIVATE_INTERFACE
45 #include "nsswitch_priv.h"
46 #undef	__NSS_PRIVATE_INTERFACE
47 
48 #include <nss_common.h>
49 #include <nss_dbdefs.h>
50 #include <unistd.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <thread.h>
55 #include <synch.h>
56 #include <pthread.h>
57 #include <sys/types.h>
58 #include <sys/mman.h>
59 #include <errno.h>
60 #include "libc.h"
61 #include "tsd.h"
62 
63 #include <getxby_door.h>
64 
65 /*
66  * configurable values for default buffer sizes
67  */
68 
69 /*
70  * PSARC/2005/133 updated the buffering mechanisms to handle
71  * up to 2^64 buffering.  But sets a practical limit of 512*1024.
72  * the expectation is the practical limit will be dynamic from
73  * nscd.  For now, set the group limit to this value.
74  */
75 
76 #define	NSS_BUFLEN_PRACTICAL	(512*1024)
77 
78 static size_t __nss_buflen_group = NSS_BUFLEN_PRACTICAL;
79 static size_t __nss_buflen_default = NSS_BUFLEN_DOOR;
80 
81 /*
82  * policy component function interposing definitions:
83  * nscd if so desired can interpose it's own switch functions over
84  * the internal unlocked counterparts.  This will allow nscd to replace
85  * the switch policy state engine with one that uses it's internal
86  * components.
87  * Only nscd can change this through it's use of nss_config.
88  * The golden rule is: ptr == NULL checking is used in the switch to
89  * see if a function was interposed.  But nscd is responsible for seeing
90  * that mutex locking to change the values are observed when the data is
91  * changed.  Especially if it happens > once.  The switch does not lock
92  * the pointer with mutexs.
93  */
94 
95 typedef struct {
96 	void	*p;
97 #if 0
98 	void		(*nss_delete_fp)(nss_db_root_t *rootp);
99 	nss_status_t	(*nss_search_fp)(nss_db_root_t *rootp,
100 				nss_db_initf_t initf, int search_fnum,
101 				void *search_args);
102 	void		(*nss_setent_u_fp)(nss_db_root_t *,
103 				nss_db_initf_t, nss_getent_t *);
104 	nss_status_t	(*nss_getent_u_fp)(nss_db_root_t *,
105 				nss_db_initf_t, nss_getent_t *, void *);
106 	void		(*nss_endent_u_fp)(nss_db_root_t *,
107 				nss_db_initf_t, nss_getent_t *);
108 	void		(*end_iter_u_fp)(nss_db_root_t *rootp,
109 				struct nss_getent_context *contextp);
110 #endif
111 } nss_policyf_t;
112 
113 static mutex_t nss_policyf_lock = DEFAULTMUTEX;
114 static nss_policyf_t nss_policyf_ptrs =
115 	{ (void *)NULL };
116 
117 /*
118  * nsswitch db_root state machine definitions:
119  * The golden rule is:  if you hold a pointer to an nss_db_state struct and
120  * you don't hold the lock, you'd better have incremented the refcount
121  * while you held the lock;  otherwise, it may vanish or change
122  * significantly when you least expect it.
123  *
124  * The pointer in nss_db_root_t is one such, so the reference count >= 1.
125  * Ditto the pointer in struct nss_getent_context.
126  */
127 
128 /*
129  * State for one nsswitch database (e.g. "passwd", "hosts")
130  */
131 struct nss_db_state {
132 	nss_db_root_t		orphan_root;	/* XXX explain */
133 	unsigned		refcount;	/* One for the pointer in    */
134 						/*   nss_db_root_t, plus one */
135 						/*   for each active thread. */
136 	nss_db_params_t		p;
137 	struct __nsw_switchconfig_v1 *config;
138 	int			max_src;	/* is == config->num_lookups */
139 	struct nss_src_state	*src;		/* Pointer to array[max_src] */
140 };
141 
142 /*
143  * State for one of the sources (e.g. "nis", "compat") for a database
144  */
145 struct nss_src_state {
146 	struct __nsw_lookup_v1	*lkp;
147 	int			n_active;
148 	int			n_dormant;
149 	int			n_waiting;	/* ... on wanna_be */
150 	cond_t			wanna_be;
151 	union {
152 		nss_backend_t	*single; /* Efficiency hack for common case */
153 					    /* when limit_dead_backends == 1 */
154 		nss_backend_t	**multi; /* array[limit_dead_backends] of */
155 	} dormant;			    /* pointers to dormant backends */
156 	nss_backend_constr_t	be_constr;
157 	nss_backend_finder_t	*finder;
158 	void			*finder_priv;
159 };
160 
161 static struct nss_db_state	*_nss_db_state_constr(nss_db_initf_t);
162 void				_nss_db_state_destr(struct nss_db_state *);
163 
164 /* ==== null definitions if !MTSAFE?  Ditto lock field in nss_db_root_t */
165 
166 #define	NSS_ROOTLOCK(r, sp)	(cancel_safe_mutex_lock(&(r)->lock), \
167 				*(sp) = (r)->s)
168 
169 #define	NSS_UNLOCK(r)		(cancel_safe_mutex_unlock(&(r)->lock))
170 
171 #define	NSS_CHECKROOT(rp, s)	((s) != (*(rp))->s &&			\
172 			(cancel_safe_mutex_unlock(&(*(rp))->lock),	\
173 			cancel_safe_mutex_lock(&(s)->orphan_root.lock), \
174 			*(rp) = &(s)->orphan_root))
175 
176 #define	NSS_RELOCK(rp, s)	(cancel_safe_mutex_lock(&(*(rp))->lock), \
177 			NSS_CHECKROOT(rp, s))
178 
179 #define	NSS_STATE_REF_u(s)	(++(s)->refcount)
180 
181 #define	NSS_UNREF_UNLOCK(r, s)	(--(s)->refcount != 0			\
182 			? ((void)NSS_UNLOCK(r))				\
183 			: ((void)NSS_UNLOCK(r), (void)_nss_db_state_destr(s)))
184 
185 #define	NSS_LOCK_CHECK(r, f, sp)    (NSS_ROOTLOCK((r), (sp)),	\
186 				    *(sp) == 0 &&		\
187 				    (r->s = *(sp) = _nss_db_state_constr(f)))
188 /* === In the future, NSS_LOCK_CHECK() may also have to check that   */
189 /* === the config info hasn't changed (by comparing version numbers) */
190 
191 
192 /*
193  * NSS_OPTIONS/NIS_OPTIONS environment varibles data definitions:
194  * This remains for backwards compatibility.  But generally nscd will
195  * decide if/how this gets used.
196  */
197 static int checked_env = 0;		/* protected by "rootlock" */
198 
199 	/* allowing __nss_debug_file to be set could be a security hole. */
200 FILE *__nss_debug_file = stdout;
201 int __nss_debug_eng_loop;
202 
203 /* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */
204 	/* allowing __nis_debug_file to be set could be a security hole. */
205 FILE *__nis_debug_file = stdout;
206 int __nis_debug_bind;
207 int __nis_debug_rpc;
208 int __nis_debug_calls;
209 char *__nis_prefsrv;
210 char *__nis_preftype;
211 char *__nis_server;   /* if set, use only this server for binding */
212 
213 #define	OPT_INT 1
214 #define	OPT_STRING 2
215 #ifdef DEBUG
216 #define	OPT_FILE 3
217 #endif
218 
219 struct option {
220 	char *name;
221 	int type;
222 	void *address;
223 };
224 
225 static struct option nss_options[] = {
226 #ifdef DEBUG
227 	/* allowing __nss_debug_file to be set could be a security hole. */
228 	{ "debug_file", OPT_FILE, &__nss_debug_file },
229 #endif
230 	{ "debug_eng_loop", OPT_INT, &__nss_debug_eng_loop },
231 	{ 0, 0, 0 },
232 };
233 
234 static struct option nis_options[] = {
235 #ifdef DEBUG
236 	/* allowing __nis_debug_file to be set could be a security hole. */
237 	{ "debug_file", OPT_FILE, &__nis_debug_file },
238 #endif
239 	{ "debug_bind", OPT_INT, &__nis_debug_bind },
240 	{ "debug_rpc", OPT_INT, &__nis_debug_rpc },
241 	{ "debug_calls", OPT_INT, &__nis_debug_calls },
242 	{ "server", OPT_STRING, &__nis_server },
243 	{ "pref_srvr", OPT_STRING, &__nis_prefsrv },
244 	{ "pref_type", OPT_STRING, &__nis_preftype },
245 	{ 0, 0, 0 },
246 };
247 
248 /*
249  * switch configuration parameter "database" definitions:
250  * The switch maintains a simmple read/write parameter database
251  * that nscd and the switch components can use to communicate
252  * nscd data to other components for configuration or out of band
253  * [IE no in the context of a getXbyY or putXbyY operation] data.
254  * The data passed are pointers to a lock  data buffer and a length.
255  * Use of this is treated as SunwPrivate between nscd and the switch
256  * unless other wise stated.
257  */
258 
259 typedef struct nss_cfgparam {
260 	char 		*name;
261 	mutex_t		*lock;
262 	void		*buffer;
263 	size_t		length;
264 } nss_cfgparam_t;
265 
266 typedef struct nss_cfglist {
267 	char 		*name;
268 	nss_cfgparam_t	*list;
269 	int		count;
270 	int		max;
271 } nss_cfglist_t;
272 
273 #define	NSS_CFG_INCR	16
274 
275 static nss_cfglist_t *nss_cfg = NULL;
276 static int nss_cfgcount = 0;
277 static int nss_cfgmax = 0;
278 static mutex_t nss_cfglock = DEFAULTMUTEX;
279 
280 static int nss_cfg_policy_init();
281 
282 /*
283  * A config parameters are in the form component:parameter
284  * as in: nss:parameter - switch (internal FE/policy/BE) parameter
285  *	  nscd:param - nscd application parameter
286  *	  ldap:param - nss_ldap BE parameter
287  *	  passwd:param - get/put passwd FE parameter
288  */
289 
290 #define	NSS_CONFIG_BRK	':'
291 
292 /*
293  * The policy components initial parameter list
294  */
295 static nss_config_t	nss_policy_params[] = {
296 	{ "nss:policyfunc", NSS_CONFIG_ADD, &nss_policyf_lock,
297 		(void *)&nss_policyf_ptrs, (size_t)sizeof (nss_policyf_t) },
298 	{ NULL,	NSS_CONFIG_ADD,	(mutex_t *)NULL, (void *)NULL, (size_t)0 },
299 };
300 
301 /*
302  * NSS parameter configuration routines
303  */
304 
305 /* compare config name (component:parameter) to a component name */
306 static int
307 nss_cfgcn_cmp(const char *cfgname, const char *compname)
308 {
309 	char *c;
310 	size_t len, len2;
311 
312 	/* this code assumes valid pointers */
313 	if ((c = strchr(cfgname, NSS_CONFIG_BRK)) == NULL)
314 		return (-1);
315 	len = (size_t)(c - cfgname);
316 	len2 = strlen(compname);
317 	if (len2 != len)
318 		return (-1);
319 	return (strncmp(cfgname, compname, len));
320 }
321 
322 /* init configuration arena */
323 static int
324 nss_cfg_init()
325 {
326 	nss_cfglist_t *cfg;
327 	int i;
328 
329 	/* First time caller? */
330 	if (nss_cfg != NULL) {
331 		membar_consumer();
332 		return (0);
333 	}
334 
335 	/* Initialize internal tables */
336 	lmutex_lock(&nss_cfglock);
337 	if (nss_cfg != NULL) {
338 		lmutex_unlock(&nss_cfglock);
339 		membar_consumer();
340 		return (0);
341 	}
342 	cfg = libc_malloc(NSS_CFG_INCR * sizeof (nss_cfglist_t));
343 	if (cfg == NULL) {
344 		errno = ENOMEM;
345 		lmutex_unlock(&nss_cfglock);
346 		return (-1);
347 	}
348 	for (i = 0; i < NSS_CFG_INCR; i++) {
349 		cfg[i].list = libc_malloc(
350 		    NSS_CFG_INCR * sizeof (nss_cfgparam_t));
351 		if (cfg[i].list == NULL) {
352 			while (--i >= 0)
353 				libc_free(cfg[i].list);
354 			libc_free(cfg);
355 			errno = ENOMEM;
356 			lmutex_unlock(&nss_cfglock);
357 			return (-1);
358 		}
359 		cfg[i].max = NSS_CFG_INCR;
360 	}
361 	nss_cfgmax = NSS_CFG_INCR;
362 	membar_producer();
363 	nss_cfg = cfg;
364 	lmutex_unlock(&nss_cfglock);
365 
366 	/* Initialize Policy Engine values */
367 	if (nss_cfg_policy_init() < 0) {
368 		return (-1);
369 	}
370 	return (0);
371 }
372 
373 /* find the name'd component list - create it if non-existent */
374 static nss_cfglist_t *
375 nss_cfgcomp_get(char *name, int add)
376 {
377 	nss_cfglist_t	*next;
378 	char	*c;
379 	int	i, len;
380 	size_t	nsize;
381 
382 	/* Make sure system is init'd */
383 	if (nss_cfg_init() < 0)
384 		return ((nss_cfglist_t *)NULL);
385 
386 	/* and check component:name validity */
387 	if (name == NULL || (c = strchr(name, NSS_CONFIG_BRK)) == NULL)
388 		return ((nss_cfglist_t *)NULL);
389 
390 	lmutex_lock(&nss_cfglock);
391 	next = nss_cfg;
392 	for (i = 0; i < nss_cfgcount; i++) {
393 		if (next->name && nss_cfgcn_cmp(name, next->name) == 0) {
394 			lmutex_unlock(&nss_cfglock);
395 			return (next);
396 		}
397 		next++;
398 	}
399 	if (!add) {
400 		lmutex_unlock(&nss_cfglock);
401 		return (NULL);
402 	}
403 
404 	/* not found, create a fresh one */
405 	if (nss_cfgcount >= nss_cfgmax) {
406 		/* realloc first */
407 		nsize = (nss_cfgmax + NSS_CFG_INCR) * sizeof (nss_cfgparam_t);
408 		next = (nss_cfglist_t *)libc_realloc(nss_cfg, nsize);
409 		if (next == NULL) {
410 			errno = ENOMEM;
411 			lmutex_unlock(&nss_cfglock);
412 			return ((nss_cfglist_t *)NULL);
413 		}
414 		(void) memset((void *)(next + nss_cfgcount), '\0',
415 		    NSS_CFG_INCR * sizeof (nss_cfglist_t));
416 		nss_cfgmax += NSS_CFG_INCR;
417 		nss_cfg = next;
418 	}
419 	next = nss_cfg + nss_cfgcount;
420 	len = (size_t)(c - name) + 1;
421 	if ((next->name = libc_malloc(len)) == NULL) {
422 		errno = ENOMEM;
423 		lmutex_unlock(&nss_cfglock);
424 		return ((nss_cfglist_t *)NULL);
425 	}
426 	nss_cfgcount++;
427 	(void) strlcpy(next->name, name, len);
428 	lmutex_unlock(&nss_cfglock);
429 	return (next);
430 }
431 
432 /* find the name'd parameter - create it if non-existent */
433 static nss_cfgparam_t *
434 nss_cfgparam_get(char *name, int add)
435 {
436 	nss_cfglist_t	*comp;
437 	nss_cfgparam_t	*next;
438 	int	count, i;
439 	size_t	nsize;
440 
441 	if ((comp = nss_cfgcomp_get(name, add)) == NULL)
442 		return ((nss_cfgparam_t *)NULL);
443 	lmutex_lock(&nss_cfglock);
444 	count = comp->count;
445 	next = comp->list;
446 	for (i = 0; i < count; i++) {
447 		if (next->name && strcmp(name, next->name) == 0) {
448 			lmutex_unlock(&nss_cfglock);
449 			return (next);
450 		}
451 		next++;
452 	}
453 	if (!add) {
454 		lmutex_unlock(&nss_cfglock);
455 		return (NULL);
456 	}
457 
458 	/* not found, create a fresh one */
459 	if (count >= comp->max) {
460 		/* realloc first */
461 		nsize = (comp->max + NSS_CFG_INCR) * sizeof (nss_cfgparam_t);
462 		next = (nss_cfgparam_t *)libc_realloc(comp->list, nsize);
463 		if (next == NULL) {
464 			errno = ENOMEM;
465 			lmutex_unlock(&nss_cfglock);
466 			return ((nss_cfgparam_t *)NULL);
467 		}
468 		comp->max += NSS_CFG_INCR;
469 		comp->list = next;
470 	}
471 	next = comp->list + comp->count;
472 	if ((next->name = libc_strdup(name)) == NULL) {
473 		errno = ENOMEM;
474 		lmutex_unlock(&nss_cfglock);
475 		return ((nss_cfgparam_t *)NULL);
476 	}
477 	comp->count++;
478 	lmutex_unlock(&nss_cfglock);
479 	return (next);
480 }
481 
482 /* find the name'd parameter - delete it if it exists */
483 static void
484 nss_cfg_del(nss_config_t *cfgp)
485 {
486 	char		*name;
487 	nss_cfglist_t	*comp;
488 	nss_cfgparam_t	*next, *cur;
489 	int	count, i, j;
490 
491 	/* exit if component name does not already exist */
492 	if ((name = cfgp->name) == NULL ||
493 	    (comp = nss_cfgcomp_get(name, 0)) == NULL)
494 		return;
495 
496 	/* find it */
497 	lmutex_lock(&nss_cfglock);
498 	count = comp->count;
499 	next = comp->list;
500 	for (i = 0; i < count; i++) {
501 		if (next->name && strcmp(name, next->name) == 0) {
502 			break;	/* found it... */
503 		}
504 		next++;
505 	}
506 	if (i >= count) {
507 		/* not found, already deleted */
508 		lmutex_unlock(&nss_cfglock);
509 		return;
510 	}
511 
512 	/* copy down the remaining parameters, and clean up */
513 	/* don't try to clean up component tables */
514 	cur = next;
515 	next++;
516 	for (j = i+1; j < count; j++) {
517 		*cur = *next;
518 		cur++;
519 		next++;
520 	}
521 	/* erase the last one */
522 	if (cur->name) {
523 		libc_free(cur->name);
524 		cur->name = (char *)NULL;
525 	}
526 	cur->lock = (mutex_t *)NULL;
527 	cur->buffer = (void *)NULL;
528 	cur->length = 0;
529 	comp->count--;
530 	lmutex_unlock(&nss_cfglock);
531 }
532 
533 static int
534 nss_cfg_get(nss_config_t *next)
535 {
536 	nss_cfgparam_t	*param;
537 
538 	errno = 0;
539 	if ((param = nss_cfgparam_get(next->name, 0)) == NULL)
540 		return (-1);
541 	next->lock = param->lock;
542 	next->buffer = param->buffer;
543 	next->length = param->length;
544 	return (0);
545 }
546 
547 static int
548 nss_cfg_put(nss_config_t *next, int add)
549 {
550 	nss_cfgparam_t	*param;
551 
552 	errno = 0;
553 	if ((param = nss_cfgparam_get(next->name, add)) == NULL)
554 		return (-1);
555 	param->lock = next->lock;
556 	param->buffer = next->buffer;
557 	param->length = next->length;
558 	return (0);
559 }
560 
561 /*
562  * Policy engine configurator - set and get interface
563  * argument is a NULL terminated list of set/get requests
564  * with input/result buffers and lengths.  nss_cname is the
565  * specifier of a set or get operation and the property being
566  * managed.  The intent is limited functions and expandability.
567  */
568 
569 nss_status_t
570 nss_config(nss_config_t **plist, int cnt)
571 {
572 	nss_config_t	*next;
573 	int 	i;
574 
575 	/* interface is only available to nscd */
576 	if (_nsc_proc_is_cache() <= 0) {
577 		return (NSS_UNAVAIL);
578 	}
579 	if (plist == NULL || cnt <= 0)
580 		return (NSS_SUCCESS);
581 	for (i = 0; i < cnt; i++) {
582 		next = plist[i];
583 		if (next == NULL)
584 			break;
585 		if (next->name == NULL) {
586 			errno = EFAULT;
587 			return (NSS_ERROR);
588 		}
589 		switch (next->cop) {
590 		case NSS_CONFIG_GET:
591 			/* get current lock/buffer/length fields */
592 			if (nss_cfg_get(next) < 0) {
593 				return (NSS_ERROR);
594 			}
595 			break;
596 		case NSS_CONFIG_PUT:
597 			/* set new lock/buffer/length fields */
598 			if (nss_cfg_put(next, 0) < 0) {
599 				return (NSS_ERROR);
600 			}
601 			break;
602 		case NSS_CONFIG_ADD:
603 			/* add parameter & set new lock/buffer/length fields */
604 			if (nss_cfg_put(next, 1) < 0) {
605 				return (NSS_ERROR);
606 			}
607 			break;
608 		case NSS_CONFIG_DELETE:
609 			/* delete parameter - should always work... */
610 			nss_cfg_del(next);
611 			break;
612 		case NSS_CONFIG_LIST:
613 			break;
614 		default:
615 			continue;
616 		}
617 	}
618 	return (NSS_SUCCESS);
619 }
620 
621 /*
622  * This routine is called immediately after nss_cfg_init but prior to
623  * any commands from nscd being processed.  The intent here is to
624  * initialize the nss:* parameters allowed by the policy component
625  * so that nscd can then proceed and modify them if so desired.
626  *
627  * We know we can only get here if we are nscd so we can skip the
628  * preliminaries.
629  */
630 
631 static int
632 nss_cfg_policy_init()
633 {
634 	nss_config_t	*next = &nss_policy_params[0];
635 
636 	for (; next && next->name != NULL; next++) {
637 		if (nss_cfg_put(next, 1) < 0)
638 			return (-1);
639 	}
640 	return (0);
641 }
642 
643 /*
644  * NSS_OPTION & NIS_OPTION environment variable functions
645  */
646 
647 static
648 void
649 set_option(struct option *opt, char *name, char *val)
650 {
651 	int n;
652 	char *p;
653 #ifdef DEBUG
654 	FILE *fp;
655 #endif
656 
657 	for (; opt->name; opt++) {
658 		if (strcmp(name, opt->name) == 0) {
659 			switch (opt->type) {
660 			case OPT_STRING:
661 				p = libc_strdup(val);
662 				*((char **)opt->address) = p;
663 				break;
664 
665 			case OPT_INT:
666 				if (strcmp(val, "") == 0)
667 					n = 1;
668 				else
669 					n = atoi(val);
670 				*((int *)opt->address) = n;
671 				break;
672 #ifdef DEBUG
673 			case OPT_FILE:
674 				fp = fopen(val, "wF");
675 				*((FILE **)opt->address) = fp;
676 				break;
677 #endif
678 			}
679 			break;
680 		}
681 	}
682 }
683 
684 static
685 void
686 __parse_environment(struct option *opt, char *p)
687 {
688 	char *base;
689 	char optname[100];
690 	char optval[100];
691 
692 	while (*p) {
693 		while (isspace(*p))
694 			p++;
695 		if (*p == '\0')
696 			break;
697 
698 		base = p;
699 		while (*p && *p != '=' && !isspace(*p))
700 			p++;
701 		/*
702 		 * play it safe and keep it simple, bail if an opt name
703 		 * is too long.
704 		 */
705 		if ((p-base) >= sizeof (optname))
706 			return;
707 
708 		(void) strncpy(optname, base, p-base);
709 		optname[p-base] = '\0';
710 
711 		if (*p == '=') {
712 			p++;
713 			base = p;
714 			while (*p && !isspace(*p))
715 				p++;
716 			/*
717 			 * play it safe and keep it simple, bail if an opt
718 			 * value is too long.
719 			 */
720 			if ((p-base) >= sizeof (optval))
721 				return;
722 
723 			(void) strncpy(optval, base, p-base);
724 			optval[p-base] = '\0';
725 		} else {
726 			optval[0] = '\0';
727 		}
728 
729 		set_option(opt, optname, optval);
730 	}
731 }
732 
733 static
734 void
735 nss_get_environment()
736 {
737 	char *p;
738 
739 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
740 	p = getenv("NSS_OPTIONS");
741 	if (p == NULL)
742 		return;
743 	__parse_environment(nss_options, p);
744 }
745 
746 /*
747  * sole external routine called from libnsl/nis/cache/cache_api.cc in the
748  * routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard
749  * Only after checking "checked_env" (which must be done with mutex
750  * "cur_cache_lock" held) and is done once, (then "checked_env" is set)
751  */
752 void
753 __nis_get_environment()
754 {
755 	char *p;
756 
757 	p = getenv("NIS_OPTIONS");
758 	if (p == NULL)
759 		return;
760 	__parse_environment(nis_options, p);
761 }
762 
763 
764 /*
765  * Switch policy component backend state machine functions
766  */
767 
768 static nss_backend_t *
769 nss_get_backend_u(nss_db_root_t **rootpp, struct nss_db_state *s, int n_src)
770 {
771 	struct nss_src_state	*src = &s->src[n_src];
772 	nss_backend_t		*be;
773 	int cancel_state;
774 
775 	for (;;) {
776 		if (src->n_dormant > 0) {
777 			src->n_dormant--;
778 			src->n_active++;
779 			if (s->p.max_dormant_per_src == 1) {
780 				be = src->dormant.single;
781 			} else {
782 				be = src->dormant.multi[src->n_dormant];
783 			}
784 			break;
785 		}
786 
787 		if (src->be_constr == 0) {
788 			nss_backend_finder_t	*bf;
789 
790 			for (bf = s->p.finders;  bf != 0;  bf = bf->next) {
791 				nss_backend_constr_t c;
792 
793 				c = (*bf->lookup) (bf->lookup_priv, s->p.name,
794 				    src->lkp->service_name, &src->finder_priv);
795 				if (c != 0) {
796 					src->be_constr = c;
797 					src->finder = bf;
798 					break;
799 				}
800 			}
801 			if (src->be_constr == 0) {
802 				/* Couldn't find the backend anywhere */
803 				be = 0;
804 				break;
805 			}
806 		}
807 
808 		if (src->n_active < s->p.max_active_per_src) {
809 			be = (*src->be_constr)(s->p.name,
810 			    src->lkp->service_name, 0 /* === unimplemented */);
811 			if (be != 0) {
812 				src->n_active++;
813 				break;
814 			} else if (src->n_active == 0) {
815 				/* Something's wrong;  we should be */
816 				/*   able to create at least one    */
817 				/*   instance of the backend	    */
818 				break;
819 			}
820 			/*
821 			 * Else it's odd that we can't create another backend
822 			 *   instance, but don't sweat it;  instead, queue for
823 			 *   an existing backend instance.
824 			 */
825 		}
826 
827 		src->n_waiting++;
828 		(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
829 		    &cancel_state);
830 		(void) cond_wait(&src->wanna_be, &(*rootpp)->lock);
831 		(void) pthread_setcancelstate(cancel_state, NULL);
832 		NSS_CHECKROOT(rootpp, s);
833 		src->n_waiting--;
834 
835 		/*
836 		 * Loop and see whether things got better for us, or whether
837 		 *   someone else got scheduled first and we have to try
838 		 *   this again.
839 		 *
840 		 * === ?? Should count iterations, assume bug if many ??
841 		 */
842 	}
843 	return (be);
844 }
845 
846 static void
847 nss_put_backend_u(struct nss_db_state *s, int n_src, nss_backend_t *be)
848 {
849 	struct nss_src_state	*src = &s->src[n_src];
850 
851 	if (be == 0) {
852 		return;
853 	}
854 
855 	src->n_active--;
856 
857 	if (src->n_dormant < s->p.max_dormant_per_src) {
858 		if (s->p.max_dormant_per_src == 1) {
859 			src->dormant.single = be;
860 			src->n_dormant++;
861 		} else if (src->dormant.multi != 0 ||
862 		    (src->dormant.multi =
863 		    libc_malloc(s->p.max_dormant_per_src *
864 		    sizeof (nss_backend_t *))) != NULL) {
865 			src->dormant.multi[src->n_dormant] = be;
866 			src->n_dormant++;
867 		} else {
868 			/* Can't store it, so toss it */
869 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
870 		}
871 	} else {
872 		/* We've stored as many as we want, so toss it */
873 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
874 	}
875 	if (src->n_waiting > 0) {
876 		(void) cond_signal(&src->wanna_be);
877 	}
878 }
879 
880 static struct nss_db_state *
881 _nss_db_state_constr(nss_db_initf_t initf)
882 {
883 	struct nss_db_state	*s;
884 	struct __nsw_switchconfig_v1 *config = 0;
885 	struct __nsw_lookup_v1	*lkp;
886 	enum __nsw_parse_err	err;
887 	const char		*config_name;
888 	int			n_src;
889 
890 	if ((s = libc_malloc(sizeof (*s))) == 0) {
891 		return (0);
892 	}
893 	(void) mutex_init(&s->orphan_root.lock, USYNC_THREAD, 0);
894 
895 	s->p.max_active_per_src	= 10;
896 	s->p.max_dormant_per_src = 1;
897 	s->p.finders = nss_default_finders;
898 	(*initf)(&s->p);
899 	if (s->p.name == 0) {
900 		_nss_db_state_destr(s);
901 		return (0);
902 	}
903 
904 	if (!checked_env) {
905 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
906 		nss_get_environment();
907 		checked_env = 1;
908 	}
909 
910 	config_name = s->p.config_name ? s->p.config_name : s->p.name;
911 	if (! (s->p.flags & NSS_USE_DEFAULT_CONFIG)) {
912 		config = __nsw_getconfig_v1(config_name, &err);
913 		/* === ? test err ? */
914 	}
915 	if (config == 0) {
916 		/* getconfig failed, or frontend demanded default config */
917 
918 		char	*str;	/* _nsw_getoneconfig() clobbers its argument */
919 
920 		if ((str = libc_strdup(s->p.default_config)) != 0) {
921 			config = _nsw_getoneconfig_v1(config_name, str, &err);
922 			libc_free(str);
923 		}
924 		if (config == 0) {
925 			_nss_db_state_destr(s);
926 			return (0);
927 		}
928 	}
929 	s->config = config;
930 	if ((s->max_src = config->num_lookups) <= 0 ||
931 	    (s->src = libc_malloc(s->max_src * sizeof (*s->src))) == 0) {
932 		_nss_db_state_destr(s);
933 		return (0);
934 	}
935 	for (n_src = 0, lkp = config->lookups;
936 	    n_src < s->max_src; n_src++, lkp = lkp->next) {
937 		s->src[n_src].lkp = lkp;
938 		(void) cond_init(&s->src[n_src].wanna_be, USYNC_THREAD, 0);
939 	}
940 	s->refcount = 1;
941 	return (s);
942 }
943 
944 void
945 _nss_src_state_destr(struct nss_src_state *src, int max_dormant)
946 {
947 	if (max_dormant == 1) {
948 		if (src->n_dormant != 0) {
949 			(void) NSS_INVOKE_DBOP(src->dormant.single,
950 			    NSS_DBOP_DESTRUCTOR, 0);
951 		};
952 	} else if (src->dormant.multi != 0) {
953 		int	n;
954 
955 		for (n = 0;  n < src->n_dormant;  n++) {
956 			(void) NSS_INVOKE_DBOP(src->dormant.multi[n],
957 			    NSS_DBOP_DESTRUCTOR, 0);
958 		}
959 		libc_free(src->dormant.multi);
960 	}
961 
962 	/* cond_destroy(&src->wanna_be); */
963 
964 	if (src->finder != 0) {
965 		(*src->finder->delete)(src->finder_priv, src->be_constr);
966 	}
967 }
968 
969 /*
970  * _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire
971  *	nss_db_state structure.
972  * Assumes that s has been ref-counted down to zero (in particular,
973  *	rootp->s has already been dealt with).
974  *
975  * Nobody else holds a pointer to *s (if they did, refcount != 0),
976  *   so we can clean up state *after* we drop the lock (also, by the
977  *   time we finish freeing the state structures, the lock may have
978  *   ceased to exist -- if we were using the orphan_root).
979  */
980 
981 void
982 _nss_db_state_destr(struct nss_db_state *s)
983 {
984 
985 	if (s == NULL)
986 		return;
987 
988 	/* === mutex_destroy(&s->orphan_root.lock); */
989 	if (s->p.cleanup != 0) {
990 		(*s->p.cleanup)(&s->p);
991 	}
992 	if (s->config != 0) {
993 		(void) __nsw_freeconfig_v1(s->config);
994 	}
995 	if (s->src != 0) {
996 		int	n_src;
997 
998 		for (n_src = 0;  n_src < s->max_src;  n_src++) {
999 			_nss_src_state_destr(&s->src[n_src],
1000 			    s->p.max_dormant_per_src);
1001 		}
1002 		libc_free(s->src);
1003 	}
1004 	libc_free(s);
1005 }
1006 
1007 
1008 /*
1009  * _nss_status_vec() returns a bit vector of all status codes returned during
1010  * the most recent call to nss_search().
1011  * _nss_status_vec_p() returns a pointer to this bit vector, or NULL on
1012  * failure.
1013  * These functions are private.  Don't use them externally without discussing
1014  * it with the switch maintainers.
1015  */
1016 static uint_t *
1017 _nss_status_vec_p()
1018 {
1019 	return (tsdalloc(_T_NSS_STATUS_VEC, sizeof (uint_t), NULL));
1020 }
1021 
1022 unsigned int
1023 _nss_status_vec(void)
1024 {
1025 	unsigned int *status_vec_p = _nss_status_vec_p();
1026 
1027 	return ((status_vec_p != NULL) ? *status_vec_p : (1 << NSS_UNAVAIL));
1028 }
1029 
1030 static void
1031 output_loop_diag_a(
1032 	int n,
1033 	char *dbase,
1034 	struct __nsw_lookup_v1 *lkp)
1035 
1036 {
1037 	(void) fprintf(__nss_debug_file,
1038 		"NSS_retry(%d): '%s': trying '%s' ... ",
1039 		n, dbase, lkp->service_name);
1040 	(void) fflush(__nss_debug_file);
1041 
1042 }
1043 
1044 static void
1045 output_loop_diag_b(
1046 	nss_status_t res,
1047 	struct __nsw_lookup_v1 *lkp)
1048 
1049 {
1050 	(void) fprintf(__nss_debug_file, "result=");
1051 	switch (res) {
1052 	case NSS_SUCCESS:
1053 		(void) fprintf(__nss_debug_file, "SUCCESS");
1054 		break;
1055 	case NSS_NOTFOUND:
1056 		(void) fprintf(__nss_debug_file, "NOTFOUND");
1057 		break;
1058 	case NSS_UNAVAIL:
1059 		(void) fprintf(__nss_debug_file, "UNAVAIL");
1060 		break;
1061 	case NSS_TRYAGAIN:
1062 		(void) fprintf(__nss_debug_file, "TRYAGAIN");
1063 		break;
1064 	case NSS_NISSERVDNS_TRYAGAIN:
1065 		(void) fprintf(__nss_debug_file, "NISSERVDNS_TRYAGAIN");
1066 		break;
1067 	default:
1068 		(void) fprintf(__nss_debug_file, "undefined");
1069 	}
1070 	(void) fprintf(__nss_debug_file, ", action=");
1071 	switch (lkp->actions[res]) {
1072 	case __NSW_CONTINUE:
1073 		(void) fprintf(__nss_debug_file, "CONTINUE");
1074 		break;
1075 	case  __NSW_RETURN:
1076 		(void) fprintf(__nss_debug_file, "RETURN");
1077 		break;
1078 	case __NSW_TRYAGAIN_FOREVER:
1079 		(void) fprintf(__nss_debug_file, "TRYAGAIN_FOREVER");
1080 		break;
1081 	case __NSW_TRYAGAIN_NTIMES:
1082 		(void) fprintf(__nss_debug_file, "TRYAGAIN_NTIMES (N=%d)",
1083 			lkp->max_retries);
1084 		break;
1085 	case __NSW_TRYAGAIN_PAUSED:
1086 		(void) fprintf(__nss_debug_file, "TRYAGAIN_PAUSED");
1087 		break;
1088 	default:
1089 		(void) fprintf(__nss_debug_file, "undefined");
1090 	}
1091 	(void) fprintf(__nss_debug_file, "\n");
1092 }
1093 
1094 #define	NSS_BACKOFF(n, b, t) \
1095 			((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1))))
1096 
1097 static int
1098 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
1099 {
1100 	if (res != NSS_TRYAGAIN && res !=  NSS_NISSERVDNS_TRYAGAIN) {
1101 		if (res == NSS_SUCCESS) {
1102 			__NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
1103 			__NSW_UNPAUSE_ACTION(
1104 			    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
1105 		}
1106 		return (0);
1107 	}
1108 
1109 	if ((res == NSS_TRYAGAIN &&
1110 	    lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
1111 	    (res == NSS_NISSERVDNS_TRYAGAIN &&
1112 	    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
1113 		return (1);
1114 
1115 	if (res == NSS_TRYAGAIN &&
1116 	    lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1117 		if (n <= lkp->max_retries)
1118 			return (1);
1119 		else {
1120 			lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
1121 			return (0);
1122 		}
1123 
1124 	if (res == NSS_NISSERVDNS_TRYAGAIN &&
1125 	    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1126 		if (n <= lkp->max_retries)
1127 			return (1);
1128 		else {
1129 			lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
1130 			    __NSW_TRYAGAIN_PAUSED;
1131 			return (0);
1132 		}
1133 
1134 	return (0);
1135 }
1136 
1137 /*
1138  * Switch policy component functional interfaces
1139  */
1140 
1141 void
1142 nss_delete(nss_db_root_t *rootp)
1143 {
1144 	struct nss_db_state	*s;
1145 
1146 	/* no name service cache daemon divert here */
1147 	/* local nss_delete decrements state reference counts */
1148 	/* and may free up opened switch resources. */
1149 
1150 	NSS_ROOTLOCK(rootp, &s);
1151 	if (s == 0) {
1152 		NSS_UNLOCK(rootp);
1153 	} else {
1154 		rootp->s = 0;
1155 		NSS_UNREF_UNLOCK(rootp, s);
1156 	}
1157 }
1158 
1159 nss_status_t
1160 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
1161 	void *search_args)
1162 {
1163 	nss_status_t		res = NSS_UNAVAIL;
1164 	struct nss_db_state	*s;
1165 	int			n_src;
1166 	unsigned int		*status_vec_p;
1167 
1168 	/* name service cache daemon divert */
1169 	res = _nsc_search(rootp, initf, search_fnum, search_args);
1170 	if (res != NSS_TRYLOCAL)
1171 		return (res);
1172 
1173 	/* fall through - process locally */
1174 	errno = 0;			/* just in case ... */
1175 	res = NSS_UNAVAIL;
1176 	status_vec_p = _nss_status_vec_p();
1177 
1178 	if (status_vec_p == NULL) {
1179 		return (NSS_UNAVAIL);
1180 	}
1181 	*status_vec_p = 0;
1182 
1183 	NSS_LOCK_CHECK(rootp, initf, &s);
1184 	if (s == 0) {
1185 		NSS_UNLOCK(rootp);
1186 		return (res);
1187 	}
1188 	NSS_STATE_REF_u(s);
1189 
1190 	for (n_src = 0;  n_src < s->max_src;  n_src++) {
1191 		nss_backend_t		*be;
1192 		nss_backend_op_t	funcp;
1193 
1194 		res = NSS_UNAVAIL;
1195 		if ((be = nss_get_backend_u(&rootp, s, n_src)) != 0) {
1196 			if ((funcp = NSS_LOOKUP_DBOP(be, search_fnum)) != 0) {
1197 				int n_loop = 0;
1198 				int no_backoff = 19;
1199 				int max_backoff = 5;	/* seconds */
1200 
1201 				do {
1202 					/*
1203 					 * Backend operation may take a while;
1204 					 * drop the lock so we don't serialize
1205 					 * more than necessary.
1206 					 */
1207 					NSS_UNLOCK(rootp);
1208 
1209 					/* After several tries, backoff... */
1210 					if (n_loop > no_backoff) {
1211 						if (__nss_debug_eng_loop > 1)
1212 							(void) fprintf(
1213 							    __nss_debug_file,
1214 							    "NSS: loop: "
1215 							    "sleeping %d ...\n",
1216 							    NSS_BACKOFF(n_loop,
1217 							    no_backoff,
1218 							    max_backoff));
1219 
1220 						(void) sleep(NSS_BACKOFF(n_loop,
1221 						    no_backoff, max_backoff));
1222 					}
1223 
1224 					if (__nss_debug_eng_loop)
1225 						output_loop_diag_a(n_loop,
1226 						    s->config->dbase,
1227 						    s->src[n_src].lkp);
1228 
1229 
1230 					res = (*funcp)(be, search_args);
1231 					NSS_RELOCK(&rootp, s);
1232 					n_loop++;
1233 					if (__nss_debug_eng_loop)
1234 						output_loop_diag_b(res,
1235 						    s->src[n_src].lkp);
1236 				} while (retry_test(res, n_loop,
1237 				    s->src[n_src].lkp));
1238 			}
1239 			nss_put_backend_u(s, n_src, be);
1240 		}
1241 		*status_vec_p |= (1 << res);
1242 		if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1243 			if (__nss_debug_eng_loop)
1244 				(void) fprintf(__nss_debug_file,
1245 				    "NSS: '%s': return.\n",
1246 				    s->config->dbase);
1247 			break;
1248 		} else
1249 			if (__nss_debug_eng_loop)
1250 				(void) fprintf(__nss_debug_file,
1251 				    "NSS: '%s': continue ...\n",
1252 				    s->config->dbase);
1253 	}
1254 	NSS_UNREF_UNLOCK(rootp, s);
1255 	return (res);
1256 }
1257 
1258 
1259 /*
1260  * Start of nss_{setent|getent|endent}
1261  */
1262 
1263 /*
1264  * State (here called "context") for one setent/getent.../endent sequence.
1265  *   In principle there could be multiple contexts active for a single
1266  *   database;  in practice, since Posix and UI have helpfully said that
1267  *   getent() state is global rather than, say, per-thread or user-supplied,
1268  *   we have at most one of these per nss_db_state.
1269  *   XXX ? Is this statement still true?
1270  *
1271  * NSS2 - a client's context is maintained as a cookie delivered by and
1272  * passed to nscd.  The cookie is a 64 bit (nssuint_t) unique opaque value
1273  * created by nscd.
1274  * cookie states:
1275  *	NSCD_NEW_COOKIE		- cookie value uninitialized
1276  *	NSCD_LOCAL_COOKIE	- setent is a local setent
1277  *	all other		- NSCD unique opaque id for this setent
1278  * A client's context is also associated with a seq_num.  This is a nscd
1279  * opaque 64 bit (nssuint_t) value passed with a cookie, and used to by nscd
1280  * to validate the sequencing of the context.  The client treats this as
1281  * a pass through value.
1282  *
1283  * XXX ??  Use Cookie as cross-check info so that we can detect an
1284  * nss_context that missed an nss_delete() or similar.
1285  */
1286 
1287 struct nss_getent_context {
1288 	int			n_src;	/* >= max_src ==> end of sequence */
1289 	nss_backend_t		*be;
1290 	struct nss_db_state	*s;
1291 	nssuint_t		cookie;
1292 	nssuint_t		seq_num;
1293 	nssuint_t		cookie_setent;
1294 	nss_db_params_t		param;
1295 };
1296 
1297 static void		nss_setent_u(nss_db_root_t *,
1298 				    nss_db_initf_t,
1299 				    nss_getent_t *);
1300 static nss_status_t	nss_getent_u(nss_db_root_t *,
1301 				    nss_db_initf_t,
1302 				    nss_getent_t *,
1303 				    void *);
1304 static void		nss_endent_u(nss_db_root_t *,
1305 				    nss_db_initf_t,
1306 				    nss_getent_t *);
1307 
1308 void
1309 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1310 {
1311 	if (contextpp == 0) {
1312 		return;
1313 	}
1314 	cancel_safe_mutex_lock(&contextpp->lock);
1315 	nss_setent_u(rootp, initf, contextpp);
1316 	cancel_safe_mutex_unlock(&contextpp->lock);
1317 }
1318 
1319 nss_status_t
1320 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1321 	void *args)
1322 {
1323 	nss_status_t		status;
1324 
1325 	if (contextpp == 0) {
1326 		return (NSS_UNAVAIL);
1327 	}
1328 	cancel_safe_mutex_lock(&contextpp->lock);
1329 	status = nss_getent_u(rootp, initf, contextpp, args);
1330 	cancel_safe_mutex_unlock(&contextpp->lock);
1331 	return (status);
1332 }
1333 
1334 void
1335 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1336 {
1337 	if (contextpp == 0) {
1338 		return;
1339 	}
1340 	cancel_safe_mutex_lock(&contextpp->lock);
1341 	nss_endent_u(rootp, initf, contextpp);
1342 	cancel_safe_mutex_unlock(&contextpp->lock);
1343 }
1344 
1345 /*
1346  * Each of the _u versions of the nss interfaces assume that the context
1347  * lock is held.  No need to divert to nscd.  Private to local sequencing.
1348  */
1349 
1350 static void
1351 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1352 {
1353 	struct nss_db_state	*s;
1354 	nss_backend_t		*be;
1355 	int			n_src;
1356 
1357 	s = contextp->s;
1358 	n_src = contextp->n_src;
1359 	be = contextp->be;
1360 
1361 	if (s != 0) {
1362 		if (n_src < s->max_src && be != 0) {
1363 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1364 			NSS_RELOCK(&rootp, s);
1365 			nss_put_backend_u(s, n_src, be);
1366 			contextp->be = 0;  /* Should be unnecessary, but hey */
1367 			NSS_UNREF_UNLOCK(rootp, s);
1368 		}
1369 		contextp->s = 0;
1370 	}
1371 }
1372 
1373 static void
1374 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1375 	nss_getent_t *contextpp)
1376 {
1377 	nss_status_t		status;
1378 	struct nss_db_state	*s;
1379 	struct nss_getent_context *contextp;
1380 	nss_backend_t		*be;
1381 	int			n_src;
1382 
1383 	/* setup process wide context while locked */
1384 	if ((contextp = contextpp->ctx) == 0) {
1385 		if ((contextp = libc_malloc(sizeof (*contextp))) == 0) {
1386 			return;
1387 		}
1388 		contextpp->ctx = contextp;
1389 		contextp->cookie = NSCD_NEW_COOKIE;	/* cookie init */
1390 		contextp->seq_num = 0;			/* seq_num init */
1391 		s = 0;
1392 	} else {
1393 		s = contextp->s;
1394 		if (contextp->cookie != NSCD_LOCAL_COOKIE)
1395 			contextp->cookie = NSCD_NEW_COOKIE;
1396 	}
1397 
1398 	/* name service cache daemon divert */
1399 	if (contextp->cookie == NSCD_NEW_COOKIE) {
1400 		status = _nsc_setent_u(rootp, initf, contextpp);
1401 		if (status != NSS_TRYLOCAL)
1402 			return;
1403 	}
1404 
1405 	/* fall through - process locally */
1406 	if (s == 0) {
1407 		NSS_LOCK_CHECK(rootp, initf, &s);
1408 		if (s == 0) {
1409 			/* Couldn't set up state, so quit */
1410 			NSS_UNLOCK(rootp);
1411 			/* ==== is there any danger of not having done an */
1412 			/* end_iter() here, and hence of losing backends? */
1413 			contextpp->ctx = 0;
1414 			libc_free(contextp);
1415 			return;
1416 		}
1417 		NSS_STATE_REF_u(s);
1418 		contextp->s = s;
1419 	} else {
1420 		s	= contextp->s;
1421 		n_src	= contextp->n_src;
1422 		be	= contextp->be;
1423 		if (n_src == 0 && be != 0) {
1424 			/*
1425 			 * Optimization:  don't do endent, don't change
1426 			 *   backends, just do the setent.  Look Ma, no locks
1427 			 *   (nor any context that needs updating).
1428 			 */
1429 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1430 			return;
1431 		}
1432 		if (n_src < s->max_src && be != 0) {
1433 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1434 			NSS_RELOCK(&rootp, s);
1435 			nss_put_backend_u(s, n_src, be);
1436 			contextp->be = 0;	/* Play it safe */
1437 		} else {
1438 			NSS_RELOCK(&rootp, s);
1439 		}
1440 	}
1441 	for (n_src = 0, be = 0; n_src < s->max_src &&
1442 	    (be = nss_get_backend_u(&rootp, s, n_src)) == 0; n_src++) {
1443 		;
1444 	}
1445 	NSS_UNLOCK(rootp);
1446 
1447 	contextp->n_src	= n_src;
1448 	contextp->be	= be;
1449 
1450 	if (be == 0) {
1451 		/* Things are broken enough that we can't do setent/getent */
1452 		nss_endent_u(rootp, initf, contextpp);
1453 		return;
1454 	}
1455 	(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1456 }
1457 
1458 static nss_status_t
1459 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1460 	nss_getent_t *contextpp, void *args)
1461 {
1462 	nss_status_t		status;
1463 	struct nss_db_state	*s;
1464 	struct nss_getent_context *contextp;
1465 	int			n_src;
1466 	nss_backend_t		*be;
1467 
1468 	if ((contextp = contextpp->ctx) == 0) {
1469 		nss_setent_u(rootp, initf, contextpp);
1470 		if ((contextp = contextpp->ctx) == 0) {
1471 			/* Give up */
1472 			return (NSS_UNAVAIL);
1473 		}
1474 	}
1475 	/* name service cache daemon divert */
1476 	status = _nsc_getent_u(rootp, initf, contextpp, args);
1477 	if (status != NSS_TRYLOCAL)
1478 		return (status);
1479 
1480 	/* fall through - process locally */
1481 	s	= contextp->s;
1482 	n_src	= contextp->n_src;
1483 	be	= contextp->be;
1484 
1485 	if (s == 0) {
1486 		/*
1487 		 * We've done an end_iter() and haven't done nss_setent()
1488 		 * or nss_endent() since;  we should stick in this state
1489 		 * until the caller invokes one of those two routines.
1490 		 */
1491 		return (NSS_SUCCESS);
1492 	}
1493 
1494 	while (n_src < s->max_src) {
1495 		nss_status_t res;
1496 
1497 		if (be == 0) {
1498 			/* If it's null it's a bug, but let's play safe */
1499 			res = NSS_UNAVAIL;
1500 		} else {
1501 			res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
1502 		}
1503 
1504 		if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1505 			if (res != __NSW_SUCCESS) {
1506 				end_iter_u(rootp, contextp);
1507 			}
1508 			return (res);
1509 		}
1510 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1511 		NSS_RELOCK(&rootp, s);
1512 		nss_put_backend_u(s, n_src, be);
1513 		do {
1514 			n_src++;
1515 		} while (n_src < s->max_src &&
1516 		    (be = nss_get_backend_u(&rootp, s, n_src)) == 0);
1517 		if (be == 0) {
1518 			/*
1519 			 * This is the case where we failed to get the backend
1520 			 * for the last source. We exhausted all sources.
1521 			 */
1522 			NSS_UNLOCK(rootp);
1523 			nss_endent_u(rootp, initf, contextpp);
1524 			nss_delete(rootp);
1525 			return (NSS_SUCCESS);
1526 		}
1527 		NSS_UNLOCK(rootp);
1528 		contextp->n_src	= n_src;
1529 		contextp->be	= be;
1530 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1531 	}
1532 	/* Got to the end of the sources without finding another entry */
1533 	end_iter_u(rootp, contextp);
1534 	return (NSS_SUCCESS);
1535 	/* success is either a successful entry or end of the sources */
1536 }
1537 
1538 /*ARGSUSED*/
1539 static void
1540 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1541 	nss_getent_t *contextpp)
1542 {
1543 	nss_status_t		status;
1544 	struct nss_getent_context *contextp;
1545 
1546 	if ((contextp = contextpp->ctx) == 0) {
1547 		/* nss_endent() on an unused context is a no-op */
1548 		return;
1549 	}
1550 
1551 	/* notify name service cache daemon */
1552 	status = _nsc_endent_u(rootp, initf, contextpp);
1553 	if (status != NSS_TRYLOCAL) {
1554 		/* clean up */
1555 		libc_free(contextp);
1556 		contextpp->ctx = 0;
1557 		return;
1558 	}
1559 
1560 	/* fall through - process locally */
1561 
1562 	/*
1563 	 * Existing code (BSD, SunOS) works in such a way that getXXXent()
1564 	 *   following an endXXXent() behaves as though the user had invoked
1565 	 *   setXXXent(), i.e. it iterates properly from the beginning.
1566 	 * We'd better not break this, so our choices are
1567 	 *	(1) leave the context structure around, and do nss_setent or
1568 	 *	    something equivalent,
1569 	 *   or	(2) free the context completely, and rely on the code in
1570 	 *	    nss_getent() that makes getXXXent() do the right thing
1571 	 *	    even without a preceding setXXXent().
1572 	 * The code below does (2), which frees up resources nicely but will
1573 	 * cost more if the user then does more getXXXent() operations.
1574 	 * Moral:  for efficiency, don't call endXXXent() prematurely.
1575 	 */
1576 	end_iter_u(rootp, contextp);
1577 	libc_free(contextp);
1578 	contextpp->ctx = 0;
1579 }
1580 
1581 /*
1582  * pack dbd data into header
1583  * Argment pointers assumed valid.
1584  * poff offset position pointer
1585  *   IN = starting offset for dbd header
1586  *   OUT = starting offset for next section
1587  */
1588 
1589 static nss_status_t
1590 nss_pack_dbd(void *buffer, size_t bufsize, nss_db_params_t *p, size_t *poff)
1591 {
1592 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1593 	nss_dbd_t		*pdbd;
1594 	size_t			off = *poff;
1595 	size_t			len, blen;
1596 	size_t			n, nc, dc;
1597 	char			*bptr;
1598 
1599 	pbuf->dbd_off = (nssuint_t)off;
1600 	bptr = (char *)buffer + off;
1601 	blen = bufsize - off;
1602 	len = sizeof (nss_dbd_t);
1603 
1604 	n = nc = dc = 0;
1605 	if (p->name == NULL) {
1606 		errno = ERANGE;			/* actually EINVAL */
1607 		return (NSS_ERROR);
1608 	}
1609 
1610 	/* if default config not specified, the flag should be reset */
1611 	if (p->default_config == NULL) {
1612 		p->default_config = "<NULL>";
1613 		p->flags = p->flags & ~NSS_USE_DEFAULT_CONFIG;
1614 	}
1615 
1616 	n = strlen(p->name) + 1;
1617 	dc = strlen(p->default_config) + 1;
1618 	if (n < 2 || dc < 2) {			/* What no DB? */
1619 		errno = ERANGE;			/* actually EINVAL */
1620 		return (NSS_ERROR);
1621 	}
1622 	if (p->config_name != NULL) {
1623 		nc = strlen(p->config_name) + 1;
1624 	}
1625 	if ((len + n + nc + dc) >= blen) {
1626 		errno = ERANGE;			/* actually EINVAL */
1627 		return (NSS_ERROR);
1628 	}
1629 
1630 	pdbd = (nss_dbd_t *)((void *)bptr);
1631 	bptr += len;
1632 	pdbd->flags = p->flags;
1633 	pdbd->o_name = len;
1634 	(void) strlcpy(bptr, p->name, n);
1635 	len += n;
1636 	bptr += n;
1637 	if (nc == 0) {
1638 		pdbd->o_config_name = 0;
1639 	} else {
1640 		pdbd->o_config_name = len;
1641 		(void) strlcpy(bptr, p->config_name, nc);
1642 		bptr += nc;
1643 		len += nc;
1644 	}
1645 	pdbd->o_default_config = len;
1646 	(void) strlcpy(bptr, p->default_config, dc);
1647 	len += dc;
1648 	pbuf->dbd_len = (nssuint_t)len;
1649 	off += ROUND_UP(len, sizeof (nssuint_t));
1650 	*poff = off;
1651 	return (NSS_SUCCESS);
1652 }
1653 
1654 /*
1655  * Switch packed and _nsc (switch->nscd) interfaces
1656  * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1657  */
1658 
1659 /*ARGSUSED*/
1660 nss_status_t
1661 nss_pack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1662 	    nss_db_initf_t initf, int search_fnum, void *search_args)
1663 {
1664 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1665 	nss_XbyY_args_t		*in = (nss_XbyY_args_t *)search_args;
1666 	nss_db_params_t		tparam = { 0 };
1667 	nss_status_t		ret = NSS_ERROR;
1668 	const char		*dbn;
1669 	size_t			blen, len, off = 0;
1670 	char			*bptr;
1671 	struct nss_groupsbymem	*gbm;
1672 
1673 	if (pbuf == NULL || in == NULL || initf == (nss_db_initf_t)NULL) {
1674 		errno = ERANGE;			/* actually EINVAL */
1675 		return (ret);
1676 	}
1677 	tparam.cleanup = NULL;
1678 	(*initf)(&tparam);
1679 	if ((dbn = tparam.name) == 0) {
1680 		if (tparam.cleanup != 0)
1681 			(tparam.cleanup)(&tparam);
1682 		errno = ERANGE;			/* actually EINVAL */
1683 		return (ret);
1684 	}
1685 
1686 	/* init buffer header */
1687 	pbuf->pbufsiz = (nssuint_t)bufsize;
1688 	pbuf->p_ruid = (uint32_t)getuid();
1689 	pbuf->p_euid = (uint32_t)geteuid();
1690 	pbuf->p_version = NSCD_HEADER_REV;
1691 	pbuf->p_status = 0;
1692 	pbuf->p_errno = 0;
1693 	pbuf->p_herrno = 0;
1694 
1695 	/* possible audituser init */
1696 	if (strcmp(dbn, NSS_DBNAM_AUTHATTR) == 0 && in->h_errno != 0)
1697 		pbuf->p_herrno = (uint32_t)in->h_errno;
1698 
1699 	pbuf->libpriv = 0;
1700 
1701 	off = sizeof (nss_pheader_t);
1702 
1703 	/* setup getXbyY operation - database and sub function */
1704 	pbuf->nss_dbop = (uint32_t)search_fnum;
1705 	ret = nss_pack_dbd(buffer, bufsize, &tparam, &off);
1706 	if (ret != NSS_SUCCESS) {
1707 		errno = ERANGE;			/* actually EINVAL */
1708 		return (ret);
1709 	}
1710 	ret = NSS_ERROR;
1711 	/* setup request key */
1712 	pbuf->key_off = (nssuint_t)off;
1713 	bptr = (char *)buffer + off;
1714 	blen = bufsize - off;
1715 	/* use key2str if provided, else call default getXbyY packer */
1716 	if (strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) {
1717 		/* This has to run locally due to backend knowledge */
1718 		if (search_fnum == NSS_DBOP_NETGROUP_SET) {
1719 			errno = 0;
1720 			return (NSS_TRYLOCAL);
1721 		}
1722 		/* use default packer for known getXbyY ops */
1723 		ret = nss_default_key2str(bptr, blen, in, dbn,
1724 		    search_fnum, &len);
1725 	} else if (in->key2str == NULL ||
1726 	    (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1727 	    strcmp(dbn, NSS_DBNAM_GROUP) == 0)) {
1728 		/* use default packer for known getXbyY ops */
1729 		ret = nss_default_key2str(bptr, blen, in, dbn,
1730 		    search_fnum, &len);
1731 	} else {
1732 		ret = (*in->key2str)(bptr, blen, &in->key, &len);
1733 	}
1734 	if (tparam.cleanup != 0)
1735 		(tparam.cleanup)(&tparam);
1736 	if (ret != NSS_SUCCESS) {
1737 		errno = ERANGE;			/* actually ENOMEM */
1738 		return (ret);
1739 	}
1740 	pbuf->key_len = (nssuint_t)len;
1741 	off += ROUND_UP(len, sizeof (nssuint_t));
1742 
1743 	pbuf->data_off = (nssuint_t)off;
1744 	pbuf->data_len = (nssuint_t)(bufsize - off);
1745 	/*
1746 	 * Prime data return with first result if
1747 	 * the first result is passed in
1748 	 * [_getgroupsbymember oddness]
1749 	 */
1750 	gbm = (struct nss_groupsbymem *)search_args;
1751 	if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1752 	    strcmp(dbn, NSS_DBNAM_GROUP) == 0 && gbm->numgids == 1) {
1753 		gid_t	*gidp;
1754 		gidp = (gid_t *)((void *)((char *)buffer + off));
1755 		*gidp = gbm->gid_array[0];
1756 	}
1757 
1758 	errno = 0;				/* just in case ... */
1759 	return (NSS_SUCCESS);
1760 }
1761 
1762 /*
1763  * Switch packed and _nsc (switch->nscd) {set/get/end}ent interfaces
1764  * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1765  */
1766 
1767 /*ARGSUSED*/
1768 nss_status_t
1769 nss_pack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1770 	    nss_db_initf_t initf, nss_getent_t *contextpp)
1771 {
1772 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1773 	struct nss_getent_context *contextp = contextpp->ctx;
1774 	nss_status_t		ret = NSS_ERROR;
1775 	size_t			blen, len = 0, off = 0;
1776 	char			*bptr;
1777 	nssuint_t		*nptr;
1778 
1779 	if (pbuf == NULL || initf == (nss_db_initf_t)NULL) {
1780 		errno = ERANGE;			/* actually EINVAL */
1781 		return (ret);
1782 	}
1783 
1784 	/* init buffer header */
1785 	pbuf->pbufsiz = (nssuint_t)bufsize;
1786 	pbuf->p_ruid = (uint32_t)getuid();
1787 	pbuf->p_euid = (uint32_t)geteuid();
1788 	pbuf->p_version = NSCD_HEADER_REV;
1789 	pbuf->p_status = 0;
1790 	pbuf->p_errno = 0;
1791 	pbuf->p_herrno = 0;
1792 	pbuf->libpriv = 0;
1793 
1794 	off = sizeof (nss_pheader_t);
1795 
1796 	/* setup getXXXent operation - database and sub function */
1797 	pbuf->nss_dbop = (uint32_t)0;	/* iterators have no dbop */
1798 	ret = nss_pack_dbd(buffer, bufsize, &contextp->param, &off);
1799 	if (ret != NSS_SUCCESS) {
1800 		errno = ERANGE;			/* actually EINVAL */
1801 		return (ret);
1802 	}
1803 	ret = NSS_ERROR;
1804 	off += ROUND_UP(len, sizeof (nssuint_t));
1805 
1806 	pbuf->key_off = (nssuint_t)off;
1807 	bptr = (char *)buffer + off;
1808 	blen = bufsize - off;
1809 	len = (size_t)(sizeof (nssuint_t) * 2);
1810 	if (len >= blen) {
1811 		errno = ERANGE;			/* actually EINVAL */
1812 		return (ret);
1813 	}
1814 	nptr = (nssuint_t *)((void *)bptr);
1815 	*nptr++ = contextp->cookie;
1816 	*nptr = contextp->seq_num;
1817 	pbuf->key_len = (nssuint_t)len;
1818 
1819 	off += len;
1820 	pbuf->data_off = (nssuint_t)off;
1821 	pbuf->data_len = (nssuint_t)(bufsize - off);
1822 	return (NSS_SUCCESS);
1823 }
1824 
1825 /*
1826  * Unpack packed arguments buffer
1827  * Return: status, errnos and results from requested operation.
1828  *
1829  * NOTES: When getgroupsbymember is being processed in the NSCD backend,
1830  * or via the backwards compatibility interfaces then the standard
1831  * str2group API is used in conjunction with process_cstr.  When,
1832  * processing a returned buffer, in NSS2 the return results are the
1833  * already digested groups array.  Therefore, unpack the digested results
1834  * back to the return buffer.
1835  *
1836  * Note: the digested results are nssuint_t quantities.  _getgroupsbymember
1837  * digests int quantities.  Therefore convert.  Assume input is in nssuint_t
1838  * quantities.  Store in an int array... Assume gid's are <= 32 bits...
1839  */
1840 
1841 /*ARGSUSED*/
1842 nss_status_t
1843 nss_unpack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1844 	    nss_db_initf_t initf, int search_fnum, void *search_args)
1845 {
1846 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1847 	nss_XbyY_args_t		*in = (nss_XbyY_args_t *)search_args;
1848 	nss_dbd_t		*pdbd;
1849 	char			*dbn;
1850 	nss_status_t		status;
1851 	char			*buf;
1852 	int			len;
1853 	int			ret;
1854 	int			i;
1855 	int			fmt_type;
1856 	gid_t			*gidp;
1857 	gid_t			*gptr;
1858 	struct nss_groupsbymem	*arg;
1859 
1860 
1861 	if (pbuf == NULL || in == NULL)
1862 		return (-1);
1863 	status = pbuf->p_status;
1864 	/* Identify odd cases */
1865 	pdbd = (nss_dbd_t *)((void *)((char *)buffer + pbuf->dbd_off));
1866 	dbn = (char *)pdbd + pdbd->o_name;
1867 	fmt_type = 0; /* nss_XbyY_args_t */
1868 	if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1869 	    strcmp(dbn, NSS_DBNAM_GROUP) == 0)
1870 		fmt_type = 1; /* struct nss_groupsbymem */
1871 	else if (search_fnum == NSS_DBOP_NETGROUP_IN &&
1872 	    strcmp(dbn, NSS_DBNAM_NETGROUP) == 0)
1873 		fmt_type = 2; /* struct nss_innetgr_args */
1874 
1875 	/* if error - door's switch error */
1876 	/* extended data could contain additional information? */
1877 	if (status != NSS_SUCCESS) {
1878 		if (fmt_type == 0) {
1879 			in->h_errno = (int)pbuf->p_herrno;
1880 			if (pbuf->p_errno == ERANGE)
1881 				in->erange = 1;
1882 		}
1883 		return (status);
1884 	}
1885 
1886 	if (pbuf->data_off == 0 || pbuf->data_len == 0)
1887 		return (NSS_NOTFOUND);
1888 
1889 	buf = (char *)buffer + pbuf->data_off;
1890 	len = pbuf->data_len;
1891 
1892 	/* sidestep odd cases */
1893 	if (fmt_type == 1) {
1894 		arg = (struct nss_groupsbymem *)in;
1895 		/* copy returned gid array from returned nscd buffer */
1896 		i = len / sizeof (gid_t);
1897 		/* not enough buffer */
1898 		if (i > arg->maxgids) {
1899 			i = arg->maxgids;
1900 		}
1901 		arg->numgids = i;
1902 		gidp = arg->gid_array;
1903 		gptr = (gid_t *)((void *)buf);
1904 		memcpy(gidp, gptr, len);
1905 		return (NSS_SUCCESS);
1906 	}
1907 	if (fmt_type == 2) {
1908 		struct nss_innetgr_args *arg = (struct nss_innetgr_args *)in;
1909 
1910 		if (pbuf->p_status == NSS_SUCCESS) {
1911 			arg->status = NSS_NETGR_FOUND;
1912 			return (NSS_SUCCESS);
1913 		} else {
1914 			arg->status = NSS_NETGR_NO;
1915 			return (NSS_NOTFOUND);
1916 		}
1917 	}
1918 
1919 	/* process the normal cases */
1920 	/* marshall data directly into users buffer */
1921 	ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1922 	    in->buf.buflen);
1923 	if (ret == NSS_STR_PARSE_ERANGE) {
1924 		in->returnval = 0;
1925 		in->returnlen = 0;
1926 		in->erange    = 1;
1927 		ret = NSS_NOTFOUND;
1928 	} else if (ret == NSS_STR_PARSE_SUCCESS) {
1929 		in->returnval = in->buf.result;
1930 		in->returnlen =  len;
1931 		ret = NSS_SUCCESS;
1932 	}
1933 	in->h_errno = (int)pbuf->p_herrno;
1934 	return ((nss_status_t)ret);
1935 }
1936 
1937 /*
1938  * Unpack a returned packed {set,get,end}ent arguments buffer
1939  * Return: status, errnos, cookie info and results from requested operation.
1940  */
1941 
1942 /*ARGSUSED*/
1943 nss_status_t
1944 nss_unpack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1945 	    nss_db_initf_t initf, nss_getent_t *contextpp, void *args)
1946 {
1947 	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1948 	nss_XbyY_args_t		*in = (nss_XbyY_args_t *)args;
1949 	struct nss_getent_context *contextp = contextpp->ctx;
1950 	nssuint_t		*nptr;
1951 	nssuint_t		cookie;
1952 	nss_status_t		status;
1953 	char			*buf;
1954 	int			len;
1955 	int			ret;
1956 
1957 	if (pbuf == NULL)
1958 		return (-1);
1959 	status = pbuf->p_status;
1960 	/* if error - door's switch error */
1961 	/* extended data could contain additional information? */
1962 	if (status != NSS_SUCCESS)
1963 		return (status);
1964 
1965 	/* unpack assigned cookie from SET/GET/END request */
1966 	if (pbuf->key_off == 0 ||
1967 	    pbuf->key_len != (sizeof (nssuint_t) * 2))
1968 		return (NSS_NOTFOUND);
1969 
1970 	nptr = (nssuint_t *)((void *)((char *)buffer + pbuf->key_off));
1971 	cookie = contextp->cookie;
1972 	if (cookie != NSCD_NEW_COOKIE && cookie != contextp->cookie_setent &&
1973 	    cookie != *nptr) {
1974 		/*
1975 		 * Should either be new, or the cookie returned by the last
1976 		 * setent (i.e., this is the first getent after the setent)
1977 		 * or a match, else error
1978 		 */
1979 		return (NSS_NOTFOUND);
1980 	}
1981 	/* save away for the next ent request */
1982 	contextp->cookie = *nptr++;
1983 	contextp->seq_num = *nptr;
1984 
1985 	/* All done if no marshalling is expected {set,end}ent */
1986 	if (args == NULL)
1987 		return (NSS_SUCCESS);
1988 
1989 	/* unmarshall the data */
1990 	if (pbuf->data_off == 0 || pbuf->data_len == 0)
1991 		return (NSS_NOTFOUND);
1992 	buf = (char *)buffer + pbuf->data_off;
1993 
1994 	len = pbuf->data_len;
1995 
1996 	/* marshall data directly into users buffer */
1997 	ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1998 	    in->buf.buflen);
1999 	if (ret == NSS_STR_PARSE_ERANGE) {
2000 		in->returnval = 0;
2001 		in->returnlen = 0;
2002 		in->erange    = 1;
2003 	} else if (ret == NSS_STR_PARSE_SUCCESS) {
2004 		in->returnval = in->buf.result;
2005 		in->returnlen =  len;
2006 	}
2007 	in->h_errno = (int)pbuf->p_herrno;
2008 	return ((nss_status_t)ret);
2009 }
2010 
2011 /*
2012  * Start of _nsc_{search|setent_u|getent_u|endent_u} NSCD interposition funcs
2013  */
2014 
2015 nss_status_t
2016 _nsc_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
2017 	void *search_args)
2018 {
2019 	nss_pheader_t		*pbuf;
2020 	void			*doorptr = NULL;
2021 	size_t			bufsize = 0;
2022 	size_t			datasize = 0;
2023 	nss_status_t		status;
2024 
2025 	if (_nsc_proc_is_cache() > 0) {
2026 		/* internal nscd call - don't use the door */
2027 		return (NSS_TRYLOCAL);
2028 	}
2029 
2030 	/* standard client calls nscd code */
2031 	if (search_args == NULL)
2032 		return (NSS_NOTFOUND);
2033 
2034 	/* get the door buffer  & configured size */
2035 	bufsize = ((nss_XbyY_args_t *)search_args)->buf.buflen;
2036 	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2037 		return (NSS_TRYLOCAL);
2038 	if (doorptr == NULL || bufsize == 0)
2039 		return (NSS_TRYLOCAL);
2040 
2041 	pbuf = (nss_pheader_t *)doorptr;
2042 	/* pack argument and request into door buffer */
2043 	pbuf->nsc_callnumber = NSCD_SEARCH;
2044 	/* copy relevant door request info into door buffer */
2045 	status = nss_pack((void *)pbuf, bufsize, rootp,
2046 	    initf, search_fnum, search_args);
2047 
2048 	/* Packing error return error results */
2049 	if (status != NSS_SUCCESS)
2050 		return (status);
2051 
2052 	/* transfer packed switch request to nscd via door */
2053 	/* data_off can be used because it is header+dbd_len+key_len */
2054 	datasize = pbuf->data_off;
2055 	status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2056 
2057 	/* If unsuccessful fallback to standard nss logic */
2058 	if (status != NSS_SUCCESS) {
2059 		/*
2060 		 * check if doors reallocated the memory underneath us
2061 		 * if they did munmap it or suffer a memory leak
2062 		 */
2063 		if (doorptr != (void *)pbuf) {
2064 			_nsc_resizedoorbuf(bufsize);
2065 			munmap((void *)doorptr, bufsize);
2066 		}
2067 		return (NSS_TRYLOCAL);
2068 	}
2069 
2070 	/* unpack and marshall data/errors to user structure */
2071 	/* set any error conditions */
2072 	status = nss_unpack((void *)doorptr, bufsize, rootp, initf,
2073 	    search_fnum, search_args);
2074 	/*
2075 	 * check if doors reallocated the memory underneath us
2076 	 * if they did munmap it or suffer a memory leak
2077 	 */
2078 	if (doorptr != (void *)pbuf) {
2079 		_nsc_resizedoorbuf(bufsize);
2080 		munmap((void *)doorptr, bufsize);
2081 	}
2082 	return (status);
2083 }
2084 
2085 /*
2086  * contact nscd for a cookie or to reset an existing cookie
2087  * if nscd fails (NSS_TRYLOCAL) then set cookie to -1 and
2088  * continue diverting to local
2089  */
2090 nss_status_t
2091 _nsc_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2092 	nss_getent_t *contextpp)
2093 {
2094 	nss_status_t		status = NSS_TRYLOCAL;
2095 	struct nss_getent_context *contextp = contextpp->ctx;
2096 	nss_pheader_t		*pbuf;
2097 	void			*doorptr = NULL;
2098 	size_t			bufsize = 0;
2099 	size_t			datasize = 0;
2100 
2101 	/* return if already in local mode */
2102 	if (contextp->cookie == NSCD_LOCAL_COOKIE)
2103 		return (NSS_TRYLOCAL);
2104 
2105 	if (_nsc_proc_is_cache() > 0) {
2106 		/* internal nscd call - don't try to use the door */
2107 		contextp->cookie = NSCD_LOCAL_COOKIE;
2108 		return (NSS_TRYLOCAL);
2109 	}
2110 
2111 	/* get the door buffer & configured size */
2112 	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) {
2113 		contextp->cookie = NSCD_LOCAL_COOKIE;
2114 		return (NSS_TRYLOCAL);
2115 	}
2116 	if (doorptr == NULL || bufsize == 0) {
2117 		contextp->cookie = NSCD_LOCAL_COOKIE;
2118 		return (NSS_TRYLOCAL);
2119 	}
2120 
2121 	pbuf = (nss_pheader_t *)doorptr;
2122 	pbuf->nsc_callnumber = NSCD_SETENT;
2123 
2124 	contextp->param.cleanup = NULL;
2125 	(*initf)(&contextp->param);
2126 	if (contextp->param.name == 0) {
2127 		if (contextp->param.cleanup != 0)
2128 			(contextp->param.cleanup)(&contextp->param);
2129 		errno = ERANGE;			/* actually EINVAL */
2130 		return (NSS_ERROR);
2131 	}
2132 
2133 	/* pack relevant setent request info into door buffer */
2134 	status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2135 	if (status != NSS_SUCCESS)
2136 		return (status);
2137 
2138 	/* transfer packed switch request to nscd via door */
2139 	/* data_off can be used because it is header+dbd_len+key_len */
2140 	datasize = pbuf->data_off;
2141 	status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2142 
2143 	/* If fallback to standard nss logic (door failure) if possible */
2144 	if (status != NSS_SUCCESS) {
2145 		if (contextp->cookie == NSCD_NEW_COOKIE) {
2146 			contextp->cookie = NSCD_LOCAL_COOKIE;
2147 			return (NSS_TRYLOCAL);
2148 		}
2149 		return (NSS_UNAVAIL);
2150 	}
2151 	/* unpack returned cookie stash it away */
2152 	status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2153 	    initf, contextpp, NULL);
2154 	/* save the setent cookie for later use */
2155 	contextp->cookie_setent = contextp->cookie;
2156 	/*
2157 	 * check if doors reallocated the memory underneath us
2158 	 * if they did munmap it or suffer a memory leak
2159 	 */
2160 	if (doorptr != (void *)pbuf) {
2161 		_nsc_resizedoorbuf(bufsize);
2162 		munmap((void *)doorptr, bufsize);
2163 	}
2164 	return (status);
2165 }
2166 
2167 nss_status_t
2168 _nsc_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2169 	nss_getent_t *contextpp, void *args)
2170 {
2171 	nss_status_t		status = NSS_TRYLOCAL;
2172 	struct nss_getent_context *contextp = contextpp->ctx;
2173 	nss_pheader_t		*pbuf;
2174 	void			*doorptr = NULL;
2175 	size_t			bufsize = 0;
2176 	size_t			datasize = 0;
2177 
2178 	/* return if already in local mode */
2179 	if (contextp->cookie == NSCD_LOCAL_COOKIE)
2180 		return (NSS_TRYLOCAL);
2181 
2182 	/* _nsc_setent_u already checked for nscd local case ... proceed */
2183 	if (args == NULL)
2184 		return (NSS_NOTFOUND);
2185 
2186 	/* get the door buffer  & configured size */
2187 	bufsize = ((nss_XbyY_args_t *)args)->buf.buflen;
2188 	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2189 		return (NSS_UNAVAIL);
2190 	if (doorptr == NULL || bufsize == 0)
2191 		return (NSS_UNAVAIL);
2192 
2193 	pbuf = (nss_pheader_t *)doorptr;
2194 	pbuf->nsc_callnumber = NSCD_GETENT;
2195 
2196 	/* pack relevant setent request info into door buffer */
2197 	status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2198 	if (status != NSS_SUCCESS)
2199 		return (status);
2200 
2201 	/* transfer packed switch request to nscd via door */
2202 	/* data_off can be used because it is header+dbd_len+key_len */
2203 	datasize = pbuf->data_off;
2204 	status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2205 
2206 	/* If fallback to standard nss logic (door failure) if possible */
2207 	if (status != NSS_SUCCESS) {
2208 		if (status == NSS_TRYLOCAL ||
2209 		    contextp->cookie == NSCD_NEW_COOKIE) {
2210 			contextp->cookie = NSCD_LOCAL_COOKIE;
2211 
2212 			/* init the local cookie */
2213 			nss_setent_u(rootp, initf, contextpp);
2214 			if (contextpp->ctx == 0)
2215 				return (NSS_UNAVAIL);
2216 			return (NSS_TRYLOCAL);
2217 		}
2218 		return (NSS_UNAVAIL);
2219 	}
2220 	/* check error, unpack and process results */
2221 	status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2222 	    initf, contextpp, args);
2223 	/*
2224 	 * check if doors reallocated the memory underneath us
2225 	 * if they did munmap it or suffer a memory leak
2226 	 */
2227 	if (doorptr != (void *)pbuf) {
2228 		_nsc_resizedoorbuf(bufsize);
2229 		munmap((void *)doorptr, bufsize);
2230 	}
2231 	return (status);
2232 }
2233 
2234 nss_status_t
2235 _nsc_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2236 	nss_getent_t *contextpp)
2237 {
2238 	nss_status_t		status = NSS_TRYLOCAL;
2239 	struct nss_getent_context *contextp = contextpp->ctx;
2240 	nss_pheader_t		*pbuf;
2241 	void			*doorptr = NULL;
2242 	size_t			bufsize = 0;
2243 	size_t			datasize = 0;
2244 
2245 	/* return if already in local mode */
2246 	if (contextp->cookie == NSCD_LOCAL_COOKIE)
2247 		return (NSS_TRYLOCAL);
2248 
2249 	/* _nsc_setent_u already checked for nscd local case ... proceed */
2250 
2251 	/* get the door buffer  & configured size */
2252 	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2253 		return (NSS_UNAVAIL);
2254 	if (doorptr == NULL || bufsize == 0)
2255 		return (NSS_UNAVAIL);
2256 
2257 	/* pack up a NSCD_ENDGET request passing in the cookie */
2258 	pbuf = (nss_pheader_t *)doorptr;
2259 	pbuf->nsc_callnumber = NSCD_ENDENT;
2260 
2261 	/* pack relevant setent request info into door buffer */
2262 	status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2263 	if (status != NSS_SUCCESS)
2264 		return (status);
2265 
2266 	/* transfer packed switch request to nscd via door */
2267 	/* data_off can be used because it is header+dbd_len+key_len */
2268 	datasize = pbuf->data_off;
2269 	(void) _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2270 
2271 	/* error codes & unpacking ret values don't matter.  We're done */
2272 
2273 	/*
2274 	 * check if doors reallocated the memory underneath us
2275 	 * if they did munmap it or suffer a memory leak
2276 	 */
2277 	if (doorptr != (void *)pbuf) {
2278 		_nsc_resizedoorbuf(bufsize);
2279 		munmap((void *)doorptr, bufsize);
2280 	}
2281 
2282 	/* clean up initf setup */
2283 	if (contextp->param.cleanup != 0)
2284 		(contextp->param.cleanup)(&contextp->param);
2285 	contextp->param.cleanup = NULL;
2286 
2287 	/* clear cookie */
2288 	contextp->cookie = NSCD_NEW_COOKIE;
2289 	return (NSS_SUCCESS);
2290 }
2291 
2292 /*
2293  * Internal private API to return default suggested buffer sizes
2294  * for nsswitch API requests
2295  */
2296 
2297 size_t
2298 _nss_get_bufsizes(int arg)
2299 {
2300 	switch (arg) {
2301 	case _SC_GETGR_R_SIZE_MAX:
2302 		return (__nss_buflen_group);
2303 	}
2304 	return (__nss_buflen_default);
2305 }
2306