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 /* 29 * Shared code used by the name-service-switch frontends (e.g. getpwnam_r()) 30 */ 31 32 #pragma weak nss_delete = _nss_delete 33 #pragma weak nss_endent = _nss_endent 34 #pragma weak nss_getent = _nss_getent 35 #pragma weak nss_search = _nss_search 36 #pragma weak nss_setent = _nss_setent 37 38 #include "synonyms.h" 39 #include <mtlib.h> 40 #include <dlfcn.h> 41 42 #define __NSS_PRIVATE_INTERFACE 43 #include "nsswitch_priv.h" 44 #undef __NSS_PRIVATE_INTERFACE 45 46 #include <nss_common.h> 47 #include <unistd.h> 48 #include <stdlib.h> 49 #include <stdio.h> 50 #include <string.h> 51 #include <thread.h> 52 #include "libc.h" 53 #include "tsd.h" 54 55 /* 56 * The golden rule is: if you hold a pointer to an nss_db_state struct and 57 * you don't hold the lock, you'd better have incremented the refcount 58 * while you held the lock; otherwise, it may vanish or change 59 * significantly when you least expect it. 60 * 61 * The pointer in nss_db_root_t is one such, so the reference count >= 1. 62 * Ditto the pointer in struct nss_getent_context. 63 */ 64 65 /* 66 * State for one nsswitch database (e.g. "passwd", "hosts") 67 */ 68 struct nss_db_state { 69 nss_db_root_t orphan_root; /* XXX explain */ 70 unsigned refcount; /* One for the pointer in */ 71 /* nss_db_root_t, plus one */ 72 /* for each active thread. */ 73 nss_db_params_t p; 74 struct __nsw_switchconfig_v1 *config; 75 int max_src; /* is == config->num_lookups */ 76 struct nss_src_state *src; /* Pointer to array[max_src] */ 77 }; 78 79 /* 80 * State for one of the sources (e.g. "nis", "compat") for a database 81 */ 82 struct nss_src_state { 83 struct __nsw_lookup_v1 *lkp; 84 int n_active; 85 int n_dormant; 86 int n_waiting; /* ... on wanna_be */ 87 cond_t wanna_be; 88 union { 89 nss_backend_t *single; /* Efficiency hack for common case */ 90 /* when limit_dead_backends == 1 */ 91 nss_backend_t **multi; /* array[limit_dead_backends] of */ 92 } dormant; /* pointers to dormant backends */ 93 nss_backend_constr_t be_constr; 94 nss_backend_finder_t *finder; 95 void *finder_priv; 96 }; 97 98 static struct nss_db_state *_nss_db_state_constr(nss_db_initf_t); 99 void _nss_db_state_destr(struct nss_db_state *); 100 101 /* ==== null definitions if !MTSAFE? Ditto lock field in nss_db_root_t */ 102 103 #define NSS_ROOTLOCK(r, sp) ((void) _private_mutex_lock(&(r)->lock), \ 104 *(sp) = (r)->s) 105 106 #define NSS_UNLOCK(r) ((void) _private_mutex_unlock(&(r)->lock)) 107 108 #define NSS_CHECKROOT(rp, s) ((s) != (*(rp))->s && \ 109 ((void) _private_mutex_unlock(&(*(rp))->lock), \ 110 (void) _private_mutex_lock(&(s)->orphan_root.lock), \ 111 *(rp) = &(s)->orphan_root)) 112 113 #define NSS_RELOCK(rp, s) ((void) _private_mutex_lock(&(*(rp))->lock), \ 114 NSS_CHECKROOT(rp, s)) 115 116 #define NSS_STATE_REF_u(s) (++(s)->refcount) 117 118 #define NSS_UNREF_UNLOCK(r, s) (--(s)->refcount != 0 \ 119 ? ((void)NSS_UNLOCK(r)) \ 120 : (NSS_UNLOCK(r), (void)_nss_db_state_destr(s))) 121 122 #define NSS_LOCK_CHECK(r, f, sp) (NSS_ROOTLOCK((r), (sp)), \ 123 *(sp) == 0 && \ 124 (r->s = *(sp) = _nss_db_state_constr(f))) 125 /* === In the future, NSS_LOCK_CHECK() may also have to check that */ 126 /* === the config info hasn't changed (by comparing version numbers) */ 127 128 129 /* NSS_OPTIONS infrastructure BEGIN */ 130 static int checked_env = 0; /* protected by "rootlock" */ 131 132 /* allowing __nss_debug_file to be set could be a security hole. */ 133 FILE *__nss_debug_file = stdout; 134 int __nss_debug_eng_loop; 135 136 /* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */ 137 /* allowing __nis_debug_file to be set could be a security hole. */ 138 FILE *__nis_debug_file = stdout; 139 int __nis_debug_bind; 140 int __nis_debug_rpc; 141 int __nis_debug_calls; 142 char *__nis_prefsrv; 143 char *__nis_preftype; 144 char *__nis_server; /* if set, use only this server for binding */ 145 146 #define OPT_INT 1 147 #define OPT_STRING 2 148 #ifdef DEBUG 149 #define OPT_FILE 3 150 #endif 151 152 struct option { 153 char *name; 154 int type; 155 void *address; 156 }; 157 158 static struct option nss_options[] = { 159 #ifdef DEBUG 160 /* allowing __nss_debug_file to be set could be a security hole. */ 161 { "debug_file", OPT_FILE, &__nss_debug_file }, 162 #endif 163 { "debug_eng_loop", OPT_INT, &__nss_debug_eng_loop }, 164 { 0, 0, 0 }, 165 }; 166 167 static struct option nis_options[] = { 168 #ifdef DEBUG 169 /* allowing __nis_debug_file to be set could be a security hole. */ 170 { "debug_file", OPT_FILE, &__nis_debug_file }, 171 #endif 172 { "debug_bind", OPT_INT, &__nis_debug_bind }, 173 { "debug_rpc", OPT_INT, &__nis_debug_rpc }, 174 { "debug_calls", OPT_INT, &__nis_debug_calls }, 175 { "server", OPT_STRING, &__nis_server }, 176 { "pref_srvr", OPT_STRING, &__nis_prefsrv }, 177 { "pref_type", OPT_STRING, &__nis_preftype }, 178 { 0, 0, 0 }, 179 }; 180 181 static 182 void 183 set_option(struct option *opt, char *name, char *val) 184 { 185 int n; 186 char *p; 187 #ifdef DEBUG 188 FILE *fp; 189 #endif 190 191 for (; opt->name; opt++) { 192 if (strcmp(name, opt->name) == 0) { 193 switch (opt->type) { 194 case OPT_STRING: 195 p = libc_strdup(val); 196 *((char **)opt->address) = p; 197 break; 198 199 case OPT_INT: 200 if (strcmp(val, "") == 0) 201 n = 1; 202 else 203 n = atoi(val); 204 *((int *)opt->address) = n; 205 break; 206 #ifdef DEBUG 207 case OPT_FILE: 208 fp = fopen(val, "wF"); 209 *((FILE **)opt->address) = fp; 210 break; 211 #endif 212 } 213 break; 214 } 215 } 216 } 217 218 static 219 void 220 __parse_environment(struct option *opt, char *p) 221 { 222 char *base; 223 char optname[100]; 224 char optval[100]; 225 226 while (*p) { 227 while (isspace(*p)) 228 p++; 229 if (*p == '\0') 230 break; 231 232 base = p; 233 while (*p && *p != '=' && !isspace(*p)) 234 p++; 235 /* 236 * play it safe and keep it simple, bail if an opt name 237 * is too long. 238 */ 239 if ((p-base) >= sizeof (optname)) 240 return; 241 242 (void) strncpy(optname, base, p-base); 243 optname[p-base] = '\0'; 244 245 if (*p == '=') { 246 p++; 247 base = p; 248 while (*p && !isspace(*p)) 249 p++; 250 /* 251 * play it safe and keep it simple, bail if an opt 252 * value is too long. 253 */ 254 if ((p-base) >= sizeof (optval)) 255 return; 256 257 (void) strncpy(optval, base, p-base); 258 optval[p-base] = '\0'; 259 } else { 260 optval[0] = '\0'; 261 } 262 263 set_option(opt, optname, optval); 264 } 265 } 266 267 static 268 void 269 nss_get_environment() 270 { 271 char *p; 272 273 /* NSS_OPTIONS is undocumented and should be used without nscd running. */ 274 p = getenv("NSS_OPTIONS"); 275 if (p == NULL) 276 return; 277 __parse_environment(nss_options, p); 278 } 279 280 /* 281 * sole external routine called from libnsl/nis/cache/cache_api.cc in the 282 * routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard 283 * Only after checking "checked_env" (which must be done with mutex 284 * "cur_cache_lock" held) and is done once, (then "checked_env" is set) 285 */ 286 void 287 __nis_get_environment() 288 { 289 char *p; 290 291 p = getenv("NIS_OPTIONS"); 292 if (p == NULL) 293 return; 294 __parse_environment(nis_options, p); 295 } 296 /* NSS_OPTIONS/NIS_OPTIONS infrastructure END */ 297 298 299 static nss_backend_t * 300 nss_get_backend_u(nss_db_root_t **rootpp, struct nss_db_state *s, int n_src) 301 { 302 struct nss_src_state *src = &s->src[n_src]; 303 nss_backend_t *be; 304 305 for (;;) { 306 if (src->n_dormant > 0) { 307 src->n_dormant--; 308 src->n_active++; 309 if (s->p.max_dormant_per_src == 1) { 310 be = src->dormant.single; 311 } else { 312 be = src->dormant.multi[src->n_dormant]; 313 } 314 break; 315 } 316 317 if (src->be_constr == 0) { 318 nss_backend_finder_t *bf; 319 320 for (bf = s->p.finders; bf != 0; bf = bf->next) { 321 nss_backend_constr_t c; 322 323 c = (*bf->lookup) 324 (bf->lookup_priv, 325 s->p.name, 326 src->lkp->service_name, 327 &src->finder_priv); 328 if (c != 0) { 329 src->be_constr = c; 330 src->finder = bf; 331 break; 332 } 333 } 334 if (src->be_constr == 0) { 335 /* Couldn't find the backend anywhere */ 336 be = 0; 337 break; 338 } 339 } 340 341 if (src->n_active < s->p.max_active_per_src) { 342 be = (*src->be_constr)(s->p.name, 343 src->lkp->service_name, 344 0 /* === unimplemented */); 345 if (be != 0) { 346 src->n_active++; 347 break; 348 } else if (src->n_active == 0) { 349 /* Something's wrong; we should be */ 350 /* able to create at least one */ 351 /* instance of the backend */ 352 break; 353 } 354 /* 355 * Else it's odd that we can't create another backend 356 * instance, but don't sweat it; instead, queue for 357 * an existing backend instance. 358 */ 359 } 360 361 src->n_waiting++; 362 (void) cond_wait(&src->wanna_be, &(*rootpp)->lock); 363 NSS_CHECKROOT(rootpp, s); 364 src->n_waiting--; 365 366 /* 367 * Loop and see whether things got better for us, or whether 368 * someone else got scheduled first and we have to try 369 * this again. 370 * 371 * === ?? Should count iterations, assume bug if many ?? 372 */ 373 } 374 return (be); 375 } 376 377 static void 378 nss_put_backend_u(struct nss_db_state *s, int n_src, nss_backend_t *be) 379 { 380 struct nss_src_state *src = &s->src[n_src]; 381 382 if (be == 0) { 383 return; 384 } 385 386 src->n_active--; 387 388 if (src->n_dormant < s->p.max_dormant_per_src) { 389 if (s->p.max_dormant_per_src == 1) { 390 src->dormant.single = be; 391 src->n_dormant++; 392 } else if (src->dormant.multi != 0 || 393 (src->dormant.multi = 394 libc_malloc(s->p.max_dormant_per_src * 395 sizeof (nss_backend_t *))) != NULL) { 396 src->dormant.multi[src->n_dormant] = be; 397 src->n_dormant++; 398 } else { 399 /* Can't store it, so toss it */ 400 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0); 401 } 402 } else { 403 /* We've stored as many as we want, so toss it */ 404 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0); 405 } 406 if (src->n_waiting > 0) { 407 (void) cond_signal(&src->wanna_be); 408 } 409 } 410 411 static struct nss_db_state * 412 _nss_db_state_constr(nss_db_initf_t initf) 413 { 414 struct nss_db_state *s; 415 struct __nsw_switchconfig_v1 *config = 0; 416 struct __nsw_lookup_v1 *lkp; 417 enum __nsw_parse_err err; 418 const char *config_name; 419 int n_src; 420 421 if ((s = libc_malloc(sizeof (*s))) == 0) { 422 return (0); 423 } 424 (void) _private_mutex_init(&s->orphan_root.lock, USYNC_THREAD, 0); 425 426 s->p.max_active_per_src = 10; 427 s->p.max_dormant_per_src = 1; 428 s->p.finders = nss_default_finders; 429 (*initf)(&s->p); 430 if (s->p.name == 0) { 431 _nss_db_state_destr(s); 432 return (0); 433 } 434 435 if (!checked_env) { 436 /* NSS_OPTIONS is undocumented and should be used without nscd running. */ 437 nss_get_environment(); 438 checked_env = 1; 439 } 440 441 config_name = s->p.config_name ? s->p.config_name : s->p.name; 442 if (! (s->p.flags & NSS_USE_DEFAULT_CONFIG)) { 443 config = __nsw_getconfig_v1(config_name, &err); 444 /* === ? test err ? */ 445 } 446 if (config == 0) { 447 /* getconfig failed, or frontend demanded default config */ 448 449 char *str; /* _nsw_getoneconfig() clobbers its argument */ 450 451 if ((str = libc_strdup(s->p.default_config)) != 0) { 452 config = _nsw_getoneconfig_v1(config_name, str, &err); 453 libc_free(str); 454 } 455 if (config == 0) { 456 _nss_db_state_destr(s); 457 return (0); 458 } 459 } 460 s->config = config; 461 if ((s->max_src = config->num_lookups) <= 0 || 462 (s->src = libc_malloc(s->max_src * sizeof (*s->src))) == 0) { 463 _nss_db_state_destr(s); 464 return (0); 465 } 466 for (n_src = 0, lkp = config->lookups; 467 n_src < s->max_src; n_src++, lkp = lkp->next) { 468 s->src[n_src].lkp = lkp; 469 (void) cond_init(&s->src[n_src].wanna_be, USYNC_THREAD, 0); 470 } 471 s->refcount = 1; 472 return (s); 473 } 474 475 void 476 _nss_src_state_destr(struct nss_src_state *src, int max_dormant) 477 { 478 if (max_dormant == 1) { 479 if (src->n_dormant != 0) { 480 (void) NSS_INVOKE_DBOP(src->dormant.single, 481 NSS_DBOP_DESTRUCTOR, 0); 482 }; 483 } else if (src->dormant.multi != 0) { 484 int n; 485 486 for (n = 0; n < src->n_dormant; n++) { 487 (void) NSS_INVOKE_DBOP(src->dormant.multi[n], 488 NSS_DBOP_DESTRUCTOR, 0); 489 } 490 libc_free(src->dormant.multi); 491 } 492 493 /* cond_destroy(&src->wanna_be); */ 494 495 if (src->finder != 0) { 496 (*src->finder->delete)(src->finder_priv, src->be_constr); 497 } 498 } 499 500 /* 501 * _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire 502 * nss_db_state structure. 503 * Assumes that s has been ref-counted down to zero (in particular, 504 * rootp->s has already been dealt with). 505 * 506 * Nobody else holds a pointer to *s (if they did, refcount != 0), 507 * so we can clean up state *after* we drop the lock (also, by the 508 * time we finish freeing the state structures, the lock may have 509 * ceased to exist -- if we were using the orphan_root). 510 */ 511 512 void 513 _nss_db_state_destr(struct nss_db_state *s) 514 { 515 /* === _private_mutex_destroy(&s->orphan_root.lock); */ 516 if (s->p.cleanup != 0) { 517 (*s->p.cleanup)(&s->p); 518 } 519 if (s->config != 0) { 520 (void) __nsw_freeconfig_v1(s->config); 521 } 522 if (s->src != 0) { 523 int n_src; 524 525 for (n_src = 0; n_src < s->max_src; n_src++) { 526 _nss_src_state_destr(&s->src[n_src], 527 s->p.max_dormant_per_src); 528 } 529 libc_free(s->src); 530 } 531 libc_free(s); 532 } 533 534 void 535 nss_delete(nss_db_root_t *rootp) 536 { 537 struct nss_db_state *s; 538 539 NSS_ROOTLOCK(rootp, &s); 540 if (s == 0) { 541 NSS_UNLOCK(rootp); 542 } else { 543 rootp->s = 0; 544 NSS_UNREF_UNLOCK(rootp, s); 545 } 546 } 547 548 549 /* 550 * _nss_status_vec() returns a bit vector of all status codes returned during 551 * the most recent call to nss_search(). 552 * _nss_status_vec_p() returns a pointer to this bit vector, or NULL on 553 * failure. 554 * These functions are private. Don't use them externally without discussing 555 * it with the switch maintainers. 556 */ 557 static uint_t * 558 _nss_status_vec_p() 559 { 560 return (tsdalloc(_T_NSS_STATUS_VEC, sizeof (uint_t), NULL)); 561 } 562 563 unsigned int 564 _nss_status_vec(void) 565 { 566 unsigned int *status_vec_p = _nss_status_vec_p(); 567 568 return ((status_vec_p != NULL) ? *status_vec_p : (1 << NSS_UNAVAIL)); 569 } 570 571 static void 572 output_loop_diag_a( 573 int n, 574 char *dbase, 575 struct __nsw_lookup_v1 *lkp) 576 577 { 578 (void) fprintf(__nss_debug_file, 579 "NSS_retry(%d): '%s': trying '%s' ... ", 580 n, dbase, lkp->service_name); 581 (void) fflush(__nss_debug_file); 582 583 } 584 585 static void 586 output_loop_diag_b( 587 nss_status_t res, 588 struct __nsw_lookup_v1 *lkp) 589 590 { 591 (void) fprintf(__nss_debug_file, "result="); 592 switch (res) { 593 case NSS_SUCCESS: 594 (void) fprintf(__nss_debug_file, "SUCCESS"); 595 break; 596 case NSS_NOTFOUND: 597 (void) fprintf(__nss_debug_file, "NOTFOUND"); 598 break; 599 case NSS_UNAVAIL: 600 (void) fprintf(__nss_debug_file, "UNAVAIL"); 601 break; 602 case NSS_TRYAGAIN: 603 (void) fprintf(__nss_debug_file, "TRYAGAIN"); 604 break; 605 case NSS_NISSERVDNS_TRYAGAIN: 606 (void) fprintf(__nss_debug_file, "NISSERVDNS_TRYAGAIN"); 607 break; 608 default: 609 (void) fprintf(__nss_debug_file, "undefined"); 610 } 611 (void) fprintf(__nss_debug_file, ", action="); 612 switch (lkp->actions[res]) { 613 case __NSW_CONTINUE: 614 (void) fprintf(__nss_debug_file, "CONTINUE"); 615 break; 616 case __NSW_RETURN: 617 (void) fprintf(__nss_debug_file, "RETURN"); 618 break; 619 case __NSW_TRYAGAIN_FOREVER: 620 (void) fprintf(__nss_debug_file, "TRYAGAIN_FOREVER"); 621 break; 622 case __NSW_TRYAGAIN_NTIMES: 623 (void) fprintf(__nss_debug_file, "TRYAGAIN_NTIMES (N=%d)", 624 lkp->max_retries); 625 break; 626 case __NSW_TRYAGAIN_PAUSED: 627 (void) fprintf(__nss_debug_file, "TRYAGAIN_PAUSED"); 628 break; 629 default: 630 (void) fprintf(__nss_debug_file, "undefined"); 631 } 632 (void) fprintf(__nss_debug_file, "\n"); 633 } 634 635 #define NSS_BACKOFF(n, b, t) \ 636 ((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1)))) 637 638 static int 639 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp) 640 { 641 if (res != NSS_TRYAGAIN && res != NSS_NISSERVDNS_TRYAGAIN) { 642 if (res == NSS_SUCCESS) { 643 __NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]); 644 __NSW_UNPAUSE_ACTION( 645 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]); 646 } 647 return (0); 648 } 649 650 if ((res == NSS_TRYAGAIN && 651 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) || 652 (res == NSS_NISSERVDNS_TRYAGAIN && 653 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER)) 654 return (1); 655 656 if (res == NSS_TRYAGAIN && 657 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES) 658 if (n <= lkp->max_retries) 659 return (1); 660 else { 661 lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED; 662 return (0); 663 } 664 665 if (res == NSS_NISSERVDNS_TRYAGAIN && 666 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES) 667 if (n <= lkp->max_retries) 668 return (1); 669 else { 670 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] = 671 __NSW_TRYAGAIN_PAUSED; 672 return (0); 673 } 674 675 return (0); 676 } 677 678 nss_status_t 679 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum, 680 void *search_args) 681 { 682 nss_status_t res = NSS_UNAVAIL; 683 struct nss_db_state *s; 684 int n_src; 685 unsigned int *status_vec_p = _nss_status_vec_p(); 686 687 if (status_vec_p == NULL) { 688 return (NSS_UNAVAIL); 689 } 690 *status_vec_p = 0; 691 692 NSS_LOCK_CHECK(rootp, initf, &s); 693 if (s == 0) { 694 NSS_UNLOCK(rootp); 695 return (res); 696 } 697 NSS_STATE_REF_u(s); 698 699 for (n_src = 0; n_src < s->max_src; n_src++) { 700 nss_backend_t *be; 701 nss_backend_op_t funcp; 702 703 res = NSS_UNAVAIL; 704 if ((be = nss_get_backend_u(&rootp, s, n_src)) != 0) { 705 if ((funcp = NSS_LOOKUP_DBOP(be, search_fnum)) != 0) { 706 int n_loop = 0; 707 int no_backoff = 19; 708 int max_backoff = 5; /* seconds */ 709 710 do { 711 /* 712 * Backend operation may take a while; 713 * drop the lock so we don't serialize 714 * more than necessary. 715 */ 716 NSS_UNLOCK(rootp); 717 718 /* After several tries, backoff... */ 719 if (n_loop > no_backoff) { 720 if (__nss_debug_eng_loop > 1) 721 (void) fprintf(__nss_debug_file, 722 "NSS: loop: sleeping %d ...\n", 723 NSS_BACKOFF(n_loop, 724 no_backoff, max_backoff)); 725 726 (void) sleep(NSS_BACKOFF(n_loop, 727 no_backoff, max_backoff)); 728 } 729 730 if (__nss_debug_eng_loop) 731 output_loop_diag_a(n_loop, 732 s->config->dbase, 733 s->src[n_src].lkp); 734 735 736 res = (*funcp)(be, search_args); 737 NSS_RELOCK(&rootp, s); 738 n_loop++; 739 if (__nss_debug_eng_loop) 740 output_loop_diag_b(res, 741 s->src[n_src].lkp); 742 } while (retry_test(res, n_loop, 743 s->src[n_src].lkp)); 744 } 745 nss_put_backend_u(s, n_src, be); 746 } 747 *status_vec_p |= (1 << res); 748 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) { 749 if (__nss_debug_eng_loop) 750 (void) fprintf(__nss_debug_file, 751 "NSS: '%s': return.\n", 752 s->config->dbase); 753 break; 754 } else 755 if (__nss_debug_eng_loop) 756 (void) fprintf(__nss_debug_file, 757 "NSS: '%s': continue ...\n", 758 s->config->dbase); 759 } 760 NSS_UNREF_UNLOCK(rootp, s); 761 return (res); 762 } 763 764 765 /* 766 * Start of nss_setent()/nss_getent()/nss_endent() 767 */ 768 769 /* 770 * State (here called "context") for one setent/getent.../endent sequence. 771 * In principle there could be multiple contexts active for a single 772 * database; in practice, since Posix and UI have helpfully said that 773 * getent() state is global rather than, say, per-thread or user-supplied, 774 * we have at most one of these per nss_db_state. 775 */ 776 777 struct nss_getent_context { 778 int n_src; /* >= max_src ==> end of sequence */ 779 nss_backend_t *be; 780 struct nss_db_state *s; 781 /* 782 * XXX ?? Should contain enough cross-check info that we can detect an 783 * nss_context that missed an nss_delete() or similar. 784 */ 785 }; 786 787 static void nss_setent_u(nss_db_root_t *, 788 nss_db_initf_t, 789 nss_getent_t *); 790 static nss_status_t nss_getent_u(nss_db_root_t *, 791 nss_db_initf_t, 792 nss_getent_t *, 793 void *); 794 static void nss_endent_u(nss_db_root_t *, 795 nss_db_initf_t, 796 nss_getent_t *); 797 798 void 799 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp) 800 { 801 if (contextpp == 0) { 802 return; 803 } 804 (void) _private_mutex_lock(&contextpp->lock); 805 nss_setent_u(rootp, initf, contextpp); 806 (void) _private_mutex_unlock(&contextpp->lock); 807 } 808 809 nss_status_t 810 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp, 811 void *args) 812 { 813 nss_status_t status; 814 815 if (contextpp == 0) { 816 return (NSS_UNAVAIL); 817 } 818 (void) _private_mutex_lock(&contextpp->lock); 819 status = nss_getent_u(rootp, initf, contextpp, args); 820 (void) _private_mutex_unlock(&contextpp->lock); 821 return (status); 822 } 823 824 void 825 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp) 826 { 827 if (contextpp == 0) { 828 return; 829 } 830 (void) _private_mutex_lock(&contextpp->lock); 831 nss_endent_u(rootp, initf, contextpp); 832 (void) _private_mutex_unlock(&contextpp->lock); 833 } 834 835 /* 836 * Each of the _u versions of the nss interfaces assume that the context 837 * lock is held. 838 */ 839 840 static void 841 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp) 842 { 843 struct nss_db_state *s; 844 nss_backend_t *be; 845 int n_src; 846 847 s = contextp->s; 848 n_src = contextp->n_src; 849 be = contextp->be; 850 851 if (s != 0) { 852 if (n_src < s->max_src && be != 0) { 853 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0); 854 NSS_RELOCK(&rootp, s); 855 nss_put_backend_u(s, n_src, be); 856 contextp->be = 0; /* Should be unnecessary, but hey */ 857 NSS_UNREF_UNLOCK(rootp, s); 858 } 859 contextp->s = 0; 860 } 861 } 862 863 static void 864 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf, 865 nss_getent_t *contextpp) 866 { 867 struct nss_db_state *s; 868 struct nss_getent_context *contextp; 869 nss_backend_t *be; 870 int n_src; 871 872 if ((contextp = contextpp->ctx) == 0) { 873 if ((contextp = libc_malloc(sizeof (*contextp))) == 0) { 874 return; 875 } 876 contextpp->ctx = contextp; 877 s = 0; 878 } else { 879 s = contextp->s; 880 } 881 882 if (s == 0) { 883 NSS_LOCK_CHECK(rootp, initf, &s); 884 if (s == 0) { 885 /* Couldn't set up state, so quit */ 886 NSS_UNLOCK(rootp); 887 /* ==== is there any danger of not having done an */ 888 /* end_iter() here, and hence of losing backends? */ 889 contextpp->ctx = 0; 890 libc_free(contextp); 891 return; 892 } 893 NSS_STATE_REF_u(s); 894 contextp->s = s; 895 } else { 896 s = contextp->s; 897 n_src = contextp->n_src; 898 be = contextp->be; 899 if (n_src == 0 && be != 0) { 900 /* 901 * Optimization: don't do endent, don't change 902 * backends, just do the setent. Look Ma, no locks 903 * (nor any context that needs updating). 904 */ 905 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0); 906 return; 907 } 908 if (n_src < s->max_src && be != 0) { 909 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0); 910 NSS_RELOCK(&rootp, s); 911 nss_put_backend_u(s, n_src, be); 912 contextp->be = 0; /* Play it safe */ 913 } else { 914 NSS_RELOCK(&rootp, s); 915 } 916 } 917 for (n_src = 0, be = 0; n_src < s->max_src && 918 (be = nss_get_backend_u(&rootp, s, n_src)) == 0; n_src++) { 919 ; 920 } 921 NSS_UNLOCK(rootp); 922 923 contextp->n_src = n_src; 924 contextp->be = be; 925 926 if (be == 0) { 927 /* Things are broken enough that we can't do setent/getent */ 928 nss_endent_u(rootp, initf, contextpp); 929 return; 930 } 931 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0); 932 } 933 934 static nss_status_t 935 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf, 936 nss_getent_t *contextpp, void *args) 937 { 938 struct nss_db_state *s; 939 struct nss_getent_context *contextp; 940 int n_src; 941 nss_backend_t *be; 942 943 if ((contextp = contextpp->ctx) == 0) { 944 nss_setent_u(rootp, initf, contextpp); 945 if ((contextp = contextpp->ctx) == 0) { 946 /* Give up */ 947 return (NSS_UNAVAIL); 948 } 949 } 950 s = contextp->s; 951 n_src = contextp->n_src; 952 be = contextp->be; 953 954 if (s == 0) { 955 /* 956 * We've done an end_iter() and haven't done nss_setent() 957 * or nss_endent() since; we should stick in this state 958 * until the caller invokes one of those two routines. 959 */ 960 return (NSS_SUCCESS); 961 } 962 963 while (n_src < s->max_src) { 964 nss_status_t res; 965 966 if (be == 0) { 967 /* If it's null it's a bug, but let's play safe */ 968 res = NSS_UNAVAIL; 969 } else { 970 res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args); 971 } 972 973 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) { 974 if (res != __NSW_SUCCESS) { 975 end_iter_u(rootp, contextp); 976 } 977 return (res); 978 } 979 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0); 980 NSS_RELOCK(&rootp, s); 981 nss_put_backend_u(s, n_src, be); 982 do { 983 n_src++; 984 } while (n_src < s->max_src && 985 (be = nss_get_backend_u(&rootp, s, n_src)) == 0); 986 if (be == 0) { 987 /* 988 * This is the case where we failed to get the backend 989 * for the last source. We exhausted all sources. 990 */ 991 NSS_UNLOCK(rootp); 992 nss_endent_u(rootp, initf, contextpp); 993 nss_delete(rootp); 994 return (NSS_SUCCESS); 995 } 996 NSS_UNLOCK(rootp); 997 contextp->n_src = n_src; 998 contextp->be = be; 999 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0); 1000 } 1001 /* Got to the end of the sources without finding another entry */ 1002 end_iter_u(rootp, contextp); 1003 return (NSS_SUCCESS); 1004 /* success is either a successful entry or end of the sources */ 1005 } 1006 1007 /*ARGSUSED*/ 1008 static void 1009 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf, 1010 nss_getent_t *contextpp) 1011 { 1012 struct nss_getent_context *contextp; 1013 1014 if ((contextp = contextpp->ctx) == 0) { 1015 /* nss_endent() on an unused context is a no-op */ 1016 return; 1017 } 1018 /* 1019 * Existing code (BSD, SunOS) works in such a way that getXXXent() 1020 * following an endXXXent() behaves as though the user had invoked 1021 * setXXXent(), i.e. it iterates properly from the beginning. 1022 * We'd better not break this, so our choices are 1023 * (1) leave the context structure around, and do nss_setent or 1024 * something equivalent, 1025 * or (2) free the context completely, and rely on the code in 1026 * nss_getent() that makes getXXXent() do the right thing 1027 * even without a preceding setXXXent(). 1028 * The code below does (2), which frees up resources nicely but will 1029 * cost more if the user then does more getXXXent() operations. 1030 * Moral: for efficiency, don't call endXXXent() prematurely. 1031 */ 1032 end_iter_u(rootp, contextp); 1033 libc_free(contextp); 1034 contextpp->ctx = 0; 1035 } 1036