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