nsparse.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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);
}