xref: /illumos-gate/usr/src/cmd/nscd/nscd_frontend.c (revision 567facca9b7f5b4eedfa239dba85c53bdd76aade)
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 2007 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 #include <stdlib.h>
29 #include <alloca.h>
30 #include <signal.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <pthread.h>
34 #include <time.h>
35 #include <errno.h>
36 #include <door.h>
37 #include <zone.h>
38 #include <resolv.h>
39 #include <sys/socket.h>
40 #include <net/route.h>
41 #include <string.h>
42 #include <net/if.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include "nscd_common.h"
46 #include "nscd_door.h"
47 #include "nscd_config.h"
48 #include "nscd_switch.h"
49 #include "nscd_log.h"
50 #include "nscd_selfcred.h"
51 #include "nscd_frontend.h"
52 #include "nscd_admin.h"
53 
54 static void rts_mon(void);
55 static void keep_open_dns_socket(void);
56 
57 extern nsc_ctx_t *cache_ctx_p[];
58 
59 /*
60  * Current active Configuration data for the frontend component
61  */
62 static nscd_cfg_global_frontend_t	frontend_cfg_g;
63 static nscd_cfg_frontend_t		*frontend_cfg;
64 
65 static int	max_servers = 0;
66 static int	max_servers_set = 0;
67 static int	per_user_is_on = 1;
68 
69 static char	*main_execname;
70 static char	**main_argv;
71 extern int	_whoami;
72 extern long	activity;
73 extern mutex_t	activity_lock;
74 
75 static sema_t	common_sema;
76 
77 static thread_key_t	lookup_state_key;
78 static mutex_t		create_lock = DEFAULTMUTEX;
79 static int		num_servers = 0;
80 static thread_key_t	server_key;
81 
82 /*
83  * Bind a TSD value to a server thread. This enables the destructor to
84  * be called if/when this thread exits.  This would be a programming
85  * error, but better safe than sorry.
86  */
87 /*ARGSUSED*/
88 static void *
89 server_tsd_bind(void *arg)
90 {
91 	static void *value = 0;
92 
93 	/* disable cancellation to avoid hangs if server threads disappear */
94 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
95 	(void) thr_setspecific(server_key, value);
96 	(void) door_return(NULL, 0, NULL, 0);
97 
98 	/* make lint happy */
99 	return (NULL);
100 }
101 
102 /*
103  * Server threads are created here.
104  */
105 /*ARGSUSED*/
106 static void
107 server_create(door_info_t *dip)
108 {
109 	(void) mutex_lock(&create_lock);
110 	if (++num_servers > max_servers) {
111 		num_servers--;
112 		(void) mutex_unlock(&create_lock);
113 		return;
114 	}
115 	(void) mutex_unlock(&create_lock);
116 	(void) thr_create(NULL, 0, server_tsd_bind, NULL,
117 			THR_BOUND|THR_DETACHED, NULL);
118 }
119 
120 /*
121  * Server thread are destroyed here
122  */
123 /*ARGSUSED*/
124 static void
125 server_destroy(void *arg)
126 {
127 	(void) mutex_lock(&create_lock);
128 	num_servers--;
129 	(void) mutex_unlock(&create_lock);
130 }
131 
132 /*
133  * get clearance
134  */
135 int
136 _nscd_get_clearance(sema_t *sema) {
137 	if (sema_trywait(&common_sema) == 0) {
138 		(void) thr_setspecific(lookup_state_key, NULL);
139 		return (0);
140 	}
141 
142 	if (sema_trywait(sema) == 0) {
143 		(void) thr_setspecific(lookup_state_key, (void*)1);
144 		return (0);
145 	}
146 
147 	return (1);
148 }
149 
150 
151 /*
152  * release clearance
153  */
154 int
155 _nscd_release_clearance(sema_t *sema) {
156 	int	which;
157 
158 	(void) thr_getspecific(lookup_state_key, (void**)&which);
159 	if (which == 0) /* from common pool */ {
160 		(void) sema_post(&common_sema);
161 		return (0);
162 	}
163 
164 	(void) sema_post(sema);
165 	return (1);
166 }
167 
168 static void
169 dozip(void)
170 {
171 	/* not much here */
172 }
173 
174 static void
175 restart_if_cfgfile_changed()
176 {
177 
178 	static mutex_t	nsswitch_lock = DEFAULTMUTEX;
179 	static time_t	last_nsswitch_check = 0;
180 	static time_t	last_nsswitch_modified = 0;
181 	static time_t	last_resolv_modified = 0;
182 	time_t		now = time(NULL);
183 	char		*me = "restart_if_cfgfile_changed";
184 
185 	if (now - last_nsswitch_check <= _NSC_FILE_CHECK_TIME)
186 		return;
187 
188 	(void) mutex_lock(&nsswitch_lock);
189 
190 	if (now - last_nsswitch_check > _NSC_FILE_CHECK_TIME) {
191 		struct stat nss_buf;
192 		struct stat res_buf;
193 
194 		last_nsswitch_check = now;
195 
196 		(void) mutex_unlock(&nsswitch_lock); /* let others continue */
197 
198 		/*
199 		 *  This code keeps us from statting resolv.conf
200 		 *  if it doesn't exist, yet prevents us from ignoring
201 		 *  it if it happens to disappear later on for a bit.
202 		 */
203 
204 		if (last_resolv_modified >= 0) {
205 			if (stat("/etc/resolv.conf", &res_buf) < 0) {
206 				if (last_resolv_modified == 0)
207 				    last_resolv_modified = -1;
208 				else
209 				    res_buf.st_mtime = last_resolv_modified;
210 			} else if (last_resolv_modified == 0) {
211 			    last_resolv_modified = res_buf.st_mtime;
212 			}
213 		}
214 
215 		if (stat("/etc/nsswitch.conf", &nss_buf) < 0) {
216 
217 			/*EMPTY*/;
218 
219 		} else if (last_nsswitch_modified == 0) {
220 
221 			last_nsswitch_modified = nss_buf.st_mtime;
222 
223 		} else if ((last_nsswitch_modified < nss_buf.st_mtime) ||
224 		    ((last_resolv_modified > 0) &&
225 		    (last_resolv_modified < res_buf.st_mtime))) {
226 			static mutex_t exit_lock = DEFAULTMUTEX;
227 			char *fmri;
228 
229 			/*
230 			 * if in self cred mode, kill the forker and
231 			 * child nscds
232 			 */
233 			if (_nscd_is_self_cred_on(0, NULL)) {
234 				_nscd_kill_forker();
235 				_nscd_kill_all_children();
236 			}
237 
238 			/*
239 			 * time for restart
240 			 */
241 			_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
242 			(me, "nscd restart due to %s or %s change\n",
243 				"/etc/nsswitch.conf", "resolv.conf");
244 			/*
245 			 * try to restart under smf
246 			 */
247 			if ((fmri = getenv("SMF_FMRI")) == NULL) {
248 				/* not running under smf - reexec */
249 				(void) execv(main_execname, main_argv);
250 				exit(1); /* just in case */
251 			}
252 
253 			/* prevent multiple restarts */
254 			(void) mutex_lock(&exit_lock);
255 
256 			if (smf_restart_instance(fmri) == 0)
257 				(void) sleep(10); /* wait a bit */
258 			exit(1); /* give up waiting for resurrection */
259 		}
260 
261 	} else
262 	    (void) mutex_unlock(&nsswitch_lock);
263 }
264 
265 uid_t
266 _nscd_get_client_euid()
267 {
268 	ucred_t	*uc = NULL;
269 	uid_t	id;
270 	char	*me = "get_client_euid";
271 
272 	if (door_ucred(&uc) != 0) {
273 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
274 		(me, "door_ucred: %s\n", strerror(errno));
275 		return ((uid_t)-1);
276 	}
277 
278 	id = ucred_geteuid(uc);
279 	ucred_free(uc);
280 	return (id);
281 }
282 
283 static void
284 N2N_check_priv(
285 	void			*buf,
286 	char			*dc_str)
287 {
288 	nss_pheader_t		*phdr = (nss_pheader_t *)buf;
289 	ucred_t			*uc = NULL;
290 	const priv_set_t	*eset;
291 	zoneid_t		zoneid;
292 	int			errnum;
293 	char			*me = "N2N_check_priv";
294 
295 	if (door_ucred(&uc) != 0) {
296 		errnum = errno;
297 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
298 		(me, "door_ucred: %s\n", strerror(errno));
299 
300 		NSCD_RETURN_STATUS(phdr, NSS_ERROR, errnum);
301 	}
302 
303 	eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
304 	zoneid = ucred_getzoneid(uc);
305 
306 	if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) ||
307 		eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) :
308 		ucred_geteuid(uc) != 0) {
309 
310 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
311 		(me, "%s call failed(cred): caller pid %d, uid %d, "
312 		"euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
313 		ucred_getruid(uc), ucred_geteuid(uc), zoneid);
314 		ucred_free(uc);
315 
316 		NSCD_RETURN_STATUS(phdr, NSS_ERROR, EACCES);
317 	}
318 
319 	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
320 	(me, "nscd received %s cmd from pid %d, uid %d, "
321 	"euid %d, zoneid %d\n", dc_str, ucred_getpid(uc),
322 	ucred_getruid(uc), ucred_geteuid(uc), zoneid);
323 
324 	ucred_free(uc);
325 
326 	NSCD_RETURN_STATUS_SUCCESS(phdr);
327 }
328 
329 void
330 _nscd_APP_check_cred(
331 	void		*buf,
332 	pid_t		*pidp,
333 	char		*dc_str,
334 	int		log_comp,
335 	int		log_level)
336 {
337 	nss_pheader_t	*phdr = (nss_pheader_t *)buf;
338 	ucred_t		*uc = NULL;
339 	uid_t		ruid;
340 	uid_t		euid;
341 	pid_t		pid;
342 	int		errnum;
343 	char		*me = "_nscd_APP_check_cred";
344 
345 	if (door_ucred(&uc) != 0) {
346 		errnum = errno;
347 		_NSCD_LOG(log_comp, NSCD_LOG_LEVEL_ERROR)
348 		(me, "door_ucred: %s\n", strerror(errno));
349 
350 		NSCD_RETURN_STATUS(phdr, NSS_ERROR, errnum);
351 	}
352 
353 	NSCD_SET_STATUS_SUCCESS(phdr);
354 	pid = ucred_getpid(uc);
355 	if (NSS_PACKED_CRED_CHECK(buf, ruid = ucred_getruid(uc),
356 		euid = ucred_geteuid(uc))) {
357 		if (pidp != NULL) {
358 			if (*pidp == (pid_t)-1)
359 				*pidp = pid;
360 			else if (*pidp != pid) {
361 				NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
362 			}
363 		}
364 	} else {
365 		NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES);
366 	}
367 
368 	ucred_free(uc);
369 
370 	if (NSCD_STATUS_IS_NOT_OK(phdr)) {
371 		_NSCD_LOG(log_comp, log_level)
372 		(me, "%s call failed: caller pid %d (input pid = %d), ruid %d, "
373 		"euid %d, header ruid %d, header euid %d\n", dc_str,
374 		pid, (pidp != NULL) ? *pidp : -1, ruid, euid,
375 		((nss_pheader_t *)(buf))->p_ruid,
376 		((nss_pheader_t *)(buf))->p_euid);
377 	}
378 }
379 
380 /* log error and return -1 when an invalid packed buffer header is found */
381 static int
382 pheader_error(nss_pheader_t *phdr, uint32_t call_number)
383 {
384 	char *call_num_str;
385 
386 	switch (call_number) {
387 	case NSCD_SEARCH:
388 		call_num_str = "NSCD_SEARCH";
389 		break;
390 	case NSCD_SETENT:
391 		call_num_str = "NSCD_SETENT";
392 		break;
393 	case NSCD_GETENT:
394 		call_num_str = "NSCD_GETENT";
395 		break;
396 	case NSCD_ENDENT:
397 		call_num_str = "NSCD_ENDENT";
398 		break;
399 	case NSCD_PUT:
400 		call_num_str = "NSCD_PUT";
401 		break;
402 	case NSCD_GETHINTS:
403 		call_num_str = "NSCD_GETHINTS";
404 		break;
405 	default:
406 		call_num_str = "UNKNOWN";
407 		break;
408 	}
409 
410 	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
411 	("pheader_error", "call number %s: invalid packed buffer header\n",
412 		call_num_str);
413 
414 	NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
415 	return (-1);
416 }
417 
418 /*
419  * Validate the header of a getXbyY or setent/getent/endent request.
420  * Return 0 if good, -1 otherwise.
421  *
422  * A valid header looks like the following (size is arg_size, does
423  * not include the output area):
424  * +----------------------------------+ --
425  * | nss_pheader_t (header fixed part)| ^
426  * |                                  | |
427  * | pbufsiz, dbd,off, key_off,       | len = sizeof(nss_pheader_t)
428  * | data_off ....                    | |
429  * |                                  | v
430  * +----------------------------------+ <----- dbd_off
431  * | dbd (database description)       | ^
432  * | nss_dbd_t + up to 3 strings      | |
433  * | length = sizeof(nss_dbd_t) +     | len = key_off - dbd_off
434  * |          length of 3 strings +   | |
435  * |          length of padding       | |
436  * | (total length in multiple of 4)  | v
437  * +----------------------------------+ <----- key_off
438  * | lookup key                       | ^
439  * | nss_XbyY_key_t, content varies,  | |
440  * | based on database and lookup op  | len = data_off - key_off
441  * | length = data_off - key_off      | |
442  * | including padding, multiple of 4 | v
443  * +----------------------------------+ <----- data_off (= arg_size)
444  * |                                  | ^
445  * | area to hold results             | |
446  * |                                  | len = data_len (= pbufsiz -
447  * |                                  | |                 data_off)
448  * |                                  | v
449  * +----------------------------------+ <----- pbufsiz
450  */
451 static int
452 validate_pheader(
453 	void		*argp,
454 	size_t		arg_size,
455 	uint32_t	call_number)
456 {
457 	nss_pheader_t	*phdr = (nss_pheader_t *)(void *)argp;
458 	nssuint_t	l1, l2;
459 
460 	/*
461 	 * current version is NSCD_HEADER_REV, length of the fixed part
462 	 * of the header must match the size of nss_pheader_t
463 	 */
464 	if (phdr->p_version != NSCD_HEADER_REV ||
465 			phdr->dbd_off != sizeof (nss_pheader_t))
466 		return (pheader_error(phdr, call_number));
467 
468 	/*
469 	 * buffer size and offsets must be in multiple of 4
470 	 */
471 	if ((arg_size & 3) || (phdr->dbd_off & 3) || (phdr->key_off & 3) ||
472 		(phdr->data_off & 3))
473 		return (pheader_error(phdr, call_number));
474 
475 	/*
476 	 * the input arg_size is the length of the request header
477 	 * and should be less than NSCD_PHDR_MAXLEN
478 	 */
479 	if (phdr->data_off != arg_size || arg_size > NSCD_PHDR_MAXLEN)
480 		return (pheader_error(phdr, call_number));
481 
482 	/* get length of the dbd area */
483 	l1 = phdr->key_off - phdr-> dbd_off;
484 
485 	/*
486 	 * dbd area may contain padding, so length of dbd should
487 	 * not be less than the length of the actual data
488 	 */
489 	if (l1 < phdr->dbd_len)
490 		return (pheader_error(phdr, call_number));
491 
492 	/* get length of the key area */
493 	l2 = phdr->data_off - phdr->key_off;
494 
495 	/*
496 	 * key area may contain padding, so length of key area should
497 	 * not be less than the length of the actual data
498 	 */
499 	if (l2 < phdr->key_len)
500 		return (pheader_error(phdr, call_number));
501 
502 	/*
503 	 * length of fixed part + lengths of dbd and key area = length of
504 	 * the request header
505 	 */
506 	if (sizeof (nss_pheader_t) + l1 + l2 != phdr->data_off)
507 		return (pheader_error(phdr, call_number));
508 
509 	/* header length + data length = buffer length */
510 	if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
511 		return (pheader_error(phdr, call_number));
512 
513 	return (0);
514 }
515 
516 /* log error and return -1 when an invalid nscd to nscd buffer is found */
517 static int
518 N2Nbuf_error(nss_pheader_t *phdr, uint32_t call_number)
519 {
520 	char *call_num_str;
521 
522 	switch (call_number) {
523 	case NSCD_PING:
524 		call_num_str = "NSCD_PING";
525 		break;
526 
527 	case NSCD_IMHERE:
528 		call_num_str = "NSCD_IMHERE";
529 		break;
530 
531 	case NSCD_PULSE:
532 		call_num_str = "NSCD_PULSE";
533 		break;
534 
535 	case NSCD_FORK:
536 		call_num_str = "NSCD_FORK";
537 		break;
538 
539 	case NSCD_KILL:
540 		call_num_str = "NSCD_KILL";
541 		break;
542 
543 	case NSCD_REFRESH:
544 		call_num_str = "NSCD_REFRESH";
545 		break;
546 
547 	case NSCD_GETPUADMIN:
548 		call_num_str = "NSCD_GETPUADMIN";
549 		break;
550 
551 	case NSCD_GETADMIN:
552 		call_num_str = "NSCD_GETADMIN";
553 		break;
554 
555 	case NSCD_SETADMIN:
556 		call_num_str = "NSCD_SETADMIN";
557 		break;
558 
559 	case NSCD_KILLSERVER:
560 		call_num_str = "NSCD_KILLSERVER";
561 		break;
562 	default:
563 		call_num_str = "UNKNOWN";
564 		break;
565 	}
566 
567 	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT)
568 	("N2Nbuf_error", "call number %s: invalid N2N buffer\n",
569 		call_num_str);
570 
571 	NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
572 			NSCD_DOOR_BUFFER_CHECK_FAILED);
573 
574 	return (-1);
575 }
576 
577 /*
578  * Validate the buffer of an nscd to nscd request.
579  * Return 0 if good, -1 otherwise.
580  *
581  * A valid buffer looks like the following (size is arg_size):
582  * +----------------------------------+ --
583  * | nss_pheader_t (header fixed part)| ^
584  * |                                  | |
585  * | pbufsiz, dbd,off, key_off,       | len = sizeof(nss_pheader_t)
586  * | data_off ....                    | |
587  * |                                  | v
588  * +----------------------------------+ <---dbd_off = key_off = data_off
589  * |                                  | ^
590  * | input data/output data           | |
591  * | OR no data                       | len = data_len (= pbufsiz -
592  * |                                  | |                 data_off)
593  * |                                  | | len could be zero
594  * |                                  | v
595  * +----------------------------------+ <--- pbufsiz
596  */
597 static int
598 validate_N2Nbuf(
599 	void		*argp,
600 	size_t		arg_size,
601 	uint32_t	call_number)
602 {
603 	nss_pheader_t	*phdr = (nss_pheader_t *)(void *)argp;
604 
605 	/*
606 	 * current version is NSCD_HEADER_REV, length of the fixed part
607 	 * of the header must match the size of nss_pheader_t
608 	 */
609 	if (phdr->p_version != NSCD_HEADER_REV ||
610 			phdr->dbd_off != sizeof (nss_pheader_t))
611 		return (N2Nbuf_error(phdr, call_number));
612 
613 	/*
614 	 * There are no dbd and key data, so the dbd, key, data
615 	 * offsets should be equal
616 	 */
617 	if (phdr->dbd_off != phdr->key_off ||
618 		phdr->dbd_off != phdr->data_off)
619 		return (N2Nbuf_error(phdr, call_number));
620 
621 	/*
622 	 * the input arg_size is the buffer length and should
623 	 * be less or equal than NSCD_N2NBUF_MAXLEN
624 	 */
625 	if (phdr->pbufsiz != arg_size || arg_size > NSCD_N2NBUF_MAXLEN)
626 		return (N2Nbuf_error(phdr, call_number));
627 
628 	/* header length + data length = buffer length */
629 	if (phdr->data_off + phdr->data_len != phdr->pbufsiz)
630 		return (N2Nbuf_error(phdr, call_number));
631 
632 	return (0);
633 }
634 
635 static void
636 lookup(char *argp, size_t arg_size)
637 {
638 	nsc_lookup_args_t	largs;
639 	char			space[NSCD_LOOKUP_BUFSIZE];
640 	nss_pheader_t		*phdr = (nss_pheader_t *)(void *)argp;
641 
642 	NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space,
643 		sizeof (space));
644 
645 	/*
646 	 * make sure the first couple bytes of the data area is null,
647 	 * so that bad strings in the packed header stop here
648 	 */
649 	(void) memset((char *)phdr + phdr->data_off, 0, 16);
650 
651 	(void) memset(&largs, 0, sizeof (largs));
652 	largs.buffer = argp;
653 	largs.bufsize = arg_size;
654 	nsc_lookup(&largs, 0);
655 
656 	/*
657 	 * only the PUN needs to keep track of the
658 	 * activity count to determine when to
659 	 * terminate itself
660 	 */
661 	if (_whoami == NSCD_CHILD) {
662 		(void) mutex_lock(&activity_lock);
663 		++activity;
664 		(void) mutex_unlock(&activity_lock);
665 	}
666 
667 	NSCD_SET_RETURN_ARG(phdr, arg_size);
668 	(void) door_return(argp, arg_size, NULL, 0);
669 }
670 
671 static void
672 getent(char *argp, size_t arg_size)
673 {
674 	char			space[NSCD_LOOKUP_BUFSIZE];
675 	nss_pheader_t		*phdr = (nss_pheader_t *)(void *)argp;
676 
677 	NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr,
678 		space, sizeof (space));
679 
680 	nss_pgetent(argp, arg_size);
681 
682 	NSCD_SET_RETURN_ARG(phdr, arg_size);
683 	(void) door_return(argp, arg_size, NULL, 0);
684 }
685 
686 static int
687 is_db_per_user(void *buf, char *dblist)
688 {
689 	nss_pheader_t	*phdr = (nss_pheader_t *)buf;
690 	nss_dbd_t	*pdbd;
691 	char		*dbname, *dbn;
692 	int		len;
693 
694 	/* copy db name into a temp buffer */
695 	pdbd = (nss_dbd_t *)((void *)((char *)buf + phdr->dbd_off));
696 	dbname = (char *)pdbd + pdbd->o_name;
697 	len = strlen(dbname);
698 	dbn = alloca(len + 2);
699 	(void) memcpy(dbn, dbname, len);
700 
701 	/* check if <dbname> + ',' can be found in the dblist string */
702 	dbn[len] = ',';
703 	dbn[len + 1] = '\0';
704 	if (strstr(dblist, dbn) != NULL)
705 		return (1);
706 
707 	/*
708 	 * check if <dbname> can be found in the last part
709 	 * of the dblist string
710 	 */
711 	dbn[len] = '\0';
712 	if (strstr(dblist, dbn) != NULL)
713 		return (1);
714 
715 	return (0);
716 }
717 
718 /*
719  * Check to see if all conditions are met for processing per-user
720  * requests. Returns 1 if yes, -1 if backend is not configured,
721  * 0 otherwise.
722  */
723 static int
724 need_per_user_door(void *buf, int whoami, uid_t uid, char **dblist)
725 {
726 	nss_pheader_t	*phdr = (nss_pheader_t *)buf;
727 
728 	NSCD_SET_STATUS_SUCCESS(phdr);
729 
730 	/* if already a per-user nscd, no need to get per-user door */
731 	if (whoami == NSCD_CHILD)
732 		return (0);
733 
734 	/* forker shouldn't be asked */
735 	if (whoami == NSCD_FORKER) {
736 		NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
737 		return (0);
738 	}
739 
740 	/* if door client is root, no need for a per-user door */
741 	if (uid == 0)
742 		return (0);
743 
744 	/*
745 	 * if per-user lookup is not configured, no per-user
746 	 * door available
747 	 */
748 	if (_nscd_is_self_cred_on(0, dblist) == 0)
749 		return (-1);
750 
751 	/*
752 	 * if per-user lookup is not configured for the db,
753 	 * don't bother
754 	 */
755 	if (is_db_per_user(phdr, *dblist) == 0)
756 		return (0);
757 
758 	return (1);
759 }
760 
761 static void
762 if_selfcred_return_per_user_door(char *argp, size_t arg_size,
763 	door_desc_t *dp, int whoami)
764 {
765 	nss_pheader_t	*phdr = (nss_pheader_t *)((void *)argp);
766 	char		*dblist;
767 	int		door = -1;
768 	int		rc = 0;
769 	door_desc_t	desc;
770 	char		*space;
771 	int		len;
772 
773 	/*
774 	 * check to see if self-cred is configured and
775 	 * need to return an alternate PUN door
776 	 */
777 	if (per_user_is_on == 1) {
778 		rc = need_per_user_door(argp, whoami,
779 			_nscd_get_client_euid(), &dblist);
780 		if (rc == -1)
781 			per_user_is_on = 0;
782 	}
783 	if (rc <= 0) {
784 		/*
785 		 * self-cred not configured, and no error detected,
786 		 * return to continue the door call processing
787 		 */
788 		if (NSCD_STATUS_IS_OK(phdr))
789 			return;
790 		else
791 			/*
792 			 * configured but error detected,
793 			 * stop the door call processing
794 			 */
795 			(void) door_return(argp, phdr->data_off, NULL, 0);
796 	}
797 
798 	/* get the alternate PUN door */
799 	_nscd_proc_alt_get(argp, &door);
800 	if (NSCD_GET_STATUS(phdr) != NSS_ALTRETRY) {
801 		(void) door_return(argp, phdr->data_off, NULL, 0);
802 	}
803 
804 	/* return the alternate door descriptor */
805 	len = strlen(dblist) + 1;
806 	space = alloca(arg_size + len);
807 	phdr->data_len = len;
808 	(void) memcpy(space, phdr, arg_size);
809 	(void) strncpy((char *)space + arg_size, dblist, len);
810 	dp = &desc;
811 	dp->d_attributes = DOOR_DESCRIPTOR;
812 	dp->d_data.d_desc.d_descriptor = door;
813 	arg_size += len;
814 	(void) door_return(space, arg_size, dp, 1);
815 }
816 
817 /*ARGSUSED*/
818 static void
819 switcher(void *cookie, char *argp, size_t arg_size,
820     door_desc_t *dp, uint_t n_desc)
821 {
822 	int			iam;
823 	pid_t			ent_pid = -1;
824 	nss_pheader_t		*phdr = (nss_pheader_t *)((void *)argp);
825 	void			*uptr;
826 	int			len;
827 	size_t			buflen;
828 	int			callnum;
829 	char			*me = "switcher";
830 
831 	_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
832 	(me, "switcher ...\n");
833 
834 	if (argp == DOOR_UNREF_DATA) {
835 		(void) printf("Door Slam... exiting\n");
836 		exit(0);
837 	}
838 
839 	if (argp == NULL) { /* empty door call */
840 		(void) door_return(NULL, 0, 0, 0); /* return the favor */
841 	}
842 
843 	/*
844 	 *  need to restart if main nscd and config file(s) changed
845 	 */
846 	if (_whoami == NSCD_MAIN)
847 		restart_if_cfgfile_changed();
848 
849 	if ((phdr->nsc_callnumber & NSCDV2CATMASK) == NSCD_CALLCAT_APP) {
850 
851 		/* make sure the packed buffer header is good */
852 		if (validate_pheader(argp, arg_size,
853 				phdr->nsc_callnumber) == -1)
854 			(void) door_return(argp, arg_size, NULL, 0);
855 
856 		switch (phdr->nsc_callnumber) {
857 
858 		case NSCD_SEARCH:
859 
860 		/* if a fallback to main nscd, skip per-user setup */
861 		if (phdr->p_status != NSS_ALTRETRY)
862 			if_selfcred_return_per_user_door(argp, arg_size,
863 				dp, _whoami);
864 		lookup(argp, arg_size);
865 
866 		break;
867 
868 		case NSCD_SETENT:
869 
870 		_nscd_APP_check_cred(argp, &ent_pid, "NSCD_SETENT",
871 			NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT);
872 		if (NSCD_STATUS_IS_OK(phdr)) {
873 			if_selfcred_return_per_user_door(argp, arg_size,
874 				dp, _whoami);
875 			nss_psetent(argp, arg_size, ent_pid);
876 		}
877 		break;
878 
879 		case NSCD_GETENT:
880 
881 		getent(argp, arg_size);
882 		break;
883 
884 		case NSCD_ENDENT:
885 
886 		nss_pendent(argp, arg_size);
887 		break;
888 
889 		case NSCD_PUT:
890 
891 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
892 		(me, "door call NSCD_PUT not supported yet\n");
893 
894 		NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
895 		break;
896 
897 		case NSCD_GETHINTS:
898 
899 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
900 		(me, "door call NSCD_GETHINTS not supported yet\n");
901 
902 		NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP);
903 		break;
904 
905 		default:
906 
907 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
908 		(me, "Unknown name service door call op %x\n",
909 		phdr->nsc_callnumber);
910 
911 		NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
912 		break;
913 		}
914 
915 		(void) door_return(argp, arg_size, NULL, 0);
916 	}
917 
918 	iam = NSCD_MAIN;
919 	callnum = phdr->nsc_callnumber & ~NSCD_WHOAMI;
920 	if (callnum == NSCD_IMHERE ||
921 		callnum == NSCD_PULSE || callnum == NSCD_FORK)
922 		iam = phdr->nsc_callnumber & NSCD_WHOAMI;
923 	else
924 		callnum = phdr->nsc_callnumber;
925 
926 	/* nscd -> nscd v2 calls */
927 
928 	/* make sure the buffer is good */
929 	if (validate_N2Nbuf(argp, arg_size, callnum) == -1)
930 		(void) door_return(argp, arg_size, NULL, 0);
931 
932 	switch (callnum) {
933 
934 	case NSCD_PING:
935 		NSCD_SET_STATUS_SUCCESS(phdr);
936 		break;
937 
938 	case NSCD_IMHERE:
939 		_nscd_proc_iamhere(argp, dp, n_desc, iam);
940 		break;
941 
942 	case NSCD_PULSE:
943 		N2N_check_priv(argp, "NSCD_PULSE");
944 		if (NSCD_STATUS_IS_OK(phdr))
945 			_nscd_proc_pulse(argp, iam);
946 		break;
947 
948 	case NSCD_FORK:
949 		N2N_check_priv(argp, "NSCD_FORK");
950 		if (NSCD_STATUS_IS_OK(phdr))
951 			_nscd_proc_fork(argp, iam);
952 		break;
953 
954 	case NSCD_KILL:
955 		N2N_check_priv(argp, "NSCD_KILL");
956 		if (NSCD_STATUS_IS_OK(phdr))
957 			exit(0);
958 		break;
959 
960 	case NSCD_REFRESH:
961 		N2N_check_priv(argp, "NSCD_REFRESH");
962 		if (NSCD_STATUS_IS_OK(phdr)) {
963 			if (_nscd_refresh() != NSCD_SUCCESS)
964 				exit(1);
965 			NSCD_SET_STATUS_SUCCESS(phdr);
966 		}
967 		break;
968 
969 	case NSCD_GETPUADMIN:
970 
971 		if (_nscd_is_self_cred_on(0, NULL)) {
972 			_nscd_peruser_getadmin(argp, sizeof (nscd_admin_t));
973 		} else {
974 			NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0,
975 				NSCD_SELF_CRED_NOT_CONFIGURED);
976 		}
977 		break;
978 
979 	case NSCD_GETADMIN:
980 
981 		len = _nscd_door_getadmin((void *)argp);
982 		if (len == 0)
983 			break;
984 
985 		/* size of door buffer not big enough, allocate one */
986 		NSCD_ALLOC_DOORBUF(NSCD_GETADMIN, len, uptr, buflen);
987 
988 		/* copy packed header */
989 		*(nss_pheader_t *)uptr = *(nss_pheader_t *)((void *)argp);
990 
991 		/* set new buffer size */
992 		((nss_pheader_t *)uptr)->pbufsiz = buflen;
993 
994 		/* try one more time */
995 		(void) _nscd_door_getadmin((void *)uptr);
996 		(void) door_return(uptr, buflen, NULL, 0);
997 		break;
998 
999 	case NSCD_SETADMIN:
1000 		N2N_check_priv(argp, "NSCD_SETADMIN");
1001 		if (NSCD_STATUS_IS_OK(phdr))
1002 			_nscd_door_setadmin(argp);
1003 		break;
1004 
1005 	case NSCD_KILLSERVER:
1006 		N2N_check_priv(argp, "NSCD_KILLSERVER");
1007 		if (NSCD_STATUS_IS_OK(phdr)) {
1008 			/* also kill the forker nscd if one is running */
1009 			_nscd_kill_forker();
1010 			exit(0);
1011 		}
1012 		break;
1013 
1014 	default:
1015 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1016 		(me, "Unknown name service door call op %d\n",
1017 			phdr->nsc_callnumber);
1018 
1019 		NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL);
1020 
1021 		(void) door_return(argp, arg_size, NULL, 0);
1022 		break;
1023 
1024 	}
1025 	(void) door_return(argp, arg_size, NULL, 0);
1026 }
1027 
1028 int
1029 _nscd_setup_server(char *execname, char **argv)
1030 {
1031 
1032 	int		fd;
1033 	int		errnum;
1034 	int		bind_failed = 0;
1035 	struct stat	buf;
1036 	sigset_t	myset;
1037 	struct sigaction action;
1038 	char		*me = "_nscd_setup_server";
1039 
1040 	main_execname = execname;
1041 	main_argv = argv;
1042 
1043 	keep_open_dns_socket();
1044 
1045 	/*
1046 	 * the max number of server threads should be fixed now, so
1047 	 * set flag to indicate that no in-flight change is allowed
1048 	 */
1049 	max_servers_set = 1;
1050 
1051 	(void) thr_keycreate(&lookup_state_key, NULL);
1052 	(void) sema_init(&common_sema,
1053 			frontend_cfg_g.common_worker_threads,
1054 			USYNC_THREAD, 0);
1055 
1056 	/* Establish server thread pool */
1057 	(void) door_server_create(server_create);
1058 	if (thr_keycreate(&server_key, server_destroy) != 0) {
1059 		errnum = errno;
1060 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1061 		(me, "thr_keycreate (server thread): %s\n",
1062 			strerror(errnum));
1063 		return (-1);
1064 	}
1065 
1066 	/* Create a door */
1067 	if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
1068 	    DOOR_UNREF | DOOR_NO_CANCEL)) < 0) {
1069 		errnum = errno;
1070 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1071 		(me, "door_create: %s\n", strerror(errnum));
1072 		return (-1);
1073 	}
1074 
1075 	/* if not main nscd, no more setup to do */
1076 	if (_whoami != NSCD_MAIN)
1077 		return (fd);
1078 
1079 	/* bind to file system */
1080 	if (is_system_labeled()) {
1081 		if (stat(TSOL_NAME_SERVICE_DOOR, &buf) < 0) {
1082 			int newfd;
1083 			if ((newfd = creat(TSOL_NAME_SERVICE_DOOR, 0444)) < 0) {
1084 				errnum = errno;
1085 				_NSCD_LOG(NSCD_LOG_FRONT_END,
1086 					NSCD_LOG_LEVEL_ERROR)
1087 				(me, "Cannot create %s: %s\n",
1088 					TSOL_NAME_SERVICE_DOOR,
1089 					strerror(errnum));
1090 				bind_failed = 1;
1091 			}
1092 			(void) close(newfd);
1093 		}
1094 		if (symlink(TSOL_NAME_SERVICE_DOOR, NAME_SERVICE_DOOR) != 0) {
1095 			if (errno != EEXIST) {
1096 				errnum = errno;
1097 				_NSCD_LOG(NSCD_LOG_FRONT_END,
1098 					NSCD_LOG_LEVEL_ERROR)
1099 				(me, "Cannot symlink %s: %s\n",
1100 					NAME_SERVICE_DOOR,
1101 					strerror(errnum));
1102 				bind_failed = 1;
1103 			}
1104 		}
1105 	} else if (stat(NAME_SERVICE_DOOR, &buf) < 0) {
1106 		int newfd;
1107 		if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) {
1108 			errnum = errno;
1109 			_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1110 			(me, "Cannot create %s: %s\n", NAME_SERVICE_DOOR,
1111 				strerror(errnum));
1112 			bind_failed = 1;
1113 		}
1114 		(void) close(newfd);
1115 	}
1116 
1117 	if (bind_failed == 1) {
1118 		(void) door_revoke(fd);
1119 		return (-1);
1120 	}
1121 
1122 	if (fattach(fd, NAME_SERVICE_DOOR) < 0) {
1123 		if ((errno != EBUSY) ||
1124 		(fdetach(NAME_SERVICE_DOOR) <  0) ||
1125 		(fattach(fd, NAME_SERVICE_DOOR) < 0)) {
1126 			errnum = errno;
1127 			_NSCD_LOG(NSCD_LOG_FRONT_END,
1128 				NSCD_LOG_LEVEL_ERROR)
1129 			(me, "fattach: %s\n", strerror(errnum));
1130 			(void) door_revoke(fd);
1131 			return (-1);
1132 		}
1133 	}
1134 
1135 	/*
1136 	 * kick off routing socket monitor thread
1137 	 */
1138 	if (thr_create(NULL, NULL,
1139 		(void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
1140 		errnum = errno;
1141 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1142 		(me, "thr_create (routing socket monitor): %s\n",
1143 			strerror(errnum));
1144 
1145 		(void) door_revoke(fd);
1146 		return (-1);
1147 	}
1148 
1149 	/*
1150 	 * set up signal handler for SIGHUP
1151 	 */
1152 	action.sa_handler = dozip;
1153 	action.sa_flags = 0;
1154 	(void) sigemptyset(&action.sa_mask);
1155 	(void) sigemptyset(&myset);
1156 	(void) sigaddset(&myset, SIGHUP);
1157 
1158 	if (sigaction(SIGHUP, &action, NULL) < 0) {
1159 		errnum = errno;
1160 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1161 		(me, "sigaction (SIGHUP): %s\n", strerror(errnum));
1162 
1163 		(void) door_revoke(fd);
1164 		return (-1);
1165 	}
1166 
1167 	return (fd);
1168 }
1169 
1170 int
1171 _nscd_setup_child_server(int did)
1172 {
1173 
1174 	int		errnum;
1175 	int		fd;
1176 	nscd_rc_t	rc;
1177 	char		*me = "_nscd_setup_child_server";
1178 
1179 	/* Re-establish our own server thread pool */
1180 	(void) door_server_create(server_create);
1181 	if (thr_keycreate(&server_key, server_destroy) != 0) {
1182 		errnum = errno;
1183 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
1184 		(me, "thr_keycreate failed: %s", strerror(errnum));
1185 		return (-1);
1186 	}
1187 
1188 	/*
1189 	 * Create a new door.
1190 	 * Keep DOOR_REFUSE_DESC (self-cred nscds don't fork)
1191 	 */
1192 	(void) close(did);
1193 	if ((fd = door_create(switcher,
1194 		NAME_SERVICE_DOOR_COOKIE,
1195 		DOOR_REFUSE_DESC|DOOR_UNREF|DOOR_NO_CANCEL)) < 0) {
1196 		errnum = errno;
1197 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG)
1198 		(me, "door_create failed: %s", strerror(errnum));
1199 		return (-1);
1200 	}
1201 
1202 	/*
1203 	 * kick off routing socket monitor thread
1204 	 */
1205 	if (thr_create(NULL, NULL,
1206 		(void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
1207 		errnum = errno;
1208 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1209 		(me, "thr_create (routing socket monitor): %s\n",
1210 			strerror(errnum));
1211 		(void) door_revoke(fd);
1212 		return (-1);
1213 	}
1214 
1215 	/*
1216 	 * start monitoring the states of the name service clients
1217 	 */
1218 	rc = _nscd_init_smf_monitor();
1219 	if (rc != NSCD_SUCCESS) {
1220 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1221 	(me, "unable to start the SMF monitor (rc = %d)\n", rc);
1222 
1223 		(void) door_revoke(fd);
1224 		return (-1);
1225 	}
1226 
1227 	return (fd);
1228 }
1229 
1230 nscd_rc_t
1231 _nscd_alloc_frontend_cfg()
1232 {
1233 	frontend_cfg  = calloc(NSCD_NUM_DB, sizeof (nscd_cfg_frontend_t));
1234 	if (frontend_cfg == NULL)
1235 		return (NSCD_NO_MEMORY);
1236 
1237 	return (NSCD_SUCCESS);
1238 }
1239 
1240 
1241 /* ARGSUSED */
1242 nscd_rc_t
1243 _nscd_cfg_frontend_notify(
1244 	void				*data,
1245 	struct nscd_cfg_param_desc	*pdesc,
1246 	nscd_cfg_id_t			*nswdb,
1247 	nscd_cfg_flag_t			dflag,
1248 	nscd_cfg_error_t		**errorp,
1249 	void				*cookie)
1250 {
1251 	void				*dp;
1252 
1253 	/*
1254 	 * At init time, the whole group of config params are received.
1255 	 * At update time, group or individual parameter value could
1256 	 * be received.
1257 	 */
1258 
1259 	if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_INIT) ||
1260 		_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
1261 		/*
1262 		 * group data is received, copy in the
1263 		 * entire strcture
1264 		 */
1265 		if (_nscd_cfg_flag_is_set(pdesc->pflag,
1266 			NSCD_CFG_PFLAG_GLOBAL))
1267 			frontend_cfg_g =
1268 				*(nscd_cfg_global_frontend_t *)data;
1269 		else
1270 			frontend_cfg[nswdb->index] =
1271 				*(nscd_cfg_frontend_t *)data;
1272 
1273 	} else {
1274 		/*
1275 		 * individual paramater is received: copy in the
1276 		 * parameter value.
1277 		 */
1278 		if (_nscd_cfg_flag_is_set(pdesc->pflag,
1279 			NSCD_CFG_PFLAG_GLOBAL))
1280 			dp = (char *)&frontend_cfg_g + pdesc->p_offset;
1281 		else
1282 			dp = (char *)&frontend_cfg[nswdb->index] +
1283 				pdesc->p_offset;
1284 		(void) memcpy(dp, data, pdesc->p_size);
1285 	}
1286 
1287 	return (NSCD_SUCCESS);
1288 }
1289 
1290 /* ARGSUSED */
1291 nscd_rc_t
1292 _nscd_cfg_frontend_verify(
1293 	void				*data,
1294 	struct	nscd_cfg_param_desc	*pdesc,
1295 	nscd_cfg_id_t			*nswdb,
1296 	nscd_cfg_flag_t			dflag,
1297 	nscd_cfg_error_t		**errorp,
1298 	void				**cookie)
1299 {
1300 
1301 	char				*me = "_nscd_cfg_frontend_verify";
1302 
1303 	/*
1304 	 * if max. number of server threads is set and in effect,
1305 	 * don't allow changing of the frontend configuration
1306 	 */
1307 	if (max_servers_set) {
1308 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO)
1309 	(me, "changing of the frontend configuration not allowed now");
1310 
1311 		return (NSCD_CFG_CHANGE_NOT_ALLOWED);
1312 	}
1313 
1314 	return (NSCD_SUCCESS);
1315 }
1316 
1317 /* ARGSUSED */
1318 nscd_rc_t
1319 _nscd_cfg_frontend_get_stat(
1320 	void				**stat,
1321 	struct nscd_cfg_stat_desc	*sdesc,
1322 	nscd_cfg_id_t			*nswdb,
1323 	nscd_cfg_flag_t			*dflag,
1324 	void				(**free_stat)(void *stat),
1325 	nscd_cfg_error_t		**errorp)
1326 {
1327 	return (NSCD_SUCCESS);
1328 }
1329 
1330 void
1331 _nscd_init_cache_sema(sema_t *sema, char *cache_name)
1332 {
1333 	int	i, j;
1334 	char	*dbn;
1335 
1336 	if (max_servers == 0)
1337 		max_servers = frontend_cfg_g.common_worker_threads +
1338 		frontend_cfg_g.cache_hit_threads;
1339 
1340 	for (i = 0; i < NSCD_NUM_DB; i++) {
1341 
1342 		dbn = NSCD_NSW_DB_NAME(i);
1343 		if (strcasecmp(dbn, cache_name) == 0) {
1344 			j = frontend_cfg[i].worker_thread_per_nsw_db;
1345 			(void) sema_init(sema, j, USYNC_THREAD, 0);
1346 			max_servers += j;
1347 			break;
1348 		}
1349 	}
1350 }
1351 
1352 /*
1353  * Monitor the routing socket.  Address lists stored in the ipnodes
1354  * cache are sorted based on destination address selection rules,
1355  * so when things change that could affect that sorting (interfaces
1356  * go up or down, flags change, etc.), we clear that cache so the
1357  * list will be re-ordered the next time the hostname is resolved.
1358  */
1359 static void
1360 rts_mon(void)
1361 {
1362 	int	rt_sock, rdlen, idx;
1363 	union {
1364 		struct {
1365 			struct rt_msghdr rtm;
1366 			struct sockaddr_storage addrs[RTA_NUMBITS];
1367 		} r;
1368 		struct if_msghdr ifm;
1369 		struct ifa_msghdr ifam;
1370 	} mbuf;
1371 	struct ifa_msghdr *ifam = &mbuf.ifam;
1372 	char	*me = "rts_mon";
1373 
1374 	rt_sock = socket(PF_ROUTE, SOCK_RAW, 0);
1375 	if (rt_sock < 0) {
1376 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1377 		(me, "Failed to open routing socket: %s\n", strerror(errno));
1378 		thr_exit(0);
1379 	}
1380 
1381 	for (;;) {
1382 		rdlen = read(rt_sock, &mbuf, sizeof (mbuf));
1383 		if (rdlen <= 0) {
1384 			if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) {
1385 				_NSCD_LOG(NSCD_LOG_FRONT_END,
1386 					NSCD_LOG_LEVEL_ERROR)
1387 				(me, "routing socket read: %s\n",
1388 					strerror(errno));
1389 				thr_exit(0);
1390 			}
1391 			continue;
1392 		}
1393 		if (ifam->ifam_version != RTM_VERSION) {
1394 				_NSCD_LOG(NSCD_LOG_FRONT_END,
1395 					NSCD_LOG_LEVEL_ERROR)
1396 				(me, "rx unknown version (%d) on "
1397 					"routing socket.\n",
1398 					ifam->ifam_version);
1399 			continue;
1400 		}
1401 		switch (ifam->ifam_type) {
1402 		case RTM_NEWADDR:
1403 		case RTM_DELADDR:
1404 			/* if no ipnodes cache, then nothing to do */
1405 			idx = get_cache_idx("ipnodes");
1406 			if (cache_ctx_p[idx] == NULL ||
1407 				cache_ctx_p[idx]->reaper_on != nscd_true)
1408 				break;
1409 			nsc_invalidate(cache_ctx_p[idx], NULL, NULL);
1410 			break;
1411 		case RTM_ADD:
1412 		case RTM_DELETE:
1413 		case RTM_CHANGE:
1414 		case RTM_GET:
1415 		case RTM_LOSING:
1416 		case RTM_REDIRECT:
1417 		case RTM_MISS:
1418 		case RTM_LOCK:
1419 		case RTM_OLDADD:
1420 		case RTM_OLDDEL:
1421 		case RTM_RESOLVE:
1422 		case RTM_IFINFO:
1423 			break;
1424 		default:
1425 			_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
1426 			(me, "rx unknown msg type (%d) on routing socket.\n",
1427 			    ifam->ifam_type);
1428 			break;
1429 		}
1430 	}
1431 }
1432 
1433 static void
1434 keep_open_dns_socket(void)
1435 {
1436 	_res.options |= RES_STAYOPEN; /* just keep this udp socket open */
1437 }
1438