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