diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libc/port/gen/nsparse.c | |
download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libc/port/gen/nsparse.c')
-rw-r--r-- | usr/src/lib/libc/port/gen/nsparse.c | 1055 |
1 files changed, 1055 insertions, 0 deletions
diff --git a/usr/src/lib/libc/port/gen/nsparse.c b/usr/src/lib/libc/port/gen/nsparse.c new file mode 100644 index 0000000000..ab879934cf --- /dev/null +++ b/usr/src/lib/libc/port/gen/nsparse.c @@ -0,0 +1,1055 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "synonyms.h" +#include "mtlib.h" +#include "libc.h" +#include <synch.h> +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#define __NSS_PRIVATE_INTERFACE +#include "nsswitch_priv.h" +#undef __NSS_PRIVATE_INTERFACE + +#include <syslog.h> + +#define islabel(c) (isalnum(c) || (c) == '_') + +#define LIBC_STRDUP(new, existing) \ + if ((new = libc_strdup(existing)) == NULL) { \ + dup_fail = 1; \ + goto barf_line; \ + } + +/* + * This file has all the routines that access the configuration + * information. + */ + +struct cons_cell_v1 { /* private to the parser */ + struct __nsw_switchconfig_v1 *sw; + struct cons_cell_v1 *next; +}; + +struct cons_cell { /* private to the parser */ + struct __nsw_switchconfig *sw; + struct cons_cell *next; +}; + +/* + * Local routines + */ + +static char *skip(char **, char); +static char *labelskip(char *); +static char *spaceskip(char *); +static struct __nsw_switchconfig_v1 *scrounge_cache_v1(const char *); +static struct __nsw_switchconfig *scrounge_cache(const char *); +static int add_concell_v1(struct __nsw_switchconfig_v1 *); +static int add_concell(struct __nsw_switchconfig *); +static void freeconf_v1(struct __nsw_switchconfig_v1 *); +static void freeconf(struct __nsw_switchconfig *); +static int alldigits(char *); + +static struct cons_cell_v1 *concell_list_v1; /* stays with add_concell() */ +static struct cons_cell *concell_list; /* stays with add_concell() */ + +/* + * + * With the "lookup control" feature, the default criteria for NIS, NIS+, + * and any new services (e.g. ldap) will be: + * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=forever] + * + * For backward compat, NIS via NIS server in DNS forwarding mode will be: + * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue] + * + * And also for backward compat, the default criteria for DNS will be: + * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue] + */ + + + +/* + * The BIND resolver normally will retry several times on server non-response. + * But now with the "lookup control" feature, we don't want the resolver doing + * many retries, rather we want it to return control (reasonably) quickly back + * to the switch engine. However, when TRYAGAIN=N or TRYAGAIN=forever is + * not explicitly set by the admin in the conf file, we want the old "resolver + * retry a few times" rather than no retries at all. + */ +static int dns_tryagain_retry = 3; + +/* + * For backward compat (pre "lookup control"), the dns default behavior is + * soft lookup. + */ +static void +set_dns_default_lkp(struct __nsw_lookup_v1 *lkp) +{ + if (strcasecmp(lkp->service_name, "dns") == 0) { + lkp->actions[__NSW_TRYAGAIN] = + __NSW_TRYAGAIN_NTIMES; + lkp->max_retries = dns_tryagain_retry; + } +} + +/* + * Private interface used by nss_common.c, hence this function is not static + */ +struct __nsw_switchconfig_v1 * +_nsw_getoneconfig_v1(const char *name, char *linep, enum __nsw_parse_err *errp) + /* linep Nota Bene: not const char * */ + /* errp Meanings are abused a bit */ +{ + struct __nsw_switchconfig_v1 *cfp; + struct __nsw_lookup_v1 *lkp, **lkq; + int end_crit, dup_fail = 0; + action_t act; + char *p, *tokenp; + + *errp = __NSW_CONF_PARSE_SUCCESS; + + if ((cfp = libc_malloc(sizeof (struct __nsw_switchconfig_v1))) + == NULL) { + *errp = __NSW_CONF_PARSE_SYSERR; + return (NULL); + } + LIBC_STRDUP(cfp->dbase, name); + lkq = &cfp->lookups; + + /* linep points to a naming service name */ + for (;;) { + int i; + + /* white space following the last service */ + if (*linep == '\0' || *linep == '\n') { + return (cfp); + } + if ((lkp = libc_malloc(sizeof (struct __nsw_lookup_v1))) + == NULL) { + *errp = __NSW_CONF_PARSE_SYSERR; + freeconf_v1(cfp); + return (NULL); + } + + *lkq = lkp; + lkq = &lkp->next; + + for (i = 0; i < __NSW_STD_ERRS_V1; i++) + if (i == __NSW_SUCCESS) + lkp->actions[i] = __NSW_RETURN; + else if (i == __NSW_TRYAGAIN) + lkp->actions[i] = __NSW_TRYAGAIN_FOREVER; + else + lkp->actions[i] = __NSW_CONTINUE; + + /* get criteria for the naming service */ + if (tokenp = skip(&linep, '[')) { /* got criteria */ + + /* premature end, illegal char following [ */ + if (!islabel(*linep)) + goto barf_line; + LIBC_STRDUP(lkp->service_name, tokenp); + cfp->num_lookups++; + + set_dns_default_lkp(lkp); + + end_crit = 0; + + /* linep points to a switch_err */ + for (;;) { + int ntimes = 0; /* try again max N times */ + int dns_continue = 0; + + if ((tokenp = skip(&linep, '=')) == NULL) { + goto barf_line; + } + + /* premature end, ill char following = */ + if (!islabel(*linep)) + goto barf_line; + + /* linep points to the string following '=' */ + p = labelskip(linep); + if (*p == ']') + end_crit = 1; + else if (*p != ' ' && *p != '\t') + goto barf_line; + *p++ = '\0'; /* null terminate linep */ + p = spaceskip(p); + if (!end_crit) { + if (*p == ']') { + end_crit = 1; + *p++ = '\0'; + } else if (*p == '\0' || *p == '\n') { + return (cfp); + } else if (!islabel(*p)) + /* p better be the next switch_err */ + goto barf_line; + } + if (strcasecmp(linep, __NSW_STR_RETURN) == 0) + act = __NSW_RETURN; + else if (strcasecmp(linep, + __NSW_STR_CONTINUE) == 0) { + if (strcasecmp(lkp->service_name, + "dns") == 0 && + strcasecmp(tokenp, + __NSW_STR_TRYAGAIN) + == 0) { + /* + * Add one more condition + * so it retries only if it's + * "dns [TRYAGAIN=continue]" + */ + dns_continue = 1; + act = __NSW_TRYAGAIN_NTIMES; + } else + act = __NSW_CONTINUE; + } else if (strcasecmp(linep, + __NSW_STR_FOREVER) == 0) + act = __NSW_TRYAGAIN_FOREVER; + else if (alldigits(linep)) { + act = __NSW_TRYAGAIN_NTIMES; + ntimes = atoi(linep); + if (ntimes < 0 || ntimes > INT_MAX) + ntimes = 0; + } + else + goto barf_line; + + if (__NSW_SUCCESS_ACTION(act) && + strcasecmp(tokenp, + __NSW_STR_SUCCESS) == 0) { + lkp->actions[__NSW_SUCCESS] = act; + } else if (__NSW_NOTFOUND_ACTION(act) && + strcasecmp(tokenp, + __NSW_STR_NOTFOUND) == 0) { + lkp->actions[__NSW_NOTFOUND] = act; + } else if (__NSW_UNAVAIL_ACTION(act) && + strcasecmp(tokenp, + __NSW_STR_UNAVAIL) == 0) { + lkp->actions[__NSW_UNAVAIL] = act; + } else if (__NSW_TRYAGAIN_ACTION(act) && + strcasecmp(tokenp, + __NSW_STR_TRYAGAIN) == 0) { + lkp->actions[__NSW_TRYAGAIN] = act; + if (strcasecmp(lkp->service_name, + "nis") == 0) + lkp->actions[ + __NSW_NISSERVDNS_TRYAGAIN] + = act; + if (act == __NSW_TRYAGAIN_NTIMES) + lkp->max_retries = + dns_continue ? + dns_tryagain_retry : ntimes; + } else { + /*EMPTY*/ + /* + * convert string tokenp to integer + * and put in long_errs + */ + } + if (end_crit) { + linep = spaceskip(p); + if (*linep == '\0' || *linep == '\n') + return (cfp); + break; /* process next naming service */ + } + linep = p; + } /* end of while loop for a name service's criteria */ + } else { + /* + * no criteria for this naming service. + * linep points to name service, but not null + * terminated. + */ + p = labelskip(linep); + if (*p == '\0' || *p == '\n') { + *p = '\0'; + LIBC_STRDUP(lkp->service_name, linep); + set_dns_default_lkp(lkp); + cfp->num_lookups++; + return (cfp); + } + if (*p != ' ' && *p != '\t') + goto barf_line; + *p++ = '\0'; + LIBC_STRDUP(lkp->service_name, linep); + set_dns_default_lkp(lkp); + cfp->num_lookups++; + linep = spaceskip(p); + } + } /* end of while(1) loop for a name service */ + +barf_line: + freeconf_v1(cfp); + *errp = dup_fail ? __NSW_CONF_PARSE_SYSERR : __NSW_CONF_PARSE_NOPOLICY; + return (NULL); +} + +/* + * Private interface used by nss_common.c, hence this function is not static + */ +struct __nsw_switchconfig * +_nsw_getoneconfig(const char *name, char *linep, enum __nsw_parse_err *errp) + /* linep Nota Bene: not const char * */ + /* errp Meanings are abused a bit */ +{ + struct __nsw_switchconfig *cfp; + struct __nsw_lookup *lkp, **lkq; + int end_crit, dup_fail = 0; + action_t act; + char *p, *tokenp; + + *errp = __NSW_CONF_PARSE_SUCCESS; + + if ((cfp = libc_malloc(sizeof (struct __nsw_switchconfig))) + == NULL) { + *errp = __NSW_CONF_PARSE_SYSERR; + return (NULL); + } + LIBC_STRDUP(cfp->dbase, name); + lkq = &cfp->lookups; + + /* linep points to a naming service name */ + for (;;) { + int i; + + /* white space following the last service */ + if (*linep == '\0' || *linep == '\n') { + return (cfp); + } + if ((lkp = libc_malloc(sizeof (struct __nsw_lookup))) + == NULL) { + *errp = __NSW_CONF_PARSE_SYSERR; + freeconf(cfp); + return (NULL); + } + + *lkq = lkp; + lkq = &lkp->next; + + for (i = 0; i < __NSW_STD_ERRS; i++) + if (i == __NSW_SUCCESS) + lkp->actions[i] = 1; + else + lkp->actions[i] = 0; + + /* get criteria for the naming service */ + if (tokenp = skip(&linep, '[')) { /* got criteria */ + + /* premature end, illegal char following [ */ + if (!islabel(*linep)) + goto barf_line; + LIBC_STRDUP(lkp->service_name, tokenp); + cfp->num_lookups++; + end_crit = 0; + + /* linep points to a switch_err */ + for (;;) { + if ((tokenp = skip(&linep, '=')) == NULL) { + goto barf_line; + } + + /* premature end, ill char following = */ + if (!islabel(*linep)) + goto barf_line; + + /* linep points to the string following '=' */ + p = labelskip(linep); + if (*p == ']') + end_crit = 1; + else if (*p != ' ' && *p != '\t') + goto barf_line; + *p++ = '\0'; /* null terminate linep */ + p = spaceskip(p); + if (!end_crit) { + if (*p == ']') { + end_crit = 1; + *p++ = '\0'; + } else if (*p == '\0' || *p == '\n') + return (cfp); + else if (!islabel(*p)) + /* p better be the next switch_err */ + goto barf_line; + } + if (strcasecmp(linep, __NSW_STR_RETURN) == 0) + act = __NSW_RETURN; + else if (strcasecmp(linep, + __NSW_STR_CONTINUE) == 0) + act = __NSW_CONTINUE; + else if (strcasecmp(linep, + __NSW_STR_FOREVER) == 0) + /* + * =forever or =N might be in conf file + * but old progs won't expect it. + */ + act = __NSW_RETURN; + else if (alldigits(linep)) + act = __NSW_CONTINUE; + else + goto barf_line; + if (strcasecmp(tokenp, + __NSW_STR_SUCCESS) == 0) { + lkp->actions[__NSW_SUCCESS] = act; + } else if (strcasecmp(tokenp, + __NSW_STR_NOTFOUND) == 0) { + lkp->actions[__NSW_NOTFOUND] = act; + } else if (strcasecmp(tokenp, + __NSW_STR_UNAVAIL) == 0) { + lkp->actions[__NSW_UNAVAIL] = act; + } else if (strcasecmp(tokenp, + __NSW_STR_TRYAGAIN) == 0) { + lkp->actions[__NSW_TRYAGAIN] = act; + } else { + /*EMPTY*/ + /* + * convert string tokenp to integer + * and put in long_errs + */ + } + if (end_crit) { + linep = spaceskip(p); + if (*linep == '\0' || *linep == '\n') + return (cfp); + break; /* process next naming service */ + } + linep = p; + } /* end of while loop for a name service's criteria */ + } else { + /* + * no criteria for this naming service. + * linep points to name service, but not null + * terminated. + */ + p = labelskip(linep); + if (*p == '\0' || *p == '\n') { + *p = '\0'; + LIBC_STRDUP(lkp->service_name, linep); + cfp->num_lookups++; + return (cfp); + } + if (*p != ' ' && *p != '\t') + goto barf_line; + *p++ = '\0'; + LIBC_STRDUP(lkp->service_name, linep); + cfp->num_lookups++; + linep = spaceskip(p); + } + } /* end of while(1) loop for a name service */ + +barf_line: + freeconf(cfp); + *errp = dup_fail ? __NSW_CONF_PARSE_SYSERR : __NSW_CONF_PARSE_NOPOLICY; + return (NULL); +} + +static mutex_t serialize_config_v1 = DEFAULTMUTEX; +static mutex_t serialize_config = DEFAULTMUTEX; + +static void +syslog_warning(const char *dbase) +{ + syslog(LOG_WARNING, + "libc: bad lookup policy for %s in %s, using defaults..\n", + dbase, __NSW_CONFIG_FILE); +} + +struct __nsw_switchconfig_v1 * +__nsw_getconfig_v1(const char *dbase, enum __nsw_parse_err *errp) +{ + struct __nsw_switchconfig_v1 *cfp, *retp = NULL; + int syslog_error = 0; + __NSL_FILE *fp; + char *linep; + char lineq[BUFSIZ]; + + /* + * ==== I don't feel entirely comfortable disabling signals for the + * duration of this, but maybe we have to. Or maybe we should + * use mutex_trylock to detect recursion? (Not clear what's + * the right thing to do when it happens, though). + */ + lmutex_lock(&serialize_config_v1); + + if (cfp = scrounge_cache_v1(dbase)) { + *errp = __NSW_CONF_PARSE_SUCCESS; + lmutex_unlock(&serialize_config_v1); + return (cfp); + } + + if ((fp = __nsl_c_fopen(__NSW_CONFIG_FILE, "r")) == NULL) { + *errp = __NSW_CONF_PARSE_NOFILE; + lmutex_unlock(&serialize_config_v1); + return (NULL); + } + + *errp = __NSW_CONF_PARSE_NOPOLICY; + while (linep = __nsl_c_fgets(lineq, BUFSIZ, fp)) { + enum __nsw_parse_err line_err; + char *tokenp, *comment; + + /* + * Ignore portion of line following the comment character '#'. + */ + if ((comment = strchr(linep, '#')) != NULL) { + *comment = '\0'; + } + /* + * skip past blank lines. + * otherwise, cache as a struct switchconfig. + */ + if ((*linep == '\0') || isspace(*linep)) { + continue; + } + if ((tokenp = skip(&linep, ':')) == NULL) { + continue; /* ignore this line */ + } + if (cfp = scrounge_cache_v1(tokenp)) { + continue; /* ? somehow this database is in the cache */ + } + if (cfp = _nsw_getoneconfig_v1(tokenp, linep, &line_err)) { + (void) add_concell_v1(cfp); + if (strcmp(cfp->dbase, dbase) == 0) { + *errp = __NSW_CONF_PARSE_SUCCESS; + retp = cfp; + } + } else { + /* + * Got an error on this line, if it is a system + * error we might as well give right now. If it + * is a parse error on the second entry of the + * database we are looking for and the first one + * was a good entry we end up logging the following + * syslog message and using a default policy instead. + */ + if (line_err == __NSW_CONF_PARSE_SYSERR) { + *errp = __NSW_CONF_PARSE_SYSERR; + break; + } else if (line_err == __NSW_CONF_PARSE_NOPOLICY && + strcmp(tokenp, dbase) == 0) { + syslog_error = 1; + *errp = __NSW_CONF_PARSE_NOPOLICY; + break; + } + /* + * Else blithely ignore problems on this line and + * go ahead with the next line. + */ + } + } + (void) __nsl_c_fclose(fp); + lmutex_unlock(&serialize_config_v1); + /* + * We have to drop the lock before calling syslog(). + */ + if (syslog_error) + syslog_warning(dbase); + return (retp); +} + +struct __nsw_switchconfig * +__nsw_getconfig(const char *dbase, enum __nsw_parse_err *errp) +{ + struct __nsw_switchconfig *cfp, *retp = NULL; + int syslog_error = 0; + __NSL_FILE *fp; + char *linep; + char lineq[BUFSIZ]; + + /* + * ==== I don't feel entirely comfortable disabling signals for the + * duration of this, but maybe we have to. Or maybe we should + * use mutex_trylock to detect recursion? (Not clear what's + * the right thing to do when it happens, though). + */ + lmutex_lock(&serialize_config); + + if (cfp = scrounge_cache(dbase)) { + *errp = __NSW_CONF_PARSE_SUCCESS; + lmutex_unlock(&serialize_config); + return (cfp); + } + + if ((fp = __nsl_c_fopen(__NSW_CONFIG_FILE, "r")) == NULL) { + *errp = __NSW_CONF_PARSE_NOFILE; + lmutex_unlock(&serialize_config); + return (NULL); + } + + *errp = __NSW_CONF_PARSE_NOPOLICY; + while (linep = __nsl_c_fgets(lineq, BUFSIZ, fp)) { + enum __nsw_parse_err line_err; + char *tokenp, *comment; + + /* + * Ignore portion of line following the comment character '#'. + */ + if ((comment = strchr(linep, '#')) != NULL) { + *comment = '\0'; + } + /* + * skip past blank lines. + * otherwise, cache as a struct switchconfig. + */ + if ((*linep == '\0') || isspace(*linep)) { + continue; + } + if ((tokenp = skip(&linep, ':')) == NULL) { + continue; /* ignore this line */ + } + if (cfp = scrounge_cache(tokenp)) { + continue; /* ? somehow this database is in the cache */ + } + if (cfp = _nsw_getoneconfig(tokenp, linep, &line_err)) { + (void) add_concell(cfp); + if (strcmp(cfp->dbase, dbase) == 0) { + *errp = __NSW_CONF_PARSE_SUCCESS; + retp = cfp; + } + } else { + /* + * Got an error on this line, if it is a system + * error we might as well give right now. If it + * is a parse error on the second entry of the + * database we are looking for and the first one + * was a good entry we end up logging the following + * syslog message and using a default policy instead. + */ + if (line_err == __NSW_CONF_PARSE_SYSERR) { + *errp = __NSW_CONF_PARSE_SYSERR; + break; + } else if (line_err == __NSW_CONF_PARSE_NOPOLICY && + strcmp(tokenp, dbase) == 0) { + syslog_error = 1; + *errp = __NSW_CONF_PARSE_NOPOLICY; + break; + } + /* + * Else blithely ignore problems on this line and + * go ahead with the next line. + */ + } + } + (void) __nsl_c_fclose(fp); + lmutex_unlock(&serialize_config); + /* + * We have to drop the lock before calling syslog(). + */ + if (syslog_error) + syslog_warning(dbase); + return (retp); +} + + +static struct __nsw_switchconfig_v1 * +scrounge_cache_v1(const char *dbase) +{ + struct cons_cell_v1 *cellp = concell_list_v1; + + for (; cellp; cellp = cellp->next) + if (strcmp(dbase, cellp->sw->dbase) == 0) + return (cellp->sw); + return (NULL); +} + +static struct __nsw_switchconfig * +scrounge_cache(const char *dbase) +{ + struct cons_cell *cellp = concell_list; + + for (; cellp; cellp = cellp->next) + if (strcmp(dbase, cellp->sw->dbase) == 0) + return (cellp->sw); + return (NULL); +} + +static void +freeconf_v1(struct __nsw_switchconfig_v1 *cfp) +{ + if (cfp) { + if (cfp->dbase) + libc_free(cfp->dbase); + if (cfp->lookups) { + struct __nsw_lookup_v1 *nex, *cur; + for (cur = cfp->lookups; cur; cur = nex) { + libc_free(cur->service_name); + nex = cur->next; + libc_free(cur); + } + } + libc_free(cfp); + } +} + +static void +freeconf(struct __nsw_switchconfig *cfp) +{ + if (cfp) { + if (cfp->dbase) + libc_free(cfp->dbase); + if (cfp->lookups) { + struct __nsw_lookup *nex, *cur; + for (cur = cfp->lookups; cur; cur = nex) { + libc_free(cur->service_name); + nex = cur->next; + libc_free(cur); + } + } + libc_free(cfp); + } +} + +action_t +__nsw_extended_action_v1(struct __nsw_lookup_v1 *lkp, int err) +{ + struct __nsw_long_err *lerrp; + + for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) { + if (lerrp->nsw_errno == err) + return (lerrp->action); + } + return (__NSW_CONTINUE); +} + +action_t +__nsw_extended_action(struct __nsw_lookup *lkp, int err) +{ + struct __nsw_long_err *lerrp; + + for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) { + if (lerrp->nsw_errno == err) + return (lerrp->action); + } + return (__NSW_CONTINUE); +} + + +/* give the next non-alpha character */ +static char * +labelskip(char *cur) +{ + char *p = cur; + while (islabel(*p)) + ++p; + return (p); +} + +/* give the next non-space character */ +static char * +spaceskip(char *cur) +{ + char *p = cur; + while (*p == ' ' || *p == '\t') + ++p; + return (p); +} + +/* + * terminate the *cur pointed string by null only if it is + * followed by "key" surrounded by zero or more spaces and + * return value is the same as the original *cur pointer and + * *cur pointer is advanced to the first non {space, key} char + * followed by the key. Otherwise, return NULL and keep + * *cur unchanged. + */ +static char * +skip(char **cur, char key) +{ + char *p, *tmp; + char *q = *cur; + int found, tmpfound; + + tmp = labelskip(*cur); + p = tmp; + found = (*p == key); + if (found) { + *p++ = '\0'; /* overwrite the key */ + p = spaceskip(p); + } else { + while (*p == ' ' || *p == '\t') { + tmpfound = (*++p == key); + if (tmpfound) { + found = tmpfound; + /* null terminate the return token */ + *tmp = '\0'; + p++; /* skip the key */ + } + } + } + if (!found) + return (NULL); /* *cur unchanged */ + *cur = p; + return (q); +} + +/* add to the front: LRU */ +static int +add_concell_v1(struct __nsw_switchconfig_v1 *cfp) +{ + struct cons_cell_v1 *cp; + + if (cfp == NULL) + return (1); + if ((cp = libc_malloc(sizeof (struct cons_cell_v1))) == NULL) + return (1); + cp->sw = cfp; + cp->next = concell_list_v1; + concell_list_v1 = cp; + return (0); +} + +/* add to the front: LRU */ +static int +add_concell(struct __nsw_switchconfig *cfp) +{ + struct cons_cell *cp; + + if (cfp == NULL) + return (1); + if ((cp = libc_malloc(sizeof (struct cons_cell))) == NULL) + return (1); + cp->sw = cfp; + cp->next = concell_list; + concell_list = cp; + return (0); +} + +int +__nsw_freeconfig_v1(struct __nsw_switchconfig_v1 *conf) +{ + struct cons_cell_v1 *cellp; + + if (conf == NULL) { + return (-1); + } + /* + * Hacked to make life easy for the code in nss_common.c. Free conf + * iff it was created by calling _nsw_getoneconfig() directly + * rather than by calling nsw_getconfig. + */ + lmutex_lock(&serialize_config_v1); + for (cellp = concell_list_v1; cellp; cellp = cellp->next) { + if (cellp->sw == conf) { + break; + } + } + lmutex_unlock(&serialize_config_v1); + if (cellp == NULL) { + /* Not in the cache; free it */ + freeconf_v1(conf); + return (1); + } else { + /* In the cache; don't free it */ + return (0); + } +} + +int +__nsw_freeconfig(struct __nsw_switchconfig *conf) +{ + struct cons_cell *cellp; + + if (conf == NULL) { + return (-1); + } + /* + * Hacked to make life easy for the code in nss_common.c. Free conf + * iff it was created by calling _nsw_getoneconfig() directly + * rather than by calling nsw_getconfig. + */ + lmutex_lock(&serialize_config); + for (cellp = concell_list; cellp; cellp = cellp->next) { + if (cellp->sw == conf) { + break; + } + } + lmutex_unlock(&serialize_config); + if (cellp == NULL) { + /* Not in the cache; free it */ + freeconf(conf); + return (1); + } else { + /* In the cache; don't free it */ + return (0); + } +} + +/* Return 1 if the string contains all digits, else return 0. */ +static int +alldigits(char *s) +{ + for (; *s; s++) + if (!isdigit(*s)) + return (0); + return (1); +} + + +/* + * To avoid the 256 open file descriptor limitation in stdio, + * we are using a private limited implementation of stdio calls. + * The private implementation is closely based on the implementation + * in the standard C library. + * To simplify, certain assumptions are made: + * - a file may be opened only in read mode. + * - Only sequential reads allowed + * - file descriptors should not be shared between threads + */ + +static int +_raise_fd(int fd) +{ + int nfd; + static const int min_fd = 256; + + if (fd >= min_fd) + return (fd); + + if ((nfd = fcntl(fd, F_DUPFD, min_fd)) == -1) { + /* + * If the shell limits [See limit(1)] the + * descriptors to 256, fcntl will fail + * and errno will be set to EINVAL. Since + * the intention is to ignore fcntl failures + * and continue working with 'fd', we should + * reset errno to _prevent_ apps relying on errno + * to treat this as an error. + */ + errno = 0; + return (fd); + } + + (void) close(fd); + + return (nfd); +} + +__NSL_FILE * +__nsl_c_fopen(const char *filename, const char *mode) +{ + int fd; + __NSL_FILE *stream; + void *buf; + + if (mode == NULL || filename == NULL) { + return (NULL); + } + + if (strcmp(mode, "r") != 0) { + return (NULL); + } + + fd = open(filename, O_RDONLY | O_LARGEFILE, 0666); + if (fd < 0) + return (NULL); + + stream = libc_malloc(sizeof (__NSL_FILE)); + buf = lmalloc(__NSL_FILE_BUF_SIZE); + if (stream != NULL && buf != NULL) { + stream->_nsl_base = buf; + stream->_nsl_file = _raise_fd(fd); + stream->_nsl_cnt = 0; + stream->_nsl_ptr = stream->_nsl_base; + } else { + (void) close(fd); + if (buf) + lfree(buf, __NSL_FILE_BUF_SIZE); + if (stream) + libc_free(stream); + stream = NULL; + } + + return (stream); +} + +int +__nsl_c_fclose(__NSL_FILE *stream) +{ + int res = 0; + + if (stream == NULL) + return (EOF); + + if (close(stream->_nsl_file) < 0) + res = EOF; + + lfree(stream->_nsl_base, __NSL_FILE_BUF_SIZE); + libc_free(stream); + + return (res); +} + +char * +__nsl_c_fgets(char *buf, int size, __NSL_FILE *stream) +{ + char *ptr = buf; + char *p; + int n; + int res; + + size--; /* room for '\0' */ + while (size > 0) { + if (stream->_nsl_cnt == 0) { + stream->_nsl_ptr = stream->_nsl_base; + + if ((res = read(stream->_nsl_file, stream->_nsl_base, + __NSL_FILE_BUF_SIZE)) > 0) { + stream->_nsl_cnt = res; + } else { + stream->_nsl_cnt = 0; + break; + } + } + n = (int)(size < stream->_nsl_cnt ? size : stream->_nsl_cnt); + if ((p = memccpy(ptr, (char *)stream->_nsl_ptr, '\n', + (size_t)n)) != NULL) + n = (int)(p - ptr); + ptr += n; + stream->_nsl_cnt -= n; + stream->_nsl_ptr += n; + if (p != NULL) + break; /* newline found */ + size -= n; + } + + if (ptr == buf) /* never read anything */ + return (NULL); + + *ptr = '\0'; + return (buf); +} |