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