xref: /illumos-gate/usr/src/cmd/nscd/server.c (revision bb25c06c)
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 <stdlib.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <stdarg.h>
38 #include <locale.h>
39 #include <tsol/label.h>
40 #include <zone.h>
41 #include "cache.h"
42 #include "nscd_log.h"
43 #include "nscd_selfcred.h"
44 #include "nscd_frontend.h"
45 #include "nscd_common.h"
46 #include "nscd_admin.h"
47 #include "nscd_door.h"
48 #include "nscd_switch.h"
49 
50 extern int 	optind;
51 extern int 	opterr;
52 extern int 	optopt;
53 extern char 	*optarg;
54 
55 #define	NSCDOPT	"S:Kf:c:ge:p:n:i:l:d:s:h:o:GFR"
56 
57 /* assume this is a single nscd  or, if multiple, the main nscd */
58 int		_whoami = NSCD_MAIN;
59 int		_doorfd = -1;
60 extern int	_logfd;
61 static char	*cfgfile = NULL;
62 
63 extern nsc_ctx_t *cache_ctx_p[];
64 
65 static void usage(char *);
66 static void detachfromtty(void);
67 
68 static int	debug_level = 0;
69 static char	logfile[128] = { 0 };
70 static int	will_become_server;
71 
72 static char *
73 getcacheopt(char *s)
74 {
75 	while (*s && *s != ',')
76 		s++;
77 	return ((*s == ',') ? (s + 1) : NULL);
78 }
79 
80 /*
81  * declaring this causes the files backend to use hashing
82  * this is of course an utter hack, but provides a nice
83  * quiet back door to enable this feature for only the nscd.
84  */
85 void
86 __nss_use_files_hash(void)
87 {
88 }
89 
90 static int	saved_argc = 0;
91 static char	**saved_argv = NULL;
92 static char	saved_execname[MAXPATHLEN];
93 
94 static void
95 save_execname()
96 {
97 	const char *name = getexecname();
98 
99 	saved_execname[0] = 0;
100 
101 	if (name[0] != '/') { /* started w/ relative path */
102 		(void) getcwd(saved_execname, MAXPATHLEN);
103 		(void) strlcat(saved_execname, "/", MAXPATHLEN);
104 	}
105 	(void) strlcat(saved_execname, name, MAXPATHLEN);
106 }
107 
108 int
109 main(int argc, char ** argv)
110 {
111 	int		opt;
112 	int		errflg = 0;
113 	int		showstats = 0;
114 	int		doset = 0;
115 	nscd_rc_t	rc;
116 	char		*me = "main()";
117 	char		*ret_locale;
118 	char		*ret_textdomain;
119 	char		msg[128];
120 
121 	ret_locale = setlocale(LC_ALL, "");
122 	if (ret_locale == NULL)
123 		(void) fprintf(stderr, gettext("Unable to set locale\n"));
124 
125 	ret_textdomain = textdomain(TEXT_DOMAIN);
126 	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,
136 gettext("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,
376 gettext("%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 
465 static void
466 usage(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  */
501 static void
502 detachfromtty(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