1 /* $NetBSD: nsdispatch.c,v 1.37 2012/03/13 21:13:42 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn; and by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /*- 33 * Copyright (c) 2003 Networks Associates Technology, Inc. 34 * All rights reserved. 35 * 36 * Portions of this software were developed for the FreeBSD Project by 37 * Jacques A. Vidrine, Safeport Network Services, and Network 38 * Associates Laboratories, the Security Research Division of Network 39 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 40 * ("CBOSS"), as part of the DARPA CHATS research program. 41 * 42 * Redistribution and use in source and binary forms, with or without 43 * modification, are permitted provided that the following conditions 44 * are met: 45 * 1. Redistributions of source code must retain the above copyright 46 * notice, this list of conditions and the following disclaimer. 47 * 2. Redistributions in binary form must reproduce the above copyright 48 * notice, this list of conditions and the following disclaimer in the 49 * documentation and/or other materials provided with the distribution. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64 #include <sys/cdefs.h> 65 #if defined(LIBC_SCCS) && !defined(lint) 66 __RCSID("$NetBSD: nsdispatch.c,v 1.37 2012/03/13 21:13:42 christos Exp $"); 67 #endif /* LIBC_SCCS and not lint */ 68 69 #include <sys/types.h> 70 #include <sys/param.h> 71 #include <sys/stat.h> 72 #include <sys/queue.h> 73 74 #include <assert.h> 75 #ifdef __ELF__ 76 #include <dlfcn.h> 77 #endif /* __ELF__ */ 78 #include <fcntl.h> 79 #define _NS_PRIVATE 80 #include <nsswitch.h> 81 #include <pthread.h> 82 #include <stdarg.h> 83 #include <stdio.h> 84 #include <stdlib.h> 85 #include <string.h> 86 #include <unistd.h> 87 88 #include <libutil.h> 89 90 #define __isthreaded 1 91 92 extern FILE *_nsyyin; 93 extern int _nsyyparse(void); 94 95 96 #ifdef __weak_alias 97 __weak_alias(nsdispatch,_nsdispatch) 98 #endif 99 100 101 /* 102 * default sourcelist: `files' 103 */ 104 const ns_src __nsdefaultsrc[] = { 105 { NSSRC_FILES, NS_SUCCESS }, 106 { 0, 0 }, 107 }; 108 109 const ns_src __nsdefaultcompat[] = { 110 { NSSRC_COMPAT, NS_SUCCESS }, 111 { 0, 0 } 112 }; 113 114 const ns_src __nsdefaultcompat_forceall[] = { 115 { NSSRC_COMPAT, NS_SUCCESS | NS_FORCEALL }, 116 { 0, 0 } 117 }; 118 119 const ns_src __nsdefaultfiles[] = { 120 { NSSRC_FILES, NS_SUCCESS }, 121 { 0, 0 }, 122 }; 123 124 const ns_src __nsdefaultfiles_forceall[] = { 125 { NSSRC_FILES, NS_SUCCESS | NS_FORCEALL }, 126 { 0, 0 }, 127 }; 128 129 const ns_src __nsdefaultnis[] = { 130 { NSSRC_NIS, NS_SUCCESS }, 131 { 0, 0 } 132 }; 133 134 const ns_src __nsdefaultnis_forceall[] = { 135 { NSSRC_NIS, NS_SUCCESS | NS_FORCEALL }, 136 { 0, 0 } 137 }; 138 139 140 /* Database, source mappings. */ 141 static u_int _nsmapsize; 142 static ns_dbt *_nsmap; 143 144 /* Nsswitch modules. */ 145 static u_int _nsmodsize; 146 static ns_mod *_nsmod; 147 148 /* Placeholder for built-in modules' dlopen() handles. */ 149 static void *_nsbuiltin = &_nsbuiltin; 150 151 #ifdef _REENTRANT 152 /* 153 * Global nsswitch data structures are mostly read-only, but we update them 154 * when we read or re-read nsswitch.conf. 155 */ 156 static pthread_rwlock_t _nslock; 157 static pthread_once_t _nslockinit = PTHREAD_ONCE_INIT; 158 159 static void lockinit() 160 { 161 int result = pthread_rwlock_init(&_nslock, NULL); 162 assert(result == 0); 163 } 164 165 /* 166 * List of threads currently in nsdispatch(). We use this to detect 167 * recursive calls and avoid reloading configuration in such cases, 168 * which could cause deadlock. 169 */ 170 struct _ns_drec { 171 LIST_ENTRY(_ns_drec) list; 172 pthread_t thr; 173 }; 174 static LIST_HEAD(, _ns_drec) _ns_drec = LIST_HEAD_INITIALIZER(&_ns_drec); 175 static pthread_mutex_t _ns_drec_lock = PTHREAD_MUTEX_INITIALIZER; 176 #endif /* _REENTRANT */ 177 178 179 /* 180 * Runtime determination of whether we are dynamically linked or not. 181 */ 182 #define is_dynamic() (0) /* don't bother - switch to ELF! */ 183 184 185 /* 186 * size of dynamic array chunk for _nsmap and _nsmap[x].srclist (and other 187 * growing arrays). 188 */ 189 #define NSELEMSPERCHUNK 8 190 191 /* 192 * Dynamically growable arrays are used for lists of databases, sources, 193 * and modules. The following "vector" API is used to isolate the 194 * common operations. 195 */ 196 typedef void (*_nsvect_free_elem)(void *); 197 198 static void * 199 _nsvect_append(const void *elem, void *vec, u_int *count, size_t esize) 200 { 201 void *p; 202 203 if ((*count % NSELEMSPERCHUNK) == 0) { 204 p = realloc(vec, (*count + NSELEMSPERCHUNK) * esize); 205 if (p == NULL) 206 return (NULL); 207 vec = p; 208 } 209 memmove((void *)(((uintptr_t)vec) + (*count * esize)), elem, esize); 210 (*count)++; 211 return (vec); 212 } 213 214 static void * 215 _nsvect_elem(u_int i, void *vec, u_int count, size_t esize) 216 { 217 218 if (i < count) 219 return ((void *)((uintptr_t)vec + (i * esize))); 220 else 221 return (NULL); 222 } 223 224 static void 225 _nsvect_free(void *vec, u_int *count, size_t esize, _nsvect_free_elem free_elem) 226 { 227 void *elem; 228 u_int i; 229 230 for (i = 0; i < *count; i++) { 231 elem = _nsvect_elem(i, vec, *count, esize); 232 if (elem != NULL) 233 (*free_elem)(elem); 234 } 235 if (vec != NULL) 236 free(vec); 237 *count = 0; 238 } 239 #define _NSVECT_FREE(v, c, s, f) \ 240 do { \ 241 _nsvect_free((v), (c), (s), (f)); \ 242 (v) = NULL; \ 243 } while (/*CONSTCOND*/0) 244 245 static int 246 _nsdbtcmp(const void *a, const void *b) 247 { 248 249 return (strcasecmp(((const ns_dbt *)a)->name, 250 ((const ns_dbt *)b)->name)); 251 } 252 253 static int 254 _nsmodcmp(const void *a, const void *b) 255 { 256 257 return (strcasecmp(((const ns_mod *)a)->name, 258 ((const ns_mod *)b)->name)); 259 } 260 261 static int 262 _nsmtabcmp(const void *a, const void *b) 263 { 264 int cmp; 265 266 cmp = strcmp(((const ns_mtab *)a)->name, 267 ((const ns_mtab *)b)->name); 268 if (cmp) 269 return (cmp); 270 271 return (strcasecmp(((const ns_mtab *)a)->database, 272 ((const ns_mtab *)b)->database)); 273 } 274 275 static void 276 _nsmodfree(ns_mod *mod) 277 { 278 279 free(__UNCONST(mod->name)); 280 if (mod->handle == NULL) 281 return; 282 if (mod->unregister != NULL) 283 (*mod->unregister)(mod->mtab, mod->mtabsize); 284 #ifdef __ELF__ 285 if (mod->handle != _nsbuiltin) 286 (void) dlclose(mod->handle); 287 #endif /* __ELF__ */ 288 } 289 290 /* 291 * Load a built-in or dyanamically linked module. If the `reg_fn' 292 * argument is non-NULL, assume a built-in module and use `reg_fn' 293 * to register it. Otherwise, search for a dynamic nsswitch module. 294 */ 295 static int 296 _nsloadmod(const char *source, nss_module_register_fn reg_fn) 297 { 298 #ifdef __ELF__ 299 char buf[PATH_MAX]; 300 #endif 301 ns_mod mod, *new; 302 303 memset(&mod, 0, sizeof(mod)); 304 mod.name = strdup(source); 305 if (mod.name == NULL) 306 return (-1); 307 308 if (reg_fn != NULL) { 309 /* 310 * The placeholder is required, as a NULL handle 311 * represents an invalid module. 312 */ 313 mod.handle = _nsbuiltin; 314 } else if (!is_dynamic()) { 315 goto out; 316 } else { 317 #ifdef __ELF__ 318 if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name, 319 NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf)) 320 goto out; 321 mod.handle = dlopen(buf, RTLD_LOCAL | RTLD_LAZY); 322 if (mod.handle == NULL) { 323 #ifdef _NSS_DEBUG 324 /* 325 * This gets pretty annoying, since the built-in 326 * sources are not yet modules. 327 */ 328 /* XXX log some error? */ 329 #endif 330 goto out; 331 } 332 reg_fn = (nss_module_register_fn) dlsym(mod.handle, 333 "nss_module_register"); 334 if (reg_fn == NULL) { 335 (void) dlclose(mod.handle); 336 mod.handle = NULL; 337 /* XXX log some error? */ 338 goto out; 339 } 340 #else /* ! __ELF__ */ 341 mod.handle = NULL; 342 #endif /* __ELF__ */ 343 } 344 mod.mtab = (*reg_fn)(mod.name, &mod.mtabsize, &mod.unregister); 345 if (mod.mtab == NULL || mod.mtabsize == 0) { 346 #ifdef __ELF__ 347 if (mod.handle != _nsbuiltin) 348 (void) dlclose(mod.handle); 349 #endif /* __ELF__ */ 350 mod.handle = NULL; 351 /* XXX log some error? */ 352 goto out; 353 } 354 if (mod.mtabsize > 1) 355 qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]), 356 _nsmtabcmp); 357 out: 358 new = _nsvect_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod)); 359 if (new == NULL) { 360 _nsmodfree(&mod); 361 return (-1); 362 } 363 _nsmod = new; 364 /* _nsmodsize already incremented */ 365 366 qsort(_nsmod, _nsmodsize, sizeof(*_nsmod), _nsmodcmp); 367 return (0); 368 } 369 370 static void 371 _nsloadbuiltin(void) 372 { 373 374 /* Do nothing, for now. */ 375 } 376 377 int 378 _nsdbtaddsrc(ns_dbt *dbt, const ns_src *src) 379 { 380 void *new; 381 const ns_mod *mod; 382 ns_mod modkey; 383 384 assert(dbt != NULL); 385 assert(src != NULL); 386 387 new = _nsvect_append(src, dbt->srclist, &dbt->srclistsize, 388 sizeof(*src)); 389 if (new == NULL) 390 return (-1); 391 dbt->srclist = new; 392 /* dbt->srclistsize already incremented */ 393 394 modkey.name = src->name; 395 mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), _nsmodcmp); 396 if (mod == NULL) 397 return (_nsloadmod(src->name, NULL)); 398 399 return (0); 400 } 401 402 void 403 _nsdbtdump(const ns_dbt *dbt) 404 { 405 unsigned int i; 406 407 assert(dbt != NULL); 408 409 printf("%s (%d source%s):", dbt->name, dbt->srclistsize, 410 dbt->srclistsize == 1 ? "" : "s"); 411 for (i = 0; i < dbt->srclistsize; i++) { 412 printf(" %s", dbt->srclist[i].name); 413 if (!(dbt->srclist[i].flags & 414 (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) && 415 (dbt->srclist[i].flags & NS_SUCCESS)) 416 continue; 417 printf(" ["); 418 if (!(dbt->srclist[i].flags & NS_SUCCESS)) 419 printf(" SUCCESS=continue"); 420 if (dbt->srclist[i].flags & NS_UNAVAIL) 421 printf(" UNAVAIL=return"); 422 if (dbt->srclist[i].flags & NS_NOTFOUND) 423 printf(" NOTFOUND=return"); 424 if (dbt->srclist[i].flags & NS_TRYAGAIN) 425 printf(" TRYAGAIN=return"); 426 printf(" ]"); 427 } 428 printf("\n"); 429 } 430 431 static void 432 _nssrclist_free(ns_src **src, u_int srclistsize) 433 { 434 u_int i; 435 436 for (i = 0; i < srclistsize; i++) { 437 if ((*src)[i].name != NULL) 438 free(__UNCONST((*src)[i].name)); 439 } 440 free(*src); 441 *src = NULL; 442 } 443 444 static void 445 _nsdbtfree(ns_dbt *dbt) 446 { 447 448 _nssrclist_free(&dbt->srclist, dbt->srclistsize); 449 if (dbt->name != NULL) 450 free(__UNCONST(dbt->name)); 451 } 452 453 int 454 _nsdbtput(const ns_dbt *dbt) 455 { 456 ns_dbt *p; 457 void *new; 458 u_int i; 459 460 assert(dbt != NULL); 461 462 for (i = 0; i < _nsmapsize; i++) { 463 p = _nsvect_elem(i, _nsmap, _nsmapsize, sizeof(*_nsmap)); 464 if (strcasecmp(dbt->name, p->name) == 0) { 465 /* overwrite existing entry */ 466 if (p->srclist != NULL) 467 _nssrclist_free(&p->srclist, p->srclistsize); 468 memmove(p, dbt, sizeof(*dbt)); 469 return (0); 470 } 471 } 472 new = _nsvect_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap)); 473 if (new == NULL) 474 return (-1); 475 _nsmap = new; 476 /* _nsmapsize already incremented */ 477 478 return (0); 479 } 480 481 /* 482 * This function is called each time nsdispatch() is called. If this 483 * is the first call, or if the configuration has changed, (re-)prepare 484 * the global data used by NSS. 485 */ 486 static int 487 _nsconfigure(void) 488 { 489 #ifdef _REENTRANT 490 static pthread_mutex_t _nsconflock = PTHREAD_MUTEX_INITIALIZER; 491 #endif 492 static time_t _nsconfmod; 493 struct stat statbuf; 494 495 pthread_mutex_lock(&_nsconflock); 496 497 if (stat(_PATH_NS_CONF, &statbuf) == -1) { 498 /* 499 * No nsswitch.conf; just use whatever configuration we 500 * currently have, or fall back on the defaults specified 501 * by the caller. 502 */ 503 pthread_mutex_unlock(&_nsconflock); 504 return (0); 505 } 506 507 if (statbuf.st_mtime <= _nsconfmod) { 508 /* Internal state is up-to-date with nsswitch.conf. */ 509 pthread_mutex_unlock(&_nsconflock); 510 return (0); 511 } 512 513 /* 514 * Ok, we've decided we need to update the nsswitch configuration 515 * structures. Acquire a write-lock on _nslock while continuing 516 * to hold _nsconflock. Acquiring a write-lock blocks while 517 * waiting for other threads already holding a read-lock to clear. 518 * We hold _nsconflock for the duration, and update the time stamp 519 * at the end of the update operation, at which time we release 520 * both locks. 521 */ 522 pthread_rwlock_wrlock(&_nslock); 523 524 _nsyyin = fopen(_PATH_NS_CONF, "r"); 525 if (_nsyyin == NULL) { 526 /* 527 * Unable to open nsswitch.conf; behave as though the 528 * stat() above failed. Even though we have already 529 * updated _nsconfmod, if the file reappears, the 530 * mtime will change. 531 */ 532 goto out; 533 } 534 535 _NSVECT_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), 536 (_nsvect_free_elem) _nsdbtfree); 537 _NSVECT_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), 538 (_nsvect_free_elem) _nsmodfree); 539 540 _nsloadbuiltin(); 541 542 _nsyyparse(); 543 (void) fclose(_nsyyin); 544 if (_nsmapsize != 0) 545 qsort(_nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 546 547 _nsconfmod = statbuf.st_mtime; 548 549 out: 550 pthread_rwlock_unlock(&_nslock); 551 pthread_mutex_unlock(&_nsconflock); 552 return (0); 553 } 554 555 static nss_method 556 _nsmethod(const char *source, const char *database, const char *method, 557 const ns_dtab disp_tab[], void **cb_data) 558 { 559 int curdisp; 560 ns_mod *mod, modkey; 561 ns_mtab *mtab, mtabkey; 562 563 if (disp_tab != NULL) { 564 for (curdisp = 0; disp_tab[curdisp].src != NULL; curdisp++) { 565 if (strcasecmp(source, disp_tab[curdisp].src) == 0) { 566 *cb_data = disp_tab[curdisp].cb_data; 567 return (disp_tab[curdisp].callback); 568 } 569 } 570 } 571 572 modkey.name = source; 573 mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), 574 _nsmodcmp); 575 if (mod != NULL && mod->handle != NULL) { 576 mtabkey.database = database; 577 mtabkey.name = method; 578 mtab = bsearch(&mtabkey, mod->mtab, mod->mtabsize, 579 sizeof(mod->mtab[0]), _nsmtabcmp); 580 if (mtab != NULL) { 581 *cb_data = mtab->mdata; 582 return (mtab->method); 583 } 584 } 585 586 *cb_data = NULL; 587 return (NULL); 588 } 589 590 int 591 /*ARGSUSED*/ 592 nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, 593 const char *method, const ns_src defaults[], ...) 594 { 595 static int _nsdispatching; 596 #ifdef _REENTRANT 597 struct _ns_drec drec, *ldrec; 598 #endif 599 va_list ap; 600 int i, result; 601 ns_dbt key; 602 const ns_dbt *dbt; 603 const ns_src *srclist; 604 int srclistsize; 605 nss_method cb; 606 void *cb_data; 607 608 /* retval may be NULL */ 609 /* disp_tab may be NULL */ 610 assert(database != NULL); 611 assert(method != NULL); 612 assert(defaults != NULL); 613 if (database == NULL || method == NULL || defaults == NULL) 614 return (NS_UNAVAIL); 615 616 pthread_once(&_nslockinit, lockinit); 617 618 /* 619 * In both the threaded and non-threaded cases, avoid reloading 620 * the configuration if the current thread is already running 621 * nsdispatch() (i.e. recursive call). 622 * 623 * In the non-threaded case, this avoids changing the data structures 624 * while we're using them. 625 * 626 * In the threaded case, this avoids trying to take a write lock 627 * while the current thread holds a read lock (which would result 628 * in deadlock). 629 */ 630 #ifdef _REENTRANT 631 if (__isthreaded) { 632 drec.thr = pthread_self(); 633 pthread_mutex_lock(&_ns_drec_lock); 634 LIST_FOREACH(ldrec, &_ns_drec, list) { 635 if (ldrec->thr == drec.thr) 636 break; 637 } 638 LIST_INSERT_HEAD(&_ns_drec, &drec, list); 639 pthread_mutex_unlock(&_ns_drec_lock); 640 if (ldrec == NULL && _nsconfigure()) { 641 pthread_mutex_lock(&_ns_drec_lock); 642 LIST_REMOVE(&drec, list); 643 pthread_mutex_unlock(&_ns_drec_lock); 644 return (NS_UNAVAIL); 645 } 646 } else 647 #endif /* _REENTRANT */ 648 649 if (_nsdispatching++ == 0 && _nsconfigure()) { 650 _nsdispatching--; 651 return (NS_UNAVAIL); 652 } 653 654 pthread_rwlock_rdlock(&_nslock); 655 656 key.name = database; 657 dbt = bsearch(&key, _nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 658 if (dbt != NULL) { 659 srclist = dbt->srclist; 660 srclistsize = dbt->srclistsize; 661 } else { 662 srclist = defaults; 663 srclistsize = 0; 664 while (srclist[srclistsize].name != NULL) 665 srclistsize++; 666 } 667 result = 0; 668 669 for (i = 0; i < srclistsize; i++) { 670 cb = _nsmethod(srclist[i].name, database, method, 671 disp_tab, &cb_data); 672 result = 0; 673 if (cb != NULL) { 674 va_start(ap, defaults); 675 result = (*cb)(retval, cb_data, ap); 676 va_end(ap); 677 if (defaults[0].flags & NS_FORCEALL) 678 continue; 679 if (result & srclist[i].flags) 680 break; 681 } 682 } 683 result &= NS_STATUSMASK; /* clear private flags in result */ 684 685 pthread_rwlock_unlock(&_nslock); 686 687 #ifdef _REENTRANT 688 if (__isthreaded) { 689 pthread_mutex_lock(&_ns_drec_lock); 690 LIST_REMOVE(&drec, list); 691 pthread_mutex_unlock(&_ns_drec_lock); 692 } else 693 #endif /* _REENTRANT */ 694 _nsdispatching--; 695 696 return (result ? result : NS_NOTFOUND); 697 } 698