xref: /illumos-gate/usr/src/cmd/nscd/server.c (revision 410cfc1c29e3c504b79366ddfd3584e9e69a33d5)
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 (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * Simple doors name server cache daemon
27  */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <locale.h>
36 #include <sys/stat.h>
37 #include <tsol/label.h>
38 #include <zone.h>
39 #include <signal.h>
40 #include <sys/resource.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 char	debug_level[32] = { 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 	struct		rlimit rl;
121 
122 	ret_locale = setlocale(LC_ALL, "");
123 	if (ret_locale == NULL)
124 		(void) fprintf(stderr, gettext("Unable to set locale\n"));
125 
126 	ret_textdomain = textdomain(TEXT_DOMAIN);
127 	if (ret_textdomain == NULL)
128 		(void) fprintf(stderr, gettext("Unable to set textdomain\n"));
129 
130 	/*
131 	 * The admin model for TX is that labeled zones are managed
132 	 * in global zone where most trusted configuration database
133 	 * resides. However, nscd will run in any labeled zone if
134 	 * file /var/tsol/doors/nscd_per_label exists.
135 	 */
136 	if (is_system_labeled() && (getzoneid() != GLOBAL_ZONEID)) {
137 		struct stat sbuf;
138 		if (stat(TSOL_NSCD_PER_LABEL_FILE, &sbuf) < 0) {
139 			(void) fprintf(stderr,
140 			gettext("With Trusted Extensions nscd runs only in the "
141 			    "global zone (if nscd_per_label flag not set)\n"));
142 			exit(1);
143 		}
144 	}
145 
146 	/*
147 	 *  Special case non-root user here - he can just print stats
148 	 */
149 	if (geteuid()) {
150 		if (argc != 2 ||
151 		    (strcmp(argv[1], "-g") && strcmp(argv[1], "-G"))) {
152 			(void) fprintf(stderr,
153 	gettext("Must be root to use any option other than -g\n\n"));
154 			usage(argv[0]);
155 		}
156 
157 		if (_nscd_doorcall(NSCD_PING) != NSS_SUCCESS) {
158 			(void) fprintf(stderr,
159 			gettext("%s doesn't appear to be running.\n"),
160 			    argv[0]);
161 			exit(1);
162 		}
163 		if (_nscd_client_getadmin(argv[1][1]) != 0) {
164 			(void) fprintf(stderr,
165 	gettext("unable to get configuration and statistics data\n"));
166 			exit(1);
167 		}
168 
169 		_nscd_client_showstats();
170 		exit(0);
171 	}
172 
173 	/*
174 	 *  Determine if there is already a daemon (main nscd) running.
175 	 *  If not, will start it. Forker NSCD will always become a
176 	 *  daemon.
177 	 */
178 	will_become_server = (_nscd_doorcall(NSCD_PING) != NSS_SUCCESS);
179 	if (argc >= 2 && strcmp(argv[1], "-F") == 0) {
180 		will_become_server = 1;
181 		_whoami = NSCD_FORKER;
182 
183 		/*
184 		 * allow time for the main nscd to get ready
185 		 * to receive the IMHERE door request this
186 		 * process will send later
187 		 */
188 		(void) usleep(100000);
189 	}
190 
191 	/*
192 	 * first get the config file path. Also detect
193 	 * invalid option as soon as possible.
194 	 */
195 	while ((opt = getopt(argc, argv, NSCDOPT)) != EOF) {
196 		switch (opt) {
197 
198 		case 'f':
199 			if ((cfgfile = strdup(optarg)) == NULL)
200 				exit(1);
201 			break;
202 		case 'g':
203 			if (will_become_server) {
204 				(void) fprintf(stderr,
205 		gettext("nscd not running, no statistics to show\n\n"));
206 				errflg++;
207 			}
208 			break;
209 		case 'i':
210 			if (will_become_server) {
211 				(void) fprintf(stderr,
212 		gettext("nscd not running, no cache to invalidate\n\n"));
213 				errflg++;
214 			}
215 			break;
216 
217 		case '?':
218 			errflg++;
219 			break;
220 		}
221 
222 	}
223 	if (errflg)
224 		usage(argv[0]);
225 
226 	/*
227 	 *  perform more initialization and load configuration
228 	 * if to become server
229 	 */
230 	if (will_become_server) {
231 
232 		/* initialize switch engine and config/stats management */
233 		if ((rc = _nscd_init(cfgfile)) != NSCD_SUCCESS) {
234 			(void) fprintf(stderr,
235 		gettext("initialization of switch failed (rc = %d)\n"), rc);
236 			exit(1);
237 		}
238 		_nscd_get_log_info(debug_level, sizeof (debug_level),
239 		    logfile, sizeof (logfile));
240 
241 		/*
242 		 * initialize cache store
243 		 */
244 		if ((rc = init_cache(0)) != NSCD_SUCCESS) {
245 			(void) fprintf(stderr,
246 	gettext("initialization of cache store failed (rc = %d)\n"), rc);
247 			exit(1);
248 		}
249 	}
250 
251 	/*
252 	 * process usual options
253 	 */
254 	optind = 1; /* this is a rescan */
255 	*msg = '\0';
256 	while ((opt = getopt(argc, argv, NSCDOPT)) != EOF) {
257 
258 		switch (opt) {
259 
260 		case 'K':		/* undocumented feature */
261 			(void) _nscd_doorcall(NSCD_KILLSERVER);
262 			exit(0);
263 			break;
264 
265 		case 'G':
266 		case 'g':
267 			showstats++;
268 			break;
269 
270 		case 'p':
271 			doset++;
272 			if (_nscd_add_admin_mod(optarg, 'p',
273 			    getcacheopt(optarg),
274 			    msg, sizeof (msg)) == -1)
275 				errflg++;
276 			break;
277 
278 		case 'n':
279 			doset++;
280 			if (_nscd_add_admin_mod(optarg, 'n',
281 			    getcacheopt(optarg),
282 			    msg, sizeof (msg)) == -1)
283 				errflg++;
284 			break;
285 
286 		case 'c':
287 			doset++;
288 			if (_nscd_add_admin_mod(optarg, 'c',
289 			    getcacheopt(optarg),
290 			    msg, sizeof (msg)) == -1)
291 				errflg++;
292 			break;
293 
294 		case 'i':
295 			doset++;
296 			if (_nscd_add_admin_mod(optarg, 'i', NULL,
297 			    msg, sizeof (msg)) == -1)
298 				errflg++;
299 			break;
300 
301 		case 'l':
302 			doset++;
303 			(void) strlcpy(logfile, optarg, sizeof (logfile));
304 			break;
305 
306 		case 'd':
307 			doset++;
308 			(void) strlcpy(debug_level, optarg,
309 			    sizeof (debug_level));
310 			break;
311 
312 		case 'S':
313 			/* silently ignore secure-mode */
314 			break;
315 
316 		case 's':
317 			/* silently ignore suggested-size */
318 			break;
319 
320 		case 'o':
321 			/* silently ignore old-data-ok */
322 			break;
323 
324 		case 'h':
325 			doset++;
326 			if (_nscd_add_admin_mod(optarg, 'h',
327 			    getcacheopt(optarg),
328 			    msg, sizeof (msg)) == -1)
329 				errflg++;
330 			break;
331 
332 		case 'e':
333 			doset++;
334 			if (_nscd_add_admin_mod(optarg, 'e',
335 			    getcacheopt(optarg),
336 			    msg, sizeof (msg)) == -1)
337 				errflg++;
338 			break;
339 
340 		case 'F':
341 			_whoami = NSCD_FORKER;
342 			break;
343 
344 		default:
345 			errflg++;
346 			break;
347 		}
348 
349 	}
350 
351 	if (errflg) {
352 		if (*msg != '\0')
353 			(void) fprintf(stderr, "\n%s: %s\n\n", argv[0], msg);
354 		usage(argv[0]);
355 	}
356 
357 	/*
358 	 * if main nscd already running and not forker nscd,
359 	 * can only do admin work
360 	 */
361 	if (_whoami == NSCD_MAIN) {
362 		if (!will_become_server) {
363 			if (showstats) {
364 				if (_nscd_client_getadmin('g')) {
365 					(void) fprintf(stderr,
366 			gettext("Cannot contact nscd properly(?)\n"));
367 					exit(1);
368 				}
369 				_nscd_client_showstats();
370 			}
371 
372 			if (doset) {
373 				if (_nscd_client_setadmin() < 0) {
374 					(void) fprintf(stderr,
375 				gettext("Error during admin call\n"));
376 					exit(1);
377 				}
378 			}
379 			if (!showstats && !doset) {
380 				(void) fprintf(stderr,
381 gettext("%s already running.... no administration option specified\n"),
382 				    argv[0]);
383 			}
384 			exit(0);
385 		}
386 	}
387 
388 	/*
389 	 *   daemon from here on
390 	 */
391 
392 	if (_whoami == NSCD_MAIN) {
393 
394 		/* save enough info in case need to restart or fork */
395 		saved_argc = argc;
396 		saved_argv = argv;
397 		save_execname();
398 
399 		/*
400 		 * if a log file is not specified, set it to
401 		 * "stderr" or "/dev/null" based on debug level
402 		 */
403 		if (*logfile == '\0') {
404 			if (*debug_level != '\0')
405 				/* we're debugging... */
406 				(void) strcpy(logfile, "stderr");
407 			else
408 				(void) strcpy(logfile, "/dev/null");
409 		}
410 		(void) _nscd_add_admin_mod(NULL, 'l', logfile,
411 		    msg, sizeof (msg));
412 		(void) _nscd_add_admin_mod(NULL, 'd', debug_level,
413 		    msg, sizeof (msg));
414 
415 		/* activate command options */
416 		if (_nscd_server_setadmin(NULL) != NSCD_SUCCESS) {
417 			(void) fprintf(stderr,
418 			gettext("unable to set command line options\n"));
419 			exit(1);
420 		}
421 
422 		if (*debug_level != '\0') {
423 			/* we're debugging, no forking of nscd */
424 
425 			/*
426 			 * forker nscd will be started if self credential
427 			 * is configured
428 			 */
429 			_nscd_start_forker(saved_execname, saved_argc,
430 			    saved_argv);
431 		} else {
432 			/*
433 			 * daemonize the nscd (forker nscd will also
434 			 * be started if self credential is configured)
435 			 */
436 			detachfromtty();
437 		}
438 	} else { /* NSCD_FORKER */
439 		/*
440 		 * To avoid PUN (Per User Nscd) processes from becoming
441 		 * zombies after they exit, the forking nscd should
442 		 * ignore the SIGCLD signal so that it does not
443 		 * need to wait for every child PUN to exit.
444 		 */
445 		(void) signal(SIGCLD, SIG_IGN);
446 		(void) open("/dev/null", O_RDWR, 0);
447 		(void) dup(0);
448 		if (_logfd != 2)
449 			(void) dup(0);
450 	}
451 
452 	/* set NOFILE to unlimited */
453 	rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
454 	if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
455 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
456 		(me, "Cannot set open file limit: %s\n", strerror(errno));
457 		exit(1);
458 	}
459 
460 	/* set up door and establish our own server thread pool */
461 	if ((_doorfd = _nscd_setup_server(saved_execname, saved_argv)) == -1) {
462 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
463 		(me, "unable to set up door\n");
464 		exit(1);
465 	}
466 
467 	/* inform the main nscd that this forker is ready */
468 	if (_whoami == NSCD_FORKER) {
469 		int	ret;
470 
471 		for (ret = NSS_ALTRETRY; ret == NSS_ALTRETRY; )
472 			ret = _nscd_doorcall_sendfd(_doorfd,
473 			    NSCD_IMHERE | (NSCD_FORKER & NSCD_WHOAMI),
474 			    NULL, 0, NULL);
475 	}
476 
477 	for (;;) {
478 		(void) pause();
479 		(void) _nscd_doorcall(NSCD_REFRESH);
480 	}
481 
482 	/* NOTREACHED */
483 	/*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
484 }
485 
486 static void
487 usage(char *s)
488 {
489 	(void) fprintf(stderr,
490 	    "Usage: %s [-d debug_level] [-l logfilename]\n", s);
491 	(void) fprintf(stderr,
492 	    "	[-p cachename,positive_time_to_live]\n");
493 	(void) fprintf(stderr,
494 	    "	[-n cachename,negative_time_to_live]\n");
495 	(void) fprintf(stderr,
496 	    "	[-i cachename]\n");
497 	(void) fprintf(stderr,
498 	    "	[-h cachename,keep_hot_count]\n");
499 	(void) fprintf(stderr,
500 	    "	[-e cachename,\"yes\"|\"no\"] [-g] " \
501 	    "[-c cachename,\"yes\"|\"no\"]\n");
502 	(void) fprintf(stderr,
503 	    "	[-f configfilename] \n");
504 	(void) fprintf(stderr,
505 	    "\n	Supported caches:\n");
506 	(void) fprintf(stderr,
507 	    "	  auth_attr, bootparams, ethers\n");
508 	(void) fprintf(stderr,
509 	    "	  exec_attr, group, hosts, ipnodes, netmasks\n");
510 	(void) fprintf(stderr,
511 	    "	  networks, passwd, printers, prof_attr, project\n");
512 	(void) fprintf(stderr,
513 	    "	  protocols, rpc, services, tnrhtp, tnrhdb\n");
514 	(void) fprintf(stderr,
515 	    "	  user_attr\n");
516 	exit(1);
517 }
518 
519 /*
520  * detach from tty
521  */
522 static void
523 detachfromtty(void)
524 {
525 	nscd_rc_t	rc;
526 	char		*me = "detachfromtty";
527 
528 	if (_logfd > 0) {
529 		int i;
530 		for (i = 0; i < _logfd; i++)
531 			(void) close(i);
532 		closefrom(_logfd + 1);
533 	} else
534 		closefrom(0);
535 
536 	(void) chdir("/");
537 
538 	switch (fork1()) {
539 	case (pid_t)-1:
540 
541 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
542 		(me, "unable to fork: pid = %d, %s\n",
543 		    getpid(), strerror(errno));
544 
545 		exit(1);
546 		break;
547 	case 0:
548 		/* start the forker nscd if so configured */
549 		_nscd_start_forker(saved_execname, saved_argc, saved_argv);
550 		break;
551 	default:
552 		exit(0);
553 	}
554 
555 	(void) setsid();
556 	(void) open("/dev/null", O_RDWR, 0);
557 	(void) dup(0);
558 	if (_logfd != 2)
559 		(void) dup(0);
560 
561 	/*
562 	 * start monitoring the states of the name service clients
563 	 */
564 	rc = _nscd_init_smf_monitor();
565 	if (rc != NSCD_SUCCESS) {
566 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
567 	(me, "unable to start the SMF monitor (rc = %d)\n", rc);
568 
569 		exit(-1);
570 	}
571 }
572