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 *
getcacheopt(char * s)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
__nss_use_files_hash(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
save_execname()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
main(int argc,char ** argv)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
usage(char * s)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
detachfromtty(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