xref: /illumos-gate/usr/src/cmd/nscd/server.c (revision 10a4fa49f51ed9ae1c857a626de6ce9ebf41661a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Simple doors name server cache daemon
30  */
31 
32 #include <stdio.h>
33 #include <signal.h>
34 #include <sys/door.h>
35 #include <sys/types.h>
36 #include <time.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <sys/time.h>
40 #include <sys/wait.h>
41 #include <sys/zone.h>
42 #include <stdlib.h>
43 #include <errno.h>
44 #include <pthread.h>
45 #include <thread.h>
46 #include <stdarg.h>
47 #include <fcntl.h>
48 #include <assert.h>
49 #include <unistd.h>
50 #include <memory.h>
51 #include <sys/socket.h>
52 #include <net/route.h>
53 #include <net/if.h>
54 #include <netinet/in.h>
55 #include <arpa/nameser.h>
56 #include <resolv.h>
57 #include <door.h>
58 #include "getxby_door.h"
59 #include "server_door.h"
60 #include "nscd.h"
61 /* Includes for filenames of databases */
62 #include <shadow.h>
63 #include <userdefs.h>
64 #include <netdb.h>
65 #include <nss_dbdefs.h>
66 #include <exec_attr.h>
67 #include <prof_attr.h>
68 #include <user_attr.h>
69 #include <ucred.h>
70 #include <priv.h>
71 #include <libscf.h>
72 #include <tsol/label.h>
73 #include <zone.h>
74 
75 #define	TSOL_NAME_SERVICE_DOOR	"/var/tsol/doors/name_service_door"
76 
77 extern int 	optind;
78 extern int 	opterr;
79 extern int 	optopt;
80 extern char 	*optarg;
81 
82 static void switcher(void *, char *, size_t, door_desc_t *, uint_t);
83 static void rts_mon(void);
84 static void usage(char *);
85 static int nsc_calllen(nsc_call_t *);
86 static int client_getadmin(admin_t *);
87 static void getadmin(nsc_return_t *, int, nsc_call_t *);
88 static int setadmin(nsc_return_t *, int, nsc_call_t *);
89 static void client_killserver(void);
90 static int client_setadmin(admin_t *);
91 static void client_showstats(admin_t *);
92 static void detachfromtty(void);
93 
94 
95 admin_t	current_admin;
96 static int will_become_server;
97 
98 void
99 nsc_reaper(char *tbl_name, hash_t *tbl, nsc_stat_t *admin_ptr,
100     mutex_t *hash_lock)
101 {
102 	uint_t count;
103 	uint_t interval;
104 
105 	while (1) {
106 
107 		if (current_admin.debug_level >= DBG_ALL) {
108 			logit("reaper_%s: %d entries in cache\n",
109 			tbl_name, admin_ptr->nsc_entries);
110 		}
111 		if (admin_ptr->nsc_entries > 0) {
112 			count = reap_hash(tbl, admin_ptr, hash_lock,
113 			admin_ptr->nsc_pos_ttl);
114 			if (current_admin.debug_level >= DBG_ALL) {
115 				logit("reaper_%s: reaped %d entries\n",
116 				tbl_name, count);
117 			}
118 		} else {
119 			/*
120 			 * We set a minimum wait of 60 before checking again;
121 			 * we don't want to sleep for no time at all.
122 			 * We don't clamp it for the reaping itself, that is
123 			 * done in reap_hash, and with a different minimum.
124 			 */
125 			interval = admin_ptr->nsc_pos_ttl;
126 			if (interval < 60) interval = 60;
127 			if (current_admin.debug_level >= DBG_ALL) {
128 				logit(
129 				    "reaper_%s: Nothing to reap, sleep %d\n",
130 				    tbl_name, interval);
131 			}
132 			sleep(interval);
133 		}
134 	}
135 }
136 
137 nsc_stat_t *
138 getcacheptr(char *s)
139 {
140 	static const char *caches[7] = {"passwd", "group", "hosts", "ipnodes",
141 	    "exec_attr", "prof_attr", "user_attr" };
142 
143 	if (strncmp(caches[0], s, strlen(caches[0])) == 0)
144 		return (&current_admin.passwd);
145 
146 	if (strncmp(caches[1], s, strlen(caches[1])) == 0)
147 		return (&current_admin.group);
148 
149 	if (strncmp(caches[2], s, strlen(caches[2])) == 0)
150 		return (&current_admin.host);
151 
152 	if (strncmp(caches[3], s, strlen(caches[3])) == 0)
153 		return (&current_admin.node);
154 
155 	if (strncmp(caches[4], s, strlen(caches[4])) == 0)
156 		return (&current_admin.exec);
157 
158 	if (strncmp(caches[5], s, strlen(caches[5])) == 0)
159 		return (&current_admin.prof);
160 
161 	if (strncmp(caches[6], s, strlen(caches[6])) == 0)
162 		return (&current_admin.user);
163 
164 	return (NULL);
165 }
166 
167 static char *
168 getcacheopt(char *s)
169 {
170 	while (*s && *s != ',')
171 		s++;
172 	return ((*s == ',') ? (s + 1) : NULL);
173 }
174 
175 /*
176  *  routine to check if server is already running
177  */
178 
179 static int
180 nsc_ping(void)
181 {
182 	nsc_data_t data;
183 	nsc_data_t *dptr;
184 	int ndata;
185 	int adata;
186 
187 	data.nsc_call.nsc_callnumber = NULLCALL;
188 	ndata = sizeof (data);
189 	adata = sizeof (data);
190 	dptr = &data;
191 	return (_nsc_trydoorcall(&dptr, &ndata, &adata));
192 }
193 
194 static void
195 dozip(void)
196 {
197 	/* not much here */
198 }
199 
200 static void
201 keep_open_dns_socket(void)
202 {
203 	_res.options |= RES_STAYOPEN; /* just keep this udp socket open */
204 }
205 
206 /*
207  * declaring this causes the files backend to use hashing
208  * this is of course an utter hack, but provides a nice
209  * quiet back door to enable this feature for only the nscd.
210  */
211 void
212 __nss_use_files_hash(void)
213 {
214 
215 }
216 /*
217  *
218  *  The allocation of resources for cache lookups is an interesting
219  *  problem, and one that has caused several bugs in the beta release
220  *  of 2.5.  In particular, the introduction of a thottle to prevent
221  *  the creation of excessive numbers of LWPs in the case of a failed
222  *  name service has led to a denial of service problem when the
223  *  name service request rate exceeds the name service's ability
224  *  to respond.  As a result, I'm implementing the following
225  *  algorithm:
226  *
227  *  1) We cap the number of total threads.
228  *  2) We save CACHE_THREADS of those for cache lookups only.
229  *  3) We use a common pool of 2/3 of the remain threads that are used first
230  *  4) We save the remainder and allocate 1/3 of it for table specific lookups
231  *
232  *  The intent is to prevent the failure of a single name service from
233  *  causing denial of service, and to always have threads available for
234  *  cached lookups.  If a request comes in and the answer isn't in the
235  *  cache and we cannot get a thread, we simply return NOSERVER, forcing
236  *  the client to lookup the
237  *  data itself.  This will prevent the types of starvation seen
238  *  at UNC due to a single threaded DNS backend, and allows the cache
239  *  to eventually become filled.
240  *
241  */
242 
243 /* 7 tables: passwd, group, hosts, ipnodes, exec_attr, prof_attr, user_attr */
244 #define	NSCD_TABLES		7
245 #define	TABLE_THREADS		10
246 #define	COMMON_THREADS		20
247 #define	CACHE_MISS_THREADS	(COMMON_THREADS + NSCD_TABLES * TABLE_THREADS)
248 #define	CACHE_HIT_THREADS	20
249 #define	MAX_SERVER_THREADS	(CACHE_HIT_THREADS + CACHE_MISS_THREADS)
250 
251 static sema_t common_sema;
252 static sema_t passwd_sema;
253 static sema_t hosts_sema;
254 static sema_t nodes_sema;
255 static sema_t group_sema;
256 static sema_t exec_sema;
257 static sema_t prof_sema;
258 static sema_t user_sema;
259 static thread_key_t lookup_state_key;
260 
261 static void
262 initialize_lookup_clearance(void)
263 {
264 	thr_keycreate(&lookup_state_key, NULL);
265 	(void) sema_init(&common_sema, COMMON_THREADS, USYNC_THREAD, 0);
266 	(void) sema_init(&passwd_sema, TABLE_THREADS, USYNC_THREAD, 0);
267 	(void) sema_init(&hosts_sema, TABLE_THREADS, USYNC_THREAD, 0);
268 	(void) sema_init(&nodes_sema, TABLE_THREADS, USYNC_THREAD, 0);
269 	(void) sema_init(&group_sema, TABLE_THREADS, USYNC_THREAD, 0);
270 	(void) sema_init(&exec_sema, TABLE_THREADS, USYNC_THREAD, 0);
271 	(void) sema_init(&prof_sema, TABLE_THREADS, USYNC_THREAD, 0);
272 	(void) sema_init(&user_sema, TABLE_THREADS, USYNC_THREAD, 0);
273 }
274 
275 int
276 get_clearance(int callnumber)
277 {
278 	sema_t *table_sema = NULL;
279 	char *tab;
280 
281 	if (sema_trywait(&common_sema) == 0) {
282 		thr_setspecific(lookup_state_key, NULL);
283 		return (0);
284 	}
285 
286 	switch (MASKUPDATEBIT(callnumber)) {
287 
288 	case GETPWUID:
289 	case GETPWNAM:
290 		tab = "passwd";
291 		table_sema = &passwd_sema;
292 		break;
293 
294 	case GETGRNAM:
295 	case GETGRGID:
296 		tab = "group";
297 		table_sema = &group_sema;
298 		break;
299 
300 	case GETHOSTBYNAME:
301 	case GETHOSTBYADDR:
302 		tab = "hosts";
303 		table_sema = &hosts_sema;
304 		break;
305 
306 	case GETIPNODEBYNAME:
307 	case GETIPNODEBYADDR:
308 		tab = "ipnodes";
309 		table_sema = &nodes_sema;
310 		break;
311 	case GETEXECID:
312 		tab = "exec_attr";
313 		table_sema = &exec_sema;
314 		break;
315 
316 	case GETPROFNAM:
317 		tab = "prof_attr";
318 		table_sema = &prof_sema;
319 		break;
320 
321 	case GETUSERNAM:
322 		tab = "user_attr";
323 		table_sema = &user_sema;
324 		break;
325 
326 	}
327 
328 	if (sema_trywait(table_sema) == 0) {
329 		thr_setspecific(lookup_state_key, (void*)1);
330 		return (0);
331 	}
332 
333 	if (current_admin.debug_level >= DBG_CANT_FIND) {
334 		logit("get_clearance: throttling load for %s table\n", tab);
335 	}
336 	return (-1);
337 }
338 
339 int
340 release_clearance(int callnumber)
341 {
342 	int which;
343 
344 	sema_t *table_sema = NULL;
345 
346 	thr_getspecific(lookup_state_key, (void**)&which);
347 
348 	if (which == 0) /* from common pool */ {
349 		(void) sema_post(&common_sema);
350 		return (0);
351 	}
352 
353 	switch (MASKUPDATEBIT(callnumber)) {
354 
355 	case GETPWUID:
356 	case GETPWNAM:
357 		table_sema = &passwd_sema;
358 		break;
359 
360 	case GETGRNAM:
361 	case GETGRGID:
362 		table_sema = &group_sema;
363 		break;
364 
365 	case GETHOSTBYNAME:
366 	case GETHOSTBYADDR:
367 		table_sema = &hosts_sema;
368 		break;
369 
370 	case GETIPNODEBYNAME:
371 	case GETIPNODEBYADDR:
372 		table_sema = &nodes_sema;
373 		break;
374 
375 	case GETEXECID:
376 		table_sema = &exec_sema;
377 		break;
378 
379 	case GETPROFNAM:
380 		table_sema = &prof_sema;
381 		break;
382 
383 	case GETUSERNAM:
384 		table_sema = &user_sema;
385 		break;
386 	}
387 
388 	(void) sema_post(table_sema);
389 	return (0);
390 }
391 
392 
393 static mutex_t		create_lock;
394 static int		nscd_max_servers = MAX_SERVER_THREADS;
395 static int		num_servers = 0;
396 static thread_key_t	server_key;
397 
398 /*
399  * Bind a TSD value to a server thread. This enables the destructor to
400  * be called if/when this thread exits.  This would be a programming error,
401  * but better safe than sorry.
402  */
403 /*ARGSUSED*/
404 static void *
405 server_tsd_bind(void *arg)
406 {
407 	static void *value = 0;
408 
409 	/* disable cancellation to avoid hangs if server threads disappear */
410 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
411 	thr_setspecific(server_key, value);
412 	door_return(NULL, 0, NULL, 0);
413 
414 	/* make lint happy */
415 	return (NULL);
416 }
417 
418 /*
419  * Server threads are created here.
420  */
421 /*ARGSUSED*/
422 static void
423 server_create(door_info_t *dip)
424 {
425 	(void) mutex_lock(&create_lock);
426 	if (++num_servers > nscd_max_servers) {
427 		num_servers--;
428 		(void) mutex_unlock(&create_lock);
429 		return;
430 	}
431 	(void) mutex_unlock(&create_lock);
432 	thr_create(NULL, 0, server_tsd_bind, NULL, THR_BOUND|THR_DETACHED,
433 	    NULL);
434 }
435 
436 /*
437  * Server thread are destroyed here
438  */
439 /*ARGSUSED*/
440 static void
441 server_destroy(void *arg)
442 {
443 	(void) mutex_lock(&create_lock);
444 	num_servers--;
445 	(void) mutex_unlock(&create_lock);
446 }
447 
448 static char **saved_argv;
449 static char saved_execname[MAXPATHLEN];
450 
451 static void
452 save_execname()
453 {
454 	const char *name = getexecname();
455 
456 	saved_execname[0] = 0;
457 
458 	if (name[0] != '/') { /* started w/ relative path */
459 		(void) getcwd(saved_execname, MAXPATHLEN);
460 		strlcat(saved_execname, "/", MAXPATHLEN);
461 	}
462 	strlcat(saved_execname, name, MAXPATHLEN);
463 }
464 
465 int
466 main(int argc, char ** argv)
467 {
468 	int did;
469 	int opt;
470 	int errflg = 0;
471 	int showstats = 0;
472 	int doset = 0;
473 	int loaded_config_file = 0;
474 	struct stat buf;
475 	sigset_t myset;
476 	struct sigaction action;
477 
478 	/*
479 	 * The admin model for TX is that labeled zones are managed
480 	 * in global zone where most trusted configuration database
481 	 * resides.
482 	 */
483 	if (is_system_labeled() && (getzoneid() != GLOBAL_ZONEID)) {
484 		(void) fprintf(stderr,
485 		    "With Trusted Extensions nscd runs only in " \
486 		    "the global zone.\n");
487 		exit(1);
488 	}
489 
490 	/*
491 	 *  Special case non-root user  here - he can just print stats
492 	 */
493 
494 	if (geteuid()) {
495 		if (argc != 2 || strcmp(argv[1], "-g")) {
496 			(void) fprintf(stderr,
497 			    "Must be root to use any option other than "\
498 			    "-g.\n\n");
499 			usage(argv[0]);
500 		}
501 
502 		if ((nsc_ping() != SUCCESS) ||
503 		    (client_getadmin(&current_admin) != 0)) {
504 			(void) fprintf(stderr,
505 			    "%s doesn't appear to be running.\n", argv[0]);
506 			exit(1);
507 		}
508 		client_showstats(&current_admin);
509 		exit(0);
510 	}
511 
512 
513 
514 	/*
515 	 *  Determine if there is already a daemon running
516 	 */
517 
518 	will_become_server = (nsc_ping() != SUCCESS);
519 
520 	/*
521 	 *	process usual options
522 	 */
523 
524 	/*
525 	 *  load normal config file
526 	 */
527 
528 	if (will_become_server) {
529 		static const nsc_stat_t defaults = {
530 			0,	/* stats */
531 			0,	/* stats */
532 			0,	/* stats */
533 			0,	/* stats */
534 			0,	/* stats */
535 			0,	/* stats */
536 			0,	/* stats */
537 			211,	/* suggested size */
538 			1,	/* enabled */
539 			0,	/* invalidate cmd */
540 			600,	/* positive ttl */
541 			10, 	/* netative ttl */
542 			20,	/* keep hot */
543 			0,	/* old data not ok */
544 			1 };	/* check files */
545 
546 		current_admin.passwd = defaults;
547 		current_admin.group  = defaults;
548 		current_admin.host   = defaults;
549 		current_admin.node   = defaults;
550 		current_admin.exec   = defaults;
551 		current_admin.prof   = defaults;
552 		current_admin.user   = defaults;
553 
554 		current_admin.logfile[0] = '\0';
555 
556 		if (access("/etc/nscd.conf", R_OK) == 0) {
557 			if (nscd_parse(argv[0], "/etc/nscd.conf") < 0) {
558 				exit(1);
559 			}
560 			loaded_config_file++;
561 		}
562 	}
563 
564 	else {
565 		if (client_getadmin(&current_admin)) {
566 			(void) fprintf(stderr,
567 			    "Cannot contact nscd properly(?)\n");
568 			exit(1);
569 		}
570 
571 		current_admin.logfile[0] = '\0';
572 	}
573 
574 	while ((opt = getopt(argc, argv,
575 	    "S:Kf:c:ge:p:n:i:l:d:s:h:o:")) != EOF) {
576 		nsc_stat_t *cache;
577 		char *cacheopt;
578 
579 		switch (opt) {
580 
581 		case 'S':		/* undocumented feature */
582 			doset++;
583 			cache = getcacheptr(optarg);
584 			cacheopt = getcacheopt(optarg);
585 			if (!cache || !cacheopt) {
586 				errflg++;
587 				break;
588 			}
589 			if (strcmp(cacheopt, "yes") == 0)
590 			    cache->nsc_secure_mode = 1;
591 			else if (strcmp(cacheopt, "no") == 0)
592 			    cache->nsc_secure_mode = 0;
593 			else
594 			    errflg++;
595 			break;
596 
597 		case 'K':		/* undocumented feature */
598 			client_killserver();
599 			exit(0);
600 			break;
601 
602 		case 'f':
603 			doset++;
604 			loaded_config_file++;
605 			if (nscd_parse(argv[0], optarg) < 0) {
606 				exit(1);
607 			}
608 			break;
609 
610 		case 'g':
611 			showstats++;
612 			break;
613 
614 		case 'p':
615 			doset++;
616 			cache = getcacheptr(optarg);
617 			cacheopt = getcacheopt(optarg);
618 			if (!cache || !cacheopt) {
619 				errflg++;
620 				break;
621 			}
622 			cache->nsc_pos_ttl = atoi(cacheopt);
623 			break;
624 
625 		case 'n':
626 			doset++;
627 			cache = getcacheptr(optarg);
628 			cacheopt = getcacheopt(optarg);
629 			if (!cache || !cacheopt) {
630 				errflg++;
631 				break;
632 			}
633 			cache->nsc_neg_ttl = atoi(cacheopt);
634 			break;
635 
636 		case 'c':
637 			doset++;
638 			cache = getcacheptr(optarg);
639 			cacheopt = getcacheopt(optarg);
640 			if (!cache || !cacheopt) {
641 				errflg++;
642 				break;
643 			}
644 
645 			if (strcmp(cacheopt, "yes") == 0)
646 			    cache->nsc_check_files = 1;
647 			else if (strcmp(cacheopt, "no") == 0)
648 			    cache->nsc_check_files = 0;
649 			else
650 			    errflg++;
651 			break;
652 
653 
654 		case 'i':
655 			doset++;
656 			cache = getcacheptr(optarg);
657 			if (!cache) {
658 				errflg++;
659 				break;
660 			}
661 			cache->nsc_invalidate = 1;
662 			break;
663 
664 		case 'l':
665 			doset++;
666 			(void) strlcpy(current_admin.logfile, optarg, 128);
667 			break;
668 
669 		case 'd':
670 
671 			doset++;
672 			current_admin.debug_level = atoi(optarg);
673 			break;
674 
675 		case 's':
676 			doset++;
677 			cache = getcacheptr(optarg);
678 			cacheopt = getcacheopt(optarg);
679 			if (!cache || !cacheopt) {
680 				errflg++;
681 				break;
682 			}
683 
684 			cache->nsc_suggestedsize = atoi(cacheopt);
685 
686 			break;
687 
688 		case 'h':
689 			doset++;
690 			cache = getcacheptr(optarg);
691 			cacheopt = getcacheopt(optarg);
692 			if (!cache || !cacheopt) {
693 				errflg++;
694 				break;
695 			}
696 			cache->nsc_keephot = atoi(cacheopt);
697 			break;
698 
699 		case 'o':
700 			doset++;
701 			cache = getcacheptr(optarg);
702 			cacheopt = getcacheopt(optarg);
703 			if (!cache || !cacheopt) {
704 				errflg++;
705 				break;
706 			}
707 			if (strcmp(cacheopt, "yes") == 0)
708 			    cache->nsc_old_data_ok = 1;
709 			else if (strcmp(cacheopt, "no") == 0)
710 			    cache->nsc_old_data_ok = 0;
711 			else
712 			    errflg++;
713 			break;
714 
715 		case 'e':
716 			doset++;
717 			cache = getcacheptr(optarg);
718 			cacheopt = getcacheopt(optarg);
719 			if (!cache || !cacheopt) {
720 				errflg++;
721 				break;
722 			}
723 			if (strcmp(cacheopt, "yes") == 0)
724 			    cache->nsc_enabled = 1;
725 			else if (strcmp(cacheopt, "no") == 0)
726 			    cache->nsc_enabled = 0;
727 			else
728 			    errflg++;
729 			break;
730 
731 		default:
732 			errflg++;
733 			break;
734 		}
735 
736 	}
737 
738 	if (errflg)
739 	    usage(argv[0]);
740 
741 	if (!will_become_server) {
742 
743 		if (showstats) {
744 			client_showstats(&current_admin);
745 		}
746 
747 		if (doset) {
748 			if (client_setadmin(&current_admin) < 0) {
749 				(void) fprintf(stderr,
750 					"Error during admin call\n");
751 				exit(1);
752 			}
753 		}
754 		if (!showstats && !doset) {
755 			(void) fprintf(stderr,
756 				"%s already running.... no admin specified\n",
757 				argv[0]);
758 		}
759 		exit(0);
760 	}
761 
762 	/*
763 	 *   daemon from here ou
764 	 */
765 
766 	if (!loaded_config_file) {
767 		(void) fprintf(stderr,
768 			"No configuration file specifed and /etc/nscd.conf" \
769 			"not present\n");
770 		exit(1);
771 	}
772 
773 	saved_argv = argv;
774 	save_execname();
775 
776 	if (current_admin.debug_level) {
777 		/* we're debugging... */
778 		if (strlen(current_admin.logfile) == 0)
779 		/* no specified log file */
780 			(void) strcpy(current_admin.logfile, "stderr");
781 		else
782 			(void) nscd_set_lf(&current_admin,
783 			    current_admin.logfile);
784 	} else {
785 		if (strlen(current_admin.logfile) == 0)
786 			(void) strcpy(current_admin.logfile, "/dev/null");
787 		(void) nscd_set_lf(&current_admin, current_admin.logfile);
788 		detachfromtty();
789 	}
790 
791 	/* perform some initialization */
792 	initialize_lookup_clearance();
793 	keep_open_dns_socket();
794 	getpw_init();
795 	getgr_init();
796 	gethost_init();
797 	getnode_init();
798 	getexec_init();
799 	getprof_init();
800 	getuser_init();
801 
802 	/* Establish our own server thread pool */
803 
804 	door_server_create(server_create);
805 	if (thr_keycreate(&server_key, server_destroy) != 0) {
806 		perror("thr_keycreate");
807 		exit(-1);
808 	}
809 
810 	/* Create a door */
811 
812 	if ((did = door_create(switcher, NAME_SERVICE_DOOR_COOKIE,
813 	    DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
814 		perror("door_create");
815 		exit(-1);
816 	}
817 
818 	/* bind to file system */
819 
820 	if (is_system_labeled()) {
821 		if (stat(TSOL_NAME_SERVICE_DOOR, &buf) < 0) {
822 			int newfd;
823 			if ((newfd = creat(TSOL_NAME_SERVICE_DOOR, 0444)) < 0) {
824 				logit("Cannot create %s:%s\n",
825 				    TSOL_NAME_SERVICE_DOOR, strerror(errno));
826 				exit(1);
827 			}
828 			(void) close(newfd);
829 		}
830 		if (symlink(TSOL_NAME_SERVICE_DOOR, NAME_SERVICE_DOOR) != 0) {
831 			if (errno != EEXIST) {
832 				logit("Cannot symlink %s:%s\n",
833 				    NAME_SERVICE_DOOR, strerror(errno));
834 				exit(1);
835 			}
836 		}
837 	} else if (stat(NAME_SERVICE_DOOR, &buf) < 0) {
838 		int newfd;
839 		if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) {
840 			logit("Cannot create %s:%s\n", NAME_SERVICE_DOOR,
841 			    strerror(errno));
842 			exit(1);
843 		}
844 		(void) close(newfd);
845 	}
846 
847 	if (fattach(did, NAME_SERVICE_DOOR) < 0) {
848 		if ((errno != EBUSY) ||
849 		    (fdetach(NAME_SERVICE_DOOR) <  0) ||
850 		    (fattach(did, NAME_SERVICE_DOOR) < 0)) {
851 			perror("door_attach");
852 			exit(2);
853 		}
854 	}
855 
856 	action.sa_handler = dozip;
857 	action.sa_flags = 0;
858 	(void) sigemptyset(&action.sa_mask);
859 	(void) sigemptyset(&myset);
860 	(void) sigaddset(&myset, SIGHUP);
861 
862 	if (sigaction(SIGHUP, &action, NULL) < 0) {
863 		perror("sigaction");
864 		exit(1);
865 	}
866 
867 	if (thr_sigsetmask(SIG_BLOCK, &myset, NULL) < 0) {
868 		perror("thr_sigsetmask");
869 		exit(1);
870 	}
871 
872 
873 	/*
874 	 *  kick off revalidate threads
875 	 */
876 
877 	if (thr_create(NULL, NULL,
878 		(void *(*)(void *))getpw_revalidate, 0, 0, NULL) != 0) {
879 		perror("thr_create");
880 		exit(1);
881 	}
882 
883 	if (thr_create(NULL, NULL,
884 		(void *(*)(void *))gethost_revalidate, 0, 0, NULL) != 0) {
885 		perror("thr_create");
886 		exit(1);
887 	}
888 
889 	if (thr_create(NULL, NULL,
890 		(void *(*)(void*))getnode_revalidate, 0, 0, NULL) != 0) {
891 		perror("thr_create");
892 		exit(1);
893 	}
894 
895 	if (thr_create(NULL, NULL,
896 		(void *(*)(void*))getgr_revalidate, 0, 0, NULL) != 0) {
897 		perror("thr_create");
898 		exit(1);
899 	}
900 
901 	if (thr_create(NULL, NULL,
902 	    (void *(*)(void*))getexec_revalidate, 0, 0, NULL) != 0) {
903 		perror("thr_create");
904 		exit(1);
905 	}
906 
907 	if (thr_create(NULL, NULL,
908 	    (void *(*)(void*))getprof_revalidate, 0, 0, NULL) != 0) {
909 		perror("thr_create");
910 		exit(1);
911 	}
912 
913 	if (thr_create(NULL, NULL,
914 	    (void *(*)(void*))getuser_revalidate, 0, 0, NULL) != 0) {
915 		perror("thr_create");
916 		exit(1);
917 	}
918 
919 	/*
920 	 *  kick off reaper threads
921 	 */
922 
923 	if (thr_create(NULL, NULL,
924 	    (void *(*)(void *))getpw_uid_reaper, 0, 0, NULL) != 0) {
925 		perror("thr_create");
926 		exit(1);
927 	}
928 
929 	if (thr_create(NULL, NULL,
930 	    (void *(*)(void *))getpw_nam_reaper, 0, 0, NULL) != 0) {
931 		perror("thr_create");
932 		exit(1);
933 	}
934 
935 	if (thr_create(NULL, NULL,
936 	    (void *(*)(void *))getgr_uid_reaper, 0, 0, NULL) != 0) {
937 		perror("thr_create");
938 		exit(1);
939 	}
940 
941 	if (thr_create(NULL, NULL,
942 	    (void *(*)(void *))getgr_nam_reaper, 0, 0, NULL) != 0) {
943 		perror("thr_create");
944 		exit(1);
945 	}
946 
947 
948 	if (thr_create(NULL, NULL,
949 	    (void *(*)(void *))gethost_nam_reaper, 0, 0, NULL) != 0) {
950 		perror("thr_create");
951 		exit(1);
952 	}
953 
954 	if (thr_create(NULL, NULL,
955 	    (void *(*)(void *))gethost_addr_reaper, 0, 0, NULL) != 0) {
956 		perror("thr_create");
957 		exit(1);
958 	}
959 
960 	if (thr_create(NULL, NULL,
961 	    (void *(*)(void *))getnode_nam_reaper, 0, 0, NULL) != 0) {
962 		perror("thr_create");
963 		exit(1);
964 	}
965 
966 	if (thr_create(NULL, NULL,
967 	    (void *(*)(void *))getnode_addr_reaper, 0, 0, NULL) != 0) {
968 		perror("thr_create");
969 		exit(1);
970 	}
971 
972 	if (thr_create(NULL, NULL,
973 	    (void *(*)(void *))getexec_reaper, 0, 0, NULL) != 0) {
974 		perror("thr_create");
975 		exit(1);
976 	}
977 
978 	if (thr_create(NULL, NULL,
979 	    (void *(*)(void *))getprof_reaper, 0, 0, NULL) != 0) {
980 		perror("thr_create");
981 		exit(1);
982 	}
983 
984 	if (thr_create(NULL, NULL,
985 	    (void *(*)(void *))getuser_reaper, 0, 0, NULL) != 0) {
986 		perror("thr_create");
987 		exit(1);
988 	}
989 
990 	/*
991 	 * kick off routing socket monitor thread
992 	 */
993 
994 	if (thr_create(NULL, NULL,
995 		(void *(*)(void *))rts_mon, 0, 0, NULL) != 0) {
996 		perror("thr_create");
997 		exit(1);
998 	}
999 
1000 	if (thr_sigsetmask(SIG_UNBLOCK, &myset, NULL) < 0) {
1001 		perror("thr_sigsetmask");
1002 		return (1);
1003 	}
1004 
1005 	for (;;) {
1006 		(void) pause();
1007 		logit("Reloading /etc/nscd.conf\n");
1008 		nscd_parse(argv[0], "/etc/nscd.conf");
1009 	}
1010 }
1011 
1012 
1013 /*ARGSUSED*/
1014 static void
1015 switcher(void *cookie, char *argp, size_t arg_size,
1016     door_desc_t *dp, uint_t n_desc)
1017 {
1018 	union {
1019 		nsc_data_t	data;
1020 		char		space[8192];
1021 	} u;
1022 
1023 	time_t now;
1024 
1025 	static time_t last_nsswitch_check;
1026 	static time_t last_nsswitch_modified;
1027 	static time_t last_resolv_modified;
1028 
1029 	static mutex_t nsswitch_lock;
1030 
1031 	nsc_call_t *ptr = (nsc_call_t *)argp;
1032 
1033 	if (argp == DOOR_UNREF_DATA) {
1034 		(void) printf("Door Slam... exiting\n");
1035 		exit(0);
1036 	}
1037 
1038 	if (ptr == NULL) { /* empty door call */
1039 		(void) door_return(NULL, 0, 0, 0); /* return the favor */
1040 	}
1041 
1042 	now = time(NULL);
1043 
1044 	/*
1045 	 *  just in case check
1046 	 */
1047 
1048 	(void) mutex_lock(&nsswitch_lock);
1049 
1050 	if (now - last_nsswitch_check > 10) {
1051 		struct stat nss_buf;
1052 		struct stat res_buf;
1053 
1054 		last_nsswitch_check = now;
1055 
1056 		(void) mutex_unlock(&nsswitch_lock); /* let others continue */
1057 
1058 		/*
1059 		 *  This code keeps us from statting resolv.conf
1060 		 *  if it doesn't exist, yet prevents us from ignoring
1061 		 *  it if it happens to disappear later on for a bit.
1062 		 */
1063 
1064 		if (last_resolv_modified >= 0) {
1065 			if (stat("/etc/resolv.conf", &res_buf) < 0) {
1066 				if (last_resolv_modified == 0)
1067 				    last_resolv_modified = -1;
1068 				else
1069 				    res_buf.st_mtime = last_resolv_modified;
1070 			} else if (last_resolv_modified == 0) {
1071 			    last_resolv_modified = res_buf.st_mtime;
1072 			}
1073 		}
1074 
1075 		if (stat("/etc/nsswitch.conf", &nss_buf) < 0) {
1076 
1077 			/*EMPTY*/;
1078 
1079 		} else if (last_nsswitch_modified == 0) {
1080 
1081 			last_nsswitch_modified = nss_buf.st_mtime;
1082 
1083 		} else if ((last_nsswitch_modified < nss_buf.st_mtime) ||
1084 		    ((last_resolv_modified > 0) &&
1085 		    (last_resolv_modified < res_buf.st_mtime))) {
1086 			static mutex_t exit_lock;
1087 			char *fmri;
1088 			/*
1089 			 * time for restart
1090 			 */
1091 			logit("nscd restart due to /etc/nsswitch.conf or "\
1092 				"resolv.conf change\n");
1093 			/*
1094 			 * try to restart under smf
1095 			 */
1096 			if ((fmri = getenv("SMF_FMRI")) == NULL) {
1097 				/* not running under smf - reexec */
1098 				execv(saved_execname, saved_argv);
1099 				exit(1); /* just in case */
1100 			}
1101 
1102 			mutex_lock(&exit_lock); /* prevent multiple restarts */
1103 			if (smf_restart_instance(fmri) == 0)
1104 				sleep(10); /* wait a bit */
1105 			exit(1); /* give up waiting for resurrection */
1106 		}
1107 
1108 	} else
1109 	    (void) mutex_unlock(&nsswitch_lock);
1110 
1111 	switch (ptr->nsc_callnumber) {
1112 
1113 	case NULLCALL:
1114 		u.data.nsc_ret.nsc_return_code = SUCCESS;
1115 		u.data.nsc_ret.nsc_bufferbytesused = sizeof (nsc_return_t);
1116 		break;
1117 
1118 
1119 	case GETPWNAM:
1120 		*(argp + arg_size - 1) = 0; /* FALLTHROUGH */
1121 	case GETPWUID:
1122 		getpw_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
1123 		break;
1124 
1125 	case GETGRNAM:
1126 		*(argp + arg_size - 1) = 0; /* FALLTHROUGH */
1127 	case GETGRGID:
1128 		getgr_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
1129 		break;
1130 
1131 	case GETHOSTBYNAME:
1132 		*(argp + arg_size - 1) = 0; /* FALLTHROUGH */
1133 	case GETHOSTBYADDR:
1134 		gethost_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
1135 		break;
1136 
1137 	case GETIPNODEBYNAME:
1138 		*(argp + arg_size - 1) = 0; /* FALLTHROUGH */
1139 	case GETIPNODEBYADDR:
1140 		getnode_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
1141 		break;
1142 
1143 	case GETEXECID:
1144 		*(argp + arg_size - 1) = 0;
1145 		getexec_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
1146 		break;
1147 
1148 	case GETPROFNAM:
1149 		*(argp + arg_size - 1) = 0;
1150 		getprof_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
1151 		break;
1152 
1153 	case GETUSERNAM:
1154 		*(argp + arg_size - 1) = 0;
1155 		getuser_lookup(&u.data.nsc_ret, sizeof (u), ptr, now);
1156 		break;
1157 
1158 	case GETADMIN:
1159 		getadmin(&u.data.nsc_ret, sizeof (u), ptr);
1160 		break;
1161 
1162 	case SETADMIN:
1163 	case KILLSERVER: {
1164 
1165 		ucred_t *uc = NULL;
1166 		const priv_set_t *eset;
1167 		zoneid_t zoneid;
1168 
1169 		if (door_ucred(&uc) != 0) {
1170 			perror("door_ucred");
1171 			u.data.nsc_ret.nsc_return_code = NOTFOUND;
1172 			break;
1173 		}
1174 
1175 		eset = ucred_getprivset(uc, PRIV_EFFECTIVE);
1176 		zoneid = ucred_getzoneid(uc);
1177 
1178 		if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) ||
1179 		    eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) :
1180 		    ucred_geteuid(uc) != 0) {
1181 			logit("SETADMIN call failed(cred): caller pid %d, "
1182 			    "uid %d, euid %d, zoneid %d\n", ucred_getpid(uc),
1183 			    ucred_getruid(uc), ucred_geteuid(uc), zoneid);
1184 			u.data.nsc_ret.nsc_return_code = NOTFOUND;
1185 			ucred_free(uc);
1186 			break;
1187 		}
1188 
1189 		if (ptr->nsc_callnumber == KILLSERVER) {
1190 			logit("Nscd received KILLSERVER cmd from pid %d, "
1191 			    "uid %d, euid %d, zoneid %d\n", ucred_getpid(uc),
1192 			    ucred_getruid(uc), ucred_geteuid(uc), zoneid);
1193 			exit(0);
1194 		} else {
1195 			if (setadmin(&u.data.nsc_ret, sizeof (u), ptr) != 0)
1196 				logit("SETADMIN call failed\n");
1197 		}
1198 		ucred_free(uc);
1199 		break;
1200 	}
1201 
1202 	default:
1203 		logit("Unknown name service door call op %d\n",
1204 		    ptr->nsc_callnumber);
1205 		u.data.nsc_ret.nsc_return_code = -1;
1206 		u.data.nsc_ret.nsc_bufferbytesused = sizeof (nsc_return_t);
1207 		break;
1208 
1209 	}
1210 	door_return((char *)&u.data, u.data.nsc_ret.nsc_bufferbytesused,
1211 	    NULL, 0);
1212 }
1213 
1214 /*
1215  * Monitor the routing socket.  Address lists stored in the ipnodes
1216  * cache are sorted based on destination address selection rules,
1217  * so when things change that could affect that sorting (interfaces
1218  * go up or down, flags change, etc.), we clear that cache so the
1219  * list will be re-ordered the next time the hostname is resolved.
1220  */
1221 static void
1222 rts_mon(void)
1223 {
1224 	int	rt_sock, rdlen;
1225 	union {
1226 		struct {
1227 			struct rt_msghdr rtm;
1228 			struct sockaddr_storage addrs[RTA_NUMBITS];
1229 		} r;
1230 		struct if_msghdr ifm;
1231 		struct ifa_msghdr ifam;
1232 	} mbuf;
1233 	struct ifa_msghdr *ifam = &mbuf.ifam;
1234 
1235 	rt_sock = socket(PF_ROUTE, SOCK_RAW, 0);
1236 	if (rt_sock < 0) {
1237 		logit("Failed to open routing socket: %s\n", strerror(errno));
1238 		thr_exit(0);
1239 	}
1240 
1241 	for (;;) {
1242 		rdlen = read(rt_sock, &mbuf, sizeof (mbuf));
1243 		if (rdlen <= 0) {
1244 			if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) {
1245 				logit("routing socket read: %s\n",
1246 				    strerror(errno));
1247 				thr_exit(0);
1248 			}
1249 			continue;
1250 		}
1251 		if (ifam->ifam_version != RTM_VERSION) {
1252 			logit("rx unknown version (%d) on routing socket.\n",
1253 			    ifam->ifam_version);
1254 			continue;
1255 		}
1256 		switch (ifam->ifam_type) {
1257 		case RTM_NEWADDR:
1258 		case RTM_DELADDR:
1259 			getnode_name_invalidate();
1260 			break;
1261 		case RTM_ADD:
1262 		case RTM_DELETE:
1263 		case RTM_CHANGE:
1264 		case RTM_GET:
1265 		case RTM_LOSING:
1266 		case RTM_REDIRECT:
1267 		case RTM_MISS:
1268 		case RTM_LOCK:
1269 		case RTM_OLDADD:
1270 		case RTM_OLDDEL:
1271 		case RTM_RESOLVE:
1272 		case RTM_IFINFO:
1273 			break;
1274 		default:
1275 			logit("rx unknown msg type (%d) on routing socket.\n",
1276 			    ifam->ifam_type);
1277 			break;
1278 		}
1279 	}
1280 }
1281 
1282 static void
1283 usage(char *s)
1284 {
1285 	(void) fprintf(stderr,
1286 		"Usage: %s [-d debug_level] [-l logfilename]\n", s);
1287 	(void) fprintf(stderr,
1288 		"	[-p cachename,positive_time_to_live]\n");
1289 	(void) fprintf(stderr,
1290 		"	[-n cachename,negative_time_to_live]\n");
1291 	(void) fprintf(stderr,
1292 		"	[-i cachename] [-s cachename,suggestedsize]\n");
1293 
1294 	(void) fprintf(stderr,
1295 		"	[-h cachename,keep_hot_count] "\
1296 		"[-o cachename,\"yes\"|\"no\"]\n");
1297 
1298 	(void) fprintf(stderr,
1299 		"	[-e cachename,\"yes\"|\"no\"] [-g] " \
1300 		"[-c cachename,\"yes\"|\"no\"]\n");
1301 
1302 	(void) fprintf(stderr,
1303 		"	[-f configfilename] \n");
1304 
1305 	(void) fprintf(stderr,
1306 		"\n	Supported caches: passwd, group, hosts, ipnodes\n");
1307 
1308 	(void) fprintf(stderr,
1309 		"         exec_attr, prof_attr, and user_attr.\n");
1310 
1311 	exit(1);
1312 
1313 }
1314 
1315 
1316 static int logfd = 2;
1317 
1318 int
1319 nscd_set_lf(admin_t *ptr, char *s)
1320 {
1321 	int newlogfd;
1322 
1323 	/*
1324 	 *  we don't really want to try and open the log file
1325 	 *  /dev/null since that will fail w/ our security fixes
1326 	 */
1327 
1328 	if (*s == 0) {
1329 		/* ignore empty log file specs */
1330 		/*EMPTY*/;
1331 	} else if (s == NULL || strcmp(s, "/dev/null") == 0) {
1332 		(void) strcpy(current_admin.logfile, "/dev/null");
1333 		(void) close(logfd);
1334 		logfd = -1;
1335 	} else {
1336 		/*
1337 		 * In order to open this file securely, we'll try a few tricks
1338 		 */
1339 
1340 		if ((newlogfd = open(s, O_EXCL|O_WRONLY|O_CREAT, 0644)) < 0) {
1341 			/*
1342 			 * File already exists... now we need to get cute
1343 			 * since opening a file in a world-writeable directory
1344 			 * safely is hard = it could be a hard link or a
1345 			 * symbolic link to a system file.
1346 			 */
1347 			struct stat before;
1348 
1349 			if (lstat(s, &before) < 0) {
1350 				logit("Cannot open new logfile \"%s\": %sn",
1351 					s, strerror(errno));
1352 				return (-1);
1353 			}
1354 
1355 			if (S_ISREG(before.st_mode) && /* no symbolic links */
1356 				(before.st_nlink == 1) && /* no hard links */
1357 				(before.st_uid == 0)) {   /* owned by root */
1358 				if ((newlogfd =
1359 				    open(s, O_APPEND|O_WRONLY, 0644)) < 0) {
1360 					logit("Cannot open new "\
1361 					    "logfile \"%s\": %s\n", s,
1362 					    strerror(errno));
1363 					return (-1);
1364 				}
1365 			} else {
1366 				logit("Cannot use specified logfile \"%s\": "\
1367 				    "file is/has links or isn't owned by "\
1368 				    "root\n", s);
1369 				return (-1);
1370 			}
1371 		}
1372 
1373 		(void) strlcpy(ptr->logfile, s, 128);
1374 		(void) close(logfd);
1375 		logfd = newlogfd;
1376 		logit("Start of new logfile %s\n", s);
1377 	}
1378 	return (0);
1379 }
1380 
1381 void
1382 logit(char *format, ...)
1383 {
1384 	static mutex_t loglock;
1385 	struct timeval tv;
1386 
1387 #define	LOGBUFLEN	1024
1388 	char buffer[LOGBUFLEN];
1389 
1390 	va_list ap;
1391 	va_start(ap, format);
1392 
1393 	if (logfd >= 0) {
1394 		int safechars, offset;
1395 		if (gettimeofday(&tv, NULL) != 0 ||
1396 		    ctime_r(&tv.tv_sec, buffer, LOGBUFLEN) == NULL) {
1397 			(void) snprintf(buffer, LOGBUFLEN,
1398 			    "<time conversion failed>\t");
1399 		} else {
1400 			/*
1401 			 * ctime_r() includes some stuff we don't want;
1402 			 * adjust length to overwrite " YYYY\n".
1403 			 */
1404 			offset = strlen(buffer) - 6;
1405 			safechars = LOGBUFLEN - (offset - 1);
1406 			(void) snprintf(buffer + offset, safechars, ".%.4ld\t",
1407 			    tv.tv_usec/100);
1408 		}
1409 		offset = strlen(buffer);
1410 		safechars = LOGBUFLEN - (offset - 1);
1411 		if (vsnprintf(buffer + offset, safechars, format, ap) >
1412 		    safechars) {
1413 			(void) strncat(buffer, "...\n", LOGBUFLEN);
1414 		}
1415 
1416 		(void) mutex_lock(&loglock);
1417 		(void) write(logfd, buffer, strlen(buffer));
1418 		(void) mutex_unlock(&loglock);
1419 	}
1420 
1421 	va_end(ap);
1422 #undef	LOGBUFLEN
1423 }
1424 
1425 static void
1426 do_update(nsc_call_t *in)
1427 {
1428 	union {
1429 		nsc_data_t	data;
1430 		char		space[8192];
1431 	} u;
1432 
1433 	time_t now = time(NULL);
1434 
1435 	switch (MASKUPDATEBIT(in->nsc_callnumber)) {
1436 
1437 	case GETPWUID:
1438 	case GETPWNAM:
1439 		getpw_lookup(&u.data.nsc_ret, sizeof (u), in, now);
1440 		break;
1441 
1442 	case GETGRNAM:
1443 	case GETGRGID:
1444 		getgr_lookup(&u.data.nsc_ret, sizeof (u), in, now);
1445 		break;
1446 
1447 	case GETHOSTBYNAME:
1448 	case GETHOSTBYADDR:
1449 		gethost_lookup(&u.data.nsc_ret, sizeof (u), in, now);
1450 		break;
1451 
1452 	case GETIPNODEBYNAME:
1453 	case GETIPNODEBYADDR:
1454 		getnode_lookup(&u.data.nsc_ret, sizeof (u), in, now);
1455 		break;
1456 
1457 	case GETEXECID:
1458 		getexec_lookup(&u.data.nsc_ret, sizeof (u), in, now);
1459 		break;
1460 
1461 	case GETPROFNAM:
1462 		getprof_lookup(&u.data.nsc_ret, sizeof (u), in, now);
1463 		break;
1464 
1465 	case GETUSERNAM:
1466 		getuser_lookup(&u.data.nsc_ret, sizeof (u), in, now);
1467 		break;
1468 
1469 	default:
1470 		assert(0);
1471 		break;
1472 	}
1473 
1474 	free(in);
1475 }
1476 
1477 int
1478 launch_update(nsc_call_t *in)
1479 {
1480 	nsc_call_t *c;
1481 
1482 	int l = nsc_calllen(in);
1483 
1484 	in->nsc_callnumber |= UPDATEBIT;
1485 
1486 	if ((c = malloc(l)) == NULL) {
1487 		logit("thread create failed: %s\n", strerror(errno));
1488 		exit(1);
1489 	}
1490 	(void) memcpy(c, in, l);
1491 
1492 	if (current_admin.debug_level >= DBG_ALL) {
1493 		logit("launching update\n");
1494 	}
1495 
1496 	if (thr_create(NULL,
1497 	    NULL,
1498 	    (void *(*)(void*))do_update,
1499 	    c,
1500 	    0|THR_DETACHED, NULL) != 0) {
1501 		logit("thread create failed\n");
1502 		exit(1);
1503 	}
1504 
1505 	return (0);
1506 }
1507 
1508 static int
1509 nsc_calllen(nsc_call_t *in)
1510 {
1511 	switch (MASKUPDATEBIT(in->nsc_callnumber)) {
1512 
1513 	case GETPWUID:
1514 	case GETGRGID:
1515 	case NULLCALL:
1516 		return (sizeof (*in));
1517 
1518 	case GETPWNAM:
1519 	case GETGRNAM:
1520 	case GETHOSTBYNAME:
1521 		return (sizeof (*in) + strlen(in->nsc_u.name));
1522 	case GETIPNODEBYNAME:
1523 		return (sizeof (*in) + strlen(in->nsc_u.ipnode.name));
1524 
1525 	case GETHOSTBYADDR:
1526 	case GETIPNODEBYADDR:
1527 		return (sizeof (*in) + in->nsc_u.addr.a_length);
1528 
1529 	case GETEXECID:
1530 	case GETPROFNAM:
1531 	case GETUSERNAM:
1532 
1533 		return (sizeof (*in) + strlen(in->nsc_u.name));
1534 	}
1535 
1536 	return (0);
1537 }
1538 
1539 static int
1540 client_getadmin(admin_t *ptr)
1541 {
1542 	union {
1543 		nsc_data_t data;
1544 		char space[8192];
1545 	} u;
1546 
1547 	nsc_data_t *dptr;
1548 	int ndata;
1549 	int adata;
1550 
1551 	u.data.nsc_call.nsc_callnumber = GETADMIN;
1552 	ndata = sizeof (u);
1553 	adata = sizeof (u.data);
1554 	dptr = &u.data;
1555 
1556 	if (_nsc_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) {
1557 		return (-1);
1558 	}
1559 
1560 	(void) memcpy(ptr, dptr->nsc_ret.nsc_u.buff, sizeof (*ptr));
1561 	return (0);
1562 }
1563 
1564 /*ARGSUSED*/
1565 static void
1566 getadmin(nsc_return_t *out, int size, nsc_call_t *ptr)
1567 {
1568 	out->nsc_return_code = SUCCESS;
1569 	out->nsc_bufferbytesused = sizeof (current_admin);
1570 	(void) memcpy(out->nsc_u.buff, &current_admin, sizeof (current_admin));
1571 }
1572 
1573 
1574 static int
1575 nscd_set_rbac(admin_t *new_admin, int invalidate)
1576 {
1577 	int		i;
1578 	char		*dbname = NULL;
1579 	nsc_stat_t	*cache = NULL;
1580 	nsc_stat_t	*new = NULL;
1581 	void		(*invalidate_func)(void);
1582 
1583 
1584 	for (i = 1; i <= 3; i++) {
1585 		/*
1586 		 * Three of the RBAC databases are cached.
1587 		 */
1588 		switch (i) {
1589 		case 1:
1590 			dbname = NSS_DBNAM_EXECATTR;
1591 			cache = &current_admin.exec;
1592 			new = &new_admin->exec;
1593 			invalidate_func = getexec_invalidate;
1594 			break;
1595 		case 2:
1596 			dbname = NSS_DBNAM_PROFATTR;
1597 			cache = &current_admin.prof;
1598 			new = &new_admin->prof;
1599 			invalidate_func = getprof_invalidate;
1600 			break;
1601 		case 3:
1602 			dbname = NSS_DBNAM_USERATTR;
1603 			cache = &current_admin.user;
1604 			new = &new_admin->user;
1605 			invalidate_func = getuser_invalidate;
1606 			break;
1607 		default:
1608 			break;
1609 		}
1610 
1611 		if (invalidate) {
1612 			if (new->nsc_invalidate) {
1613 				logit("Invalidating %s cache\n", dbname);
1614 				(*invalidate_func)();
1615 			}
1616 		} else {
1617 			if (nscd_set_ttl_positive(cache, dbname,
1618 			    new->nsc_pos_ttl) < 0 ||
1619 			    nscd_set_ttl_negative(cache, dbname,
1620 			    new->nsc_neg_ttl) < 0 ||
1621 			    nscd_set_khc(cache, dbname, new->nsc_keephot) < 0 ||
1622 			    nscd_set_odo(cache, dbname,
1623 			    new->nsc_old_data_ok) < 0 ||
1624 			    nscd_set_ec(cache, dbname, new->nsc_enabled) < 0 ||
1625 			    nscd_set_ss(cache, dbname,
1626 			    new->nsc_suggestedsize) < 0)
1627 				return (-1);
1628 		}
1629 	}
1630 
1631 	return (0);
1632 }
1633 
1634 /*ARGSUSED*/
1635 static int
1636 setadmin(nsc_return_t *out, int size, nsc_call_t *ptr)
1637 {
1638 	admin_t *new;
1639 
1640 	out->nsc_return_code = SUCCESS;
1641 	out->nsc_bufferbytesused = sizeof (nsc_return_t);
1642 
1643 	new = (admin_t *)ptr->nsc_u.name;
1644 
1645 
1646 	/*
1647 	 *  global admin stuff
1648 	 */
1649 
1650 	if ((nscd_set_lf(&current_admin, new->logfile) < 0) ||
1651 	    nscd_set_dl(&current_admin, new->debug_level) < 0) {
1652 		out->nsc_return_code = NOTFOUND;
1653 		return (-1);
1654 	}
1655 
1656 	/*
1657 	 * per cache items
1658 	 */
1659 
1660 	if (new->passwd.nsc_invalidate) {
1661 		logit("Invalidating passwd cache\n");
1662 		getpw_invalidate();
1663 	}
1664 
1665 	if (new->group.nsc_invalidate) {
1666 		logit("Invalidating group cache\n");
1667 		getgr_invalidate();
1668 	}
1669 
1670 	if (new->host.nsc_invalidate) {
1671 		logit("Invalidating host cache\n");
1672 		gethost_invalidate();
1673 	}
1674 
1675 	if (new->node.nsc_invalidate) {
1676 		logit("Invalidating ipnodes cache\n");
1677 		getnode_invalidate();
1678 	}
1679 
1680 	(void) nscd_set_rbac(new, 1);		/* invalidate rbac cache */
1681 
1682 	if (nscd_set_ttl_positive(&current_admin.passwd,
1683 			"passwd",
1684 			new->passwd.nsc_pos_ttl) < 0		||
1685 	    nscd_set_ttl_negative(&current_admin.passwd,
1686 			"passwd",
1687 			new->passwd.nsc_neg_ttl) < 0		||
1688 	    nscd_set_khc(&current_admin.passwd,
1689 			"passwd",
1690 			new->passwd.nsc_keephot) < 0		||
1691 	    nscd_set_odo(&current_admin.passwd,
1692 			"passwd",
1693 			new->passwd.nsc_old_data_ok) < 0	||
1694 	    nscd_set_ec(&current_admin.passwd,
1695 			"passwd",
1696 			new->passwd.nsc_enabled) < 0		||
1697 	    nscd_set_ss(&current_admin.passwd,
1698 			"passwd",
1699 			new->passwd.nsc_suggestedsize) < 0	   ||
1700 
1701 	    nscd_set_ttl_positive(&current_admin.group,
1702 			"group",
1703 			new->group.nsc_pos_ttl) < 0		||
1704 	    nscd_set_ttl_negative(&current_admin.group,
1705 			"group",
1706 			new->group.nsc_neg_ttl) < 0		||
1707 	    nscd_set_khc(&current_admin.group,
1708 			"group",
1709 			new->group.nsc_keephot) < 0		||
1710 	    nscd_set_odo(&current_admin.group,
1711 			"group",
1712 			new->group.nsc_old_data_ok) < 0		||
1713 	    nscd_set_ec(&current_admin.group,
1714 			"group",
1715 			new->group.nsc_enabled) < 0		||
1716 	    nscd_set_ss(&current_admin.group,
1717 			"group",
1718 			new->group.nsc_suggestedsize) < 0	||
1719 
1720 	    nscd_set_ttl_positive(&current_admin.node,
1721 			"ipnodes",
1722 			new->node.nsc_pos_ttl) < 0		||
1723 	    nscd_set_ttl_negative(&current_admin.node,
1724 			"ipnodes",
1725 			new->node.nsc_neg_ttl) < 0		||
1726 	    nscd_set_khc(&current_admin.node,
1727 			"ipnodes",
1728 			new->node.nsc_keephot) < 0		||
1729 	    nscd_set_odo(&current_admin.node,
1730 			"ipnodes",
1731 			new->node.nsc_old_data_ok) < 0		||
1732 	    nscd_set_ec(&current_admin.node,
1733 			"ipnodes",
1734 			new->node.nsc_enabled) < 0		||
1735 	    nscd_set_ss(&current_admin.node,
1736 			"ipnodes",
1737 			new->node.nsc_suggestedsize) < 0	||
1738 
1739 	    nscd_set_ttl_positive(&current_admin.host,
1740 			"hosts",
1741 			new->host.nsc_pos_ttl) < 0		||
1742 	    nscd_set_ttl_negative(&current_admin.host,
1743 			"hosts",
1744 			new->host.nsc_neg_ttl) < 0		||
1745 	    nscd_set_khc(&current_admin.host,
1746 			"hosts",
1747 			new->host.nsc_keephot) < 0		||
1748 	    nscd_set_odo(&current_admin.host,
1749 			"hosts",
1750 			new->host.nsc_old_data_ok) < 0		||
1751 	    nscd_set_ec(&current_admin.host,
1752 			"hosts",
1753 			new->host.nsc_enabled) < 0		||
1754 	    nscd_set_ss(&current_admin.host,
1755 			"hosts",
1756 			new->host.nsc_suggestedsize) < 0	||
1757 	    nscd_set_rbac(new, 0) < 0) {
1758 		out->nsc_return_code = NOTFOUND;
1759 		return (-1);
1760 	}
1761 	out->nsc_return_code = SUCCESS;
1762 	return (0);
1763 }
1764 
1765 void
1766 client_killserver(void)
1767 {
1768 	union {
1769 		nsc_data_t data;
1770 		char space[8192];
1771 	} u;
1772 
1773 	nsc_data_t *dptr;
1774 	int ndata;
1775 	int adata;
1776 
1777 	u.data.nsc_call.nsc_callnumber = KILLSERVER;
1778 
1779 	ndata = sizeof (u);
1780 	adata = sizeof (nsc_call_t);
1781 
1782 	dptr = &u.data;
1783 
1784 	_nsc_trydoorcall(&dptr, &ndata, &adata);
1785 }
1786 
1787 
1788 static int
1789 client_setadmin(admin_t *ptr)
1790 {
1791 	union {
1792 		nsc_data_t data;
1793 		char space[8192];
1794 	} u;
1795 
1796 	nsc_data_t *dptr;
1797 	int ndata;
1798 	int adata;
1799 
1800 	u.data.nsc_call.nsc_callnumber = SETADMIN;
1801 
1802 	(void) memcpy(u.data.nsc_call.nsc_u.name, ptr, sizeof (*ptr));
1803 
1804 	ndata = sizeof (u);
1805 	adata = sizeof (*ptr);
1806 
1807 	dptr = &u.data;
1808 
1809 	if (_nsc_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) {
1810 		return (-1);
1811 	}
1812 
1813 	return (0);
1814 }
1815 
1816 static void
1817 dump_stat(nsc_stat_t *ptr)
1818 {
1819 	double hitrate;
1820 	(void) printf("%10s  cache is enabled\n",
1821 	    (ptr->nsc_enabled?"Yes":"No"));
1822 	(void) printf("%10d  cache hits on positive entries\n",
1823 	    ptr->nsc_pos_cache_hits);
1824 	(void) printf("%10d  cache hits on negative entries\n",
1825 	    ptr->nsc_neg_cache_hits);
1826 	(void) printf("%10d  cache misses on positive entries\n",
1827 	    ptr->nsc_pos_cache_misses);
1828 	(void) printf("%10d  cache misses on negative entries\n",
1829 	    ptr->nsc_neg_cache_misses);
1830 	hitrate = ptr->nsc_pos_cache_misses + ptr->nsc_neg_cache_misses +
1831 	    ptr->nsc_pos_cache_hits + ptr->nsc_neg_cache_hits;
1832 
1833 	if (hitrate > 0.0)
1834 		hitrate = (100.0 * ((double)ptr->nsc_pos_cache_hits +
1835 		    (double)ptr->nsc_neg_cache_hits))/hitrate;
1836 
1837 	(void) printf("%10.1f%% cache hit rate\n",  hitrate);
1838 	(void) printf("%10d  queries deferred\n", ptr->nsc_throttle_count);
1839 	(void) printf("%10d  total entries\n", ptr->nsc_entries);
1840 	(void) printf("%10d  complete cache invalidations\n",
1841 	    ptr->nsc_invalidate_count);
1842 	(void) printf("%10d  suggested size\n", ptr->nsc_suggestedsize);
1843 	(void) printf("%10d  seconds time to live for positive entries\n",
1844 	    ptr->nsc_pos_ttl);
1845 	(void) printf("%10d  seconds time to live for negative entries\n",
1846 	    ptr->nsc_neg_ttl);
1847 	(void) printf("%10d  most active entries to be kept valid\n",
1848 	    ptr->nsc_keephot);
1849 	(void) printf("%10s  check /etc/{passwd, group, hosts, inet/ipnodes} "
1850 	    "file for changes\n",
1851 	    (ptr->nsc_check_files?"Yes":"No"));
1852 
1853 	(void) printf("%10s  use possibly stale data rather than waiting for "
1854 	    "refresh\n",
1855 	    (ptr->nsc_old_data_ok?"Yes":"No"));
1856 }
1857 
1858 static void
1859 client_showstats(admin_t *ptr)
1860 {
1861 
1862 	(void) printf("nscd configuration:\n\n");
1863 	(void) printf("%10d  server debug level\n", ptr->debug_level);
1864 	(void) printf("\"%s\"  is server log file\n", ptr->logfile);
1865 
1866 	(void) printf("\npasswd cache:\n\n");
1867 	dump_stat(&(ptr->passwd));
1868 	(void) printf("\ngroup cache:\n\n");
1869 	dump_stat(&(ptr->group));
1870 	(void) printf("\nhosts cache:\n\n");
1871 	dump_stat(&(ptr->host));
1872 	(void) printf("\nipnodes cache:\n\n");
1873 	dump_stat(&(ptr->node));
1874 	(void) printf("\nexec_attr cache:\n\n");
1875 	dump_stat(&(ptr->exec));
1876 	(void) printf("\nprof_attr cache:\n\n");
1877 	dump_stat(&(ptr->prof));
1878 	(void) printf("\nuser_attr cache:\n\n");
1879 	dump_stat(&(ptr->user));
1880 }
1881 
1882 
1883 
1884 /*
1885  * detach from tty
1886  */
1887 static void
1888 detachfromtty(void)
1889 {
1890 	if (logfd > 0) {
1891 		int i;
1892 		for (i = 0; i < logfd; i++)
1893 			(void) close(i);
1894 		closefrom(logfd+1);
1895 	} else
1896 		closefrom(0);
1897 
1898 	(void) chdir("/");
1899 
1900 	switch (fork1()) {
1901 	case (pid_t)-1:
1902 		exit(1);
1903 		break;
1904 	case 0:
1905 		break;
1906 	default:
1907 		exit(0);
1908 	}
1909 	(void) setsid();
1910 	(void) open("/dev/null", O_RDWR, 0);
1911 	(void) dup(0);
1912 	(void) dup(0);
1913 }
1914