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