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 gnctx->pid = -1; 299 300 if (_nscd_get_nsw_state(&db_root, params) != NSCD_SUCCESS) { 301 free(gnctx); 302 return (NULL); 303 } 304 gnctx->nsw_state = (nscd_nsw_state_t *)db_root.s; 305 /* this is a nsw_state used for getent processing */ 306 gnctx->nsw_state->getent = 1; 307 308 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 309 (me, "got nsw_state %p\n", gnctx->nsw_state); 310 311 return (gnctx); 312 } 313 314 315 nscd_rc_t 316 _nscd_get_getent_ctx( 317 nss_getent_t *contextpp, 318 nscd_nsw_params_t *params) 319 { 320 321 nscd_getent_context_t *c; 322 nscd_getent_ctx_base_t *base, *tmp; 323 nscd_rc_t rc; 324 char *me = "_nscd_get_getent_ctx"; 325 326 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 327 (me, "entering ...\n"); 328 329 (void) rw_rdlock(&nscd_getent_ctx_base_lock); 330 base = nscd_getent_ctx_base[params->dbi]; 331 (void) rw_unlock(&nscd_getent_ctx_base_lock); 332 assert(base != NULL); 333 334 /* 335 * If the context list is not empty, return the first one 336 * on the list. Otherwise, create and return a new one if 337 * limit is not reached. if reacehed, wait for the 'one is 338 * available' signal. 339 */ 340 tmp = (nscd_getent_ctx_base_t *)_nscd_mutex_lock( 341 (nscd_acc_data_t *)base); 342 assert(base == tmp); 343 if (base->first == NULL) { 344 if (base->num_getent_ctx == base->max_getent_ctx) { 345 base->num_waiter++; 346 while (base->first == NULL) { 347 348 _NSCD_LOG(NSCD_LOG_GETENT_CTX, 349 NSCD_LOG_LEVEL_DEBUG) 350 (me, "waiting for signal\n"); 351 352 _nscd_cond_wait((nscd_acc_data_t *)base, NULL); 353 354 _NSCD_LOG(NSCD_LOG_GETENT_CTX, 355 NSCD_LOG_LEVEL_DEBUG) 356 (me, "woke up\n"); 357 } 358 base->num_waiter--; 359 } else { 360 base->first = _nscd_create_getent_ctx(params); 361 if (base->first != NULL) { 362 base->first->base = base; 363 base->num_getent_ctx++; 364 } else { 365 /* not able to create an getent ctx */ 366 367 _NSCD_LOG(NSCD_LOG_GETENT_CTX, 368 NSCD_LOG_LEVEL_ERROR) 369 (me, "create getent ctx failed\n"); 370 371 _nscd_mutex_unlock((nscd_acc_data_t *)base); 372 return (NSCD_CREATE_GETENT_CTX_FAILED); 373 } 374 375 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 376 (me, "got a new getent ctx %p\n", base->first); 377 } 378 } 379 380 assert(base->first != NULL); 381 382 c = base->first; 383 base->first = c->next; 384 c->next = NULL; 385 c->seq_num = 1; 386 387 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 388 (me, "got a getent ctx %p\n", c); 389 390 _nscd_mutex_unlock((nscd_acc_data_t *)base); 391 392 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 393 (me, "adding new ctx %p, cookie = %lld\n", c, c->cookie); 394 395 if ((rc = _nscd_add_getent_ctx(c, c->cookie)) != NSCD_SUCCESS) { 396 _nscd_put_getent_ctx(c); 397 return (rc); 398 } 399 contextpp->ctx = (struct nss_getent_context *)c; 400 401 /* start monitor and reclaim orphan getent context */ 402 if (getent_monitor_started == 0) { 403 (void) mutex_lock(&getent_monitor_mutex); 404 if (getent_monitor_started == 0) { 405 getent_monitor_started = 1; 406 (void) _nscd_init_getent_ctx_monitor(); 407 } 408 (void) mutex_unlock(&getent_monitor_mutex); 409 } 410 411 return (NSCD_SUCCESS); 412 } 413 414 void 415 _nscd_put_getent_ctx( 416 nscd_getent_context_t *gnctx) 417 { 418 419 nscd_getent_ctx_base_t *base; 420 char *me = "_nscd_put_getent_ctx"; 421 422 base = gnctx->base; 423 gnctx->seq_num = 0; 424 425 /* if context base is gone, so should this context */ 426 if ((_nscd_mutex_lock((nscd_acc_data_t *)base)) == NULL) { 427 _nscd_free_getent_ctx(gnctx); 428 return; 429 } 430 431 if (base->first != NULL) { 432 gnctx->next = base->first; 433 base->first = gnctx; 434 } else 435 base->first = gnctx; 436 437 /* put back the db state */ 438 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 439 (me, "putting back nsw state %p\n", gnctx->nsw_state); 440 441 /* this nsw_state is no longer used for getent processing */ 442 if (gnctx->nsw_state != NULL) 443 gnctx->nsw_state->getent = 0; 444 _nscd_put_nsw_state(gnctx->nsw_state); 445 gnctx->nsw_state = NULL; 446 447 _nscd_del_getent_ctx(gnctx, gnctx->cookie); 448 449 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 450 (me, "ctx (%p seq# = %lld) removed from getent ctx DB\n", 451 gnctx, gnctx->cookie); 452 453 if (base->num_waiter > 0) { 454 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 455 (me, "signaling (waiter = %d)\n", base->num_waiter); 456 457 _nscd_cond_signal((nscd_acc_data_t *)base); 458 } 459 460 _nscd_mutex_unlock((nscd_acc_data_t *)base); 461 } 462 463 nscd_rc_t 464 _nscd_init_getent_ctx_base( 465 int dbi, 466 int lock) 467 { 468 nscd_getent_ctx_base_t *base = NULL; 469 char *me = "_nscd_init_getent_ctx_base"; 470 471 if (lock) 472 (void) rw_rdlock(&nscd_getent_ctx_base_lock); 473 474 base = (nscd_getent_ctx_base_t *)_nscd_alloc( 475 NSCD_DATA_GETENT_CTX_BASE, 476 sizeof (nscd_getent_ctx_base_t), 477 _nscd_free_getent_ctx_base, 478 NSCD_ALLOC_MUTEX | NSCD_ALLOC_COND); 479 480 if (base == NULL) { 481 if (lock) 482 (void) rw_unlock(&nscd_getent_ctx_base_lock); 483 return (NSCD_NO_MEMORY); 484 } 485 _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) 486 (me, "base %p allocated\n", base); 487 488 /* 489 * initialize and activate the new getent_ctx base 490 */ 491 base->dbi = dbi; 492 base->max_getent_ctx = NSCD_SW_CFG(dbi).max_getent_ctx_per_db; 493 nscd_getent_ctx_base[dbi] = 494 (nscd_getent_ctx_base_t *)_nscd_set( 495 (nscd_acc_data_t *)nscd_getent_ctx_base[dbi], 496 (nscd_acc_data_t *)base); 497 498 if (lock) 499 (void) rw_unlock(&nscd_getent_ctx_base_lock); 500 501 return (NSCD_SUCCESS); 502 } 503 504 nscd_rc_t 505 _nscd_init_all_getent_ctx_base() 506 { 507 int i; 508 nscd_rc_t rc; 509 char *me = "_nscd_init_all_getent_ctx_base"; 510 511 (void) rw_wrlock(&nscd_getent_ctx_base_lock); 512 513 for (i = 0; i < NSCD_NUM_DB; i++) { 514 515 rc = _nscd_init_getent_ctx_base(i, 0); 516 517 if (rc != NSCD_SUCCESS) { 518 (void) rw_unlock(&nscd_getent_ctx_base_lock); 519 return (rc); 520 } 521 } 522 523 _NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG) 524 (me, "all getent context base initialized\n"); 525 526 (void) rw_unlock(&nscd_getent_ctx_base_lock); 527 528 return (NSCD_SUCCESS); 529 } 530 nscd_rc_t 531 _nscd_alloc_getent_ctx_base() 532 { 533 534 (void) rw_wrlock(&nscd_getent_ctx_base_lock); 535 536 nscd_getent_ctx_base = calloc(NSCD_NUM_DB, 537 sizeof (nscd_getent_ctx_base_t *)); 538 if (nscd_getent_ctx_base == NULL) { 539 (void) rw_unlock(&nscd_getent_ctx_base_lock); 540 return (NSCD_NO_MEMORY); 541 } 542 543 (void) rw_unlock(&nscd_getent_ctx_base_lock); 544 545 return (NSCD_SUCCESS); 546 } 547 548 static int 549 process_exited(pid_t pid) 550 { 551 char pname[PATH_MAX]; 552 int fd; 553 554 (void) snprintf(pname, sizeof (pname), "/proc/%d/psinfo", pid); 555 if ((fd = open(pname, O_RDONLY)) == -1) 556 return (1); 557 else { 558 (void) close(fd); 559 return (0); 560 } 561 } 562 563 /* 564 * FUNCTION: reclaim_getent_ctx 565 */ 566 /*ARGSUSED*/ 567 static void * 568 reclaim_getent_ctx(void *arg) 569 { 570 void *cookie = NULL; 571 nscd_db_entry_t *ep; 572 nscd_getent_ctx_t *ctx; 573 nscd_getent_context_t *gctx, *c; 574 nscd_getent_context_t *first = NULL, *last = NULL; 575 char *me = "reclaim_getent_ctx"; 576 577 /*CONSTCOND*/ 578 while (1) { 579 580 (void) sleep(60); 581 582 (void) rw_rdlock(&getent_ctxDB_rwlock); 583 584 for (ep = _nscd_walk_db(getent_ctxDB, &cookie); ep != NULL; 585 ep = _nscd_walk_db(getent_ctxDB, &cookie)) { 586 587 ctx = (nscd_getent_ctx_t *)*(ep->data_array); 588 589 gctx = ctx->ptr; 590 591 /* 592 * if the client process, which did the setent, 593 * exited, add the context to the orphan list 594 */ 595 if (gctx->pid != -1 && process_exited(gctx->pid)) { 596 597 _NSCD_LOG(NSCD_LOG_GETENT_CTX, 598 NSCD_LOG_LEVEL_DEBUG) 599 (me, "process %d exited, " 600 "getent context = %p, " 601 "db index = %d, cookie = %lld, " 602 "sequence # = %lld\n", 603 gctx->pid, gctx, gctx->dbi, 604 gctx->cookie, gctx->seq_num); 605 606 if (first != NULL) { 607 last->next = gctx; 608 last = gctx; 609 } else { 610 first = gctx; 611 last = gctx; 612 } 613 } 614 } 615 616 (void) rw_unlock(&getent_ctxDB_rwlock); 617 618 619 /* 620 * return all the orphan getent contexts to the pool 621 */ 622 for (gctx = first; gctx; ) { 623 c = gctx->next; 624 gctx->next = NULL; 625 _nscd_put_getent_ctx(gctx); 626 gctx = c; 627 } 628 first = last = NULL; 629 } 630 /*NOTREACHED*/ 631 /*LINTED E_FUNC_HAS_NO_RETURN_STMT*/ 632 } 633 634 static nscd_rc_t 635 _nscd_init_getent_ctx_monitor() { 636 637 int errnum; 638 char *me = "_nscd_init_getent_ctx_monitor"; 639 640 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG) 641 (me, "initializing the getent context monitor\n"); 642 643 /* 644 * the forker nscd does not process getent requests 645 * so no need to monitor orphan getent contexts 646 */ 647 if (_whoami == NSCD_FORKER) 648 return (NSCD_SUCCESS); 649 650 /* 651 * start a thread to reclaim unused getent contexts 652 */ 653 if (thr_create(NULL, NULL, reclaim_getent_ctx, 654 NULL, THR_DETACHED, NULL) != 0) { 655 errnum = errno; 656 _NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_ERROR) 657 (me, "thr_create: %s\n", strerror(errnum)); 658 return (NSCD_THREAD_CREATE_ERROR); 659 } 660 661 return (NSCD_SUCCESS); 662 } 663