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