xref: /illumos-gate/usr/src/cmd/nscd/server.c (revision 437220cd296f6d8b6654d6d52508b40b1e2d1ac7)
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 2007 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 <sys/stat.h>
40 #include <tsol/label.h>
41 #include <zone.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 int	debug_level = 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 
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 
239 		/*
240 		 * initialize cache store
241 		 */
242 		if ((rc = init_cache(0)) != NSCD_SUCCESS) {
243 			(void) fprintf(stderr,
244 	gettext("initialization of cache store failed (rc = %d)\n"), rc);
245 			exit(1);
246 		}
247 	}
248 
249 	/*
250 	 * process usual options
251 	 */
252 	optind = 1; /* this is a rescan */
253 	*msg = '\0';
254 	while ((opt = getopt(argc, argv, NSCDOPT)) != EOF) {
255 
256 		switch (opt) {
257 
258 		case 'K':		/* undocumented feature */
259 			(void) _nscd_doorcall(NSCD_KILLSERVER);
260 			exit(0);
261 			break;
262 
263 		case 'G':
264 		case 'g':
265 			showstats++;
266 			break;
267 
268 		case 'p':
269 			doset++;
270 			if (_nscd_add_admin_mod(optarg, 'p',
271 			    getcacheopt(optarg),
272 			    msg, sizeof (msg)) == -1)
273 				errflg++;
274 			break;
275 
276 		case 'n':
277 			doset++;
278 			if (_nscd_add_admin_mod(optarg, 'n',
279 			    getcacheopt(optarg),
280 			    msg, sizeof (msg)) == -1)
281 				errflg++;
282 			break;
283 
284 		case 'c':
285 			doset++;
286 			if (_nscd_add_admin_mod(optarg, 'c',
287 			    getcacheopt(optarg),
288 			    msg, sizeof (msg)) == -1)
289 				errflg++;
290 			break;
291 
292 		case 'i':
293 			doset++;
294 			if (_nscd_add_admin_mod(optarg, 'i', NULL,
295 			    msg, sizeof (msg)) == -1)
296 				errflg++;
297 			break;
298 
299 		case 'l':
300 			doset++;
301 			(void) strlcpy(logfile, optarg, 128);
302 			(void) _nscd_add_admin_mod(NULL, 'l', optarg,
303 			    msg, sizeof (msg));
304 			break;
305 
306 		case 'd':
307 			doset++;
308 			debug_level = atoi(optarg);
309 			(void) _nscd_add_admin_mod(NULL, 'd', optarg,
310 			    msg, sizeof (msg));
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 (_logfd < 0 && *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 		}
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) {
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 		(void) open("/dev/null", O_RDWR, 0);
440 		(void) dup(0);
441 		if (_logfd != 2)
442 			(void) dup(0);
443 	}
444 
445 	/* set up door and establish our own server thread pool */
446 	if ((_doorfd = _nscd_setup_server(saved_execname, saved_argv)) == -1) {
447 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
448 		(me, "unable to set up door\n");
449 		exit(1);
450 	}
451 
452 	/* inform the main nscd that this forker is ready */
453 	if (_whoami == NSCD_FORKER) {
454 		int	ret;
455 
456 		for (ret = NSS_ALTRETRY; ret == NSS_ALTRETRY; )
457 			ret = _nscd_doorcall_sendfd(_doorfd,
458 			    NSCD_IMHERE | (NSCD_FORKER & NSCD_WHOAMI),
459 			    NULL, 0, NULL);
460 	}
461 
462 	for (;;) {
463 		(void) pause();
464 		(void) _nscd_doorcall(NSCD_REFRESH);
465 	}
466 
467 	/* NOTREACHED */
468 	/*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
469 }
470 
471 static void
472 usage(char *s)
473 {
474 	(void) fprintf(stderr,
475 	    "Usage: %s [-d debug_level] [-l logfilename]\n", s);
476 	(void) fprintf(stderr,
477 	    "	[-p cachename,positive_time_to_live]\n");
478 	(void) fprintf(stderr,
479 	    "	[-n cachename,negative_time_to_live]\n");
480 	(void) fprintf(stderr,
481 	    "	[-i cachename]\n");
482 	(void) fprintf(stderr,
483 	    "	[-h cachename,keep_hot_count]\n");
484 	(void) fprintf(stderr,
485 	    "	[-e cachename,\"yes\"|\"no\"] [-g] " \
486 	    "[-c cachename,\"yes\"|\"no\"]\n");
487 	(void) fprintf(stderr,
488 	    "	[-f configfilename] \n");
489 	(void) fprintf(stderr,
490 	    "\n	Supported caches:\n");
491 	(void) fprintf(stderr,
492 	    "	  audit_user, auth_attr, bootparams, ethers\n");
493 	(void) fprintf(stderr,
494 	    "	  exec_attr, group, hosts, ipnodes, netmasks\n");
495 	(void) fprintf(stderr,
496 	    "	  networks, passwd, printers, prof_attr, project\n");
497 	(void) fprintf(stderr,
498 	    "	  protocols, rpc, services, tnrhtp, tnrhdb\n");
499 	(void) fprintf(stderr,
500 	    "	  user_attr\n");
501 	exit(1);
502 }
503 
504 /*
505  * detach from tty
506  */
507 static void
508 detachfromtty(void)
509 {
510 	nscd_rc_t	rc;
511 	char		*me = "detachfromtty";
512 
513 	if (_logfd > 0) {
514 		int i;
515 		for (i = 0; i < _logfd; i++)
516 			(void) close(i);
517 		closefrom(_logfd + 1);
518 	} else
519 		closefrom(0);
520 
521 	(void) chdir("/");
522 
523 	switch (fork1()) {
524 	case (pid_t)-1:
525 
526 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
527 		(me, "unable to fork: pid = %d, %s\n",
528 		    getpid(), strerror(errno));
529 
530 		exit(1);
531 		break;
532 	case 0:
533 		/* start the forker nscd if so configured */
534 		_nscd_start_forker(saved_execname, saved_argc, saved_argv);
535 		break;
536 	default:
537 		exit(0);
538 	}
539 	(void) setsid();
540 	(void) open("/dev/null", O_RDWR, 0);
541 	(void) dup(0);
542 	if (_logfd != 2)
543 		(void) dup(0);
544 
545 	/*
546 	 * start monitoring the states of the name service clients
547 	 */
548 	rc = _nscd_init_smf_monitor();
549 	if (rc != NSCD_SUCCESS) {
550 		_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
551 	(me, "unable to start the SMF monitor (rc = %d)\n", rc);
552 
553 		exit(-1);
554 	}
555 }
556