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