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