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