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 2005 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 #include "synonyms.h" 30 #include "mtlib.h" 31 #include "libc.h" 32 #include <synch.h> 33 #include <sys/types.h> 34 #include <stdlib.h> 35 #include <stdio.h> 36 #include <string.h> 37 #include <ctype.h> 38 #include <limits.h> 39 #include <dlfcn.h> 40 #include <fcntl.h> 41 #include <unistd.h> 42 #include <errno.h> 43 44 #define __NSS_PRIVATE_INTERFACE 45 #include "nsswitch_priv.h" 46 #undef __NSS_PRIVATE_INTERFACE 47 48 #include <syslog.h> 49 50 #define islabel(c) (isalnum(c) || (c) == '_') 51 52 #define LIBC_STRDUP(new, existing) \ 53 if ((new = libc_strdup(existing)) == NULL) { \ 54 dup_fail = 1; \ 55 goto barf_line; \ 56 } 57 58 /* 59 * This file has all the routines that access the configuration 60 * information. 61 */ 62 63 struct cons_cell_v1 { /* private to the parser */ 64 struct __nsw_switchconfig_v1 *sw; 65 struct cons_cell_v1 *next; 66 }; 67 68 struct cons_cell { /* private to the parser */ 69 struct __nsw_switchconfig *sw; 70 struct cons_cell *next; 71 }; 72 73 /* 74 * Local routines 75 */ 76 77 static char *skip(char **, char); 78 static char *labelskip(char *); 79 static char *spaceskip(char *); 80 static struct __nsw_switchconfig_v1 *scrounge_cache_v1(const char *); 81 static struct __nsw_switchconfig *scrounge_cache(const char *); 82 static int add_concell_v1(struct __nsw_switchconfig_v1 *); 83 static int add_concell(struct __nsw_switchconfig *); 84 static void freeconf_v1(struct __nsw_switchconfig_v1 *); 85 static void freeconf(struct __nsw_switchconfig *); 86 static int alldigits(char *); 87 88 static struct cons_cell_v1 *concell_list_v1; /* stays with add_concell() */ 89 static struct cons_cell *concell_list; /* stays with add_concell() */ 90 91 /* 92 * 93 * With the "lookup control" feature, the default criteria for NIS, NIS+, 94 * and any new services (e.g. ldap) will be: 95 * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=forever] 96 * 97 * For backward compat, NIS via NIS server in DNS forwarding mode will be: 98 * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue] 99 * 100 * And also for backward compat, the default criteria for DNS will be: 101 * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue] 102 */ 103 104 105 106 /* 107 * The BIND resolver normally will retry several times on server non-response. 108 * But now with the "lookup control" feature, we don't want the resolver doing 109 * many retries, rather we want it to return control (reasonably) quickly back 110 * to the switch engine. However, when TRYAGAIN=N or TRYAGAIN=forever is 111 * not explicitly set by the admin in the conf file, we want the old "resolver 112 * retry a few times" rather than no retries at all. 113 */ 114 static int dns_tryagain_retry = 3; 115 116 /* 117 * For backward compat (pre "lookup control"), the dns default behavior is 118 * soft lookup. 119 */ 120 static void 121 set_dns_default_lkp(struct __nsw_lookup_v1 *lkp) 122 { 123 if (strcasecmp(lkp->service_name, "dns") == 0) { 124 lkp->actions[__NSW_TRYAGAIN] = 125 __NSW_TRYAGAIN_NTIMES; 126 lkp->max_retries = dns_tryagain_retry; 127 } 128 } 129 130 /* 131 * Private interface used by nss_common.c, hence this function is not static 132 */ 133 struct __nsw_switchconfig_v1 * 134 _nsw_getoneconfig_v1(const char *name, char *linep, enum __nsw_parse_err *errp) 135 /* linep Nota Bene: not const char * */ 136 /* errp Meanings are abused a bit */ 137 { 138 struct __nsw_switchconfig_v1 *cfp; 139 struct __nsw_lookup_v1 *lkp, **lkq; 140 int end_crit, dup_fail = 0; 141 action_t act; 142 char *p, *tokenp; 143 144 *errp = __NSW_CONF_PARSE_SUCCESS; 145 146 if ((cfp = libc_malloc(sizeof (struct __nsw_switchconfig_v1))) 147 == NULL) { 148 *errp = __NSW_CONF_PARSE_SYSERR; 149 return (NULL); 150 } 151 LIBC_STRDUP(cfp->dbase, name); 152 lkq = &cfp->lookups; 153 154 /* linep points to a naming service name */ 155 for (;;) { 156 int i; 157 158 /* white space following the last service */ 159 if (*linep == '\0' || *linep == '\n') { 160 return (cfp); 161 } 162 if ((lkp = libc_malloc(sizeof (struct __nsw_lookup_v1))) 163 == NULL) { 164 *errp = __NSW_CONF_PARSE_SYSERR; 165 freeconf_v1(cfp); 166 return (NULL); 167 } 168 169 *lkq = lkp; 170 lkq = &lkp->next; 171 172 for (i = 0; i < __NSW_STD_ERRS_V1; i++) 173 if (i == __NSW_SUCCESS) 174 lkp->actions[i] = __NSW_RETURN; 175 else if (i == __NSW_TRYAGAIN) 176 lkp->actions[i] = __NSW_TRYAGAIN_FOREVER; 177 else 178 lkp->actions[i] = __NSW_CONTINUE; 179 180 /* get criteria for the naming service */ 181 if (tokenp = skip(&linep, '[')) { /* got criteria */ 182 183 /* premature end, illegal char following [ */ 184 if (!islabel(*linep)) 185 goto barf_line; 186 LIBC_STRDUP(lkp->service_name, tokenp); 187 cfp->num_lookups++; 188 189 set_dns_default_lkp(lkp); 190 191 end_crit = 0; 192 193 /* linep points to a switch_err */ 194 for (;;) { 195 int ntimes = 0; /* try again max N times */ 196 int dns_continue = 0; 197 198 if ((tokenp = skip(&linep, '=')) == NULL) { 199 goto barf_line; 200 } 201 202 /* premature end, ill char following = */ 203 if (!islabel(*linep)) 204 goto barf_line; 205 206 /* linep points to the string following '=' */ 207 p = labelskip(linep); 208 if (*p == ']') 209 end_crit = 1; 210 else if (*p != ' ' && *p != '\t') 211 goto barf_line; 212 *p++ = '\0'; /* null terminate linep */ 213 p = spaceskip(p); 214 if (!end_crit) { 215 if (*p == ']') { 216 end_crit = 1; 217 *p++ = '\0'; 218 } else if (*p == '\0' || *p == '\n') { 219 return (cfp); 220 } else if (!islabel(*p)) 221 /* p better be the next switch_err */ 222 goto barf_line; 223 } 224 if (strcasecmp(linep, __NSW_STR_RETURN) == 0) 225 act = __NSW_RETURN; 226 else if (strcasecmp(linep, 227 __NSW_STR_CONTINUE) == 0) { 228 if (strcasecmp(lkp->service_name, 229 "dns") == 0 && 230 strcasecmp(tokenp, 231 __NSW_STR_TRYAGAIN) 232 == 0) { 233 /* 234 * Add one more condition 235 * so it retries only if it's 236 * "dns [TRYAGAIN=continue]" 237 */ 238 dns_continue = 1; 239 act = __NSW_TRYAGAIN_NTIMES; 240 } else 241 act = __NSW_CONTINUE; 242 } else if (strcasecmp(linep, 243 __NSW_STR_FOREVER) == 0) 244 act = __NSW_TRYAGAIN_FOREVER; 245 else if (alldigits(linep)) { 246 act = __NSW_TRYAGAIN_NTIMES; 247 ntimes = atoi(linep); 248 if (ntimes < 0 || ntimes > INT_MAX) 249 ntimes = 0; 250 } 251 else 252 goto barf_line; 253 254 if (__NSW_SUCCESS_ACTION(act) && 255 strcasecmp(tokenp, 256 __NSW_STR_SUCCESS) == 0) { 257 lkp->actions[__NSW_SUCCESS] = act; 258 } else if (__NSW_NOTFOUND_ACTION(act) && 259 strcasecmp(tokenp, 260 __NSW_STR_NOTFOUND) == 0) { 261 lkp->actions[__NSW_NOTFOUND] = act; 262 } else if (__NSW_UNAVAIL_ACTION(act) && 263 strcasecmp(tokenp, 264 __NSW_STR_UNAVAIL) == 0) { 265 lkp->actions[__NSW_UNAVAIL] = act; 266 } else if (__NSW_TRYAGAIN_ACTION(act) && 267 strcasecmp(tokenp, 268 __NSW_STR_TRYAGAIN) == 0) { 269 lkp->actions[__NSW_TRYAGAIN] = act; 270 if (strcasecmp(lkp->service_name, 271 "nis") == 0) 272 lkp->actions[ 273 __NSW_NISSERVDNS_TRYAGAIN] 274 = act; 275 if (act == __NSW_TRYAGAIN_NTIMES) 276 lkp->max_retries = 277 dns_continue ? 278 dns_tryagain_retry : ntimes; 279 } else { 280 /*EMPTY*/ 281 /* 282 * convert string tokenp to integer 283 * and put in long_errs 284 */ 285 } 286 if (end_crit) { 287 linep = spaceskip(p); 288 if (*linep == '\0' || *linep == '\n') 289 return (cfp); 290 break; /* process next naming service */ 291 } 292 linep = p; 293 } /* end of while loop for a name service's criteria */ 294 } else { 295 /* 296 * no criteria for this naming service. 297 * linep points to name service, but not null 298 * terminated. 299 */ 300 p = labelskip(linep); 301 if (*p == '\0' || *p == '\n') { 302 *p = '\0'; 303 LIBC_STRDUP(lkp->service_name, linep); 304 set_dns_default_lkp(lkp); 305 cfp->num_lookups++; 306 return (cfp); 307 } 308 if (*p != ' ' && *p != '\t') 309 goto barf_line; 310 *p++ = '\0'; 311 LIBC_STRDUP(lkp->service_name, linep); 312 set_dns_default_lkp(lkp); 313 cfp->num_lookups++; 314 linep = spaceskip(p); 315 } 316 } /* end of while(1) loop for a name service */ 317 318 barf_line: 319 freeconf_v1(cfp); 320 *errp = dup_fail ? __NSW_CONF_PARSE_SYSERR : __NSW_CONF_PARSE_NOPOLICY; 321 return (NULL); 322 } 323 324 /* 325 * Private interface used by nss_common.c, hence this function is not static 326 */ 327 struct __nsw_switchconfig * 328 _nsw_getoneconfig(const char *name, char *linep, enum __nsw_parse_err *errp) 329 /* linep Nota Bene: not const char * */ 330 /* errp Meanings are abused a bit */ 331 { 332 struct __nsw_switchconfig *cfp; 333 struct __nsw_lookup *lkp, **lkq; 334 int end_crit, dup_fail = 0; 335 action_t act; 336 char *p, *tokenp; 337 338 *errp = __NSW_CONF_PARSE_SUCCESS; 339 340 if ((cfp = libc_malloc(sizeof (struct __nsw_switchconfig))) 341 == NULL) { 342 *errp = __NSW_CONF_PARSE_SYSERR; 343 return (NULL); 344 } 345 LIBC_STRDUP(cfp->dbase, name); 346 lkq = &cfp->lookups; 347 348 /* linep points to a naming service name */ 349 for (;;) { 350 int i; 351 352 /* white space following the last service */ 353 if (*linep == '\0' || *linep == '\n') { 354 return (cfp); 355 } 356 if ((lkp = libc_malloc(sizeof (struct __nsw_lookup))) 357 == NULL) { 358 *errp = __NSW_CONF_PARSE_SYSERR; 359 freeconf(cfp); 360 return (NULL); 361 } 362 363 *lkq = lkp; 364 lkq = &lkp->next; 365 366 for (i = 0; i < __NSW_STD_ERRS; i++) 367 if (i == __NSW_SUCCESS) 368 lkp->actions[i] = 1; 369 else 370 lkp->actions[i] = 0; 371 372 /* get criteria for the naming service */ 373 if (tokenp = skip(&linep, '[')) { /* got criteria */ 374 375 /* premature end, illegal char following [ */ 376 if (!islabel(*linep)) 377 goto barf_line; 378 LIBC_STRDUP(lkp->service_name, tokenp); 379 cfp->num_lookups++; 380 end_crit = 0; 381 382 /* linep points to a switch_err */ 383 for (;;) { 384 if ((tokenp = skip(&linep, '=')) == NULL) { 385 goto barf_line; 386 } 387 388 /* premature end, ill char following = */ 389 if (!islabel(*linep)) 390 goto barf_line; 391 392 /* linep points to the string following '=' */ 393 p = labelskip(linep); 394 if (*p == ']') 395 end_crit = 1; 396 else if (*p != ' ' && *p != '\t') 397 goto barf_line; 398 *p++ = '\0'; /* null terminate linep */ 399 p = spaceskip(p); 400 if (!end_crit) { 401 if (*p == ']') { 402 end_crit = 1; 403 *p++ = '\0'; 404 } else if (*p == '\0' || *p == '\n') 405 return (cfp); 406 else if (!islabel(*p)) 407 /* p better be the next switch_err */ 408 goto barf_line; 409 } 410 if (strcasecmp(linep, __NSW_STR_RETURN) == 0) 411 act = __NSW_RETURN; 412 else if (strcasecmp(linep, 413 __NSW_STR_CONTINUE) == 0) 414 act = __NSW_CONTINUE; 415 else if (strcasecmp(linep, 416 __NSW_STR_FOREVER) == 0) 417 /* 418 * =forever or =N might be in conf file 419 * but old progs won't expect it. 420 */ 421 act = __NSW_RETURN; 422 else if (alldigits(linep)) 423 act = __NSW_CONTINUE; 424 else 425 goto barf_line; 426 if (strcasecmp(tokenp, 427 __NSW_STR_SUCCESS) == 0) { 428 lkp->actions[__NSW_SUCCESS] = act; 429 } else if (strcasecmp(tokenp, 430 __NSW_STR_NOTFOUND) == 0) { 431 lkp->actions[__NSW_NOTFOUND] = act; 432 } else if (strcasecmp(tokenp, 433 __NSW_STR_UNAVAIL) == 0) { 434 lkp->actions[__NSW_UNAVAIL] = act; 435 } else if (strcasecmp(tokenp, 436 __NSW_STR_TRYAGAIN) == 0) { 437 lkp->actions[__NSW_TRYAGAIN] = act; 438 } else { 439 /*EMPTY*/ 440 /* 441 * convert string tokenp to integer 442 * and put in long_errs 443 */ 444 } 445 if (end_crit) { 446 linep = spaceskip(p); 447 if (*linep == '\0' || *linep == '\n') 448 return (cfp); 449 break; /* process next naming service */ 450 } 451 linep = p; 452 } /* end of while loop for a name service's criteria */ 453 } else { 454 /* 455 * no criteria for this naming service. 456 * linep points to name service, but not null 457 * terminated. 458 */ 459 p = labelskip(linep); 460 if (*p == '\0' || *p == '\n') { 461 *p = '\0'; 462 LIBC_STRDUP(lkp->service_name, linep); 463 cfp->num_lookups++; 464 return (cfp); 465 } 466 if (*p != ' ' && *p != '\t') 467 goto barf_line; 468 *p++ = '\0'; 469 LIBC_STRDUP(lkp->service_name, linep); 470 cfp->num_lookups++; 471 linep = spaceskip(p); 472 } 473 } /* end of while(1) loop for a name service */ 474 475 barf_line: 476 freeconf(cfp); 477 *errp = dup_fail ? __NSW_CONF_PARSE_SYSERR : __NSW_CONF_PARSE_NOPOLICY; 478 return (NULL); 479 } 480 481 static mutex_t serialize_config_v1 = DEFAULTMUTEX; 482 static mutex_t serialize_config = DEFAULTMUTEX; 483 484 static void 485 syslog_warning(const char *dbase) 486 { 487 syslog(LOG_WARNING, 488 "libc: bad lookup policy for %s in %s, using defaults..\n", 489 dbase, __NSW_CONFIG_FILE); 490 } 491 492 struct __nsw_switchconfig_v1 * 493 __nsw_getconfig_v1(const char *dbase, enum __nsw_parse_err *errp) 494 { 495 struct __nsw_switchconfig_v1 *cfp, *retp = NULL; 496 int syslog_error = 0; 497 __NSL_FILE *fp; 498 char *linep; 499 char lineq[BUFSIZ]; 500 501 /* 502 * ==== I don't feel entirely comfortable disabling signals for the 503 * duration of this, but maybe we have to. Or maybe we should 504 * use mutex_trylock to detect recursion? (Not clear what's 505 * the right thing to do when it happens, though). 506 */ 507 lmutex_lock(&serialize_config_v1); 508 509 if (cfp = scrounge_cache_v1(dbase)) { 510 *errp = __NSW_CONF_PARSE_SUCCESS; 511 lmutex_unlock(&serialize_config_v1); 512 return (cfp); 513 } 514 515 if ((fp = __nsl_c_fopen(__NSW_CONFIG_FILE, "r")) == NULL) { 516 *errp = __NSW_CONF_PARSE_NOFILE; 517 lmutex_unlock(&serialize_config_v1); 518 return (NULL); 519 } 520 521 *errp = __NSW_CONF_PARSE_NOPOLICY; 522 while (linep = __nsl_c_fgets(lineq, BUFSIZ, fp)) { 523 enum __nsw_parse_err line_err; 524 char *tokenp, *comment; 525 526 /* 527 * Ignore portion of line following the comment character '#'. 528 */ 529 if ((comment = strchr(linep, '#')) != NULL) { 530 *comment = '\0'; 531 } 532 /* 533 * skip past blank lines. 534 * otherwise, cache as a struct switchconfig. 535 */ 536 if ((*linep == '\0') || isspace(*linep)) { 537 continue; 538 } 539 if ((tokenp = skip(&linep, ':')) == NULL) { 540 continue; /* ignore this line */ 541 } 542 if (cfp = scrounge_cache_v1(tokenp)) { 543 continue; /* ? somehow this database is in the cache */ 544 } 545 if (cfp = _nsw_getoneconfig_v1(tokenp, linep, &line_err)) { 546 (void) add_concell_v1(cfp); 547 if (strcmp(cfp->dbase, dbase) == 0) { 548 *errp = __NSW_CONF_PARSE_SUCCESS; 549 retp = cfp; 550 } 551 } else { 552 /* 553 * Got an error on this line, if it is a system 554 * error we might as well give right now. If it 555 * is a parse error on the second entry of the 556 * database we are looking for and the first one 557 * was a good entry we end up logging the following 558 * syslog message and using a default policy instead. 559 */ 560 if (line_err == __NSW_CONF_PARSE_SYSERR) { 561 *errp = __NSW_CONF_PARSE_SYSERR; 562 break; 563 } else if (line_err == __NSW_CONF_PARSE_NOPOLICY && 564 strcmp(tokenp, dbase) == 0) { 565 syslog_error = 1; 566 *errp = __NSW_CONF_PARSE_NOPOLICY; 567 break; 568 } 569 /* 570 * Else blithely ignore problems on this line and 571 * go ahead with the next line. 572 */ 573 } 574 } 575 (void) __nsl_c_fclose(fp); 576 lmutex_unlock(&serialize_config_v1); 577 /* 578 * We have to drop the lock before calling syslog(). 579 */ 580 if (syslog_error) 581 syslog_warning(dbase); 582 return (retp); 583 } 584 585 struct __nsw_switchconfig * 586 __nsw_getconfig(const char *dbase, enum __nsw_parse_err *errp) 587 { 588 struct __nsw_switchconfig *cfp, *retp = NULL; 589 int syslog_error = 0; 590 __NSL_FILE *fp; 591 char *linep; 592 char lineq[BUFSIZ]; 593 594 /* 595 * ==== I don't feel entirely comfortable disabling signals for the 596 * duration of this, but maybe we have to. Or maybe we should 597 * use mutex_trylock to detect recursion? (Not clear what's 598 * the right thing to do when it happens, though). 599 */ 600 lmutex_lock(&serialize_config); 601 602 if (cfp = scrounge_cache(dbase)) { 603 *errp = __NSW_CONF_PARSE_SUCCESS; 604 lmutex_unlock(&serialize_config); 605 return (cfp); 606 } 607 608 if ((fp = __nsl_c_fopen(__NSW_CONFIG_FILE, "r")) == NULL) { 609 *errp = __NSW_CONF_PARSE_NOFILE; 610 lmutex_unlock(&serialize_config); 611 return (NULL); 612 } 613 614 *errp = __NSW_CONF_PARSE_NOPOLICY; 615 while (linep = __nsl_c_fgets(lineq, BUFSIZ, fp)) { 616 enum __nsw_parse_err line_err; 617 char *tokenp, *comment; 618 619 /* 620 * Ignore portion of line following the comment character '#'. 621 */ 622 if ((comment = strchr(linep, '#')) != NULL) { 623 *comment = '\0'; 624 } 625 /* 626 * skip past blank lines. 627 * otherwise, cache as a struct switchconfig. 628 */ 629 if ((*linep == '\0') || isspace(*linep)) { 630 continue; 631 } 632 if ((tokenp = skip(&linep, ':')) == NULL) { 633 continue; /* ignore this line */ 634 } 635 if (cfp = scrounge_cache(tokenp)) { 636 continue; /* ? somehow this database is in the cache */ 637 } 638 if (cfp = _nsw_getoneconfig(tokenp, linep, &line_err)) { 639 (void) add_concell(cfp); 640 if (strcmp(cfp->dbase, dbase) == 0) { 641 *errp = __NSW_CONF_PARSE_SUCCESS; 642 retp = cfp; 643 } 644 } else { 645 /* 646 * Got an error on this line, if it is a system 647 * error we might as well give right now. If it 648 * is a parse error on the second entry of the 649 * database we are looking for and the first one 650 * was a good entry we end up logging the following 651 * syslog message and using a default policy instead. 652 */ 653 if (line_err == __NSW_CONF_PARSE_SYSERR) { 654 *errp = __NSW_CONF_PARSE_SYSERR; 655 break; 656 } else if (line_err == __NSW_CONF_PARSE_NOPOLICY && 657 strcmp(tokenp, dbase) == 0) { 658 syslog_error = 1; 659 *errp = __NSW_CONF_PARSE_NOPOLICY; 660 break; 661 } 662 /* 663 * Else blithely ignore problems on this line and 664 * go ahead with the next line. 665 */ 666 } 667 } 668 (void) __nsl_c_fclose(fp); 669 lmutex_unlock(&serialize_config); 670 /* 671 * We have to drop the lock before calling syslog(). 672 */ 673 if (syslog_error) 674 syslog_warning(dbase); 675 return (retp); 676 } 677 678 679 static struct __nsw_switchconfig_v1 * 680 scrounge_cache_v1(const char *dbase) 681 { 682 struct cons_cell_v1 *cellp = concell_list_v1; 683 684 for (; cellp; cellp = cellp->next) 685 if (strcmp(dbase, cellp->sw->dbase) == 0) 686 return (cellp->sw); 687 return (NULL); 688 } 689 690 static struct __nsw_switchconfig * 691 scrounge_cache(const char *dbase) 692 { 693 struct cons_cell *cellp = concell_list; 694 695 for (; cellp; cellp = cellp->next) 696 if (strcmp(dbase, cellp->sw->dbase) == 0) 697 return (cellp->sw); 698 return (NULL); 699 } 700 701 static void 702 freeconf_v1(struct __nsw_switchconfig_v1 *cfp) 703 { 704 if (cfp) { 705 if (cfp->dbase) 706 libc_free(cfp->dbase); 707 if (cfp->lookups) { 708 struct __nsw_lookup_v1 *nex, *cur; 709 for (cur = cfp->lookups; cur; cur = nex) { 710 libc_free(cur->service_name); 711 nex = cur->next; 712 libc_free(cur); 713 } 714 } 715 libc_free(cfp); 716 } 717 } 718 719 static void 720 freeconf(struct __nsw_switchconfig *cfp) 721 { 722 if (cfp) { 723 if (cfp->dbase) 724 libc_free(cfp->dbase); 725 if (cfp->lookups) { 726 struct __nsw_lookup *nex, *cur; 727 for (cur = cfp->lookups; cur; cur = nex) { 728 libc_free(cur->service_name); 729 nex = cur->next; 730 libc_free(cur); 731 } 732 } 733 libc_free(cfp); 734 } 735 } 736 737 action_t 738 __nsw_extended_action_v1(struct __nsw_lookup_v1 *lkp, int err) 739 { 740 struct __nsw_long_err *lerrp; 741 742 for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) { 743 if (lerrp->nsw_errno == err) 744 return (lerrp->action); 745 } 746 return (__NSW_CONTINUE); 747 } 748 749 action_t 750 __nsw_extended_action(struct __nsw_lookup *lkp, int err) 751 { 752 struct __nsw_long_err *lerrp; 753 754 for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) { 755 if (lerrp->nsw_errno == err) 756 return (lerrp->action); 757 } 758 return (__NSW_CONTINUE); 759 } 760 761 762 /* give the next non-alpha character */ 763 static char * 764 labelskip(char *cur) 765 { 766 char *p = cur; 767 while (islabel(*p)) 768 ++p; 769 return (p); 770 } 771 772 /* give the next non-space character */ 773 static char * 774 spaceskip(char *cur) 775 { 776 char *p = cur; 777 while (*p == ' ' || *p == '\t') 778 ++p; 779 return (p); 780 } 781 782 /* 783 * terminate the *cur pointed string by null only if it is 784 * followed by "key" surrounded by zero or more spaces and 785 * return value is the same as the original *cur pointer and 786 * *cur pointer is advanced to the first non {space, key} char 787 * followed by the key. Otherwise, return NULL and keep 788 * *cur unchanged. 789 */ 790 static char * 791 skip(char **cur, char key) 792 { 793 char *p, *tmp; 794 char *q = *cur; 795 int found, tmpfound; 796 797 tmp = labelskip(*cur); 798 p = tmp; 799 found = (*p == key); 800 if (found) { 801 *p++ = '\0'; /* overwrite the key */ 802 p = spaceskip(p); 803 } else { 804 while (*p == ' ' || *p == '\t') { 805 tmpfound = (*++p == key); 806 if (tmpfound) { 807 found = tmpfound; 808 /* null terminate the return token */ 809 *tmp = '\0'; 810 p++; /* skip the key */ 811 } 812 } 813 } 814 if (!found) 815 return (NULL); /* *cur unchanged */ 816 *cur = p; 817 return (q); 818 } 819 820 /* add to the front: LRU */ 821 static int 822 add_concell_v1(struct __nsw_switchconfig_v1 *cfp) 823 { 824 struct cons_cell_v1 *cp; 825 826 if (cfp == NULL) 827 return (1); 828 if ((cp = libc_malloc(sizeof (struct cons_cell_v1))) == NULL) 829 return (1); 830 cp->sw = cfp; 831 cp->next = concell_list_v1; 832 concell_list_v1 = cp; 833 return (0); 834 } 835 836 /* add to the front: LRU */ 837 static int 838 add_concell(struct __nsw_switchconfig *cfp) 839 { 840 struct cons_cell *cp; 841 842 if (cfp == NULL) 843 return (1); 844 if ((cp = libc_malloc(sizeof (struct cons_cell))) == NULL) 845 return (1); 846 cp->sw = cfp; 847 cp->next = concell_list; 848 concell_list = cp; 849 return (0); 850 } 851 852 int 853 __nsw_freeconfig_v1(struct __nsw_switchconfig_v1 *conf) 854 { 855 struct cons_cell_v1 *cellp; 856 857 if (conf == NULL) { 858 return (-1); 859 } 860 /* 861 * Hacked to make life easy for the code in nss_common.c. Free conf 862 * iff it was created by calling _nsw_getoneconfig() directly 863 * rather than by calling nsw_getconfig. 864 */ 865 lmutex_lock(&serialize_config_v1); 866 for (cellp = concell_list_v1; cellp; cellp = cellp->next) { 867 if (cellp->sw == conf) { 868 break; 869 } 870 } 871 lmutex_unlock(&serialize_config_v1); 872 if (cellp == NULL) { 873 /* Not in the cache; free it */ 874 freeconf_v1(conf); 875 return (1); 876 } else { 877 /* In the cache; don't free it */ 878 return (0); 879 } 880 } 881 882 int 883 __nsw_freeconfig(struct __nsw_switchconfig *conf) 884 { 885 struct cons_cell *cellp; 886 887 if (conf == NULL) { 888 return (-1); 889 } 890 /* 891 * Hacked to make life easy for the code in nss_common.c. Free conf 892 * iff it was created by calling _nsw_getoneconfig() directly 893 * rather than by calling nsw_getconfig. 894 */ 895 lmutex_lock(&serialize_config); 896 for (cellp = concell_list; cellp; cellp = cellp->next) { 897 if (cellp->sw == conf) { 898 break; 899 } 900 } 901 lmutex_unlock(&serialize_config); 902 if (cellp == NULL) { 903 /* Not in the cache; free it */ 904 freeconf(conf); 905 return (1); 906 } else { 907 /* In the cache; don't free it */ 908 return (0); 909 } 910 } 911 912 /* Return 1 if the string contains all digits, else return 0. */ 913 static int 914 alldigits(char *s) 915 { 916 for (; *s; s++) 917 if (!isdigit(*s)) 918 return (0); 919 return (1); 920 } 921 922 923 /* 924 * To avoid the 256 open file descriptor limitation in stdio, 925 * we are using a private limited implementation of stdio calls. 926 * The private implementation is closely based on the implementation 927 * in the standard C library. 928 * To simplify, certain assumptions are made: 929 * - a file may be opened only in read mode. 930 * - Only sequential reads allowed 931 * - file descriptors should not be shared between threads 932 */ 933 934 static int 935 _raise_fd(int fd) 936 { 937 int nfd; 938 static const int min_fd = 256; 939 940 if (fd >= min_fd) 941 return (fd); 942 943 if ((nfd = fcntl(fd, F_DUPFD, min_fd)) == -1) { 944 /* 945 * If the shell limits [See limit(1)] the 946 * descriptors to 256, fcntl will fail 947 * and errno will be set to EINVAL. Since 948 * the intention is to ignore fcntl failures 949 * and continue working with 'fd', we should 950 * reset errno to _prevent_ apps relying on errno 951 * to treat this as an error. 952 */ 953 errno = 0; 954 return (fd); 955 } 956 957 (void) close(fd); 958 959 return (nfd); 960 } 961 962 __NSL_FILE * 963 __nsl_c_fopen(const char *filename, const char *mode) 964 { 965 int fd; 966 __NSL_FILE *stream; 967 void *buf; 968 969 if (mode == NULL || filename == NULL) { 970 return (NULL); 971 } 972 973 if (strcmp(mode, "r") != 0) { 974 return (NULL); 975 } 976 977 fd = open(filename, O_RDONLY | O_LARGEFILE, 0666); 978 if (fd < 0) 979 return (NULL); 980 981 stream = libc_malloc(sizeof (__NSL_FILE)); 982 buf = lmalloc(__NSL_FILE_BUF_SIZE); 983 if (stream != NULL && buf != NULL) { 984 stream->_nsl_base = buf; 985 stream->_nsl_file = _raise_fd(fd); 986 stream->_nsl_cnt = 0; 987 stream->_nsl_ptr = stream->_nsl_base; 988 } else { 989 (void) close(fd); 990 if (buf) 991 lfree(buf, __NSL_FILE_BUF_SIZE); 992 if (stream) 993 libc_free(stream); 994 stream = NULL; 995 } 996 997 return (stream); 998 } 999 1000 int 1001 __nsl_c_fclose(__NSL_FILE *stream) 1002 { 1003 int res = 0; 1004 1005 if (stream == NULL) 1006 return (EOF); 1007 1008 if (close(stream->_nsl_file) < 0) 1009 res = EOF; 1010 1011 lfree(stream->_nsl_base, __NSL_FILE_BUF_SIZE); 1012 libc_free(stream); 1013 1014 return (res); 1015 } 1016 1017 char * 1018 __nsl_c_fgets(char *buf, int size, __NSL_FILE *stream) 1019 { 1020 char *ptr = buf; 1021 char *p; 1022 int n; 1023 int res; 1024 1025 size--; /* room for '\0' */ 1026 while (size > 0) { 1027 if (stream->_nsl_cnt == 0) { 1028 stream->_nsl_ptr = stream->_nsl_base; 1029 1030 if ((res = read(stream->_nsl_file, stream->_nsl_base, 1031 __NSL_FILE_BUF_SIZE)) > 0) { 1032 stream->_nsl_cnt = res; 1033 } else { 1034 stream->_nsl_cnt = 0; 1035 break; 1036 } 1037 } 1038 n = (int)(size < stream->_nsl_cnt ? size : stream->_nsl_cnt); 1039 if ((p = memccpy(ptr, (char *)stream->_nsl_ptr, '\n', 1040 (size_t)n)) != NULL) 1041 n = (int)(p - ptr); 1042 ptr += n; 1043 stream->_nsl_cnt -= n; 1044 stream->_nsl_ptr += n; 1045 if (p != NULL) 1046 break; /* newline found */ 1047 size -= n; 1048 } 1049 1050 if (ptr == buf) /* never read anything */ 1051 return (NULL); 1052 1053 *ptr = '\0'; 1054 return (buf); 1055 } 1056