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