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