xref: /illumos-gate/usr/src/cmd/nscd/nscd_nswconfig.c (revision 14c2ce9048cb9407fdf675f7ef76081a1dd5e715)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <nss_common.h>
27 #include <dlfcn.h>
28 #include <alloca.h>
29 
30 #include <stdlib.h>
31 #include <libscf_priv.h>
32 #include <string.h>
33 #include <assert.h>
34 #include "nscd_switch.h"
35 #include "nscd_log.h"
36 #include "nscd_db.h"
37 
38 /*
39  * _nscd_nss_finders is used to replace the nss_default_finders in libc
40  * to allow nscd to have more control over the dl handles when using
41  * dlsym to get the address of the nss backend instance constructors
42  */
43 static nss_backend_constr_t _nscd_per_src_lookup(void *,
44 	const char *, const char *, void **);
45 static void _nscd_per_src_delete(void *, nss_backend_constr_t);
46 
47 static nss_backend_finder_t _nscd_per_src = {
48 	_nscd_per_src_lookup,
49 	_nscd_per_src_delete,
50 	0,
51 	0 };
52 
53 nss_backend_finder_t *_nscd_nss_finders = &_nscd_per_src;
54 
55 /*
56  * nscd database for each source. It contains backend
57  * info (nscd_be_info_t) for each naming database.
58  * Protected by nscd_src_backend_db_lock.
59  */
60 nscd_db_t	***nscd_src_backend_db;
61 int		*nscd_src_backend_db_loaded;
62 static		rwlock_t nscd_src_backend_db_lock = DEFAULTRWLOCK;
63 
64 /*
65  * nsswitch config monitored by nscd. Protected by
66  * readers/writer lock nscd_nsw_config_lock
67  */
68 nscd_nsw_config_t ***nscd_nsw_config;
69 static rwlock_t nscd_nsw_config_lock = DEFAULTRWLOCK;
70 
71 /*
72  * nsswitch source index/name array
73  * (allow 32 foreign nsswitch sources/backends)
74  */
75 #define		NSCD_NUM_SRC_FOREIGN 32
76 nscd_cfg_id_t	*_nscd_cfg_nsw_src_all;
77 int		_nscd_cfg_num_nsw_src_all;
78 
79 static void
80 free_nscd_nsw_config(
81 	nscd_acc_data_t		*data)
82 {
83 
84 	nscd_nsw_config_t	*nsw_cfg = *(nscd_nsw_config_t **)data;
85 	char			*me = "free_nscd_nsw_config";
86 
87 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
88 	(me, "freeing nscd nsw config %p \n", nsw_cfg);
89 	if (nsw_cfg == NULL)
90 		return;
91 
92 	if (nsw_cfg->db_name != NULL)
93 		free(nsw_cfg->db_name);
94 	if (nsw_cfg->nsw_cfg_str != NULL)
95 		free(nsw_cfg->nsw_cfg_str);
96 	if (nsw_cfg->nsw_config != NULL)
97 		(void) __nsw_freeconfig_v1(nsw_cfg->nsw_config);
98 	if (nsw_cfg->src_idx != NULL)
99 		free(nsw_cfg->src_idx);
100 
101 	free(nsw_cfg);
102 }
103 
104 
105 void
106 _nscd_free_nsw_config(
107 	nscd_nsw_config_t *nswcfg)
108 {
109 	free_nscd_nsw_config((nscd_acc_data_t *)&nswcfg);
110 }
111 
112 void
113 _nscd_free_all_nsw_config()
114 {
115 
116 	nscd_nsw_config_t	**nsw_cfg;
117 	int			i;
118 	char			*me = "_nscd_free_all_nsw_config";
119 
120 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
121 	(me, "freeing all nscd nsw config \n");
122 
123 	(void) rw_wrlock(&nscd_nsw_config_lock);
124 	for (i = 0; i < NSCD_NUM_DB; i++) {
125 
126 		if ((nsw_cfg = nscd_nsw_config[i]) == NULL)
127 			continue;
128 
129 		nscd_nsw_config[i] = (nscd_nsw_config_t **)_nscd_set(
130 		    (nscd_acc_data_t *)nsw_cfg, NULL);
131 	}
132 	(void) rw_unlock(&nscd_nsw_config_lock);
133 }
134 
135 
136 static void
137 free_nsw_backend_info_db(nscd_acc_data_t *data)
138 {
139 
140 	nscd_db_t	*db = *(nscd_db_t **)data;
141 	char		*me = "free_nsw_backend_info_db";
142 
143 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
144 	(me, "freeing nsw backend info db %p\n", db);
145 
146 	if (db == NULL)
147 		return;
148 
149 	_nscd_free_db(db);
150 
151 }
152 
153 void
154 _nscd_free_all_nsw_backend_info_db()
155 {
156 
157 	nscd_db_t	**db;
158 	int		i;
159 	char		*me = " _nscd_free_all_nsw_backend_info_db";
160 
161 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
162 	(me, "freeing all nsw backend info db\n");
163 
164 	(void) rw_wrlock(&nscd_src_backend_db_lock);
165 	for (i = 0; i < NSCD_NUM_SRC; i++) {
166 
167 		if ((db = nscd_src_backend_db[i]) == NULL)
168 			continue;
169 
170 		nscd_src_backend_db[i] = (nscd_db_t **)_nscd_set(
171 		    (nscd_acc_data_t *)db, NULL);
172 		nscd_src_backend_db_loaded[i] = 0;
173 	}
174 	(void) rw_unlock(&nscd_src_backend_db_lock);
175 }
176 
177 /*
178  * Populate the backend info db for the 'NSCD_NSW_SRC_NAME(srci)'
179  * source.  Create one entry for each source/database pair
180  * (e.g., ldap:passwd, nis:hosts, etc).
181  */
182 static nscd_rc_t
183 _nscd_populate_nsw_backend_info_db(int srci)
184 {
185 	nscd_be_info_t		be_info, *bi;
186 	nss_backend_finder_t	*bf;
187 	nscd_nsw_config_t	*nsw_cfg;
188 	int			i, size;
189 	nscd_db_entry_t		*db_entry;
190 	char			*src = NSCD_NSW_SRC_NAME(srci);
191 	const char		*dbn;
192 	char			*me = "_nscd_populate_nsw_backend_info_db";
193 	void			*handle = NULL;
194 	nss_backend_constr_t	c;
195 	void			*be_version = &_nscd_be_version;
196 
197 	/* get the version number of the backend (if available) */
198 	if (srci >= _nscd_cfg_num_nsw_src) { /* a foreign backend */
199 		c = _nscd_per_src_lookup(handle, NULL, src, &handle);
200 		if (c == NULL)
201 			be_version = NULL;
202 		else
203 			be_version = (void *)c;
204 
205 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
206 		(me, "foreign backend: _nss_%s_version = %p ", src, be_version);
207 	}
208 
209 	for (i = 0; i < NSCD_NUM_DB; i++) {
210 
211 		/* no nsswitch configuration, no backend info db population */
212 		if (nscd_nsw_config[i] == NULL || *nscd_nsw_config[i] == NULL)
213 			continue;
214 
215 		nsw_cfg = *nscd_nsw_config[i];
216 		dbn = NSCD_NSW_DB_NAME(i);
217 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
218 		(me, "adding backend info for <%s : %s>\n", src, dbn);
219 
220 		(void) memset(&be_info, 0, sizeof (be_info));
221 
222 		for (bf = nsw_cfg->fe_params.finders;  bf != 0; bf = bf->next) {
223 
224 			c = (*bf->lookup)(handle, dbn, src, &handle);
225 
226 			if (c != 0) {
227 				be_info.be_constr = c;
228 				be_info.finder = bf;
229 				be_info.finder_priv = handle;
230 				be_info.be_version = be_version;
231 				break;
232 			}
233 		}
234 		if (be_info.be_constr == NULL) {
235 			/*
236 			 * Couldn't find the backend anywhere.
237 			 * This is fine, some backend just don't
238 			 * support certain databases.
239 			 */
240 			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
241 			(me, "unable to find backend info "
242 			    "for <%s : %s>\n", src, dbn);
243 		}
244 
245 		size = sizeof (nscd_be_info_t);
246 
247 		db_entry = _nscd_alloc_db_entry(NSCD_DATA_BACKEND_INFO,
248 		    dbn, size, 1, 1);
249 
250 		if (db_entry == NULL) {
251 			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
252 			(me, "unable to allocate db entry for "
253 			    "<%s : %s>\n", src, dbn);
254 			return (NSCD_NO_MEMORY);
255 		}
256 
257 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
258 		(me, "adding be db entry %p for <%s : %s> to db %p: "
259 		    "constr = %p\n", db_entry, src, dbn,
260 		    *nscd_src_backend_db[srci], be_info.be_constr);
261 
262 		bi = (nscd_be_info_t *)*(db_entry->data_array);
263 		*bi = be_info;
264 
265 		(void) _nscd_wrlock((nscd_acc_data_t *)
266 		    nscd_src_backend_db[srci]);
267 		nscd_src_backend_db_loaded[srci] = 1;
268 		(void) _nscd_add_db_entry(*nscd_src_backend_db[srci],
269 		    dbn, db_entry, NSCD_ADD_DB_ENTRY_LAST);
270 		(void) _nscd_rw_unlock((nscd_acc_data_t *)
271 		    nscd_src_backend_db[srci]);
272 	}
273 
274 	return (NSCD_SUCCESS);
275 }
276 
277 /*
278  * create data structures (used by the switch engine) based
279  * on the input switch policy configuration and database
280  * name and indexes
281  */
282 nscd_rc_t
283 _nscd_create_sw_struct(
284 	int				dbi,
285 	int				compat_basei,
286 	const char			*dbn,
287 	const char			*cfgstr,
288 	void				*swcfgv1,
289 	nscd_nsw_params_t		*params)
290 {
291 	char				*me = "_nscd_create_sw_struct";
292 	nscd_rc_t			rc = NSCD_SUCCESS;
293 	nscd_nsw_config_t		*nsw_cfg = NULL;
294 	nscd_nsw_config_t		**nsw_cfg_p = NULL;
295 	struct __nsw_switchconfig_v1	*swcfg = NULL;
296 	struct __nsw_lookup_v1		*lkp;
297 	enum __nsw_parse_err		err;
298 	int				maxsrc;
299 	int				*src_idx_a = NULL;
300 	int				j, k;
301 
302 	/*
303 	 * if the nsw config string has been parsed into
304 	 * a struct __nsw_switchconfig_v1, use it. If not,
305 	 * create the struct.
306 	 */
307 	if (swcfgv1 != NULL)
308 		swcfg = (struct __nsw_switchconfig_v1 *)swcfgv1;
309 	else {
310 		char	*cstr;
311 
312 		cstr = strdup(cfgstr);
313 		if (cstr == NULL)
314 			return (NSCD_NO_MEMORY);
315 
316 		/*
317 		 * parse the nsw config string and create
318 		 * a struct __nsw_switchconfig_v1
319 		 */
320 		swcfg = _nsw_getoneconfig_v1(dbn, cstr, &err);
321 		free(cstr);
322 		if (swcfg == NULL) {
323 			rc = NSCD_CFG_SYNTAX_ERROR;
324 			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
325 			(me, "error: unable to process nsw config string\n");
326 			goto error_exit;
327 		}
328 	}
329 
330 	/* allocate the space for a nscd_nsw_config_t */
331 	nsw_cfg = calloc(1, sizeof (nscd_nsw_config_t));
332 	if (nsw_cfg == NULL) {
333 		rc = NSCD_NO_MEMORY;
334 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
335 		(me, "error: unable to allocate an nscd_nsw_config_t\n");
336 		goto error_exit;
337 	}
338 
339 	/* need to know how many backends (sources) */
340 	maxsrc = swcfg->num_lookups;
341 	nsw_cfg->max_src = maxsrc;
342 
343 	/*
344 	 * allocate an array to store the index for each
345 	 * backend (source)
346 	 */
347 	src_idx_a = calloc(1, maxsrc * sizeof (int));
348 	if (src_idx_a == NULL) {
349 		rc = NSCD_NO_MEMORY;
350 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
351 		(me, "error: unable to allocate an array for source index\n");
352 		goto error_exit;
353 	}
354 
355 	/*
356 	 * set the index for each backend (source)
357 	 */
358 	lkp = swcfg->lookups;
359 	for (j = 0; j < maxsrc; j++) {
360 		char *usrc;
361 
362 		for (k = 0; k < NSCD_NUM_SRC && NSCD_NSW_SRC_NAME(k) != NULL &&
363 		    strcmp(lkp->service_name, NSCD_NSW_SRC_NAME(k)) != 0;
364 		    k++) {
365 			/* empty */
366 		}
367 
368 		if (k < NSCD_NUM_SRC && nscd_src_backend_db_loaded[k] == 0) {
369 			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
370 			(me, "unknown nsw source name %s\n", lkp->service_name);
371 			usrc = strdup(lkp->service_name);
372 			if (usrc == NULL) {
373 				rc = NSCD_NO_MEMORY;
374 				_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
375 				(me, "unable to strdup() source name\n");
376 				goto error_exit;
377 			}
378 			NSCD_NSW_SRC_NAME(k) = usrc;
379 
380 			rc = _nscd_populate_nsw_backend_info_db(k);
381 			if (rc != NSCD_SUCCESS) {
382 				free(usrc);
383 				NSCD_NSW_SRC_NAME(k) = NULL;
384 				goto error_exit;
385 			}
386 		} else if (NSCD_NSW_SRC_NAME(k) == NULL) {
387 			/*
388 			 * number of user-defined source exceeded
389 			 */
390 			rc = NSCD_CFG_SYNTAX_ERROR;
391 			_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
392 			(me, "error: number of user_defined source exceeded\n");
393 
394 			goto error_exit;
395 		}
396 
397 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
398 		(me, "setting source index array [%d] = %d (%s)\n",
399 		    j, k, lkp->service_name);
400 
401 		src_idx_a[j] = k;
402 
403 		lkp = lkp->next;
404 		if (lkp == NULL) break;
405 
406 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
407 		(me, "number of nsw sources = %d\n", nsw_cfg->max_src);
408 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
409 		(me, "next nsw source is %s\n", lkp->service_name);
410 	}
411 
412 	/* set it up to reference count the switch policy config */
413 	nsw_cfg_p = (nscd_nsw_config_t **)_nscd_alloc(NSCD_DATA_NSW_CONFIG,
414 	    sizeof (nscd_nsw_config_t **), free_nscd_nsw_config,
415 	    NSCD_ALLOC_RWLOCK);
416 
417 	if (nsw_cfg_p == NULL) {
418 		rc = NSCD_NO_MEMORY;
419 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
420 		(me, "unable to allocate a new nsw config DB\n");
421 		goto error_exit;
422 	}
423 	*nsw_cfg_p = nsw_cfg;
424 
425 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
426 	(me, "new nsw config DB %p allocated\n", nsw_cfg_p);
427 
428 	/* save all the data in the new nscd_nsw_config_t */
429 	nsw_cfg->db_name = strdup(dbn);
430 	nsw_cfg->nsw_cfg_str = strdup(cfgstr);
431 	if (nsw_cfg->db_name == NULL || nsw_cfg->nsw_cfg_str == NULL) {
432 		rc = NSCD_NO_MEMORY;
433 		goto error_exit;
434 	}
435 
436 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
437 	(me, "switch policy \"%s\" for database is \"%s\"\n",
438 	    nsw_cfg->db_name, nsw_cfg->nsw_cfg_str);
439 
440 	nsw_cfg->nsw_config = swcfg;
441 	nsw_cfg->src_idx = src_idx_a;
442 
443 	/*
444 	 * set default frontend params and if necessary call initf()
445 	 * to initialize or override
446 	 */
447 	nsw_cfg->fe_params.max_active_per_src = 10;
448 	nsw_cfg->fe_params.max_dormant_per_src = 1;
449 	nsw_cfg->fe_params.finders = _nscd_nss_finders;
450 	if (params != NULL) {
451 		nsw_cfg->fe_params = params->p;
452 
453 		if (params->p.flags & NSS_USE_DEFAULT_CONFIG) {
454 			params->nswcfg = nsw_cfg_p;
455 			/*
456 			 * this nsw_cfg is not meant to last long, no need
457 			 * to set up the nsw state and getent bases, just
458 			 * exit with NSCD_SUCCESS
459 			 */
460 			nsw_cfg->nobase = 1;
461 			goto error_exit;
462 		}
463 	} else
464 		(void) (nscd_nss_db_initf[dbi])(&nsw_cfg->fe_params);
465 
466 	/*
467 	 * also create a new nsw state base
468 	 */
469 	if ((rc = _nscd_init_nsw_state_base(dbi, compat_basei, 1)) !=
470 	    NSCD_SUCCESS) {
471 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
472 		(me, "unable to initialize a nsw state base(%d)\n", dbi);
473 		goto error_exit;
474 	}
475 
476 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
477 	(me, "new nsw state base(%d) %p created\n", dbi,
478 	    nscd_nsw_state_base[dbi]);
479 
480 	/*
481 	 * also create a new getent context base
482 	 */
483 	if ((rc = _nscd_init_getent_ctx_base(dbi, 1)) != NSCD_SUCCESS) {
484 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
485 		(me, "unable to initialize a getent context base(%d)\n", dbi);
486 		goto error_exit;
487 	}
488 
489 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
490 	(me, "new getent context base(%d) %p created\n", dbi,
491 	    nscd_getent_ctx_base[dbi]);
492 
493 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
494 	(me, "new nsw config created (database = %s, "
495 	"config = %s)\n", dbn, cfgstr);
496 
497 	/*
498 	 * Activate the new nscd_nsw_config_t and make it the
499 	 * current nsswitch config. The old one pointed to by
500 	 * nscd_nsw_config[dbi] will either be destroyed
501 	 * immediately or left on the side line (and be
502 	 * destroyed eventually). __nscd_set() will set the
503 	 * associated flags to make it happen.
504 	 */
505 	nscd_nsw_config[dbi] = (nscd_nsw_config_t **)_nscd_set(
506 	    (nscd_acc_data_t *)nscd_nsw_config[dbi],
507 	    (nscd_acc_data_t *)nsw_cfg_p);
508 
509 	error_exit:
510 
511 	if (rc != NSCD_SUCCESS) {
512 
513 		if (swcfgv1 == NULL && swcfg != NULL)
514 			(void) __nsw_freeconfig_v1(swcfg);
515 		if (src_idx_a != NULL)
516 			free(src_idx_a);
517 		if (nsw_cfg_p)
518 			free(nsw_cfg_p);
519 		if (nsw_cfg != NULL) {
520 			if (nsw_cfg->db_name != NULL)
521 				free(nsw_cfg->db_name);
522 			if (nsw_cfg->nsw_cfg_str != NULL)
523 				free(nsw_cfg->nsw_cfg_str);
524 			free(nsw_cfg);
525 		}
526 
527 		return (rc);
528 	} else
529 		return (NSCD_SUCCESS);
530 }
531 
532 static nscd_rc_t
533 create_nsw_config(int dbi)
534 {
535 
536 	nscd_nsw_config_t	*nsw_cfg = NULL;
537 	nscd_nsw_config_t	**nsw_cfg_p = NULL;
538 	char			*me = "create_nsw_config";
539 
540 	/*
541 	 * Allocate a pointer space for saving a pointer to a
542 	 * nscd_nsw_config_t. _nscd_alloc() will also create
543 	 * a nscd_access_t header with a rwlock_t and mutex_t
544 	 * for controlling later access to the data pointed
545 	 * to by the pointer.
546 	 */
547 	nsw_cfg_p = (nscd_nsw_config_t **)_nscd_alloc(NSCD_DATA_NSW_CONFIG,
548 	    sizeof (nscd_nsw_config_t **), free_nscd_nsw_config,
549 	    NSCD_ALLOC_RWLOCK | NSCD_ALLOC_MUTEX);
550 
551 	if (nsw_cfg_p == NULL) {
552 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
553 		(me, "unable to allocate a space for a nsw config pointer\n");
554 		return (NSCD_NO_MEMORY);
555 	}
556 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
557 		(me, "addr for saving nsw config pointer = %p\n", nsw_cfg_p);
558 
559 	/*
560 	 * If pseudo-databases (initf function not defined),
561 	 * no need to create a nscd_nsw_config_t yet,
562 	 * so put a NULL pointer in the pointer space.
563 	 */
564 	if (nscd_nss_db_initf[dbi] == NULL) {
565 		*nsw_cfg_p = NULL;
566 		nscd_nsw_config[dbi] = (nscd_nsw_config_t **)_nscd_set(
567 		    (nscd_acc_data_t *)nscd_nsw_config[dbi],
568 		    (nscd_acc_data_t *)nsw_cfg_p);
569 
570 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
571 		(me, "pointer to nsw config has been set to NULL\n");
572 
573 		return (NSCD_SUCCESS);
574 	}
575 
576 	/* allocate the space for a nscd_nsw_config_t */
577 	nsw_cfg = calloc(1, sizeof (nscd_nsw_config_t));
578 	if (nsw_cfg == NULL) {
579 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
580 		(me, "unable to allocate a nsw config structure\n");
581 		return (NSCD_NO_MEMORY);
582 	}
583 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
584 	(me, "nsw config structure %pallocated\n", nsw_cfg);
585 
586 	nsw_cfg->db_name = strdup(NSCD_NSW_DB_NAME(dbi));
587 	if (nsw_cfg->db_name == NULL) {
588 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
589 		(me, "unable to strdup the db name\n");
590 		return (NSCD_NO_MEMORY);
591 	}
592 
593 	/*
594 	 * set default frontend params and then call initf()
595 	 * to initialize or override
596 	 */
597 	nsw_cfg->fe_params.max_active_per_src = 10;
598 	nsw_cfg->fe_params.max_dormant_per_src = 1;
599 	nsw_cfg->fe_params.finders = _nscd_nss_finders;
600 	(void) (nscd_nss_db_initf[dbi])(&nsw_cfg->fe_params);
601 
602 	/*
603 	 * activate the new nscd_nsw_config_t
604 	 */
605 	*nsw_cfg_p = nsw_cfg;
606 	nscd_nsw_config[dbi] = (nscd_nsw_config_t **)_nscd_set(
607 	    (nscd_acc_data_t *)nscd_nsw_config[dbi],
608 	    (nscd_acc_data_t *)nsw_cfg_p);
609 
610 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
611 	(me, "nsw config %p activated\n", nsw_cfg);
612 
613 	return (NSCD_SUCCESS);
614 }
615 
616 nscd_rc_t
617 _nscd_init_all_nsw_config(void)
618 {
619 	nscd_rc_t	rc;
620 	int		i;
621 	char		*me = "_nscd_init_all_nsw_config";
622 
623 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
624 	(me, "initializing all nsw config\n");
625 
626 	for (i = 0; i < NSCD_NUM_DB; i++) {
627 		if ((rc = create_nsw_config(i)) != NSCD_SUCCESS)
628 			return (rc);
629 	}
630 
631 	return (NSCD_SUCCESS);
632 }
633 
634 static nscd_rc_t
635 init_nsw_be_info_db(int srci)
636 {
637 	nscd_db_t	*ret, **db_p;
638 	char		*me = "init_nsw_be_info_db";
639 
640 	ret = _nscd_alloc_db(NSCD_DB_SIZE_SMALL);
641 
642 	if (ret == NULL) {
643 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
644 		(me, "unable to allocate a nsw be info database\n");
645 		return (NSCD_NO_MEMORY);
646 	}
647 
648 	/* set up to reference count the backend info db */
649 	db_p = (nscd_db_t **)_nscd_alloc(NSCD_DATA_BACKEND_INFO_DB,
650 	    sizeof (nscd_db_t **), free_nsw_backend_info_db,
651 	    NSCD_ALLOC_RWLOCK);
652 
653 	if (db_p == NULL) {
654 		_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_ERROR)
655 		(me, "unable to allocate the pointer to the nsw "
656 		"be info database\n");
657 		return (NSCD_NO_MEMORY);
658 	}
659 
660 	*db_p = ret;
661 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
662 	(me, "backend database (db_p = %p, db = %p)\n", db_p, *db_p);
663 
664 	nscd_src_backend_db[srci] = (nscd_db_t **)_nscd_set(
665 	    (nscd_acc_data_t *)nscd_src_backend_db[srci],
666 	    (nscd_acc_data_t *)db_p);
667 
668 	return (NSCD_SUCCESS);
669 }
670 
671 nscd_rc_t
672 _nscd_init_all_nsw_be_info_db(void)
673 {
674 
675 	int		i;
676 	nscd_rc_t	rc;
677 	char		*me = "_nscd_init_all_nsw_be_info_db";
678 
679 	_NSCD_LOG(NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
680 	(me, "initializing all nsw be info databases\n");
681 
682 	for (i = 0; i < NSCD_NUM_SRC; i++) {
683 		if ((rc = init_nsw_be_info_db(i)) != NSCD_SUCCESS)
684 			return (rc);
685 	}
686 
687 	return (NSCD_SUCCESS);
688 }
689 
690 
691 nscd_rc_t
692 _nscd_alloc_nsw_config()
693 {
694 	nscd_nsw_config = calloc(NSCD_NUM_DB, sizeof (nscd_nsw_config_t **));
695 	if (nscd_nsw_config == NULL)
696 		return (NSCD_NO_MEMORY);
697 
698 	return (NSCD_SUCCESS);
699 }
700 
701 nscd_rc_t
702 _nscd_alloc_nsw_be_info_db()
703 {
704 	int	i;
705 
706 	_nscd_cfg_num_nsw_src_all = _nscd_cfg_num_nsw_src +
707 	    NSCD_NUM_SRC_FOREIGN;
708 	nscd_src_backend_db = calloc(NSCD_NUM_SRC, sizeof (nscd_db_t **));
709 	if (nscd_src_backend_db == NULL)
710 		return (NSCD_NO_MEMORY);
711 	nscd_src_backend_db_loaded = calloc(NSCD_NUM_SRC, sizeof (int));
712 	if (nscd_src_backend_db_loaded == NULL) {
713 		free(nscd_src_backend_db);
714 		return (NSCD_NO_MEMORY);
715 	}
716 
717 	/* also allocate/init the nsswitch source index/name array */
718 	_nscd_cfg_nsw_src_all = (nscd_cfg_id_t *)calloc(
719 	    _nscd_cfg_num_nsw_src_all + 1, sizeof (nscd_cfg_id_t));
720 	for (i = 0; i < _nscd_cfg_num_nsw_src_all + 1; i++)
721 		(_nscd_cfg_nsw_src_all + i)->index = -1;
722 
723 	(void) memcpy(_nscd_cfg_nsw_src_all, _nscd_cfg_nsw_src,
724 	    _nscd_cfg_num_nsw_src * sizeof (nscd_cfg_id_t));
725 	return (NSCD_SUCCESS);
726 }
727 
728 nscd_rc_t
729 _nscd_populate_nsw_backend_info()
730 {
731 	int		i;
732 	nscd_rc_t	rc;
733 
734 	for (i = 0; i < NSCD_NUM_SRC; i++) {
735 		if (NSCD_NSW_SRC_NAME(i) == NULL)
736 			continue;
737 		rc = _nscd_populate_nsw_backend_info_db(i);
738 		if (rc != NSCD_SUCCESS)
739 		return (rc);
740 	}
741 
742 	return (NSCD_SUCCESS);
743 }
744 
745 /*
746  * The following defines nscd's own lookup and delete functions
747  * that are to be stored in nss_backend_finder_t which is used
748  * by _nscd_populate_nsw_backend_info_db() to initialize the
749  * various nss backend instances
750  */
751 
752 static const int  dlopen_version  = 1;
753 #ifndef NSS_DLOPEN_FORMAT
754 #define	NSS_DLOPEN_FORMAT "nss_%s.so.%d"
755 #endif
756 #ifndef NSS_DLSYM_FORMAT
757 #define	NSS_DLSYM_FORMAT   "_nss_%s_%s_constr"
758 #define	NSS_DLSYM_FORMAT_V "_nss_%s_version"
759 #endif
760 static const char dlopen_format[] = NSS_DLOPEN_FORMAT;
761 static const char dlsym_format [] = NSS_DLSYM_FORMAT;
762 static const char dlsym_format_v [] = NSS_DLSYM_FORMAT_V;
763 static const size_t  format_maxlen   = sizeof (dlsym_format);
764 
765 /*ARGSUSED*/
766 static nss_backend_constr_t
767 _nscd_per_src_lookup(void *handle, const char *db_name, const char *src_name,
768 	void **delete_privp)
769 {
770 	char			*name;
771 	void			*dlhandle;
772 	void			*sym;
773 	size_t			len;
774 	nss_backend_constr_t	res = NULL;
775 
776 	len = format_maxlen + strlen(src_name);
777 	if (db_name != NULL)
778 		len += strlen(db_name);
779 	name = alloca(len);
780 	dlhandle = handle;
781 	if ((dlhandle = handle) == NULL) {
782 		(void) sprintf(name, dlopen_format, src_name, dlopen_version);
783 		dlhandle = dlopen(name, RTLD_LAZY);
784 	}
785 
786 	if (dlhandle != NULL) {
787 		if (db_name != NULL)
788 			(void) sprintf(name, dlsym_format, src_name, db_name);
789 		else
790 			(void) sprintf(name, dlsym_format_v, src_name);
791 		if ((sym = dlsym(dlhandle, name)) == 0) {
792 			if (handle == NULL)
793 				(void) dlclose(dlhandle);
794 		} else {
795 			*delete_privp = dlhandle;
796 			res = (nss_backend_constr_t)sym;
797 		}
798 	}
799 	return (res);
800 }
801 
802 /*ARGSUSED*/
803 static void
804 _nscd_per_src_delete(void *delete_priv, nss_backend_constr_t dummy)
805 {
806 	(void) dlclose(delete_priv);
807 }
808