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