nss_common.c revision 2830:5228d1267a01
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
75typedef 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
93static mutex_t nss_policyf_lock = DEFAULTMUTEX;
94static 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 */
111struct 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 */
125struct 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
141static struct nss_db_state	*_nss_db_state_constr(nss_db_initf_t);
142void				_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 */
177static int checked_env = 0;		/* protected by "rootlock" */
178
179	/* allowing __nss_debug_file to be set could be a security hole. */
180FILE *__nss_debug_file = stdout;
181int __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. */
185FILE *__nis_debug_file = stdout;
186int __nis_debug_bind;
187int __nis_debug_rpc;
188int __nis_debug_calls;
189char *__nis_prefsrv;
190char *__nis_preftype;
191char *__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
199struct option {
200	char *name;
201	int type;
202	void *address;
203};
204
205static 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
214static 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
239typedef struct nss_cfgparam {
240	char 		*name;
241	mutex_t		*lock;
242	void		*buffer;
243	size_t		length;
244} nss_cfgparam_t;
245
246typedef 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
255static nss_cfglist_t *nss_cfg = (nss_cfglist_t *)NULL;
256static int nss_cfgcount = 0;
257static int nss_cfgmax = 0;
258static mutex_t nss_cfglock = DEFAULTMUTEX;
259
260static 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 */
275static 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 */
286static int
287nss_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 */
303static int
304nss_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 */
346static nss_cfglist_t *
347nss_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 */
405static nss_cfgparam_t *
406nss_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 */
455static void
456nss_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
505static int
506nss_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
519static int
520nss_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
541nss_status_t
542nss_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
603static int
604nss_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
619static
620void
621set_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
656static
657void
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
705static
706void
707nss_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 */
724void
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
740static nss_backend_t *
741nss_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
818static void
819nss_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
852static 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
916void
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
953void
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 */
988static uint_t *
989_nss_status_vec_p()
990{
991	return (tsdalloc(_T_NSS_STATUS_VEC, sizeof (uint_t), NULL));
992}
993
994unsigned 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
1002static void
1003output_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
1016static void
1017output_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
1069static int
1070retry_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
1113void
1114nss_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
1131nss_status_t
1132nss_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
1256struct 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	nss_db_params_t		param;
1263};
1264
1265static void		nss_setent_u(nss_db_root_t *,
1266				    nss_db_initf_t,
1267				    nss_getent_t *);
1268static nss_status_t	nss_getent_u(nss_db_root_t *,
1269				    nss_db_initf_t,
1270				    nss_getent_t *,
1271				    void *);
1272static void		nss_endent_u(nss_db_root_t *,
1273				    nss_db_initf_t,
1274				    nss_getent_t *);
1275
1276void
1277nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1278{
1279	if (contextpp == 0) {
1280		return;
1281	}
1282	(void) _private_mutex_lock(&contextpp->lock);
1283	nss_setent_u(rootp, initf, contextpp);
1284	(void) _private_mutex_unlock(&contextpp->lock);
1285}
1286
1287nss_status_t
1288nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1289	void *args)
1290{
1291	nss_status_t		status;
1292
1293	if (contextpp == 0) {
1294		return (NSS_UNAVAIL);
1295	}
1296	(void) _private_mutex_lock(&contextpp->lock);
1297	status = nss_getent_u(rootp, initf, contextpp, args);
1298	(void) _private_mutex_unlock(&contextpp->lock);
1299	return (status);
1300}
1301
1302void
1303nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1304{
1305	if (contextpp == 0) {
1306		return;
1307	}
1308	(void) _private_mutex_lock(&contextpp->lock);
1309	nss_endent_u(rootp, initf, contextpp);
1310	(void) _private_mutex_unlock(&contextpp->lock);
1311}
1312
1313/*
1314 * Each of the _u versions of the nss interfaces assume that the context
1315 * lock is held.  No need to divert to nscd.  Private to local sequencing.
1316 */
1317
1318static void
1319end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1320{
1321	struct nss_db_state	*s;
1322	nss_backend_t		*be;
1323	int			n_src;
1324
1325	s = contextp->s;
1326	n_src = contextp->n_src;
1327	be = contextp->be;
1328
1329	if (s != 0) {
1330		if (n_src < s->max_src && be != 0) {
1331			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1332			NSS_RELOCK(&rootp, s);
1333			nss_put_backend_u(s, n_src, be);
1334			contextp->be = 0;  /* Should be unnecessary, but hey */
1335			NSS_UNREF_UNLOCK(rootp, s);
1336		}
1337		contextp->s = 0;
1338	}
1339}
1340
1341static void
1342nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1343	nss_getent_t *contextpp)
1344{
1345	nss_status_t		status;
1346	struct nss_db_state	*s;
1347	struct nss_getent_context *contextp;
1348	nss_backend_t		*be;
1349	int			n_src;
1350
1351	/* setup process wide context while locked */
1352	if ((contextp = contextpp->ctx) == 0) {
1353		if ((contextp = libc_malloc(sizeof (*contextp))) == 0) {
1354			return;
1355		}
1356		contextpp->ctx = contextp;
1357		contextp->cookie = NSCD_NEW_COOKIE;	/* cookie init */
1358		contextp->seq_num = 0;			/* seq_num init */
1359		s = 0;
1360	} else {
1361		s = contextp->s;
1362	}
1363
1364	/* name service cache daemon divert */
1365	status = _nsc_setent_u(rootp, initf, contextpp);
1366	if (status != NSS_TRYLOCAL)
1367		return;
1368
1369	/* fall through - process locally */
1370	if (s == 0) {
1371		NSS_LOCK_CHECK(rootp, initf, &s);
1372		if (s == 0) {
1373			/* Couldn't set up state, so quit */
1374			NSS_UNLOCK(rootp);
1375			/* ==== is there any danger of not having done an */
1376			/* end_iter() here, and hence of losing backends? */
1377			contextpp->ctx = 0;
1378			libc_free(contextp);
1379			return;
1380		}
1381		NSS_STATE_REF_u(s);
1382		contextp->s = s;
1383	} else {
1384		s	= contextp->s;
1385		n_src	= contextp->n_src;
1386		be	= contextp->be;
1387		if (n_src == 0 && be != 0) {
1388			/*
1389			 * Optimization:  don't do endent, don't change
1390			 *   backends, just do the setent.  Look Ma, no locks
1391			 *   (nor any context that needs updating).
1392			 */
1393			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1394			return;
1395		}
1396		if (n_src < s->max_src && be != 0) {
1397			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1398			NSS_RELOCK(&rootp, s);
1399			nss_put_backend_u(s, n_src, be);
1400			contextp->be = 0;	/* Play it safe */
1401		} else {
1402			NSS_RELOCK(&rootp, s);
1403		}
1404	}
1405	for (n_src = 0, be = 0; n_src < s->max_src &&
1406		(be = nss_get_backend_u(&rootp, s, n_src)) == 0; n_src++) {
1407		;
1408	}
1409	NSS_UNLOCK(rootp);
1410
1411	contextp->n_src	= n_src;
1412	contextp->be	= be;
1413
1414	if (be == 0) {
1415		/* Things are broken enough that we can't do setent/getent */
1416		nss_endent_u(rootp, initf, contextpp);
1417		return;
1418	}
1419	(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1420}
1421
1422static nss_status_t
1423nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1424	nss_getent_t *contextpp, void *args)
1425{
1426	nss_status_t		status;
1427	struct nss_db_state	*s;
1428	struct nss_getent_context *contextp;
1429	int			n_src;
1430	nss_backend_t		*be;
1431
1432	if ((contextp = contextpp->ctx) == 0) {
1433		nss_setent_u(rootp, initf, contextpp);
1434		if ((contextp = contextpp->ctx) == 0) {
1435			/* Give up */
1436			return (NSS_UNAVAIL);
1437		}
1438	}
1439	/* name service cache daemon divert */
1440	status = _nsc_getent_u(rootp, initf, contextpp, args);
1441	if (status != NSS_TRYLOCAL)
1442		return (status);
1443
1444	/* fall through - process locally */
1445	s	= contextp->s;
1446	n_src	= contextp->n_src;
1447	be	= contextp->be;
1448
1449	if (s == 0) {
1450		/*
1451		 * We've done an end_iter() and haven't done nss_setent()
1452		 * or nss_endent() since;  we should stick in this state
1453		 * until the caller invokes one of those two routines.
1454		 */
1455		return (NSS_SUCCESS);
1456	}
1457
1458	while (n_src < s->max_src) {
1459		nss_status_t res;
1460
1461		if (be == 0) {
1462			/* If it's null it's a bug, but let's play safe */
1463			res = NSS_UNAVAIL;
1464		} else {
1465			res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
1466		}
1467
1468		if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1469			if (res != __NSW_SUCCESS) {
1470				end_iter_u(rootp, contextp);
1471			}
1472			return (res);
1473		}
1474		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1475		NSS_RELOCK(&rootp, s);
1476		nss_put_backend_u(s, n_src, be);
1477		do {
1478			n_src++;
1479		} while (n_src < s->max_src &&
1480			(be = nss_get_backend_u(&rootp, s, n_src)) == 0);
1481		if (be == 0) {
1482			/*
1483			 * This is the case where we failed to get the backend
1484			 * for the last source. We exhausted all sources.
1485			 */
1486			NSS_UNLOCK(rootp);
1487			nss_endent_u(rootp, initf, contextpp);
1488			nss_delete(rootp);
1489			return (NSS_SUCCESS);
1490		}
1491		NSS_UNLOCK(rootp);
1492		contextp->n_src	= n_src;
1493		contextp->be	= be;
1494		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1495	}
1496	/* Got to the end of the sources without finding another entry */
1497	end_iter_u(rootp, contextp);
1498	return (NSS_SUCCESS);
1499	/* success is either a successful entry or end of the sources */
1500}
1501
1502/*ARGSUSED*/
1503static void
1504nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1505	nss_getent_t *contextpp)
1506{
1507	nss_status_t		status;
1508	struct nss_getent_context *contextp;
1509
1510	if ((contextp = contextpp->ctx) == 0) {
1511		/* nss_endent() on an unused context is a no-op */
1512		return;
1513	}
1514
1515	/* notify name service cache daemon */
1516	status = _nsc_endent_u(rootp, initf, contextpp);
1517	if (status != NSS_TRYLOCAL) {
1518		/* clean up */
1519		libc_free(contextp);
1520		contextpp->ctx = 0;
1521		return;
1522	}
1523
1524	/* fall through - process locally */
1525
1526	/*
1527	 * Existing code (BSD, SunOS) works in such a way that getXXXent()
1528	 *   following an endXXXent() behaves as though the user had invoked
1529	 *   setXXXent(), i.e. it iterates properly from the beginning.
1530	 * We'd better not break this, so our choices are
1531	 *	(1) leave the context structure around, and do nss_setent or
1532	 *	    something equivalent,
1533	 *   or	(2) free the context completely, and rely on the code in
1534	 *	    nss_getent() that makes getXXXent() do the right thing
1535	 *	    even without a preceding setXXXent().
1536	 * The code below does (2), which frees up resources nicely but will
1537	 * cost more if the user then does more getXXXent() operations.
1538	 * Moral:  for efficiency, don't call endXXXent() prematurely.
1539	 */
1540	end_iter_u(rootp, contextp);
1541	libc_free(contextp);
1542	contextpp->ctx = 0;
1543}
1544
1545/*
1546 * pack dbd data into header
1547 * Argment pointers assumed valid.
1548 * poff offset position pointer
1549 *   IN = starting offset for dbd header
1550 *   OUT = starting offset for next section
1551 */
1552
1553static nss_status_t
1554nss_pack_dbd(void *buffer, size_t bufsize, nss_db_params_t *p, size_t *poff)
1555{
1556	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1557	nss_dbd_t		*pdbd;
1558	size_t			off = *poff;
1559	size_t			len, blen;
1560	size_t			n, nc, dc;
1561	char			*bptr;
1562
1563	pbuf->dbd_off = (nssuint_t)off;
1564	bptr = (char *)buffer + off;
1565	blen = bufsize - off;
1566	len = sizeof (nss_dbd_t);
1567
1568	n = nc = dc = 0;
1569	if (p->name == NULL) {
1570		errno = ERANGE;			/* actually EINVAL */
1571		return (NSS_ERROR);
1572	}
1573
1574	/* if default config not specified, the flag should be reset */
1575	if (p->default_config == NULL) {
1576		p->default_config = "<NULL>";
1577		p->flags = p->flags & ~NSS_USE_DEFAULT_CONFIG;
1578	}
1579
1580	n = strlen(p->name) + 1;
1581	dc = strlen(p->default_config) + 1;
1582	if (n < 2 || dc < 2) {			/* What no DB? */
1583		errno = ERANGE;			/* actually EINVAL */
1584		return (NSS_ERROR);
1585	}
1586	if (p->config_name != NULL) {
1587		nc = strlen(p->config_name) + 1;
1588	}
1589	if ((len + n + nc + dc) >= blen) {
1590		errno = ERANGE;			/* actually EINVAL */
1591		return (NSS_ERROR);
1592	}
1593
1594	pdbd = (nss_dbd_t *)((void *)bptr);
1595	bptr += len;
1596	pdbd->flags = p->flags;
1597	pdbd->o_name = len;
1598	(void) strlcpy(bptr, p->name, n);
1599	len += n;
1600	bptr += n;
1601	if (nc == 0) {
1602		pdbd->o_config_name = 0;
1603	} else {
1604		pdbd->o_config_name = len;
1605		(void) strlcpy(bptr, p->config_name, nc);
1606		bptr += nc;
1607		len += nc;
1608	}
1609	pdbd->o_default_config = len;
1610	(void) strlcpy(bptr, p->default_config, dc);
1611	len += dc;
1612	pbuf->dbd_len = (nssuint_t)len;
1613	off += ROUND_UP(len, sizeof (nssuint_t));
1614	*poff = off;
1615	return (NSS_SUCCESS);
1616}
1617
1618/*
1619 * Switch packed and _nsc (switch->nscd) interfaces
1620 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1621 */
1622
1623/*ARGSUSED*/
1624nss_status_t
1625nss_pack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1626	    nss_db_initf_t initf, int search_fnum, void *search_args)
1627{
1628	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1629	nss_XbyY_args_t		*in = (nss_XbyY_args_t *)search_args;
1630	nss_db_params_t		tparam = { 0 };
1631	nss_status_t		ret = NSS_ERROR;
1632	const char		*dbn;
1633	size_t			blen, len, off = 0;
1634	char			*bptr;
1635	nssuint_t		*uptr;
1636	struct nss_groupsbymem	*gbm;
1637
1638	if (pbuf == NULL || in == NULL || initf == (nss_db_initf_t)NULL) {
1639		errno = ERANGE;			/* actually EINVAL */
1640		return (ret);
1641	}
1642	tparam.cleanup = NULL;
1643	(*initf)(&tparam);
1644	if ((dbn = tparam.name) == 0) {
1645		if (tparam.cleanup != 0)
1646			(tparam.cleanup)(&tparam);
1647		errno = ERANGE;			/* actually EINVAL */
1648		return (ret);
1649	}
1650
1651	/* init buffer header */
1652	pbuf->pbufsiz = (nssuint_t)bufsize;
1653	pbuf->p_ruid = (uint32_t)getuid();
1654	pbuf->p_euid = (uint32_t)geteuid();
1655	pbuf->p_version = NSCD_HEADER_REV;
1656	pbuf->p_status = 0;
1657	pbuf->p_errno = 0;
1658	pbuf->p_herrno = 0;
1659
1660	/* possible audituser init */
1661	if (strcmp(dbn, NSS_DBNAM_AUTHATTR) == 0 && in->h_errno != 0)
1662		pbuf->p_herrno = (uint32_t)in->h_errno;
1663
1664	pbuf->libpriv = 0;
1665
1666	off = sizeof (nss_pheader_t);
1667
1668	/* setup getXbyY operation - database and sub function */
1669	pbuf->nss_dbop = (uint32_t)search_fnum;
1670	ret = nss_pack_dbd(buffer, bufsize, &tparam, &off);
1671	if (ret != NSS_SUCCESS) {
1672		errno = ERANGE;			/* actually EINVAL */
1673		return (ret);
1674	}
1675	ret = NSS_ERROR;
1676	/* setup request key */
1677	pbuf->key_off = (nssuint_t)off;
1678	bptr = (char *)buffer + off;
1679	blen = bufsize - off;
1680	/* use key2str if provided, else call default getXbyY packer */
1681	if (strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) {
1682		/* This has to run locally due to backend knowledge */
1683		if (search_fnum == NSS_DBOP_NETGROUP_SET) {
1684			errno = 0;
1685			return (NSS_TRYLOCAL);
1686		}
1687		/* use default packer for known getXbyY ops */
1688		ret = nss_default_key2str(bptr, blen, in, dbn,
1689						search_fnum, &len);
1690	} else if (in->key2str == NULL ||
1691		(search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1692			strcmp(dbn, NSS_DBNAM_GROUP) == 0)) {
1693		/* use default packer for known getXbyY ops */
1694		ret = nss_default_key2str(bptr, blen, in, dbn,
1695						search_fnum, &len);
1696	} else {
1697		ret = (*in->key2str)(bptr, blen, &in->key, &len);
1698	}
1699	if (tparam.cleanup != 0)
1700		(tparam.cleanup)(&tparam);
1701	if (ret != NSS_SUCCESS) {
1702		errno = ERANGE;			/* actually ENOMEM */
1703		return (ret);
1704	}
1705	pbuf->key_len = (nssuint_t)len;
1706	off += ROUND_UP(len, sizeof (nssuint_t));
1707
1708	pbuf->data_off = (nssuint_t)off;
1709	pbuf->data_len = (nssuint_t)(bufsize - off);
1710	/*
1711	 * Prime data return with first result if
1712	 * the first result is passed in
1713	 * [_getgroupsbymember oddness]
1714	 */
1715	gbm = (struct nss_groupsbymem *)search_args;
1716	if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1717	    strcmp(dbn, NSS_DBNAM_GROUP) == 0 && gbm->numgids == 1) {
1718		uptr = (nssuint_t *)((void *)((char *)buffer + off));
1719		*uptr = (nssuint_t)gbm->gid_array[0];
1720	}
1721
1722	errno = 0;				/* just in case ... */
1723	return (NSS_SUCCESS);
1724}
1725
1726/*
1727 * Switch packed and _nsc (switch->nscd) {set/get/end}ent interfaces
1728 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1729 */
1730
1731/*ARGSUSED*/
1732nss_status_t
1733nss_pack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1734	    nss_db_initf_t initf, nss_getent_t *contextpp)
1735{
1736	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1737	struct nss_getent_context *contextp = contextpp->ctx;
1738	nss_status_t		ret = NSS_ERROR;
1739	size_t			blen, len = 0, off = 0;
1740	char			*bptr;
1741	nssuint_t		*nptr;
1742
1743	if (pbuf == NULL || initf == (nss_db_initf_t)NULL) {
1744		errno = ERANGE;			/* actually EINVAL */
1745		return (ret);
1746	}
1747
1748	/* init buffer header */
1749	pbuf->pbufsiz = (nssuint_t)bufsize;
1750	pbuf->p_ruid = (uint32_t)getuid();
1751	pbuf->p_euid = (uint32_t)geteuid();
1752	pbuf->p_version = NSCD_HEADER_REV;
1753	pbuf->p_status = 0;
1754	pbuf->p_errno = 0;
1755	pbuf->p_herrno = 0;
1756	pbuf->libpriv = 0;
1757
1758	off = sizeof (nss_pheader_t);
1759
1760	/* setup getXXXent operation - database and sub function */
1761	pbuf->nss_dbop = (uint32_t)0;	/* iterators have no dbop */
1762	ret = nss_pack_dbd(buffer, bufsize, &contextp->param, &off);
1763	if (ret != NSS_SUCCESS) {
1764		errno = ERANGE;			/* actually EINVAL */
1765		return (ret);
1766	}
1767	ret = NSS_ERROR;
1768	off += ROUND_UP(len, sizeof (nssuint_t));
1769
1770	pbuf->key_off = (nssuint_t)off;
1771	bptr = (char *)buffer + off;
1772	blen = bufsize - off;
1773	len = (size_t)(sizeof (nssuint_t) * 2);
1774	if (len >= blen) {
1775		errno = ERANGE;			/* actually EINVAL */
1776		return (ret);
1777	}
1778	nptr = (nssuint_t *)((void *)bptr);
1779	*nptr++ = contextp->cookie;
1780	*nptr = contextp->seq_num;
1781	pbuf->key_len = (nssuint_t)len;
1782
1783	off += len;
1784	pbuf->data_off = (nssuint_t)off;
1785	pbuf->data_len = (nssuint_t)(bufsize - off);
1786	return (NSS_SUCCESS);
1787}
1788
1789/*
1790 * Unpack packed arguments buffer
1791 * Return: status, errnos and results from requested operation.
1792 *
1793 * NOTES: When getgroupsbymember is being processed in the NSCD backend,
1794 * or via the backwards compatibility interfaces then the standard
1795 * str2group API is used in conjunction with process_cstr.  When,
1796 * processing a returned buffer, in NSS2 the return results are the
1797 * already digested groups array.  Therefore, unpack the digested results
1798 * back to the return buffer.
1799 *
1800 * Note: the digested results are nssuint_t quantities.  _getgroupsbymember
1801 * digests int quantities.  Therefore convert.  Assume input is in nssuint_t
1802 * quantities.  Store in an int array... Assume gid's are <= 32 bits...
1803 */
1804
1805/*ARGSUSED*/
1806nss_status_t
1807nss_unpack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1808	    nss_db_initf_t initf, int search_fnum, void *search_args)
1809{
1810	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1811	nss_XbyY_args_t		*in = (nss_XbyY_args_t *)search_args;
1812	nss_dbd_t		*pdbd;
1813	char			*dbn;
1814	nss_status_t		status;
1815	char			*buf;
1816	int			len;
1817	int			ret;
1818	int			i;
1819	gid_t			*gidp;
1820	nssuint_t		*uptr;
1821	struct nss_groupsbymem	*arg;
1822
1823
1824	if (pbuf == NULL || in == NULL)
1825		return (-1);
1826	status = pbuf->p_status;
1827	/* if error - door's switch error */
1828	/* extended data could contain additional information? */
1829	if (status != NSS_SUCCESS) {
1830		in->h_errno = (int)pbuf->p_herrno;
1831		if (pbuf->p_errno == ERANGE)
1832			in->erange = 1;
1833		return (status);
1834	}
1835
1836	if (pbuf->data_off == 0 || pbuf->data_len == 0)
1837		return (NSS_NOTFOUND);
1838
1839	buf = (char *)buffer + pbuf->data_off;
1840	len = pbuf->data_len;
1841
1842	/* sidestep odd cases */
1843	pdbd = (nss_dbd_t *)((void *)((char *)buffer + pbuf->dbd_off));
1844	dbn = (char *)pdbd + pdbd->o_name;
1845	if (search_fnum == NSS_DBOP_GROUP_BYMEMBER) {
1846		if (strcmp(dbn, NSS_DBNAM_GROUP) == 0) {
1847			arg = (struct nss_groupsbymem *)in;
1848			/* copy returned gid array from returned nscd buffer */
1849			i = len / sizeof (nssuint_t);
1850			/* not enough buffer */
1851			if (i > arg->maxgids) {
1852				i = arg->maxgids;
1853			}
1854			arg->numgids = i;
1855			gidp = arg->gid_array;
1856			uptr = (nssuint_t *)((void *)buf);
1857			while (--i >= 0)
1858				*gidp++ = (gid_t)*uptr++;
1859			return (NSS_SUCCESS);
1860		}
1861	}
1862	if (search_fnum == NSS_DBOP_NETGROUP_IN) {
1863		if (strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) {
1864			struct nss_innetgr_args *arg =
1865				(struct nss_innetgr_args *)in;
1866
1867			if (pbuf->p_status == NSS_SUCCESS) {
1868				arg->status = NSS_NETGR_FOUND;
1869				return (NSS_SUCCESS);
1870			} else {
1871				arg->status = NSS_NETGR_NO;
1872				return (NSS_NOTFOUND);
1873			}
1874		}
1875	}
1876
1877	/* process the normal cases */
1878	/* marshall data directly into users buffer */
1879	ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1880		in->buf.buflen);
1881	if (ret == NSS_STR_PARSE_ERANGE) {
1882		in->returnval = 0;
1883		in->returnlen = 0;
1884		in->erange    = 1;
1885		ret = NSS_NOTFOUND;
1886	} else if (ret == NSS_STR_PARSE_SUCCESS) {
1887		in->returnval = in->buf.result;
1888		in->returnlen =  len;
1889		ret = NSS_SUCCESS;
1890	}
1891	in->h_errno = (int)pbuf->p_herrno;
1892	return ((nss_status_t)ret);
1893}
1894
1895/*
1896 * Unpack a returned packed {set,get,end}ent arguments buffer
1897 * Return: status, errnos, cookie info and results from requested operation.
1898 */
1899
1900/*ARGSUSED*/
1901nss_status_t
1902nss_unpack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1903	    nss_db_initf_t initf, nss_getent_t *contextpp, void *args)
1904{
1905	nss_pheader_t		*pbuf = (nss_pheader_t *)buffer;
1906	nss_XbyY_args_t		*in = (nss_XbyY_args_t *)args;
1907	struct nss_getent_context *contextp = contextpp->ctx;
1908	nssuint_t		*nptr;
1909	nssuint_t		cookie;
1910	nss_status_t		status;
1911	char			*buf;
1912	int			len;
1913	int			ret;
1914
1915	if (pbuf == NULL)
1916		return (-1);
1917	status = pbuf->p_status;
1918	/* if error - door's switch error */
1919	/* extended data could contain additional information? */
1920	if (status != NSS_SUCCESS)
1921		return (status);
1922
1923	/* unpack assigned cookie from SET/GET/END request */
1924	if (pbuf->key_off == 0 ||
1925	    pbuf->key_len != (sizeof (nssuint_t) * 2))
1926		return (NSS_NOTFOUND);
1927
1928	nptr = (nssuint_t *)((void *)((char *)buffer + pbuf->key_off));
1929	cookie = contextp->cookie;
1930	if (cookie != NSCD_NEW_COOKIE && cookie != *nptr) {
1931		/* Should either be new or a match, else error */
1932		return (NSS_NOTFOUND);
1933	}
1934	/* save away for the next ent request */
1935	contextp->cookie = *nptr++;
1936	contextp->seq_num = *nptr;
1937
1938	/* All done if no marshalling is expected {set,end}ent */
1939	if (args == NULL)
1940		return (NSS_SUCCESS);
1941
1942	/* unmarshall the data */
1943	if (pbuf->data_off == 0 || pbuf->data_len == 0)
1944		return (NSS_NOTFOUND);
1945	buf = (char *)buffer + pbuf->data_off;
1946
1947	len = pbuf->data_len;
1948
1949	/* marshall data directly into users buffer */
1950	ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1951		in->buf.buflen);
1952	if (ret == NSS_STR_PARSE_ERANGE) {
1953		in->returnval = 0;
1954		in->returnlen = 0;
1955		in->erange    = 1;
1956	} else if (ret == NSS_STR_PARSE_SUCCESS) {
1957		in->returnval = in->buf.result;
1958		in->returnlen =  len;
1959	}
1960	in->h_errno = (int)pbuf->p_herrno;
1961	return ((nss_status_t)ret);
1962}
1963
1964/*
1965 * Start of _nsc_{search|setent_u|getent_u|endent_u} NSCD interposition funcs
1966 */
1967
1968nss_status_t
1969_nsc_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
1970	void *search_args)
1971{
1972	nss_pheader_t		*pbuf;
1973	void			*doorptr = NULL;
1974	size_t			bufsize = 0;
1975	size_t			datasize = 0;
1976	nss_status_t		status;
1977
1978	if (_nsc_proc_is_cache() > 0) {
1979		/* internal nscd call - don't use the door */
1980		return (NSS_TRYLOCAL);
1981	}
1982
1983	/* standard client calls nscd code */
1984	if (search_args == NULL)
1985		return (NSS_NOTFOUND);
1986
1987	/* get the door buffer  & configured size */
1988	bufsize = ((nss_XbyY_args_t *)search_args)->buf.buflen;
1989	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
1990		return (NSS_TRYLOCAL);
1991	if (doorptr == NULL || bufsize == 0)
1992		return (NSS_TRYLOCAL);
1993
1994	pbuf = (nss_pheader_t *)doorptr;
1995	/* pack argument and request into door buffer */
1996	pbuf->nsc_callnumber = NSCD_SEARCH;
1997	/* copy relevant door request info into door buffer */
1998	status = nss_pack((void *)pbuf, bufsize, rootp,
1999			initf, search_fnum, search_args);
2000
2001	/* Packing error return error results */
2002	if (status != NSS_SUCCESS)
2003		return (status);
2004
2005	/* transfer packed switch request to nscd via door */
2006	/* data_off can be used because it is header+dbd_len+key_len */
2007	datasize = pbuf->data_off;
2008	status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2009
2010	/* If unsuccessful fallback to standard nss logic */
2011	if (status != NSS_SUCCESS) {
2012		/*
2013		 * check if doors reallocated the memory underneath us
2014		 * if they did munmap it or suffer a memory leak
2015		 */
2016		if (doorptr != (void *)pbuf) {
2017			_nsc_resizedoorbuf(bufsize);
2018			munmap((void *)doorptr, bufsize);
2019		}
2020		return (NSS_TRYLOCAL);
2021	}
2022
2023	/* unpack and marshall data/errors to user structure */
2024	/* set any error conditions */
2025	status = nss_unpack((void *)doorptr, bufsize, rootp, initf,
2026			search_fnum, search_args);
2027	/*
2028	 * check if doors reallocated the memory underneath us
2029	 * if they did munmap it or suffer a memory leak
2030	 */
2031	if (doorptr != (void *)pbuf) {
2032		_nsc_resizedoorbuf(bufsize);
2033		munmap((void *)doorptr, bufsize);
2034	}
2035	return (status);
2036}
2037
2038/*
2039 * contact nscd for a cookie or to reset an existing cookie
2040 * if nscd fails (NSS_TRYLOCAL) then set cookie to -1 and
2041 * continue diverting to local
2042 */
2043nss_status_t
2044_nsc_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2045	nss_getent_t *contextpp)
2046{
2047	nss_status_t		status = NSS_TRYLOCAL;
2048	struct nss_getent_context *contextp = contextpp->ctx;
2049	nss_pheader_t		*pbuf;
2050	void			*doorptr = NULL;
2051	size_t			bufsize = 0;
2052	size_t			datasize = 0;
2053
2054	/* return if already in local mode */
2055	if (contextp->cookie == NSCD_LOCAL_COOKIE)
2056		return (NSS_TRYLOCAL);
2057
2058	if (_nsc_proc_is_cache() > 0) {
2059		/* internal nscd call - don't try to use the door */
2060		contextp->cookie = NSCD_LOCAL_COOKIE;
2061		return (NSS_TRYLOCAL);
2062	}
2063
2064	/* get the door buffer & configured size */
2065	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) {
2066		contextp->cookie = NSCD_LOCAL_COOKIE;
2067		return (NSS_TRYLOCAL);
2068	}
2069	if (doorptr == NULL || bufsize == 0) {
2070		contextp->cookie = NSCD_LOCAL_COOKIE;
2071		return (NSS_TRYLOCAL);
2072	}
2073
2074	pbuf = (nss_pheader_t *)doorptr;
2075	pbuf->nsc_callnumber = NSCD_SETENT;
2076
2077	contextp->param.cleanup = NULL;
2078	(*initf)(&contextp->param);
2079	if (contextp->param.name == 0) {
2080		if (contextp->param.cleanup != 0)
2081			(contextp->param.cleanup)(&contextp->param);
2082		errno = ERANGE;			/* actually EINVAL */
2083		return (NSS_ERROR);
2084	}
2085
2086	/* pack relevant setent request info into door buffer */
2087	status = nss_pack_ent((void *)pbuf, bufsize, rootp,
2088			initf, contextpp);
2089	if (status != NSS_SUCCESS)
2090		return (status);
2091
2092	/* transfer packed switch request to nscd via door */
2093	/* data_off can be used because it is header+dbd_len+key_len */
2094	datasize = pbuf->data_off;
2095	status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2096
2097	/* If fallback to standard nss logic (door failure) if possible */
2098	if (status != NSS_SUCCESS) {
2099		if (contextp->cookie == NSCD_NEW_COOKIE) {
2100			contextp->cookie = NSCD_LOCAL_COOKIE;
2101			return (NSS_TRYLOCAL);
2102		}
2103		return (NSS_UNAVAIL);
2104	}
2105	/* unpack returned cookie stash it away */
2106	status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2107			initf, contextpp, NULL);
2108	/*
2109	 * check if doors reallocated the memory underneath us
2110	 * if they did munmap it or suffer a memory leak
2111	 */
2112	if (doorptr != (void *)pbuf) {
2113		_nsc_resizedoorbuf(bufsize);
2114		munmap((void *)doorptr, bufsize);
2115	}
2116	return (status);
2117}
2118
2119nss_status_t
2120_nsc_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2121	nss_getent_t *contextpp, void *args)
2122{
2123	nss_status_t		status = NSS_TRYLOCAL;
2124	struct nss_getent_context *contextp = contextpp->ctx;
2125	nss_pheader_t		*pbuf;
2126	void			*doorptr = NULL;
2127	size_t			bufsize = 0;
2128	size_t			datasize = 0;
2129
2130	/* return if already in local mode */
2131	if (contextp->cookie == NSCD_LOCAL_COOKIE)
2132		return (NSS_TRYLOCAL);
2133
2134	/* _nsc_setent_u already checked for nscd local case ... proceed */
2135	if (args == NULL)
2136		return (NSS_NOTFOUND);
2137
2138	/* get the door buffer  & configured size */
2139	bufsize = ((nss_XbyY_args_t *)args)->buf.buflen;
2140	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2141		return (NSS_UNAVAIL);
2142	if (doorptr == NULL || bufsize == 0)
2143		return (NSS_UNAVAIL);
2144
2145	pbuf = (nss_pheader_t *)doorptr;
2146	pbuf->nsc_callnumber = NSCD_GETENT;
2147
2148	/* pack relevant setent request info into door buffer */
2149	status = nss_pack_ent((void *)pbuf, bufsize, rootp,
2150			initf, contextpp);
2151	if (status != NSS_SUCCESS)
2152		return (status);
2153
2154	/* transfer packed switch request to nscd via door */
2155	/* data_off can be used because it is header+dbd_len+key_len */
2156	datasize = pbuf->data_off;
2157	status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2158
2159	/* If fallback to standard nss logic (door failure) if possible */
2160	if (status != NSS_SUCCESS) {
2161		if (contextp->cookie == NSCD_NEW_COOKIE) {
2162			contextp->cookie = NSCD_LOCAL_COOKIE;
2163			return (NSS_TRYLOCAL);
2164		}
2165		return (NSS_UNAVAIL);
2166	}
2167	/* check error, unpack and process results */
2168	status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2169			initf, contextpp, args);
2170	/*
2171	 * check if doors reallocated the memory underneath us
2172	 * if they did munmap it or suffer a memory leak
2173	 */
2174	if (doorptr != (void *)pbuf) {
2175		_nsc_resizedoorbuf(bufsize);
2176		munmap((void *)doorptr, bufsize);
2177	}
2178	return (status);
2179}
2180
2181nss_status_t
2182_nsc_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2183	nss_getent_t *contextpp)
2184{
2185	nss_status_t		status = NSS_TRYLOCAL;
2186	struct nss_getent_context *contextp = contextpp->ctx;
2187	nss_pheader_t		*pbuf;
2188	void			*doorptr = NULL;
2189	size_t			bufsize = 0;
2190	size_t			datasize = 0;
2191
2192	/* return if already in local mode */
2193	if (contextp->cookie == NSCD_LOCAL_COOKIE)
2194		return (NSS_TRYLOCAL);
2195
2196	/* _nsc_setent_u already checked for nscd local case ... proceed */
2197
2198	/* get the door buffer  & configured size */
2199	if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2200		return (NSS_UNAVAIL);
2201	if (doorptr == NULL || bufsize == 0)
2202		return (NSS_UNAVAIL);
2203
2204	/* pack up a NSCD_ENDGET request passing in the cookie */
2205	pbuf = (nss_pheader_t *)doorptr;
2206	pbuf->nsc_callnumber = NSCD_ENDENT;
2207
2208	/* pack relevant setent request info into door buffer */
2209	status = nss_pack_ent((void *)pbuf, bufsize, rootp,
2210			initf, contextpp);
2211	if (status != NSS_SUCCESS)
2212		return (status);
2213
2214	/* transfer packed switch request to nscd via door */
2215	/* data_off can be used because it is header+dbd_len+key_len */
2216	datasize = pbuf->data_off;
2217	(void) _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2218
2219	/* error codes & unpacking ret values don't matter.  We're done */
2220
2221	/*
2222	 * check if doors reallocated the memory underneath us
2223	 * if they did munmap it or suffer a memory leak
2224	 */
2225	if (doorptr != (void *)pbuf) {
2226		_nsc_resizedoorbuf(bufsize);
2227		munmap((void *)doorptr, bufsize);
2228	}
2229
2230	/* clean up initf setup */
2231	if (contextp->param.cleanup != 0)
2232		(contextp->param.cleanup)(&contextp->param);
2233	contextp->param.cleanup = NULL;
2234
2235	/* clear cookie */
2236	contextp->cookie = NSCD_NEW_COOKIE;
2237	return (NSS_SUCCESS);
2238}
2239