cachemgr.c revision 2830:5228d1267a01
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 ldap cache daemon
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <signal.h>
35#include <door.h>
36#include <time.h>
37#include <string.h>
38#include <libintl.h>
39#include <sys/stat.h>
40#include <sys/time.h>
41#include <sys/wait.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/types.h>
52#include <syslog.h>
53#include <locale.h>	/* LC_ALL */
54#include "cachemgr.h"
55
56static void	detachfromtty();
57admin_t		current_admin;
58static int	will_become_server;
59
60static void switcher(void *cookie, char *argp, size_t arg_size,
61			door_desc_t *dp, uint_t n_desc);
62static void usage(char *s);
63static int cachemgr_set_lf(admin_t *ptr, char *logfile);
64static int client_getadmin(admin_t *ptr);
65static int getadmin(ldap_return_t *out);
66static int setadmin(ldap_return_t *out, ldap_call_t *ptr);
67static  int client_setadmin(admin_t *ptr);
68static int client_showstats(admin_t *ptr);
69
70#ifdef SLP
71int			use_slp = 0;
72static unsigned int	refresh = 10800;	/* dynamic discovery interval */
73#endif /* SLP */
74
75static ldap_stat_t *
76getcacheptr(char *s)
77{
78	static const char *caches[1] = {"ldap"};
79
80	if (strncmp(caches[0], s, strlen(caches[0])) == 0)
81		return (&current_admin.ldap_stat);
82
83	return (NULL);
84}
85
86char *
87getcacheopt(char *s)
88{
89	while (*s && *s != ',')
90		s++;
91	return ((*s == ',') ? (s + 1) : NULL);
92}
93
94/*
95 *  This is here to prevent the ldap_cachemgr becomes
96 *  daemonlized to early to soon during boot time.
97 *  This causes problems during boot when automounter
98 *  and others try to use libsldap before ldap_cachemgr
99 *  finishes walking the server list.
100 */
101static void
102sig_ok_to_exit(int signo)
103{
104	if (signo == SIGUSR1) {
105		logit("sig_ok_to_exit(): parent exiting...\n");
106		exit(0);
107	} else {
108		logit("sig_ok_to_exit(): invalid signal(%d) received.\n",
109			signo);
110		syslog(LOG_ERR, gettext("ldap_cachemgr: "
111			"invalid signal(%d) received."), signo);
112		exit(1);
113	}
114}
115#define	LDAP_TABLES		1	/* ldap */
116#define	TABLE_THREADS		10
117#define	COMMON_THREADS		20
118#define	CACHE_MISS_THREADS	(COMMON_THREADS + LDAP_TABLES * TABLE_THREADS)
119#define	CACHE_HIT_THREADS	20
120#define	MAX_SERVER_THREADS	(CACHE_HIT_THREADS + CACHE_MISS_THREADS)
121
122static sema_t common_sema;
123static sema_t ldap_sema;
124static thread_key_t lookup_state_key;
125
126static void
127initialize_lookup_clearance()
128{
129	(void) thr_keycreate(&lookup_state_key, NULL);
130	(void) sema_init(&common_sema, COMMON_THREADS, USYNC_THREAD, 0);
131	(void) sema_init(&ldap_sema, TABLE_THREADS, USYNC_THREAD, 0);
132}
133
134int
135get_clearance(int callnumber)
136{
137	sema_t	*table_sema = NULL;
138	char	*tab;
139
140	if (sema_trywait(&common_sema) == 0) {
141		(void) thr_setspecific(lookup_state_key, NULL);
142		return (0);
143	}
144
145	switch (callnumber) {
146		case GETLDAPCONFIG:
147			tab = "ldap";
148			table_sema = &ldap_sema;
149			break;
150		default:
151			logit("Internal Error: get_clearance\n");
152			break;
153	}
154
155	if (sema_trywait(table_sema) == 0) {
156		(void) thr_setspecific(lookup_state_key, (void*)1);
157		return (0);
158	}
159
160	if (current_admin.debug_level >= DBG_CANT_FIND) {
161		logit("get_clearance: throttling load for %s table\n", tab);
162	}
163
164	return (-1);
165}
166
167int
168release_clearance(int callnumber)
169{
170	int	which;
171	sema_t	*table_sema = NULL;
172
173	(void) thr_getspecific(lookup_state_key, (void**)&which);
174	if (which == 0) /* from common pool */ {
175		(void) sema_post(&common_sema);
176		return (0);
177	}
178
179	switch (callnumber) {
180		case GETLDAPCONFIG:
181			table_sema = &ldap_sema;
182			break;
183		default:
184			logit("Internal Error: release_clearance\n");
185			break;
186	}
187	(void) sema_post(table_sema);
188
189	return (0);
190}
191
192
193static mutex_t		create_lock;
194static int		num_servers = 0;
195static thread_key_t	server_key;
196
197
198/*
199 * Bind a TSD value to a server thread. This enables the destructor to
200 * be called if/when this thread exits.  This would be a programming error,
201 * but better safe than sorry.
202 */
203
204/*ARGSUSED*/
205static void *
206server_tsd_bind(void *arg)
207{
208	static void	*value = 0;
209
210	/*
211	 * disable cancellation to prevent hangs when server
212	 * threads disappear
213	 */
214
215	(void) thr_setspecific(server_key, value);
216	(void) door_return(NULL, 0, NULL, 0);
217
218	return (value);
219}
220
221/*
222 * Server threads are created here.
223 */
224
225/*ARGSUSED*/
226static void
227server_create(door_info_t *dip)
228{
229	(void) mutex_lock(&create_lock);
230	if (++num_servers > MAX_SERVER_THREADS) {
231		num_servers--;
232		(void) mutex_unlock(&create_lock);
233		return;
234	}
235	(void) mutex_unlock(&create_lock);
236	(void) thr_create(NULL, 0, server_tsd_bind, NULL,
237		THR_BOUND|THR_DETACHED, NULL);
238}
239
240/*
241 * Server thread are destroyed here
242 */
243
244/*ARGSUSED*/
245static void
246server_destroy(void *arg)
247{
248	(void) mutex_lock(&create_lock);
249	num_servers--;
250	(void) mutex_unlock(&create_lock);
251}
252
253int
254main(int argc, char ** argv)
255{
256	int			did;
257	int			opt;
258	int			errflg = 0;
259	int			showstats = 0;
260	int			doset = 0;
261	int			dofg = 0;
262	struct stat		buf;
263	sigset_t		myset;
264	struct sigaction	sighupaction;
265	static void		client_killserver();
266	int			debug_level = 0;
267
268	/* setup for localization */
269	(void) setlocale(LC_ALL, "");
270	(void) textdomain(TEXT_DOMAIN);
271
272	openlog("ldap_cachemgr", LOG_PID, LOG_DAEMON);
273
274	if (chdir(NSLDAPDIRECTORY) < 0) {
275		(void) fprintf(stderr, gettext("chdir(\"%s\") failed: %s\n"),
276			NSLDAPDIRECTORY, strerror(errno));
277		exit(1);
278	}
279
280	/*
281	 * Correctly set file mode creation mask, so to make the new files
282	 * created for door calls being readable by all.
283	 */
284	(void) umask(0);
285
286	/*
287	 *  Special case non-root user here -	he/she/they/it can just print
288	 *					stats
289	 */
290
291	if (geteuid()) {
292		if (argc != 2 || strcmp(argv[1], "-g")) {
293			(void) fprintf(stderr,
294			    gettext("Must be root to use any option "
295			    "other than -g.\n\n"));
296			usage(argv[0]);
297		}
298
299		if ((__ns_ldap_cache_ping() != SUCCESS) ||
300		    (client_getadmin(&current_admin) != 0)) {
301			(void) fprintf(stderr,
302				gettext("%s doesn't appear to be running.\n"),
303				argv[0]);
304			exit(1);
305		}
306		(void) client_showstats(&current_admin);
307		exit(0);
308	}
309
310
311
312	/*
313	 *  Determine if there is already a daemon running
314	 */
315
316	will_become_server = (__ns_ldap_cache_ping() != SUCCESS);
317
318	/*
319	 *  load normal config file
320	 */
321
322	if (will_become_server) {
323		static const ldap_stat_t defaults = {
324			0,		/* stat */
325			DEFAULTTTL};	/* ttl */
326
327		current_admin.ldap_stat = defaults;
328		(void) strcpy(current_admin.logfile, LOGFILE);
329	} else {
330		if (client_getadmin(&current_admin)) {
331			(void) fprintf(stderr, gettext("Cannot contact %s "
332				"properly(?)\n"), argv[0]);
333			exit(1);
334		}
335	}
336
337#ifndef SLP
338	while ((opt = getopt(argc, argv, "fKgl:r:d:")) != EOF) {
339#else
340	while ((opt = getopt(argc, argv, "fKgs:l:r:d:")) != EOF) {
341#endif /* SLP */
342		ldap_stat_t	*cache;
343
344		switch (opt) {
345		case 'K':
346			client_killserver();
347			exit(0);
348			break;
349		case 'g':
350			showstats++;
351			break;
352		case 'f':
353			dofg++;
354			break;
355		case 'r':
356			doset++;
357			cache = getcacheptr("ldap");
358			if (!optarg) {
359				errflg++;
360				break;
361			}
362			cache->ldap_ttl = atoi(optarg);
363			break;
364		case 'l':
365			doset++;
366			(void) strlcpy(current_admin.logfile,
367				optarg, sizeof (current_admin.logfile));
368			break;
369		case 'd':
370			doset++;
371			debug_level = atoi(optarg);
372			break;
373#ifdef SLP
374		case 's':	/* undocumented: use dynamic (SLP) config */
375			use_slp = 1;
376			break;
377#endif /* SLP */
378		default:
379			errflg++;
380			break;
381		}
382	}
383
384	if (errflg)
385	    usage(argv[0]);
386
387	/*
388	 * will not show statistics if no daemon running
389	 */
390	if (will_become_server && showstats) {
391		(void) fprintf(stderr,
392			gettext("%s doesn't appear to be running.\n"),
393				argv[0]);
394		exit(1);
395	}
396
397	if (!will_become_server) {
398		if (showstats) {
399			(void) client_showstats(&current_admin);
400		}
401		if (doset) {
402			current_admin.debug_level = debug_level;
403			if (client_setadmin(&current_admin) < 0) {
404				(void) fprintf(stderr,
405					gettext("Error during admin call\n"));
406				exit(1);
407			}
408		}
409		if (!showstats && !doset) {
410			(void) fprintf(stderr,
411			gettext("%s already running....use '%s "
412				"-K' to stop\n"), argv[0], argv[0]);
413		}
414		exit(0);
415	}
416
417	/*
418	 *   daemon from here on
419	 */
420
421	if (debug_level) {
422		/*
423		 * we're debugging...
424		 */
425		if (strlen(current_admin.logfile) == 0)
426			/*
427			 * no specified log file
428			 */
429			(void) strcpy(current_admin.logfile, LOGFILE);
430		else
431			(void) cachemgr_set_lf(&current_admin,
432				current_admin.logfile);
433		/*
434		 * validate the range of debug level number
435		 * and set the number to current_admin.debug_level
436		 */
437		if (cachemgr_set_dl(&current_admin, debug_level) < 0) {
438				/*
439				 * print error messages to the screen
440				 * cachemgr_set_dl prints msgs to cachemgr.log
441				 * only
442				 */
443				(void) fprintf(stderr,
444				gettext("Incorrect Debug Level: %d\n"
445				"It should be between %d and %d\n"),
446				debug_level, DBG_OFF, MAXDEBUG);
447			exit(-1);
448		}
449	} else {
450		if (strlen(current_admin.logfile) == 0)
451			(void) strcpy(current_admin.logfile, "/dev/null");
452			(void) cachemgr_set_lf(&current_admin,
453				current_admin.logfile);
454	}
455
456	if (dofg == 0)
457		detachfromtty(argv[0]);
458
459	/*
460	 * perform some initialization
461	 */
462
463	initialize_lookup_clearance();
464
465	if (getldap_init() != 0)
466		exit(-1);
467
468	/*
469	 * Establish our own server thread pool
470	 */
471
472	(void) door_server_create(server_create);
473	if (thr_keycreate(&server_key, server_destroy) != 0) {
474		logit("thr_keycreate() call failed\n");
475		syslog(LOG_ERR,
476			gettext("ldap_cachemgr: thr_keycreate() call failed"));
477		perror("thr_keycreate");
478		exit(-1);
479	}
480
481	/*
482	 * Create a door
483	 */
484
485	if ((did = door_create(switcher, LDAP_CACHE_DOOR_COOKIE,
486	    DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
487		logit("door_create() call failed\n");
488		syslog(LOG_ERR, gettext(
489			"ldap_cachemgr: door_create() call failed"));
490		perror("door_create");
491		exit(-1);
492	}
493
494	/*
495	 * bind to file system
496	 */
497
498	if (stat(LDAP_CACHE_DOOR, &buf) < 0) {
499		int	newfd;
500
501		if ((newfd = creat(LDAP_CACHE_DOOR, 0444)) < 0) {
502			logit("Cannot create %s:%s\n",
503				LDAP_CACHE_DOOR,
504				strerror(errno));
505			exit(1);
506		}
507		(void) close(newfd);
508	}
509
510	if (fattach(did, LDAP_CACHE_DOOR) < 0) {
511		if ((errno != EBUSY) ||
512		    (fdetach(LDAP_CACHE_DOOR) <  0) ||
513		    (fattach(did, LDAP_CACHE_DOOR) < 0)) {
514			logit("fattach() call failed\n");
515			syslog(LOG_ERR, gettext(
516				"ldap_cachemgr: fattach() call failed"));
517			perror("fattach");
518			exit(2);
519		}
520	}
521
522	/* catch SIGHUP revalid signals */
523	sighupaction.sa_handler = getldap_revalidate;
524	sighupaction.sa_flags = 0;
525	(void) sigemptyset(&sighupaction.sa_mask);
526	(void) sigemptyset(&myset);
527	(void) sigaddset(&myset, SIGHUP);
528
529	if (sigaction(SIGHUP, &sighupaction, NULL) < 0) {
530		logit("sigaction() call failed\n");
531		syslog(LOG_ERR,
532			gettext("ldap_cachemgr: sigaction() call failed"));
533		perror("sigaction");
534		exit(1);
535	}
536
537	if (thr_sigsetmask(SIG_BLOCK, &myset, NULL) < 0) {
538		logit("thr_sigsetmask() call failed\n");
539		syslog(LOG_ERR,
540			gettext("ldap_cachemgr: thr_sigsetmask() call failed"));
541		perror("thr_sigsetmask");
542		exit(1);
543	}
544
545	/*
546	 *  kick off revalidate threads only if ttl != 0
547	 */
548
549	if (thr_create(NULL, NULL, (void *(*)(void*))getldap_refresh,
550		0, 0, NULL) != 0) {
551		logit("thr_create() call failed\n");
552		syslog(LOG_ERR,
553			gettext("ldap_cachemgr: thr_create() call failed"));
554		perror("thr_create");
555		exit(1);
556	}
557
558	/*
559	 *  kick off the thread which refreshes the server info
560	 */
561
562	if (thr_create(NULL, NULL, (void *(*)(void*))getldap_serverInfo_refresh,
563		0, 0, NULL) != 0) {
564		logit("thr_create() call failed\n");
565		syslog(LOG_ERR,
566			gettext("ldap_cachemgr: thr_create() call failed"));
567		perror("thr_create");
568		exit(1);
569	}
570
571#ifdef SLP
572	if (use_slp) {
573		/* kick off SLP discovery thread */
574		if (thr_create(NULL, NULL, (void *(*)(void *))discover,
575			(void *)&refresh, 0, NULL) != 0) {
576			logit("thr_create() call failed\n");
577			syslog(LOG_ERR, gettext("ldap_cachemgr: thr_create() "
578				"call failed"));
579			perror("thr_create");
580			exit(1);
581		}
582	}
583#endif /* SLP */
584
585	if (thr_sigsetmask(SIG_UNBLOCK, &myset, NULL) < 0) {
586		logit("thr_sigsetmask() call failed\n");
587		syslog(LOG_ERR,
588			gettext("ldap_cachemgr: the_sigsetmask() call failed"));
589		perror("thr_sigsetmask");
590		exit(1);
591	}
592
593	/*CONSTCOND*/
594	while (1) {
595		(void) pause();
596	}
597	/* NOTREACHED */
598	/*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
599}
600
601
602/*ARGSUSED*/
603static void
604switcher(void *cookie, char *argp, size_t arg_size,
605    door_desc_t *dp, uint_t n_desc)
606{
607	dataunion		u;
608	ldap_call_t	*ptr = (ldap_call_t *)argp;
609	door_cred_t	dc;
610
611	if (argp == DOOR_UNREF_DATA) {
612		logit("Door Slam... invalid door param\n");
613		syslog(LOG_ERR, gettext("ldap_cachemgr: Door Slam... "
614			"invalid door param"));
615		(void) printf(gettext("Door Slam... invalid door param\n"));
616		exit(0);
617	}
618
619	if (ptr == NULL) { /* empty door call */
620		(void) door_return(NULL, 0, 0, 0); /* return the favor */
621	}
622
623	switch (ptr->ldap_callnumber) {
624	case NULLCALL:
625		u.data.ldap_ret.ldap_return_code = SUCCESS;
626		u.data.ldap_ret.ldap_bufferbytesused = sizeof (ldap_return_t);
627		break;
628	case GETLDAPCONFIG:
629		getldap_lookup(&u.data.ldap_ret, ptr);
630		current_admin.ldap_stat.ldap_numbercalls++;
631		break;
632	case GETADMIN:
633		(void) getadmin(&u.data.ldap_ret);
634		break;
635	case SETADMIN:
636	case KILLSERVER:
637		if (door_cred(&dc) < 0) {
638			logit("door_cred() call failed\n");
639			syslog(LOG_ERR, gettext("ldap_cachemgr: door_cred() "
640				"call failed"));
641			perror("door_cred");
642			break;
643		}
644		if (dc.dc_euid != 0 && ptr->ldap_callnumber == SETADMIN) {
645			logit("SETADMIN call failed (cred): caller "
646			    "pid %ld, uid %ld, euid %ld\n",
647			    dc.dc_pid, dc.dc_ruid, dc.dc_euid);
648			u.data.ldap_ret.ldap_return_code = NOTFOUND;
649			break;
650		}
651		if (ptr->ldap_callnumber == KILLSERVER) {
652			logit("ldap_cachemgr received KILLSERVER cmd from "
653			    "pid %ld, uid %ld, euid %ld\n",
654			    dc.dc_pid, dc.dc_ruid, dc.dc_euid);
655			exit(0);
656		} else {
657			(void) setadmin(&u.data.ldap_ret, ptr);
658		}
659		break;
660	case GETLDAPSERVER:
661		getldap_getserver(&u.data.ldap_ret, ptr);
662		current_admin.ldap_stat.ldap_numbercalls++;
663		break;
664	case GETCACHE:
665		getldap_get_cacheData(&u.data.ldap_ret, ptr);
666		current_admin.ldap_stat.ldap_numbercalls++;
667		break;
668	case SETCACHE:
669		getldap_set_cacheData(&u.data.ldap_ret, ptr);
670		current_admin.ldap_stat.ldap_numbercalls++;
671		break;
672	case GETCACHESTAT:
673		getldap_get_cacheStat(&u.data.ldap_ret);
674		current_admin.ldap_stat.ldap_numbercalls++;
675		break;
676	default:
677		logit("Unknown ldap service door call op %d\n",
678		    ptr->ldap_callnumber);
679		u.data.ldap_ret.ldap_return_code = -99;
680		u.data.ldap_ret.ldap_bufferbytesused = sizeof (ldap_return_t);
681		break;
682	}
683	(void) door_return((char *)&u.data,
684		u.data.ldap_ret.ldap_bufferbytesused, NULL, 0);
685}
686
687static void
688usage(char *s)
689{
690	(void) fprintf(stderr,
691		gettext("Usage: %s [-d debug_level] [-l logfilename]\n"), s);
692	(void) fprintf(stderr, gettext("	[-K] "
693					"[-r revalidate_interval] "));
694#ifndef SLP
695	(void) fprintf(stderr, gettext("	[-g]\n"));
696#else
697	(void) fprintf(stderr, gettext("	[-g] [-s]\n"));
698#endif /* SLP */
699	exit(1);
700}
701
702
703static int	logfd = -1;
704
705static int
706cachemgr_set_lf(admin_t *ptr, char *logfile)
707{
708	int	newlogfd;
709
710	/*
711	 *  we don't really want to try and open the log file
712	 *  /dev/null since that will fail w/ our security fixes
713	 */
714
715	if (logfile == NULL || *logfile == 0) {
716		/*EMPTY*/;
717	} else if (strcmp(logfile, "/dev/null") == 0) {
718		(void) strcpy(current_admin.logfile, "/dev/null");
719		(void) close(logfd);
720		logfd = -1;
721	} else {
722		if ((newlogfd =
723			open(logfile, O_EXCL|O_WRONLY|O_CREAT, 0644)) < 0) {
724			/*
725			 * File already exists... now we need to get cute
726			 * since opening a file in a world-writeable directory
727			 * safely is hard = it could be a hard link or a
728			 * symbolic link to a system file.
729			 *
730			 */
731			struct stat	before;
732
733			if (lstat(logfile, &before) < 0) {
734				logit("Cannot open new logfile \"%s\": %sn",
735					logfile, strerror(errno));
736				return (-1);
737			}
738			if (S_ISREG(before.st_mode) &&	/* no symbolic links */
739			    (before.st_nlink == 1) &&	/* no hard links */
740			    (before.st_uid == 0)) {	/* owned by root */
741				if ((newlogfd =
742				    open(logfile,
743				    O_APPEND|O_WRONLY, 0644)) < 0) {
744					logit("Cannot open new logfile "
745						"\"%s\": %s\n",
746						logfile, strerror(errno));
747					return (-1);
748				}
749			} else {
750				logit("Cannot use specified logfile "
751				    "\"%s\": file is/has links or isn't "
752				    "owned by root\n", logfile);
753				return (-1);
754			}
755		}
756		(void) strlcpy(ptr->logfile, logfile, sizeof (ptr->logfile));
757		(void) close(logfd);
758		logfd = newlogfd;
759		logit("Starting ldap_cachemgr, logfile %s\n", logfile);
760	}
761	return (0);
762}
763
764/*PRINTFLIKE1*/
765void
766logit(char *format, ...)
767{
768	static mutex_t	loglock;
769	struct timeval	tv;
770	char		buffer[BUFSIZ];
771	va_list		ap;
772
773	va_start(ap, format);
774
775	if (logfd >= 0) {
776		int	safechars;
777
778		(void) gettimeofday(&tv, NULL);
779		(void) ctime_r(&tv.tv_sec, buffer, BUFSIZ);
780		(void) snprintf(buffer+19, BUFSIZE, ".%.4ld	",
781			tv.tv_usec/100);
782		safechars = sizeof (buffer) - 30;
783		if (vsnprintf(buffer+25, safechars, format, ap) > safechars)
784			(void) strcat(buffer, "...\n");
785		(void) mutex_lock(&loglock);
786		(void) write(logfd, buffer, strlen(buffer));
787		(void) mutex_unlock(&loglock);
788	}
789	va_end(ap);
790}
791
792
793void
794do_update(ldap_call_t *in)
795{
796	dataunion		u;
797
798	switch (in->ldap_callnumber) {
799	case GETLDAPCONFIG:
800		getldap_lookup(&u.data.ldap_ret, in);
801		break;
802	default:
803		assert(0);
804		break;
805	}
806
807	free(in);
808}
809
810
811static int
812client_getadmin(admin_t *ptr)
813{
814	dataunion		u;
815	ldap_data_t	*dptr;
816	int		ndata;
817	int		adata;
818
819	u.data.ldap_call.ldap_callnumber = GETADMIN;
820	ndata = sizeof (u);
821	adata = sizeof (u.data);
822	dptr = &u.data;
823
824	if (__ns_ldap_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) {
825		return (-1);
826	}
827	(void) memcpy(ptr, dptr->ldap_ret.ldap_u.buff, sizeof (*ptr));
828
829	return (0);
830}
831
832static int
833getadmin(ldap_return_t *out)
834{
835	out->ldap_return_code = SUCCESS;
836	out->ldap_bufferbytesused = sizeof (current_admin);
837	(void) memcpy(out->ldap_u.buff, &current_admin, sizeof (current_admin));
838
839	return (0);
840}
841
842
843static int
844setadmin(ldap_return_t *out, ldap_call_t *ptr)
845{
846	admin_t	*new;
847
848	out->ldap_return_code = SUCCESS;
849	out->ldap_bufferbytesused = sizeof (ldap_return_t);
850	new = (admin_t *)ptr->ldap_u.domainname;
851
852	/*
853	 *  global admin stuff
854	 */
855
856	if ((cachemgr_set_lf(&current_admin, new->logfile) < 0) ||
857	    cachemgr_set_dl(&current_admin, new->debug_level) < 0) {
858		out->ldap_return_code = NOTFOUND;
859		return (-1);
860	}
861
862	if (cachemgr_set_ttl(&current_admin.ldap_stat,
863			"ldap",
864			new->ldap_stat.ldap_ttl) < 0) {
865		out->ldap_return_code = NOTFOUND;
866		return (-1);
867	}
868	out->ldap_return_code = SUCCESS;
869
870	return (0);
871}
872
873
874static void
875client_killserver()
876{
877	dataunion		u;
878	ldap_data_t		*dptr;
879	int			ndata;
880	int			adata;
881
882	u.data.ldap_call.ldap_callnumber = KILLSERVER;
883	ndata = sizeof (u);
884	adata = sizeof (ldap_call_t);
885	dptr = &u.data;
886
887	__ns_ldap_trydoorcall(&dptr, &ndata, &adata);
888}
889
890
891static int
892client_setadmin(admin_t *ptr)
893{
894	dataunion		u;
895	ldap_data_t		*dptr;
896	int			ndata;
897	int			adata;
898
899	u.data.ldap_call.ldap_callnumber = SETADMIN;
900	(void) memcpy(u.data.ldap_call.ldap_u.domainname, ptr, sizeof (*ptr));
901	ndata = sizeof (u);
902	adata = sizeof (*ptr);
903	dptr = &u.data;
904
905	if (__ns_ldap_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) {
906		return (-1);
907	}
908
909	return (0);
910}
911
912static int
913client_showstats(admin_t *ptr)
914{
915	dataunion	u;
916	ldap_data_t	*dptr;
917	int		ndata;
918	int		adata;
919	char		*rbuf, *sptr, *rest;
920
921	/*
922	 * print admin data
923	 */
924	(void) printf(gettext("\ncachemgr configuration:\n"));
925	(void) printf(gettext("server debug level %10d\n"), ptr->debug_level);
926	(void) printf(gettext("server log file\t\"%s\"\n"), ptr->logfile);
927	(void) printf(gettext("number of calls to ldapcachemgr %10d\n"),
928		ptr->ldap_stat.ldap_numbercalls);
929
930	/*
931	 * get cache data statistics
932	 */
933	u.data.ldap_call.ldap_callnumber = GETCACHESTAT;
934	ndata = sizeof (u);
935	adata = sizeof (ldap_call_t);
936	dptr = &u.data;
937
938	if (__ns_ldap_trydoorcall(&dptr, &ndata, &adata) != SUCCESS) {
939		(void) printf(
940			gettext("\nCache data statistics not available!\n"));
941		return (0);
942	}
943
944	/*
945	 * print cache data statistics line by line
946	 */
947	(void) printf(gettext("\ncachemgr cache data statistics:\n"));
948	rbuf = dptr->ldap_ret.ldap_u.buff;
949	sptr = strtok_r(rbuf, DOORLINESEP, &rest);
950	for (;;) {
951		(void) printf("%s\n", sptr);
952		sptr = strtok_r(NULL, DOORLINESEP, &rest);
953		if (sptr == NULL)
954			break;
955	}
956	return (0);
957}
958
959
960/*
961 * detach from tty
962 */
963static void
964detachfromtty(char *pgm)
965{
966	int 	status;
967	pid_t	pid, wret;
968
969	(void) close(0);
970	(void) close(1);
971	/*
972	 * Block the SIGUSR1 signal
973	 * just in case that the child
974	 * process may run faster than
975	 * the parent process and
976	 * send this signal before
977	 * the signal handler is ready
978	 * in the parent process.
979	 * This error will cause the parent
980	 * to exit with the User Signal 1
981	 * exit code (144).
982	 */
983	(void) sighold(SIGUSR1);
984	pid = fork1();
985	switch (pid) {
986		case (pid_t)-1:
987			logit("detachfromtty(): fork1() call failed\n");
988			(void) fprintf(stderr,
989					gettext("%s: fork1() call failed.\n"),
990					pgm);
991			syslog(LOG_ERR,
992				gettext("ldap_cachemgr: fork1() call failed."));
993			exit(1);
994			break;
995		case 0:
996			/*
997			 * child process does not
998			 * need to worry about
999			 * the SIGUSR1 signal
1000			 */
1001			(void) sigrelse(SIGUSR1);
1002			(void) close(2);
1003			break;
1004		default:
1005			/*
1006			 * Wait forever until the child process
1007			 * has exited, or has signalled that at
1008			 * least one server in the server list
1009			 * is up.
1010			 */
1011			if (signal(SIGUSR1, sig_ok_to_exit) == SIG_ERR) {
1012				logit("detachfromtty(): "
1013					"can't set up signal handler to "
1014					" catch SIGUSR1.\n");
1015				(void) fprintf(stderr,
1016					gettext("%s: signal() call failed.\n"),
1017					pgm);
1018				syslog(LOG_ERR, gettext("ldap_cachemgr: "
1019					"can't set up signal handler to "
1020					" catch SIGUSR1."));
1021				exit(1);
1022			}
1023
1024			/*
1025			 * now unblock the SIGUSR1 signal
1026			 * to handle the pending or
1027			 * soon to arrive SIGUSR1 signal
1028			 */
1029			(void) sigrelse(SIGUSR1);
1030			wret = waitpid(pid, &status, 0);
1031
1032			if (wret == -1) {
1033				logit("detachfromtty(): "
1034					"waitpid() call failed\n");
1035				(void) fprintf(stderr,
1036					gettext("%s: waitpid() call failed.\n"),
1037					pgm);
1038				syslog(LOG_ERR,
1039					gettext("ldap_cachemgr: waitpid() "
1040						"call failed."));
1041				exit(1);
1042			}
1043			if (wret != pid) {
1044				logit("detachfromtty(): "
1045					"waitpid() returned %ld when "
1046					"child pid was %ld\n",
1047					wret, pid);
1048				(void) fprintf(stderr,
1049					gettext(
1050					"%s: waitpid() returned %ld when "
1051					"child pid was %ld.\n"),
1052					pgm, wret, pid);
1053				syslog(LOG_ERR,
1054					gettext("ldap_cachemgr: waitpid() "
1055						"returned different "
1056						"child pid."));
1057				exit(1);
1058			}
1059
1060			/* evaluate return status */
1061			if (WIFEXITED(status)) {
1062				if (WEXITSTATUS(status) == 0) {
1063					exit(0);
1064				}
1065				logit("detachfromtty(): "
1066					"child failed (rc = %d).\n",
1067					WEXITSTATUS(status));
1068				(void) fprintf(stderr,
1069					gettext("%s: failed. Please see "
1070					"syslog for details.\n"),
1071					pgm);
1072				syslog(LOG_ERR,
1073					gettext("ldap_cachemgr: failed "
1074					"(rc = %d)."),
1075					WEXITSTATUS(status));
1076			} else if (WIFSIGNALED(status)) {
1077				logit("detachfromtty(): "
1078					"child terminated by signal %d.\n",
1079					WTERMSIG(status));
1080				(void) fprintf(stderr,
1081				gettext("%s: terminated by signal %d.\n"),
1082					pgm, WTERMSIG(status));
1083				syslog(LOG_ERR,
1084					gettext("ldap_cachemgr: terminated by "
1085					"signal %d.\n"),
1086					WTERMSIG(status));
1087			} else if (WCOREDUMP(status)) {
1088				logit("detachfromtty(): child core dumped.\n"),
1089				(void) fprintf(stderr,
1090					gettext("%s: core dumped.\n"),
1091					pgm);
1092				syslog(LOG_ERR,
1093					gettext("ldap_cachemgr: "
1094						"core dumped.\n"));
1095			}
1096
1097			exit(1);
1098	}
1099	(void) setsid();
1100	if (open("/dev/null", O_RDWR, 0) != -1) {
1101		(void) dup(0);
1102		(void) dup(0);
1103	}
1104}
1105