server.c revision 2830:5228d1267a01
18348SEric.Yu@Sun.COM/*
28348SEric.Yu@Sun.COM * CDDL HEADER START
38348SEric.Yu@Sun.COM *
48348SEric.Yu@Sun.COM * The contents of this file are subject to the terms of the
58348SEric.Yu@Sun.COM * Common Development and Distribution License (the "License").
68348SEric.Yu@Sun.COM * You may not use this file except in compliance with the License.
78348SEric.Yu@Sun.COM *
88348SEric.Yu@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98348SEric.Yu@Sun.COM * or http://www.opensolaris.org/os/licensing.
108348SEric.Yu@Sun.COM * See the License for the specific language governing permissions
118348SEric.Yu@Sun.COM * and limitations under the License.
128348SEric.Yu@Sun.COM *
138348SEric.Yu@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
148348SEric.Yu@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158348SEric.Yu@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
168348SEric.Yu@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
178348SEric.Yu@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
188348SEric.Yu@Sun.COM *
198348SEric.Yu@Sun.COM * CDDL HEADER END
208348SEric.Yu@Sun.COM */
218348SEric.Yu@Sun.COM/*
2212643SAnders.Persson@Sun.COM * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
238348SEric.Yu@Sun.COM * Use is subject to license terms.
248348SEric.Yu@Sun.COM */
258348SEric.Yu@Sun.COM
268348SEric.Yu@Sun.COM#pragma ident	"%Z%%M%	%I%	%E% SMI"
278348SEric.Yu@Sun.COM
288348SEric.Yu@Sun.COM/*
298348SEric.Yu@Sun.COM * Simple doors name server cache daemon
308348SEric.Yu@Sun.COM */
318348SEric.Yu@Sun.COM
328348SEric.Yu@Sun.COM#include <stdio.h>
338348SEric.Yu@Sun.COM#include <stdlib.h>
348348SEric.Yu@Sun.COM#include <fcntl.h>
358348SEric.Yu@Sun.COM#include <string.h>
368348SEric.Yu@Sun.COM#include <errno.h>
378348SEric.Yu@Sun.COM#include <stdarg.h>
388348SEric.Yu@Sun.COM#include <locale.h>
398348SEric.Yu@Sun.COM#include <tsol/label.h>
408348SEric.Yu@Sun.COM#include <zone.h>
418348SEric.Yu@Sun.COM#include "cache.h"
428348SEric.Yu@Sun.COM#include "nscd_log.h"
438348SEric.Yu@Sun.COM#include "nscd_selfcred.h"
448348SEric.Yu@Sun.COM#include "nscd_frontend.h"
458348SEric.Yu@Sun.COM#include "nscd_common.h"
468348SEric.Yu@Sun.COM#include "nscd_admin.h"
478348SEric.Yu@Sun.COM#include "nscd_door.h"
488348SEric.Yu@Sun.COM#include "nscd_switch.h"
498348SEric.Yu@Sun.COM
508348SEric.Yu@Sun.COMextern int 	optind;
518348SEric.Yu@Sun.COMextern int 	opterr;
528348SEric.Yu@Sun.COMextern int 	optopt;
538348SEric.Yu@Sun.COMextern char 	*optarg;
548348SEric.Yu@Sun.COM
558348SEric.Yu@Sun.COM#define	NSCDOPT	"S:Kf:c:ge:p:n:i:l:d:s:h:o:GFR"
568348SEric.Yu@Sun.COM
578348SEric.Yu@Sun.COM/* assume this is a single nscd  or, if multiple, the main nscd */
588348SEric.Yu@Sun.COMint		_whoami = NSCD_MAIN;
598348SEric.Yu@Sun.COMint		_doorfd = -1;
608348SEric.Yu@Sun.COMextern int	_logfd;
618348SEric.Yu@Sun.COMstatic char	*cfgfile = NULL;
628348SEric.Yu@Sun.COM
638348SEric.Yu@Sun.COMextern nsc_ctx_t *cache_ctx_p[];
648348SEric.Yu@Sun.COM
658348SEric.Yu@Sun.COMstatic void usage(char *);
668348SEric.Yu@Sun.COMstatic void detachfromtty(void);
678348SEric.Yu@Sun.COM
688348SEric.Yu@Sun.COMstatic int	debug_level = 0;
698348SEric.Yu@Sun.COMstatic char	logfile[128] = { 0 };
708348SEric.Yu@Sun.COMstatic int	will_become_server;
718348SEric.Yu@Sun.COM
728348SEric.Yu@Sun.COMstatic char *
738348SEric.Yu@Sun.COMgetcacheopt(char *s)
748348SEric.Yu@Sun.COM{
758348SEric.Yu@Sun.COM	while (*s && *s != ',')
768348SEric.Yu@Sun.COM		s++;
778348SEric.Yu@Sun.COM	return ((*s == ',') ? (s + 1) : NULL);
788348SEric.Yu@Sun.COM}
798348SEric.Yu@Sun.COM
808348SEric.Yu@Sun.COM/*
818348SEric.Yu@Sun.COM * declaring this causes the files backend to use hashing
828348SEric.Yu@Sun.COM * this is of course an utter hack, but provides a nice
838348SEric.Yu@Sun.COM * quiet back door to enable this feature for only the nscd.
848348SEric.Yu@Sun.COM */
858348SEric.Yu@Sun.COMvoid
868348SEric.Yu@Sun.COM__nss_use_files_hash(void)
878348SEric.Yu@Sun.COM{
888348SEric.Yu@Sun.COM}
898348SEric.Yu@Sun.COM
9012643SAnders.Persson@Sun.COMstatic int	saved_argc = 0;
918348SEric.Yu@Sun.COMstatic char	**saved_argv = NULL;
928348SEric.Yu@Sun.COMstatic char	saved_execname[MAXPATHLEN];
938348SEric.Yu@Sun.COM
948348SEric.Yu@Sun.COMstatic void
958348SEric.Yu@Sun.COMsave_execname()
968348SEric.Yu@Sun.COM{
978348SEric.Yu@Sun.COM	const char *name = getexecname();
988348SEric.Yu@Sun.COM
998348SEric.Yu@Sun.COM	saved_execname[0] = 0;
1008348SEric.Yu@Sun.COM
1018348SEric.Yu@Sun.COM	if (name[0] != '/') { /* started w/ relative path */
1028348SEric.Yu@Sun.COM		(void) getcwd(saved_execname, MAXPATHLEN);
1038348SEric.Yu@Sun.COM		(void) strlcat(saved_execname, "/", MAXPATHLEN);
1048348SEric.Yu@Sun.COM	}
1058348SEric.Yu@Sun.COM	(void) strlcat(saved_execname, name, MAXPATHLEN);
1068348SEric.Yu@Sun.COM}
1078348SEric.Yu@Sun.COM
1088348SEric.Yu@Sun.COMint
1098348SEric.Yu@Sun.COMmain(int argc, char ** argv)
1108348SEric.Yu@Sun.COM{
1118348SEric.Yu@Sun.COM	int		opt;
1128348SEric.Yu@Sun.COM	int		errflg = 0;
1138348SEric.Yu@Sun.COM	int		showstats = 0;
1148348SEric.Yu@Sun.COM	int		doset = 0;
1158348SEric.Yu@Sun.COM	nscd_rc_t	rc;
1168348SEric.Yu@Sun.COM	char		*me = "main()";
1178348SEric.Yu@Sun.COM	char		*ret_locale;
1188348SEric.Yu@Sun.COM	char		*ret_textdomain;
1198348SEric.Yu@Sun.COM	char		msg[128];
1208348SEric.Yu@Sun.COM
1218348SEric.Yu@Sun.COM	ret_locale = setlocale(LC_ALL, "");
1228348SEric.Yu@Sun.COM	if (ret_locale == NULL)
1238348SEric.Yu@Sun.COM		(void) fprintf(stderr, gettext("Unable to set locale\n"));
1248348SEric.Yu@Sun.COM
1258348SEric.Yu@Sun.COM	ret_textdomain = textdomain(TEXT_DOMAIN);
1268348SEric.Yu@Sun.COM	if (ret_textdomain == NULL)
127		(void) fprintf(stderr, gettext("Unable to set textdomain\n"));
128
129	/*
130	 * The admin model for TX is that labeled zones are managed
131	 * in global zone where most trusted configuration database
132	 * resides.
133	 */
134	if (is_system_labeled() && (getzoneid() != GLOBAL_ZONEID)) {
135		(void) fprintf(stderr,
136gettext("With Trusted Extensions nscd runs only in the global zone.\n"));
137		exit(1);
138	}
139
140	/*
141	 *  Special case non-root user here - he can just print stats
142	 */
143	if (geteuid()) {
144		if (argc != 2 ||
145			(strcmp(argv[1], "-g") && strcmp(argv[1], "-G"))) {
146			(void) fprintf(stderr,
147	gettext("Must be root to use any option other than -g\n\n"));
148			usage(argv[0]);
149		}
150
151		if (_nscd_doorcall(NSCD_PING) != NSS_SUCCESS) {
152			(void) fprintf(stderr,
153			gettext("%s doesn't appear to be running.\n"),
154				argv[0]);
155			exit(1);
156		}
157		if (_nscd_client_getadmin(argv[1][1]) != 0) {
158			(void) fprintf(stderr,
159	gettext("unable to get configuration and statistics data\n"));
160			exit(1);
161		}
162
163		_nscd_client_showstats();
164		exit(0);
165	}
166
167	/*
168	 *  Determine if there is already a daemon (main nscd) running.
169	 *  If not, will start it. Forker NSCD will always become a
170	 *  daemon.
171	 */
172	will_become_server = (_nscd_doorcall(NSCD_PING) != NSS_SUCCESS);
173	if (argc >= 2 && strcmp(argv[1], "-F") == 0) {
174		will_become_server = 1;
175		_whoami = NSCD_FORKER;
176
177		/*
178		 * allow time for the main nscd to get ready
179		 * to receive the IMHERE door request this
180		 * process will send later
181		 */
182		(void) usleep(100000);
183	}
184
185	/*
186	 * first get the config file path. Also detect
187	 * invalid option as soon as possible.
188	 */
189	while ((opt = getopt(argc, argv, NSCDOPT)) != EOF) {
190		switch (opt) {
191
192		case 'f':
193			if ((cfgfile = strdup(optarg)) == NULL)
194				exit(1);
195			break;
196		case 'g':
197			if (will_become_server) {
198				(void) fprintf(stderr,
199		gettext("nscd not running, no statistics to show\n\n"));
200				errflg++;
201			}
202			break;
203		case 'i':
204			if (will_become_server) {
205				(void) fprintf(stderr,
206		gettext("nscd not running, no cache to invalidate\n\n"));
207				errflg++;
208			}
209			break;
210
211		case '?':
212			errflg++;
213			break;
214		}
215
216	}
217	if (errflg)
218	    usage(argv[0]);
219
220	/*
221	 *  perform more initialization and load configuration
222	 * if to become server
223	 */
224	if (will_become_server) {
225
226		/* initialize switch engine and config/stats management */
227		if ((rc = _nscd_init(cfgfile)) != NSCD_SUCCESS) {
228			(void) fprintf(stderr,
229		gettext("initialization of switch failed (rc = %d)\n"), rc);
230			exit(1);
231		}
232
233		/*
234		 * initialize cache store
235		 */
236		if ((rc = init_cache(0)) != NSCD_SUCCESS) {
237			(void) fprintf(stderr,
238	gettext("initialization of cache store failed (rc = %d)\n"), rc);
239			exit(1);
240		}
241	}
242
243	/*
244	 * process usual options
245	 */
246	optind = 1; /* this is a rescan */
247	*msg = '\0';
248	while ((opt = getopt(argc, argv, NSCDOPT)) != EOF) {
249
250		switch (opt) {
251
252		case 'K':		/* undocumented feature */
253			(void) _nscd_doorcall(NSCD_KILLSERVER);
254			exit(0);
255			break;
256
257		case 'G':
258		case 'g':
259			showstats++;
260			break;
261
262		case 'p':
263			doset++;
264			if (_nscd_add_admin_mod(optarg, 'p',
265				getcacheopt(optarg),
266				msg, sizeof (msg)) == -1)
267				errflg++;
268			break;
269
270		case 'n':
271			doset++;
272			if (_nscd_add_admin_mod(optarg, 'n',
273				getcacheopt(optarg),
274				msg, sizeof (msg)) == -1)
275				errflg++;
276			break;
277
278		case 'c':
279			doset++;
280			if (_nscd_add_admin_mod(optarg, 'c',
281				getcacheopt(optarg),
282				msg, sizeof (msg)) == -1)
283				errflg++;
284			break;
285
286		case 'i':
287			doset++;
288			if (_nscd_add_admin_mod(optarg, 'i', NULL,
289				msg, sizeof (msg)) == -1)
290				errflg++;
291			break;
292
293		case 'l':
294			doset++;
295			(void) strlcpy(logfile, optarg, 128);
296			(void) _nscd_add_admin_mod(NULL, 'l', optarg,
297				msg, sizeof (msg));
298			break;
299
300		case 'd':
301			doset++;
302			debug_level = atoi(optarg);
303			(void) _nscd_add_admin_mod(NULL, 'd', optarg,
304				msg, sizeof (msg));
305			break;
306
307		case 'S':
308			/* silently ignore secure-mode */
309			break;
310
311		case 's':
312			/* silently ignore suggested-size */
313			break;
314
315		case 'o':
316			/* silently ignore old-data-ok */
317			break;
318
319		case 'h':
320			doset++;
321			if (_nscd_add_admin_mod(optarg, 'h',
322				getcacheopt(optarg),
323				msg, sizeof (msg)) == -1)
324				errflg++;
325			break;
326
327		case 'e':
328			doset++;
329			if (_nscd_add_admin_mod(optarg, 'e',
330				getcacheopt(optarg),
331				msg, sizeof (msg)) == -1)
332				errflg++;
333			break;
334
335		case 'F':
336			_whoami = NSCD_FORKER;
337			break;
338
339		default:
340			errflg++;
341			break;
342		}
343
344	}
345
346	if (errflg) {
347	    if (*msg != '\0')
348		(void) fprintf(stderr, "\n%s: %s\n\n", argv[0], msg);
349	    usage(argv[0]);
350	}
351
352	/*
353	 * if main nscd already running and not forker nscd,
354	 * can only do admin work
355	 */
356	if (_whoami == NSCD_MAIN) {
357		if (!will_become_server) {
358			if (showstats) {
359				if (_nscd_client_getadmin('g')) {
360					(void) fprintf(stderr,
361			gettext("Cannot contact nscd properly(?)\n"));
362					exit(1);
363				}
364				_nscd_client_showstats();
365			}
366
367			if (doset) {
368				if (_nscd_client_setadmin() < 0) {
369					(void) fprintf(stderr,
370				gettext("Error during admin call\n"));
371					exit(1);
372				}
373			}
374			if (!showstats && !doset) {
375				(void) fprintf(stderr,
376gettext("%s already running.... no administration option specified\n"),
377					argv[0]);
378			}
379			exit(0);
380		}
381	}
382
383	/*
384	 *   daemon from here on
385	 */
386
387	if (_whoami == NSCD_MAIN) {
388
389		/* save enough info in case need to restart or fork */
390		saved_argc = argc;
391		saved_argv = argv;
392		save_execname();
393
394		/*
395		 * if a log file is not specified, set it to
396		 * "stderr" or "/dev/null" based on debug level
397		 */
398		if (_logfd < 0 && *logfile == '\0') {
399			if (debug_level != 0)
400				/* we're debugging... */
401				(void) strcpy(logfile, "stderr");
402			else
403				(void) strcpy(logfile, "/dev/null");
404
405			(void) _nscd_add_admin_mod(NULL, 'l', logfile,
406				msg, sizeof (msg));
407		}
408
409		/* activate command options */
410		if (_nscd_server_setadmin(NULL) != NSCD_SUCCESS) {
411			(void) fprintf(stderr,
412			gettext("unable to set command line options\n"));
413			exit(1);
414		}
415
416		if (debug_level) {
417			/* we're debugging, no forking of nscd */
418
419			/*
420			 * forker nscd will be started if self credential
421			 * is configured
422			 */
423			_nscd_start_forker(saved_execname, saved_argc,
424				saved_argv);
425		} else {
426			/*
427			 * daemonize the nscd (forker nscd will also
428			 * be started if self credential is configured)
429			 */
430			detachfromtty();
431		}
432	} else { /* NSCD_FORKER */
433		(void) open("/dev/null", O_RDWR, 0);
434		(void) dup(0);
435		if (_logfd != 2)
436			(void) dup(0);
437	}
438
439	/* set up door and establish our own server thread pool */
440	if ((_doorfd = _nscd_setup_server(saved_execname, saved_argv)) == -1) {
441		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
442		(me, "unable to set up door\n");
443		exit(1);
444	}
445
446	/* inform the main nscd that this forker is ready */
447	if (_whoami == NSCD_FORKER) {
448		int	ret;
449
450		for (ret = NSS_ALTRETRY; ret == NSS_ALTRETRY; )
451			ret = _nscd_doorcall_sendfd(_doorfd,
452				NSCD_IMHERE | (NSCD_FORKER & NSCD_WHOAMI),
453				NULL, 0, NULL);
454	}
455
456	for (;;) {
457		(void) pause();
458		(void) _nscd_doorcall(NSCD_REFRESH);
459	}
460
461	/* NOTREACHED */
462	/*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
463}
464
465static void
466usage(char *s)
467{
468	(void) fprintf(stderr,
469		"Usage: %s [-d debug_level] [-l logfilename]\n", s);
470	(void) fprintf(stderr,
471		"	[-p cachename,positive_time_to_live]\n");
472	(void) fprintf(stderr,
473		"	[-n cachename,negative_time_to_live]\n");
474	(void) fprintf(stderr,
475		"	[-i cachename]\n");
476	(void) fprintf(stderr,
477		"	[-h cachename,keep_hot_count]\n");
478	(void) fprintf(stderr,
479		"	[-e cachename,\"yes\"|\"no\"] [-g] " \
480		"[-c cachename,\"yes\"|\"no\"]\n");
481	(void) fprintf(stderr,
482		"	[-f configfilename] \n");
483	(void) fprintf(stderr,
484		"\n	Supported caches:\n");
485	(void) fprintf(stderr,
486		"	  audit_user, auth_attr, bootparams, ethers\n");
487	(void) fprintf(stderr,
488		"	  exec_attr, group, hosts, ipnodes, netmasks\n");
489	(void) fprintf(stderr,
490		"	  networks, passwd, printers, prof_attr, project\n");
491	(void) fprintf(stderr,
492		"	  protocols, rpc, services, tnrhtp, tnrhdb\n");
493	(void) fprintf(stderr,
494		"	  user_attr\n");
495	exit(1);
496}
497
498/*
499 * detach from tty
500 */
501static void
502detachfromtty(void)
503{
504	nscd_rc_t	rc;
505	char		*me = "detachfromtty";
506
507	if (_logfd > 0) {
508		int i;
509		for (i = 0; i < _logfd; i++)
510			(void) close(i);
511		closefrom(_logfd + 1);
512	} else
513		closefrom(0);
514
515	(void) chdir("/");
516
517	switch (fork1()) {
518	case (pid_t)-1:
519
520		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
521		(me, "unable to fork: pid = %d, %s\n",
522		getpid(), strerror(errno));
523
524		exit(1);
525		break;
526	case 0:
527		/* start the forker nscd if so configured */
528		_nscd_start_forker(saved_execname, saved_argc, saved_argv);
529		break;
530	default:
531		exit(0);
532	}
533	(void) setsid();
534	(void) open("/dev/null", O_RDWR, 0);
535	(void) dup(0);
536	if (_logfd != 2)
537		(void) dup(0);
538
539	/*
540	 * start monitoring the states of the name service clients
541	 */
542	rc = _nscd_init_smf_monitor();
543	if (rc != NSCD_SUCCESS) {
544		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
545	(me, "unable to start the SMF monitor (rc = %d)\n", rc);
546
547		exit(-1);
548	}
549}
550