summaryrefslogtreecommitdiff
path: root/usr/src/lib/libc/port/gen/nsparse.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libc/port/gen/nsparse.c
downloadillumos-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.c1055
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);
+}