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 2006 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 #include <stdlib.h> 29 #include <assert.h> 30 #include <string.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include "nscd_db.h" 34 #include "nscd_log.h" 35 #include "nscd_switch.h" 36 #include "nscd_door.h" 37 38 extern int _whoami; 39 static mutex_t getent_monitor_mutex = DEFAULTMUTEX; 40 static int getent_monitor_started = 0; 41 42 static rwlock_t getent_ctxDB_rwlock = DEFAULTRWLOCK; 43 static nscd_db_t *getent_ctxDB = NULL; 44 45 /* 46 * internal structure representing a nscd getent context 47 */ 48 typedef struct nscd_getent_ctx { 49 int to_delete; /* this ctx no longer valid */ 50 nscd_getent_context_t *ptr; 51 nscd_cookie_t cookie; 52 } nscd_getent_ctx_t; 53 54 /* 55 * nscd_getent_context_t list for each nss database. Protected 56 * by the readers/writer lock nscd_getent_ctx_lock. 57 */ 58 nscd_getent_ctx_base_t **nscd_getent_ctx_base; 59 static rwlock_t nscd_getent_ctx_base_lock = DEFAULTRWLOCK; 60 61 extern nscd_db_entry_t *_nscd_walk_db(nscd_db_t *db, void **cookie); 62 63 static nscd_rc_t _nscd_init_getent_ctx_monitor(); 64 65 /* 66 * FUNCTION: _nscd_create_getent_ctxDB 67 * 68 * Create the internal getent context database to keep track of the 69 * getent contexts currently being used. 70 */ 71 nscd_db_t * 72 _nscd_create_getent_ctxDB() 73 { 74 75 nscd_db_t *ret; 76 77 (void) rw_wrlock(&getent_ctxDB_rwlock); 78 79 if (getent_ctxDB != NULL) { 80 (void) rw_unlock(&getent_ctxDB_rwlock); 81 return (getent_ctxDB); 82 } 83 84 ret = _nscd_alloc_db(NSCD_DB_SIZE_LARGE); 85 86 if (ret != NULL) 87 getent_ctxDB = ret; 88 89 (void) rw_unlock(&getent_ctxDB_rwlock); 90 91 return (ret); 92 } 93 94 /* 95 * FUNCTION: _nscd_add_getent_ctx 96 * 97 * Add a getent context to the internal context database. 98 */ 99 static nscd_rc_t 100 _nscd_add_getent_ctx( 101 nscd_getent_context_t *ptr, 102 nscd_cookie_t cookie) 103 { 104 int size; 105 char buf[2 * sizeof (cookie) + 1]; 106 nscd_db_entry_t *db_entry; 107 nscd_getent_ctx_t *gnctx; 108 109 if (ptr == NULL) 110 return (NSCD_INVALID_ARGUMENT); 111 112 (void) snprintf(buf, sizeof (buf), "%lld", cookie); 113 114 size = sizeof (*gnctx); 115 116 db_entry = _nscd_alloc_db_entry(NSCD_DATA_CTX_ADDR, 117 (const char *)buf, size, 1, 1); 118 if (db_entry == NULL) 119 return (NSCD_NO_MEMORY); 120 121 gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array); 122 gnctx->ptr = ptr; 123 gnctx->cookie = cookie; 124 125 (void) rw_wrlock(&getent_ctxDB_rwlock); 126 (void) _nscd_add_db_entry(getent_ctxDB, buf, db_entry, 127 NSCD_ADD_DB_ENTRY_FIRST); 128 (void) rw_unlock(&getent_ctxDB_rwlock); 129 130 return (NSCD_SUCCESS); 131 } 132 133 /* 134 * FUNCTION: _nscd_is_getent_ctx 135 * 136 * Check to see if a getent context can be found in the internal 137 * getent context database. 138 */ 139 nscd_getent_context_t * 140 _nscd_is_getent_ctx( 141 nscd_cookie_t cookie) 142 { 143 char ptrstr[1 + 2 * sizeof (cookie)]; 144 const nscd_db_entry_t *db_entry; 145 nscd_getent_context_t *ret = NULL; 146 147 (void) snprintf(ptrstr, sizeof (ptrstr), "%lld", cookie); 148 149 (void) rw_rdlock(&getent_ctxDB_rwlock); 150 151 db_entry = _nscd_get_db_entry(getent_ctxDB, NSCD_DATA_CTX_ADDR, 152 (const char *)ptrstr, NSCD_GET_FIRST_DB_ENTRY, 0); 153 154 if (db_entry != NULL) { 155 nscd_getent_ctx_t *gnctx; 156 157 gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array); 158 159 /* 160 * If the ctx is not to be deleted and 161 * the cookie numbers match, return the ctx. 162 * Otherwise return NULL. 163 */ 164 if (gnctx->to_delete == 0 && gnctx->cookie == cookie) 165 ret = gnctx->ptr; 166 } 167 168 (void) rw_unlock(&getent_ctxDB_rwlock); 169 170 return (ret); 171 } 172 173 /* 174 * FUNCTION: _nscd_del_getent_ctx 175 * 176 * Delete a getent context from the internal getent context database. 177 */ 178 static void 179 _nscd_del_getent_ctx( 180 nscd_getent_context_t *ptr, 181 nscd_cookie_t cookie) 182 { 183 char ptrstr[1 + 2 * sizeof (cookie)]; 184 nscd_getent_ctx_t *gnctx; 185 const nscd_db_entry_t *db_entry; 186 187 if (ptr == NULL) 188 return; 189 190 (void) snprintf(ptrstr, sizeof (ptrstr), "%lld", cookie); 191 192 (void) rw_rdlock(&getent_ctxDB_rwlock); 193 /* 194 * first find the db entry and make sure the 195 * sequence number matched, then delete it from 196 * the database. 197 */ 198 db_entry = _nscd_get_db_entry(getent_ctxDB, 199 NSCD_DATA_CTX_ADDR, 200 (const char *)ptrstr, 201 NSCD_GET_FIRST_DB_ENTRY, 0); 202 if (db_entry != NULL) { 203 gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array); 204 if (gnctx->ptr == ptr && gnctx->cookie == cookie) { 205 206 (void) rw_unlock(&getent_ctxDB_rwlock); 207 (void) rw_wrlock(&getent_ctxDB_rwlock); 208 209 (void) _nscd_delete_db_entry(getent_ctxDB, 210 NSCD_DATA_CTX_ADDR, 211 (const char *)ptrstr, 212 NSCD_DEL_FIRST_DB_ENTRY, 0); 213 } 214 } 215 (void) rw_unlock(&getent_ctxDB_rwlock); 216 } 217 218 static void 219 _nscd_free_getent_ctx( 220 nscd_getent_context_t *gnctx) 221 { 222 223 char *me = "_nscd_free_getent_ctx"; 224 225 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 226 (me, "getent context %p\n", gnctx); 227 228 _nscd_put_nsw_state(gnctx->nsw_state); 229 _nscd_del_getent_ctx(gnctx, gnctx->cookie); 230 free(gnctx); 231 } 232 233 234 static void 235 _nscd_free_getent_ctx_base( 236 nscd_acc_data_t *data) 237 { 238 nscd_getent_ctx_base_t *base = (nscd_getent_ctx_base_t *)data; 239 nscd_getent_context_t *c, *tc; 240 char *me = "_nscd_free_getent_ctx_base"; 241 242 _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) 243 (me, "getent context base %p\n", base); 244 245 if (base == NULL) 246 return; 247 248 c = base->first; 249 while (c != NULL) { 250 tc = c->next; 251 _nscd_free_getent_ctx(c); 252 c = tc; 253 } 254 } 255 256 void 257 _nscd_free_all_getent_ctx_base() 258 { 259 nscd_getent_ctx_base_t *base; 260 int i; 261 char *me = "_nscd_free_all_getent_ctx_base"; 262 263 _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) 264 (me, "entering ..\n"); 265 266 (void) rw_wrlock(&nscd_getent_ctx_base_lock); 267 268 for (i = 0; i < NSCD_NUM_DB; i++) { 269 270 base = nscd_getent_ctx_base[i]; 271 if (base == NULL) 272 continue; 273 274 nscd_getent_ctx_base[i] = (nscd_getent_ctx_base_t *) 275 _nscd_set((nscd_acc_data_t *)base, NULL); 276 } 277 (void) rw_unlock(&nscd_getent_ctx_base_lock); 278 } 279 280 static nscd_getent_context_t * 281 _nscd_create_getent_ctx( 282 nscd_nsw_params_t *params) 283 { 284 nscd_getent_context_t *gnctx; 285 nss_db_root_t db_root; 286 char *me = "_nscd_create_getent_ctx"; 287 288 gnctx = calloc(1, sizeof (nscd_getent_context_t)); 289 if (gnctx == NULL) 290 return (NULL); 291 else { 292 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 293 (me, "getent context allocated %p\n", gnctx); 294 } 295 296 gnctx->dbi = params->dbi; 297 gnctx->cookie = _nscd_get_cookie(); 298 299 if (_nscd_get_nsw_state(&db_root, params) != NSCD_SUCCESS) { 300 free(gnctx); 301 return (NULL); 302 } 303 gnctx->nsw_state = (nscd_nsw_state_t *)db_root.s; 304 /* this is a nsw_state used for getent processing */ 305 gnctx->nsw_state->getent = 1; 306 307 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 308 (me, "got nsw_state %p\n", gnctx->nsw_state); 309 310 return (gnctx); 311 } 312 313 314 nscd_rc_t 315 _nscd_get_getent_ctx( 316 nss_getent_t *contextpp, 317 nscd_nsw_params_t *params) 318 { 319 320 nscd_getent_context_t *c; 321 nscd_getent_ctx_base_t *base, *tmp; 322 nscd_rc_t rc; 323 char *me = "_nscd_get_getent_ctx"; 324 325 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 326 (me, "entering ...\n"); 327 328 (void) rw_rdlock(&nscd_getent_ctx_base_lock); 329 base = nscd_getent_ctx_base[params->dbi]; 330 (void) rw_unlock(&nscd_getent_ctx_base_lock); 331 assert(base != NULL); 332 333 /* 334 * If the context list is not empty, return the first one 335 * on the list. Otherwise, create and return a new one if 336 * limit is not reached. if reacehed, wait for the 'one is 337 * available' signal. 338 */ 339 tmp = (nscd_getent_ctx_base_t *)_nscd_mutex_lock( 340 (nscd_acc_data_t *)base); 341 assert(base == tmp); 342 if (base->first == NULL) { 343 if (base->num_getent_ctx == base->max_getent_ctx) { 344 base->num_waiter++; 345 while (base->first == NULL) { 346 347 _NSCD_LOG(NSCD_LOG_GETENT_CTX, 348 NSCD_LOG_LEVEL_DEBUG) 349 (me, "waiting for signal\n"); 350 351 _nscd_cond_wait((nscd_acc_data_t *)base, NULL); 352 353 _NSCD_LOG(NSCD_LOG_GETENT_CTX, 354 NSCD_LOG_LEVEL_DEBUG) 355 (me, "woke up\n"); 356 } 357 base->num_waiter--; 358 } else { 359 base->first = _nscd_create_getent_ctx(params); 360 if (base->first != NULL) { 361 base->first->base = base; 362 base->num_getent_ctx++; 363 } else { 364 /* not able to create an getent ctx */ 365 366 _NSCD_LOG(NSCD_LOG_GETENT_CTX, 367 NSCD_LOG_LEVEL_ERROR) 368 (me, "create getent ctx failed\n"); 369 370 _nscd_mutex_unlock((nscd_acc_data_t *)base); 371 return (NSCD_CREATE_GETENT_CTX_FAILED); 372 } 373 374 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 375 (me, "got a new getent ctx %p\n", base->first); 376 } 377 } 378 379 assert(base->first != NULL); 380 381 c = base->first; 382 base->first = c->next; 383 c->next = NULL; 384 c->seq_num = 1; 385 386 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 387 (me, "got a getent ctx %p\n", c); 388 389 _nscd_mutex_unlock((nscd_acc_data_t *)base); 390 391 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 392 (me, "adding new ctx %p, cookie = %lld\n", c, c->cookie); 393 394 if ((rc = _nscd_add_getent_ctx(c, c->cookie)) != NSCD_SUCCESS) { 395 _nscd_put_getent_ctx(c); 396 return (rc); 397 } 398 contextpp->ctx = (struct nss_getent_context *)c; 399 400 /* start monitor and reclaim orphan getent context */ 401 if (getent_monitor_started == 0) { 402 (void) mutex_lock(&getent_monitor_mutex); 403 if (getent_monitor_started == 0) { 404 getent_monitor_started = 1; 405 (void) _nscd_init_getent_ctx_monitor(); 406 } 407 (void) mutex_unlock(&getent_monitor_mutex); 408 } 409 410 return (NSCD_SUCCESS); 411 } 412 413 void 414 _nscd_put_getent_ctx( 415 nscd_getent_context_t *gnctx) 416 { 417 418 nscd_getent_ctx_base_t *base; 419 char *me = "_nscd_put_getent_ctx"; 420 421 base = gnctx->base; 422 gnctx->seq_num = 0; 423 424 /* if context base is gone, so should this context */ 425 if ((_nscd_mutex_lock((nscd_acc_data_t *)base)) == NULL) { 426 _nscd_free_getent_ctx(gnctx); 427 return; 428 } 429 430 if (base->first != NULL) { 431 gnctx->next = base->first; 432 base->first = gnctx; 433 } else 434 base->first = gnctx; 435 436 /* put back the db state */ 437 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 438 (me, "putting back nsw state %p\n", gnctx->nsw_state); 439 440 /* this nsw_state is no longer used for getent processing */ 441 if (gnctx->nsw_state != NULL) 442 gnctx->nsw_state->getent = 0; 443 _nscd_put_nsw_state(gnctx->nsw_state); 444 gnctx->nsw_state = NULL; 445 446 _nscd_del_getent_ctx(gnctx, gnctx->cookie); 447 448 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 449 (me, "ctx (%p seq# = %lld) removed from getent ctx DB\n", 450 gnctx, gnctx->cookie); 451 452 if (base->num_waiter > 0) { 453 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 454 (me, "signaling (waiter = %d)\n", base->num_waiter); 455 456 _nscd_cond_signal((nscd_acc_data_t *)base); 457 } 458 459 _nscd_mutex_unlock((nscd_acc_data_t *)base); 460 } 461 462 nscd_rc_t 463 _nscd_init_getent_ctx_base( 464 int dbi, 465 int lock) 466 { 467 nscd_getent_ctx_base_t *base = NULL; 468 char *me = "_nscd_init_getent_ctx_base"; 469 470 if (lock) 471 (void) rw_rdlock(&nscd_getent_ctx_base_lock); 472 473 base = (nscd_getent_ctx_base_t *)_nscd_alloc( 474 NSCD_DATA_GETENT_CTX_BASE, 475 sizeof (nscd_getent_ctx_base_t), 476 _nscd_free_getent_ctx_base, 477 NSCD_ALLOC_MUTEX | NSCD_ALLOC_COND); 478 479 if (base == NULL) { 480 if (lock) 481 (void) rw_unlock(&nscd_getent_ctx_base_lock); 482 return (NSCD_NO_MEMORY); 483 } 484 _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) 485 (me, "base %p allocated\n", base); 486 487 /* 488 * initialize and activate the new getent_ctx base 489 */ 490 base->dbi = dbi; 491 base->max_getent_ctx = NSCD_SW_CFG(dbi).max_getent_ctx_per_db; 492 nscd_getent_ctx_base[dbi] = 493 (nscd_getent_ctx_base_t *)_nscd_set( 494 (nscd_acc_data_t *)nscd_getent_ctx_base[dbi], 495 (nscd_acc_data_t *)base); 496 497 if (lock) 498 (void) rw_unlock(&nscd_getent_ctx_base_lock); 499 500 return (NSCD_SUCCESS); 501 } 502 503 nscd_rc_t 504 _nscd_init_all_getent_ctx_base() 505 { 506 int i; 507 nscd_rc_t rc; 508 char *me = "_nscd_init_all_getent_ctx_base"; 509 510 (void) rw_wrlock(&nscd_getent_ctx_base_lock); 511 512 for (i = 0; i < NSCD_NUM_DB; i++) { 513 514 rc = _nscd_init_getent_ctx_base(i, 0); 515 516 if (rc != NSCD_SUCCESS) { 517 (void) rw_unlock(&nscd_getent_ctx_base_lock); 518 return (rc); 519 } 520 } 521 522 _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) 523 (me, "all getent context base initialized\n"); 524 525 (void) rw_unlock(&nscd_getent_ctx_base_lock); 526 527 return (NSCD_SUCCESS); 528 } 529 nscd_rc_t 530 _nscd_alloc_getent_ctx_base() 531 { 532 533 (void) rw_wrlock(&nscd_getent_ctx_base_lock); 534 535 nscd_getent_ctx_base = calloc(NSCD_NUM_DB, 536 sizeof (nscd_getent_ctx_base_t *)); 537 if (nscd_getent_ctx_base == NULL) { 538 (void) rw_unlock(&nscd_getent_ctx_base_lock); 539 return (NSCD_NO_MEMORY); 540 } 541 542 (void) rw_unlock(&nscd_getent_ctx_base_lock); 543 544 return (NSCD_SUCCESS); 545 } 546 547 static int 548 process_exited(pid_t pid) 549 { 550 char pname[PATH_MAX]; 551 int fd; 552 553 (void) snprintf(pname, sizeof (pname), "/proc/%d/psinfo", pid); 554 if ((fd = open(pname, O_RDONLY)) == -1) 555 return (1); 556 else { 557 (void) close(fd); 558 return (0); 559 } 560 } 561 562 /* 563 * FUNCTION: reclaim_getent_ctx 564 */ 565 /*ARGSUSED*/ 566 static void * 567 reclaim_getent_ctx(void *arg) 568 { 569 void *cookie = NULL; 570 nscd_db_entry_t *ep; 571 nscd_getent_ctx_t *ctx; 572 nscd_getent_context_t *gctx, *c; 573 nscd_getent_context_t *first = NULL, *last = NULL; 574 char *me = "reclaim_getent_ctx"; 575 576 /*CONSTCOND*/ 577 while (1) { 578 579 (void) rw_rdlock(&getent_ctxDB_rwlock); 580 581 for (ep = _nscd_walk_db(getent_ctxDB, &cookie); ep != NULL; 582 ep = _nscd_walk_db(getent_ctxDB, &cookie)) { 583 584 ctx = (nscd_getent_ctx_t *)*(ep->data_array); 585 586 gctx = ctx->ptr; 587 588 /* 589 * if the client process, which did the setent, 590 * exited, add the context to the orphan list 591 */ 592 if (gctx->pid != -1 && process_exited(gctx->pid)) { 593 594 _NSCD_LOG(NSCD_LOG_GETENT_CTX, 595 NSCD_LOG_LEVEL_DEBUG) 596 (me, "process %d exited, " 597 "getent context = %p, " 598 "db index = %d, cookie = %lld, " 599 "sequence # = %lld\n", 600 gctx->pid, gctx, gctx->dbi, 601 gctx->cookie, gctx->seq_num); 602 603 if (first != NULL) { 604 last->next = gctx; 605 last = gctx; 606 } else { 607 first = gctx; 608 last = gctx; 609 } 610 } 611 } 612 613 (void) rw_unlock(&getent_ctxDB_rwlock); 614 615 616 /* 617 * return all the orphan getent contexts to the pool 618 */ 619 for (gctx = first; gctx; ) { 620 c = gctx->next; 621 gctx->next = NULL; 622 _nscd_put_getent_ctx(gctx); 623 gctx = c; 624 } 625 first = last = NULL; 626 627 (void) sleep(60); 628 } 629 /*NOTREACHED*/ 630 /*LINTED E_FUNC_HAS_NO_RETURN_STMT*/ 631 } 632 633 static nscd_rc_t 634 _nscd_init_getent_ctx_monitor() { 635 636 int errnum; 637 char *me = "_nscd_init_getent_ctx_monitor"; 638 639 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 640 (me, "initializing the getent context monitor\n"); 641 642 /* 643 * the forker nscd does not process getent requests 644 * so no need to monitor orphan getent contexts 645 */ 646 if (_whoami == NSCD_FORKER) 647 return (NSCD_SUCCESS); 648 649 /* 650 * start a thread to reclaim unused getent contexts 651 */ 652 if (thr_create(NULL, NULL, reclaim_getent_ctx, 653 NULL, THR_DETACHED, NULL) != 0) { 654 errnum = errno; 655 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_ERROR) 656 (me, "thr_create: %s\n", strerror(errnum)); 657 return (NSCD_THREAD_CREATE_ERROR); 658 } 659 660 return (NSCD_SUCCESS); 661 } 662