xref: /illumos-gate/usr/src/lib/libc/port/gen/nss_common.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Shared code used by the name-service-switch frontends (e.g. getpwnam_r())
31  */
32 
33 #pragma weak nss_delete = _nss_delete
34 #pragma weak nss_endent = _nss_endent
35 #pragma weak nss_getent = _nss_getent
36 #pragma weak nss_search = _nss_search
37 #pragma weak nss_setent = _nss_setent
38 
39 #include "synonyms.h"
40 #include <mtlib.h>
41 #include <dlfcn.h>
42 
43 #define	__NSS_PRIVATE_INTERFACE
44 #include "nsswitch_priv.h"
45 #undef	__NSS_PRIVATE_INTERFACE
46 
47 #include <nss_common.h>
48 #include <unistd.h>
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <string.h>
52 #include <thread.h>
53 #include "libc.h"
54 #include "tsd.h"
55 
56 /*
57  * The golden rule is:  if you hold a pointer to an nss_db_state struct and
58  * you don't hold the lock, you'd better have incremented the refcount
59  * while you held the lock;  otherwise, it may vanish or change
60  * significantly when you least expect it.
61  *
62  * The pointer in nss_db_root_t is one such, so the reference count >= 1.
63  * Ditto the pointer in struct nss_getent_context.
64  */
65 
66 /*
67  * State for one nsswitch database (e.g. "passwd", "hosts")
68  */
69 struct nss_db_state {
70 	nss_db_root_t		orphan_root;	/* XXX explain */
71 	unsigned		refcount;	/* One for the pointer in    */
72 						/*   nss_db_root_t, plus one */
73 						/*   for each active thread. */
74 	nss_db_params_t		p;
75 	struct __nsw_switchconfig_v1 *config;
76 	int			max_src;	/* is == config->num_lookups */
77 	struct nss_src_state	*src;		/* Pointer to array[max_src] */
78 };
79 
80 /*
81  * State for one of the sources (e.g. "nis", "compat") for a database
82  */
83 struct nss_src_state {
84 	struct __nsw_lookup_v1	*lkp;
85 	int			n_active;
86 	int			n_dormant;
87 	int			n_waiting;	/* ... on wanna_be */
88 	cond_t			wanna_be;
89 	union {
90 		nss_backend_t	*single; /* Efficiency hack for common case */
91 					    /* when limit_dead_backends == 1 */
92 		nss_backend_t	**multi; /* array[limit_dead_backends] of */
93 	} dormant;			    /* pointers to dormant backends */
94 	nss_backend_constr_t	be_constr;
95 	nss_backend_finder_t	*finder;
96 	void			*finder_priv;
97 };
98 
99 static struct nss_db_state	*_nss_db_state_constr(nss_db_initf_t);
100 void				_nss_db_state_destr(struct nss_db_state *);
101 
102 /* ==== null definitions if !MTSAFE?  Ditto lock field in nss_db_root_t */
103 
104 #define	NSS_ROOTLOCK(r, sp)	((void) _private_mutex_lock(&(r)->lock), \
105 				*(sp) = (r)->s)
106 
107 #define	NSS_UNLOCK(r)		((void) _private_mutex_unlock(&(r)->lock))
108 
109 #define	NSS_CHECKROOT(rp, s)	((s) != (*(rp))->s &&			\
110 			((void) _private_mutex_unlock(&(*(rp))->lock),	\
111 			(void) _private_mutex_lock(&(s)->orphan_root.lock), \
112 			*(rp) = &(s)->orphan_root))
113 
114 #define	NSS_RELOCK(rp, s)	((void) _private_mutex_lock(&(*(rp))->lock), \
115 			NSS_CHECKROOT(rp, s))
116 
117 #define	NSS_STATE_REF_u(s)	(++(s)->refcount)
118 
119 #define	NSS_UNREF_UNLOCK(r, s)	(--(s)->refcount != 0			\
120 			? ((void)NSS_UNLOCK(r))				\
121 			: (NSS_UNLOCK(r), (void)_nss_db_state_destr(s)))
122 
123 #define	NSS_LOCK_CHECK(r, f, sp)    (NSS_ROOTLOCK((r), (sp)),	\
124 				    *(sp) == 0 &&		\
125 				    (r->s = *(sp) = _nss_db_state_constr(f)))
126 /* === In the future, NSS_LOCK_CHECK() may also have to check that   */
127 /* === the config info hasn't changed (by comparing version numbers) */
128 
129 
130 /* NSS_OPTIONS infrastructure BEGIN */
131 static int checked_env = 0;		/* protected by "rootlock" */
132 
133 	/* allowing __nss_debug_file to be set could be a security hole. */
134 FILE *__nss_debug_file = stdout;
135 int __nss_debug_eng_loop;
136 
137 /* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */
138 	/* allowing __nis_debug_file to be set could be a security hole. */
139 FILE *__nis_debug_file = stdout;
140 int __nis_debug_bind;
141 int __nis_debug_rpc;
142 int __nis_debug_calls;
143 char *__nis_prefsrv;
144 char *__nis_preftype;
145 char *__nis_server;   /* if set, use only this server for binding */
146 
147 #define	OPT_INT 1
148 #define	OPT_STRING 2
149 #ifdef DEBUG
150 #define	OPT_FILE 3
151 #endif
152 
153 struct option {
154 	char *name;
155 	int type;
156 	void *address;
157 };
158 
159 static struct option nss_options[] = {
160 #ifdef DEBUG
161 	/* allowing __nss_debug_file to be set could be a security hole. */
162 	{ "debug_file", OPT_FILE, &__nss_debug_file },
163 #endif
164 	{ "debug_eng_loop", OPT_INT, &__nss_debug_eng_loop },
165 	{ 0, 0, 0 },
166 };
167 
168 static struct option nis_options[] = {
169 #ifdef DEBUG
170 	/* allowing __nis_debug_file to be set could be a security hole. */
171 	{ "debug_file", OPT_FILE, &__nis_debug_file },
172 #endif
173 	{ "debug_bind", OPT_INT, &__nis_debug_bind },
174 	{ "debug_rpc", OPT_INT, &__nis_debug_rpc },
175 	{ "debug_calls", OPT_INT, &__nis_debug_calls },
176 	{ "server", OPT_STRING, &__nis_server },
177 	{ "pref_srvr", OPT_STRING, &__nis_prefsrv },
178 	{ "pref_type", OPT_STRING, &__nis_preftype },
179 	{ 0, 0, 0 },
180 };
181 
182 static
183 void
184 set_option(struct option *opt, char *name, char *val)
185 {
186 	int n;
187 	char *p;
188 #ifdef DEBUG
189 	FILE *fp;
190 #endif
191 
192 	for (; opt->name; opt++) {
193 		if (strcmp(name, opt->name) == 0) {
194 			switch (opt->type) {
195 			    case OPT_STRING:
196 				p = libc_strdup(val);
197 				*((char **)opt->address) = p;
198 				break;
199 
200 			    case OPT_INT:
201 				if (strcmp(val, "") == 0)
202 					n = 1;
203 				else
204 					n = atoi(val);
205 				*((int *)opt->address) = n;
206 				break;
207 #ifdef DEBUG
208 			    case OPT_FILE:
209 				fp = fopen(val, "w");
210 				*((FILE **)opt->address) = fp;
211 				break;
212 #endif
213 			}
214 			break;
215 		}
216 	}
217 }
218 
219 static
220 void
221 __parse_environment(struct option *opt, char *p)
222 {
223 	char *base;
224 	char optname[100];
225 	char optval[100];
226 
227 	while (*p) {
228 		while (isspace(*p))
229 			p++;
230 		if (*p == '\0')
231 			break;
232 
233 		base = p;
234 		while (*p && *p != '=' && !isspace(*p))
235 			p++;
236 		/*
237 		 * play it safe and keep it simple, bail if an opt name
238 		 * is too long.
239 		 */
240 		if ((p-base) >= sizeof (optname))
241 			return;
242 
243 		(void) strncpy(optname, base, p-base);
244 		optname[p-base] = '\0';
245 
246 		if (*p == '=') {
247 			p++;
248 			base = p;
249 			while (*p && !isspace(*p))
250 				p++;
251 			/*
252 			 * play it safe and keep it simple, bail if an opt
253 			 * value is too long.
254 			 */
255 			if ((p-base) >= sizeof (optval))
256 				return;
257 
258 			(void) strncpy(optval, base, p-base);
259 			optval[p-base] = '\0';
260 		} else {
261 			optval[0] = '\0';
262 		}
263 
264 		set_option(opt, optname, optval);
265 	}
266 }
267 
268 static
269 void
270 nss_get_environment()
271 {
272 	char *p;
273 
274 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
275 	p = getenv("NSS_OPTIONS");
276 	if (p == NULL)
277 		return;
278 	__parse_environment(nss_options, p);
279 }
280 
281 /*
282  * sole external routine called from libnsl/nis/cache/cache_api.cc in the
283  * routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard
284  * Only after checking "checked_env" (which must be done with mutex
285  * "cur_cache_lock" held) and is done once, (then "checked_env" is set)
286  */
287 void
288 __nis_get_environment()
289 {
290 	char *p;
291 
292 	p = getenv("NIS_OPTIONS");
293 	if (p == NULL)
294 		return;
295 	__parse_environment(nis_options, p);
296 }
297 /* NSS_OPTIONS/NIS_OPTIONS infrastructure END */
298 
299 
300 static nss_backend_t *
301 nss_get_backend_u(nss_db_root_t **rootpp, struct nss_db_state *s, int n_src)
302 {
303 	struct nss_src_state	*src = &s->src[n_src];
304 	nss_backend_t		*be;
305 
306 	for (;;) {
307 		if (src->n_dormant > 0) {
308 			src->n_dormant--;
309 			src->n_active++;
310 			if (s->p.max_dormant_per_src == 1) {
311 				be = src->dormant.single;
312 			} else {
313 				be = src->dormant.multi[src->n_dormant];
314 			}
315 			break;
316 		}
317 
318 		if (src->be_constr == 0) {
319 			nss_backend_finder_t	*bf;
320 
321 			for (bf = s->p.finders;  bf != 0;  bf = bf->next) {
322 				nss_backend_constr_t c;
323 
324 				c = (*bf->lookup)
325 					(bf->lookup_priv,
326 						s->p.name,
327 						src->lkp->service_name,
328 						&src->finder_priv);
329 				if (c != 0) {
330 					src->be_constr = c;
331 					src->finder = bf;
332 					break;
333 				}
334 			}
335 			if (src->be_constr == 0) {
336 				/* Couldn't find the backend anywhere */
337 				be = 0;
338 				break;
339 			}
340 		}
341 
342 		if (src->n_active < s->p.max_active_per_src) {
343 			be = (*src->be_constr)(s->p.name,
344 						src->lkp->service_name,
345 						0 /* === unimplemented */);
346 			if (be != 0) {
347 				src->n_active++;
348 				break;
349 			} else if (src->n_active == 0) {
350 				/* Something's wrong;  we should be */
351 				/*   able to create at least one    */
352 				/*   instance of the backend	    */
353 				break;
354 			}
355 			/*
356 			 * Else it's odd that we can't create another backend
357 			 *   instance, but don't sweat it;  instead, queue for
358 			 *   an existing backend instance.
359 			 */
360 		}
361 
362 		src->n_waiting++;
363 		(void) cond_wait(&src->wanna_be, &(*rootpp)->lock);
364 		NSS_CHECKROOT(rootpp, s);
365 		src->n_waiting--;
366 
367 		/*
368 		 * Loop and see whether things got better for us, or whether
369 		 *   someone else got scheduled first and we have to try
370 		 *   this again.
371 		 *
372 		 * === ?? Should count iterations, assume bug if many ??
373 		 */
374 	}
375 	return (be);
376 }
377 
378 static void
379 nss_put_backend_u(struct nss_db_state *s, int n_src, nss_backend_t *be)
380 {
381 	struct nss_src_state	*src = &s->src[n_src];
382 
383 	if (be == 0) {
384 		return;
385 	}
386 
387 	src->n_active--;
388 
389 	if (src->n_dormant < s->p.max_dormant_per_src) {
390 		if (s->p.max_dormant_per_src == 1) {
391 			src->dormant.single = be;
392 			src->n_dormant++;
393 		} else if (src->dormant.multi != 0 ||
394 			(src->dormant.multi =
395 			    libc_malloc(s->p.max_dormant_per_src *
396 			    sizeof (nss_backend_t *))) != NULL) {
397 			src->dormant.multi[src->n_dormant] = be;
398 			src->n_dormant++;
399 		} else {
400 			/* Can't store it, so toss it */
401 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
402 		}
403 	} else {
404 		/* We've stored as many as we want, so toss it */
405 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
406 	}
407 	if (src->n_waiting > 0) {
408 		(void) cond_signal(&src->wanna_be);
409 	}
410 }
411 
412 static struct nss_db_state *
413 _nss_db_state_constr(nss_db_initf_t initf)
414 {
415 	struct nss_db_state	*s;
416 	struct __nsw_switchconfig_v1 *config = 0;
417 	struct __nsw_lookup_v1	*lkp;
418 	enum __nsw_parse_err	err;
419 	const char		*config_name;
420 	int			n_src;
421 
422 	if ((s = libc_malloc(sizeof (*s))) == 0) {
423 		return (0);
424 	}
425 	(void) _private_mutex_init(&s->orphan_root.lock, USYNC_THREAD, 0);
426 
427 	s->p.max_active_per_src	= 10;
428 	s->p.max_dormant_per_src = 1;
429 	s->p.finders = nss_default_finders;
430 	(*initf)(&s->p);
431 	if (s->p.name == 0) {
432 		_nss_db_state_destr(s);
433 		return (0);
434 	}
435 
436 	if (!checked_env) {
437 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
438 		nss_get_environment();
439 		checked_env = 1;
440 	}
441 
442 	config_name = s->p.config_name ? s->p.config_name : s->p.name;
443 	if (! (s->p.flags & NSS_USE_DEFAULT_CONFIG)) {
444 		config = __nsw_getconfig_v1(config_name, &err);
445 		/* === ? test err ? */
446 	}
447 	if (config == 0) {
448 		/* getconfig failed, or frontend demanded default config */
449 
450 		char	*str;	/* _nsw_getoneconfig() clobbers its argument */
451 
452 		if ((str = libc_strdup(s->p.default_config)) != 0) {
453 			config = _nsw_getoneconfig_v1(config_name, str, &err);
454 			libc_free(str);
455 		}
456 		if (config == 0) {
457 			_nss_db_state_destr(s);
458 			return (0);
459 		}
460 	}
461 	s->config = config;
462 	if ((s->max_src = config->num_lookups) <= 0 ||
463 	    (s->src = libc_malloc(s->max_src * sizeof (*s->src))) == 0) {
464 		_nss_db_state_destr(s);
465 		return (0);
466 	}
467 	for (n_src = 0, lkp = config->lookups;
468 	    n_src < s->max_src; n_src++, lkp = lkp->next) {
469 		s->src[n_src].lkp = lkp;
470 		(void) cond_init(&s->src[n_src].wanna_be, USYNC_THREAD, 0);
471 	}
472 	s->refcount = 1;
473 	return (s);
474 }
475 
476 void
477 _nss_src_state_destr(struct nss_src_state *src, int max_dormant)
478 {
479 	if (max_dormant == 1) {
480 		if (src->n_dormant != 0) {
481 			(void) NSS_INVOKE_DBOP(src->dormant.single,
482 					NSS_DBOP_DESTRUCTOR, 0);
483 		};
484 	} else if (src->dormant.multi != 0) {
485 		int	n;
486 
487 		for (n = 0;  n < src->n_dormant;  n++) {
488 			(void) NSS_INVOKE_DBOP(src->dormant.multi[n],
489 					NSS_DBOP_DESTRUCTOR, 0);
490 		}
491 		libc_free(src->dormant.multi);
492 	}
493 
494 	/* cond_destroy(&src->wanna_be); */
495 
496 	if (src->finder != 0) {
497 		(*src->finder->delete)(src->finder_priv, src->be_constr);
498 	}
499 }
500 
501 /*
502  * _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire
503  *	nss_db_state structure.
504  * Assumes that s has been ref-counted down to zero (in particular,
505  *	rootp->s has already been dealt with).
506  *
507  * Nobody else holds a pointer to *s (if they did, refcount != 0),
508  *   so we can clean up state *after* we drop the lock (also, by the
509  *   time we finish freeing the state structures, the lock may have
510  *   ceased to exist -- if we were using the orphan_root).
511  */
512 
513 void
514 _nss_db_state_destr(struct nss_db_state *s)
515 {
516 	/* === _private_mutex_destroy(&s->orphan_root.lock); */
517 	if (s->p.cleanup != 0) {
518 		(*s->p.cleanup)(&s->p);
519 	}
520 	if (s->config != 0) {
521 		(void) __nsw_freeconfig_v1(s->config);
522 	}
523 	if (s->src != 0) {
524 		int	n_src;
525 
526 		for (n_src = 0;  n_src < s->max_src;  n_src++) {
527 			_nss_src_state_destr(&s->src[n_src],
528 				s->p.max_dormant_per_src);
529 		}
530 		libc_free(s->src);
531 	}
532 	libc_free(s);
533 }
534 
535 void
536 nss_delete(nss_db_root_t *rootp)
537 {
538 	struct nss_db_state	*s;
539 
540 	NSS_ROOTLOCK(rootp, &s);
541 	if (s == 0) {
542 		NSS_UNLOCK(rootp);
543 	} else {
544 		rootp->s = 0;
545 		NSS_UNREF_UNLOCK(rootp, s);
546 	}
547 }
548 
549 
550 /*
551  * _nss_status_vec() returns a bit vector of all status codes returned during
552  * the most recent call to nss_search().
553  * _nss_status_vec_p() returns a pointer to this bit vector, or NULL on
554  * failure.
555  * These functions are private.  Don't use them externally without discussing
556  * it with the switch maintainers.
557  */
558 static uint_t *
559 _nss_status_vec_p()
560 {
561 	return (tsdalloc(_T_NSS_STATUS_VEC, sizeof (uint_t), NULL));
562 }
563 
564 unsigned int
565 _nss_status_vec(void)
566 {
567 	unsigned int *status_vec_p = _nss_status_vec_p();
568 
569 	return ((status_vec_p != NULL) ? *status_vec_p : (1 << NSS_UNAVAIL));
570 }
571 
572 static void
573 output_loop_diag_a(
574 	int n,
575 	char *dbase,
576 	struct __nsw_lookup_v1 *lkp)
577 
578 {
579 	(void) fprintf(__nss_debug_file,
580 		"NSS_retry(%d): '%s': trying '%s' ... ",
581 		n, dbase, lkp->service_name);
582 	(void) fflush(__nss_debug_file);
583 
584 }
585 
586 static void
587 output_loop_diag_b(
588 	nss_status_t res,
589 	struct __nsw_lookup_v1 *lkp)
590 
591 {
592 	(void) fprintf(__nss_debug_file, "result=");
593 	switch (res) {
594 	case NSS_SUCCESS:
595 		(void) fprintf(__nss_debug_file, "SUCCESS");
596 		break;
597 	case NSS_NOTFOUND:
598 		(void) fprintf(__nss_debug_file, "NOTFOUND");
599 		break;
600 	case NSS_UNAVAIL:
601 		(void) fprintf(__nss_debug_file, "UNAVAIL");
602 		break;
603 	case NSS_TRYAGAIN:
604 		(void) fprintf(__nss_debug_file, "TRYAGAIN");
605 		break;
606 	case NSS_NISSERVDNS_TRYAGAIN:
607 		(void) fprintf(__nss_debug_file, "NISSERVDNS_TRYAGAIN");
608 		break;
609 	default:
610 		(void) fprintf(__nss_debug_file, "undefined");
611 	}
612 	(void) fprintf(__nss_debug_file, ", action=");
613 	switch (lkp->actions[res]) {
614 	case __NSW_CONTINUE:
615 		(void) fprintf(__nss_debug_file, "CONTINUE");
616 		break;
617 	case  __NSW_RETURN:
618 		(void) fprintf(__nss_debug_file, "RETURN");
619 		break;
620 	case __NSW_TRYAGAIN_FOREVER:
621 		(void) fprintf(__nss_debug_file, "TRYAGAIN_FOREVER");
622 		break;
623 	case __NSW_TRYAGAIN_NTIMES:
624 		(void) fprintf(__nss_debug_file, "TRYAGAIN_NTIMES (N=%d)",
625 			lkp->max_retries);
626 		break;
627 	case __NSW_TRYAGAIN_PAUSED:
628 		(void) fprintf(__nss_debug_file, "TRYAGAIN_PAUSED");
629 		break;
630 	default:
631 		(void) fprintf(__nss_debug_file, "undefined");
632 	}
633 	(void) fprintf(__nss_debug_file, "\n");
634 }
635 
636 #define	NSS_BACKOFF(n, b, t) \
637 			((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1))))
638 
639 static int
640 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
641 {
642 	if (res != NSS_TRYAGAIN && res !=  NSS_NISSERVDNS_TRYAGAIN) {
643 		if (res == NSS_SUCCESS) {
644 			__NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
645 			__NSW_UNPAUSE_ACTION(
646 				lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
647 		}
648 		return (0);
649 	}
650 
651 	if ((res == NSS_TRYAGAIN &&
652 	    lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
653 	    (res == NSS_NISSERVDNS_TRYAGAIN &&
654 	    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
655 		return (1);
656 
657 	if (res == NSS_TRYAGAIN &&
658 	    lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
659 		if (n <= lkp->max_retries)
660 			return (1);
661 		else {
662 			lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
663 			return (0);
664 		}
665 
666 	if (res == NSS_NISSERVDNS_TRYAGAIN &&
667 	    lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
668 		if (n <= lkp->max_retries)
669 			return (1);
670 		else {
671 			lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
672 			    __NSW_TRYAGAIN_PAUSED;
673 			return (0);
674 		}
675 
676 	return (0);
677 }
678 
679 nss_status_t
680 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
681 	void *search_args)
682 {
683 	nss_status_t		res = NSS_UNAVAIL;
684 	struct nss_db_state	*s;
685 	int			n_src;
686 	unsigned int		*status_vec_p = _nss_status_vec_p();
687 
688 	if (status_vec_p == NULL) {
689 		return (NSS_UNAVAIL);
690 	}
691 	*status_vec_p = 0;
692 
693 	NSS_LOCK_CHECK(rootp, initf, &s);
694 	if (s == 0) {
695 		NSS_UNLOCK(rootp);
696 		return (res);
697 	}
698 	NSS_STATE_REF_u(s);
699 
700 	for (n_src = 0;  n_src < s->max_src;  n_src++) {
701 		nss_backend_t		*be;
702 		nss_backend_op_t	funcp;
703 
704 		res = NSS_UNAVAIL;
705 		if ((be = nss_get_backend_u(&rootp, s, n_src)) != 0) {
706 			if ((funcp = NSS_LOOKUP_DBOP(be, search_fnum)) != 0) {
707 				int n_loop = 0;
708 				int no_backoff = 19;
709 				int max_backoff = 5;	/* seconds */
710 
711 				do {
712 					/*
713 					 * Backend operation may take a while;
714 					 * drop the lock so we don't serialize
715 					 * more than necessary.
716 					 */
717 					NSS_UNLOCK(rootp);
718 
719 					/* After several tries, backoff... */
720 					if (n_loop > no_backoff) {
721 					    if (__nss_debug_eng_loop > 1)
722 						(void) fprintf(__nss_debug_file,
723 						"NSS: loop: sleeping %d ...\n",
724 						    NSS_BACKOFF(n_loop,
725 						    no_backoff, max_backoff));
726 
727 					    (void) sleep(NSS_BACKOFF(n_loop,
728 						    no_backoff, max_backoff));
729 					}
730 
731 					if (__nss_debug_eng_loop)
732 						output_loop_diag_a(n_loop,
733 							s->config->dbase,
734 							s->src[n_src].lkp);
735 
736 
737 					res = (*funcp)(be, search_args);
738 					NSS_RELOCK(&rootp, s);
739 					n_loop++;
740 					if (__nss_debug_eng_loop)
741 						output_loop_diag_b(res,
742 							s->src[n_src].lkp);
743 				} while (retry_test(res, n_loop,
744 							s->src[n_src].lkp));
745 			}
746 			nss_put_backend_u(s, n_src, be);
747 		}
748 		*status_vec_p |= (1 << res);
749 		if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
750 			if (__nss_debug_eng_loop)
751 				(void) fprintf(__nss_debug_file,
752 					"NSS: '%s': return.\n",
753 					s->config->dbase);
754 			break;
755 		} else
756 			if (__nss_debug_eng_loop)
757 				(void) fprintf(__nss_debug_file,
758 					"NSS: '%s': continue ...\n",
759 					s->config->dbase);
760 	}
761 	NSS_UNREF_UNLOCK(rootp, s);
762 	return (res);
763 }
764 
765 
766 /*
767  * Start of nss_setent()/nss_getent()/nss_endent()
768  */
769 
770 /*
771  * State (here called "context") for one setent/getent.../endent sequence.
772  *   In principle there could be multiple contexts active for a single
773  *   database;  in practice, since Posix and UI have helpfully said that
774  *   getent() state is global rather than, say, per-thread or user-supplied,
775  *   we have at most one of these per nss_db_state.
776  */
777 
778 struct nss_getent_context {
779 	int			n_src;	/* >= max_src ==> end of sequence */
780 	nss_backend_t		*be;
781 	struct nss_db_state	*s;
782 	/*
783 	 * XXX ??  Should contain enough cross-check info that we can detect an
784 	 * nss_context that missed an nss_delete() or similar.
785 	 */
786 };
787 
788 static void		nss_setent_u(nss_db_root_t *,
789 				    nss_db_initf_t,
790 				    nss_getent_t *);
791 static nss_status_t	nss_getent_u(nss_db_root_t *,
792 				    nss_db_initf_t,
793 				    nss_getent_t *,
794 				    void *);
795 static void		nss_endent_u(nss_db_root_t *,
796 				    nss_db_initf_t,
797 				    nss_getent_t *);
798 
799 void
800 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
801 {
802 	if (contextpp == 0) {
803 		return;
804 	}
805 	(void) _private_mutex_lock(&contextpp->lock);
806 	nss_setent_u(rootp, initf, contextpp);
807 	(void) _private_mutex_unlock(&contextpp->lock);
808 }
809 
810 nss_status_t
811 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
812 	void *args)
813 {
814 	nss_status_t		status;
815 
816 	if (contextpp == 0) {
817 		return (NSS_UNAVAIL);
818 	}
819 	(void) _private_mutex_lock(&contextpp->lock);
820 	status = nss_getent_u(rootp, initf, contextpp, args);
821 	(void) _private_mutex_unlock(&contextpp->lock);
822 	return (status);
823 }
824 
825 void
826 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
827 {
828 	if (contextpp == 0) {
829 		return;
830 	}
831 	(void) _private_mutex_lock(&contextpp->lock);
832 	nss_endent_u(rootp, initf, contextpp);
833 	(void) _private_mutex_unlock(&contextpp->lock);
834 }
835 
836 /*
837  * Each of the _u versions of the nss interfaces assume that the context
838  * lock is held.
839  */
840 
841 static void
842 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
843 {
844 	struct nss_db_state	*s;
845 	nss_backend_t		*be;
846 	int			n_src;
847 
848 	s = contextp->s;
849 	n_src = contextp->n_src;
850 	be = contextp->be;
851 
852 	if (s != 0) {
853 		if (n_src < s->max_src && be != 0) {
854 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
855 			NSS_RELOCK(&rootp, s);
856 			nss_put_backend_u(s, n_src, be);
857 			contextp->be = 0;  /* Should be unnecessary, but hey */
858 			NSS_UNREF_UNLOCK(rootp, s);
859 		}
860 		contextp->s = 0;
861 	}
862 }
863 
864 static void
865 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
866 	nss_getent_t *contextpp)
867 {
868 	struct nss_db_state	*s;
869 	struct nss_getent_context *contextp;
870 	nss_backend_t		*be;
871 	int			n_src;
872 
873 	if ((contextp = contextpp->ctx) == 0) {
874 		if ((contextp = libc_malloc(sizeof (*contextp))) == 0) {
875 			return;
876 		}
877 		contextpp->ctx = contextp;
878 		s = 0;
879 	} else {
880 		s = contextp->s;
881 	}
882 
883 	if (s == 0) {
884 		NSS_LOCK_CHECK(rootp, initf, &s);
885 		if (s == 0) {
886 			/* Couldn't set up state, so quit */
887 			NSS_UNLOCK(rootp);
888 			/* ==== is there any danger of not having done an */
889 			/* end_iter() here, and hence of losing backends? */
890 			contextpp->ctx = 0;
891 			libc_free(contextp);
892 			return;
893 		}
894 		NSS_STATE_REF_u(s);
895 		contextp->s = s;
896 	} else {
897 		s	= contextp->s;
898 		n_src	= contextp->n_src;
899 		be	= contextp->be;
900 		if (n_src == 0 && be != 0) {
901 			/*
902 			 * Optimization:  don't do endent, don't change
903 			 *   backends, just do the setent.  Look Ma, no locks
904 			 *   (nor any context that needs updating).
905 			 */
906 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
907 			return;
908 		}
909 		if (n_src < s->max_src && be != 0) {
910 			(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
911 			NSS_RELOCK(&rootp, s);
912 			nss_put_backend_u(s, n_src, be);
913 			contextp->be = 0;	/* Play it safe */
914 		} else {
915 			NSS_RELOCK(&rootp, s);
916 		}
917 	}
918 	for (n_src = 0, be = 0; n_src < s->max_src &&
919 		(be = nss_get_backend_u(&rootp, s, n_src)) == 0; n_src++) {
920 		;
921 	}
922 	NSS_UNLOCK(rootp);
923 
924 	contextp->n_src	= n_src;
925 	contextp->be	= be;
926 
927 	if (be == 0) {
928 		/* Things are broken enough that we can't do setent/getent */
929 		nss_endent_u(rootp, initf, contextpp);
930 		return;
931 	}
932 	(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
933 }
934 
935 static nss_status_t
936 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
937 	nss_getent_t *contextpp, void *args)
938 {
939 	struct nss_db_state	*s;
940 	struct nss_getent_context *contextp;
941 	int			n_src;
942 	nss_backend_t		*be;
943 
944 	if ((contextp = contextpp->ctx) == 0) {
945 		nss_setent_u(rootp, initf, contextpp);
946 		if ((contextp = contextpp->ctx) == 0) {
947 			/* Give up */
948 			return (NSS_UNAVAIL);
949 		}
950 	}
951 	s	= contextp->s;
952 	n_src	= contextp->n_src;
953 	be	= contextp->be;
954 
955 	if (s == 0) {
956 		/*
957 		 * We've done an end_iter() and haven't done nss_setent()
958 		 * or nss_endent() since;  we should stick in this state
959 		 * until the caller invokes one of those two routines.
960 		 */
961 		return (NSS_SUCCESS);
962 	}
963 
964 	while (n_src < s->max_src) {
965 		nss_status_t res;
966 
967 		if (be == 0) {
968 			/* If it's null it's a bug, but let's play safe */
969 			res = NSS_UNAVAIL;
970 		} else {
971 			res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
972 		}
973 
974 		if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
975 			if (res != __NSW_SUCCESS) {
976 				end_iter_u(rootp, contextp);
977 			}
978 			return (res);
979 		}
980 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
981 		NSS_RELOCK(&rootp, s);
982 		nss_put_backend_u(s, n_src, be);
983 		do {
984 			n_src++;
985 		} while (n_src < s->max_src &&
986 			(be = nss_get_backend_u(&rootp, s, n_src)) == 0);
987 		if (be == 0) {
988 			/*
989 			 * This is the case where we failed to get the backend
990 			 * for the last source. We exhausted all sources.
991 			 */
992 			NSS_UNLOCK(rootp);
993 			nss_endent_u(rootp, initf, contextpp);
994 			nss_delete(rootp);
995 			return (NSS_SUCCESS);
996 		}
997 		NSS_UNLOCK(rootp);
998 		contextp->n_src	= n_src;
999 		contextp->be	= be;
1000 		(void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1001 	}
1002 	/* Got to the end of the sources without finding another entry */
1003 	end_iter_u(rootp, contextp);
1004 	return (NSS_SUCCESS);
1005 	/* success is either a successful entry or end of the sources */
1006 }
1007 
1008 /*ARGSUSED*/
1009 static void
1010 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1011 	nss_getent_t *contextpp)
1012 {
1013 	struct nss_getent_context *contextp;
1014 
1015 	if ((contextp = contextpp->ctx) == 0) {
1016 		/* nss_endent() on an unused context is a no-op */
1017 		return;
1018 	}
1019 	/*
1020 	 * Existing code (BSD, SunOS) works in such a way that getXXXent()
1021 	 *   following an endXXXent() behaves as though the user had invoked
1022 	 *   setXXXent(), i.e. it iterates properly from the beginning.
1023 	 * We'd better not break this, so our choices are
1024 	 *	(1) leave the context structure around, and do nss_setent or
1025 	 *	    something equivalent,
1026 	 *   or	(2) free the context completely, and rely on the code in
1027 	 *	    nss_getent() that makes getXXXent() do the right thing
1028 	 *	    even without a preceding setXXXent().
1029 	 * The code below does (2), which frees up resources nicely but will
1030 	 * cost more if the user then does more getXXXent() operations.
1031 	 * Moral:  for efficiency, don't call endXXXent() prematurely.
1032 	 */
1033 	end_iter_u(rootp, contextp);
1034 	libc_free(contextp);
1035 	contextpp->ctx = 0;
1036 }
1037