nss_common.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
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Shared code used by the name-service-switch frontends (e.g. getpwnam_r())
*/
#include "synonyms.h"
#include <mtlib.h>
#include <dlfcn.h>
#define __NSS_PRIVATE_INTERFACE
#include "nsswitch_priv.h"
#include <nss_common.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <thread.h>
#include "libc.h"
#include "tsd.h"
/*
* The golden rule is: if you hold a pointer to an nss_db_state struct and
* you don't hold the lock, you'd better have incremented the refcount
* while you held the lock; otherwise, it may vanish or change
* significantly when you least expect it.
*
* The pointer in nss_db_root_t is one such, so the reference count >= 1.
* Ditto the pointer in struct nss_getent_context.
*/
/*
* State for one nsswitch database (e.g. "passwd", "hosts")
*/
struct nss_db_state {
unsigned refcount; /* One for the pointer in */
/* nss_db_root_t, plus one */
/* for each active thread. */
struct __nsw_switchconfig_v1 *config;
int max_src; /* is == config->num_lookups */
};
/*
* State for one of the sources (e.g. "nis", "compat") for a database
*/
struct nss_src_state {
struct __nsw_lookup_v1 *lkp;
int n_active;
int n_dormant;
int n_waiting; /* ... on wanna_be */
union {
/* when limit_dead_backends == 1 */
} dormant; /* pointers to dormant backends */
void *finder_priv;
};
void _nss_db_state_destr(struct nss_db_state *);
/* ==== null definitions if !MTSAFE? Ditto lock field in nss_db_root_t */
*(sp) = (r)->s)
*(rp) = &(s)->orphan_root))
NSS_CHECKROOT(rp, s))
#define NSS_STATE_REF_u(s) (++(s)->refcount)
#define NSS_UNREF_UNLOCK(r, s) (--(s)->refcount != 0 \
? ((void)NSS_UNLOCK(r)) \
: (NSS_UNLOCK(r), (void)_nss_db_state_destr(s)))
*(sp) == 0 && \
(r->s = *(sp) = _nss_db_state_constr(f)))
/* === In the future, NSS_LOCK_CHECK() may also have to check that */
/* === the config info hasn't changed (by comparing version numbers) */
/* NSS_OPTIONS infrastructure BEGIN */
static int checked_env = 0; /* protected by "rootlock" */
/* allowing __nss_debug_file to be set could be a security hole. */
int __nss_debug_eng_loop;
/* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */
/* allowing __nis_debug_file to be set could be a security hole. */
int __nis_debug_bind;
int __nis_debug_rpc;
int __nis_debug_calls;
char *__nis_prefsrv;
char *__nis_preftype;
char *__nis_server; /* if set, use only this server for binding */
#define OPT_INT 1
#define OPT_STRING 2
#ifdef DEBUG
#define OPT_FILE 3
#endif
struct option {
char *name;
int type;
void *address;
};
static struct option nss_options[] = {
#ifdef DEBUG
/* allowing __nss_debug_file to be set could be a security hole. */
#endif
{ 0, 0, 0 },
};
static struct option nis_options[] = {
#ifdef DEBUG
/* allowing __nis_debug_file to be set could be a security hole. */
#endif
{ 0, 0, 0 },
};
static
void
{
int n;
char *p;
#ifdef DEBUG
#endif
case OPT_STRING:
p = libc_strdup(val);
break;
case OPT_INT:
n = 1;
else
break;
#ifdef DEBUG
case OPT_FILE:
break;
#endif
}
break;
}
}
}
static
void
{
char *base;
char optname[100];
char optval[100];
while (*p) {
while (isspace(*p))
p++;
if (*p == '\0')
break;
base = p;
while (*p && *p != '=' && !isspace(*p))
p++;
/*
* play it safe and keep it simple, bail if an opt name
* is too long.
*/
return;
if (*p == '=') {
p++;
base = p;
while (*p && !isspace(*p))
p++;
/*
* play it safe and keep it simple, bail if an opt
* value is too long.
*/
return;
} else {
optval[0] = '\0';
}
}
}
static
void
{
char *p;
/* NSS_OPTIONS is undocumented and should be used without nscd running. */
p = getenv("NSS_OPTIONS");
if (p == NULL)
return;
}
/*
* sole external routine called from libnsl/nis/cache/cache_api.cc in the
* routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard
* Only after checking "checked_env" (which must be done with mutex
* "cur_cache_lock" held) and is done once, (then "checked_env" is set)
*/
void
{
char *p;
p = getenv("NIS_OPTIONS");
if (p == NULL)
return;
}
/* NSS_OPTIONS/NIS_OPTIONS infrastructure END */
static nss_backend_t *
{
for (;;) {
if (s->p.max_dormant_per_src == 1) {
} else {
}
break;
}
(bf->lookup_priv,
s->p.name,
&src->finder_priv);
if (c != 0) {
break;
}
}
/* Couldn't find the backend anywhere */
be = 0;
break;
}
}
0 /* === unimplemented */);
if (be != 0) {
break;
/* Something's wrong; we should be */
/* able to create at least one */
/* instance of the backend */
break;
}
/*
* Else it's odd that we can't create another backend
* instance, but don't sweat it; instead, queue for
* an existing backend instance.
*/
}
NSS_CHECKROOT(rootpp, s);
/*
* Loop and see whether things got better for us, or whether
* someone else got scheduled first and we have to try
* this again.
*
* === ?? Should count iterations, assume bug if many ??
*/
}
return (be);
}
static void
{
if (be == 0) {
return;
}
if (s->p.max_dormant_per_src == 1) {
libc_malloc(s->p.max_dormant_per_src *
sizeof (nss_backend_t *))) != NULL) {
} else {
/* Can't store it, so toss it */
}
} else {
/* We've stored as many as we want, so toss it */
}
}
}
static struct nss_db_state *
{
struct nss_db_state *s;
struct __nsw_switchconfig_v1 *config = 0;
struct __nsw_lookup_v1 *lkp;
enum __nsw_parse_err err;
const char *config_name;
int n_src;
if ((s = libc_malloc(sizeof (*s))) == 0) {
return (0);
}
s->p.max_active_per_src = 10;
s->p.max_dormant_per_src = 1;
s->p.finders = nss_default_finders;
(*initf)(&s->p);
if (s->p.name == 0) {
return (0);
}
if (!checked_env) {
/* NSS_OPTIONS is undocumented and should be used without nscd running. */
checked_env = 1;
}
if (! (s->p.flags & NSS_USE_DEFAULT_CONFIG)) {
/* === ? test err ? */
}
if (config == 0) {
/* getconfig failed, or frontend demanded default config */
char *str; /* _nsw_getoneconfig() clobbers its argument */
}
if (config == 0) {
return (0);
}
}
return (0);
}
}
s->refcount = 1;
return (s);
}
void
{
if (max_dormant == 1) {
NSS_DBOP_DESTRUCTOR, 0);
};
int n;
NSS_DBOP_DESTRUCTOR, 0);
}
}
/* cond_destroy(&src->wanna_be); */
}
}
/*
* _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire
* nss_db_state structure.
* Assumes that s has been ref-counted down to zero (in particular,
* rootp->s has already been dealt with).
*
* Nobody else holds a pointer to *s (if they did, refcount != 0),
* so we can clean up state *after* we drop the lock (also, by the
* time we finish freeing the state structures, the lock may have
* ceased to exist -- if we were using the orphan_root).
*/
void
_nss_db_state_destr(struct nss_db_state *s)
{
/* === _private_mutex_destroy(&s->orphan_root.lock); */
if (s->p.cleanup != 0) {
(*s->p.cleanup)(&s->p);
}
if (s->config != 0) {
(void) __nsw_freeconfig_v1(s->config);
}
if (s->src != 0) {
int n_src;
s->p.max_dormant_per_src);
}
}
libc_free(s);
}
void
{
struct nss_db_state *s;
NSS_ROOTLOCK(rootp, &s);
if (s == 0) {
} else {
rootp->s = 0;
NSS_UNREF_UNLOCK(rootp, s);
}
}
/*
* _nss_status_vec() returns a bit vector of all status codes returned during
* the most recent call to nss_search().
* _nss_status_vec_p() returns a pointer to this bit vector, or NULL on
* failure.
* These functions are private. Don't use them externally without discussing
* it with the switch maintainers.
*/
static uint_t *
{
}
unsigned int
_nss_status_vec(void)
{
unsigned int *status_vec_p = _nss_status_vec_p();
}
static void
int n,
char *dbase,
struct __nsw_lookup_v1 *lkp)
{
(void) fprintf(__nss_debug_file,
"NSS_retry(%d): '%s': trying '%s' ... ",
(void) fflush(__nss_debug_file);
}
static void
struct __nsw_lookup_v1 *lkp)
{
switch (res) {
case NSS_SUCCESS:
break;
case NSS_NOTFOUND:
break;
case NSS_UNAVAIL:
break;
case NSS_TRYAGAIN:
break;
case NSS_NISSERVDNS_TRYAGAIN:
break;
default:
}
case __NSW_CONTINUE:
break;
case __NSW_RETURN:
break;
case __NSW_TRYAGAIN_FOREVER:
break;
case __NSW_TRYAGAIN_NTIMES:
lkp->max_retries);
break;
case __NSW_TRYAGAIN_PAUSED:
break;
default:
}
}
#define NSS_BACKOFF(n, b, t) \
((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1))))
static int
{
if (res == NSS_SUCCESS) {
}
return (0);
}
if ((res == NSS_TRYAGAIN &&
(res == NSS_NISSERVDNS_TRYAGAIN &&
return (1);
if (res == NSS_TRYAGAIN &&
if (n <= lkp->max_retries)
return (1);
else {
return (0);
}
if (res == NSS_NISSERVDNS_TRYAGAIN &&
if (n <= lkp->max_retries)
return (1);
else {
return (0);
}
return (0);
}
void *search_args)
{
struct nss_db_state *s;
int n_src;
unsigned int *status_vec_p = _nss_status_vec_p();
if (status_vec_p == NULL) {
return (NSS_UNAVAIL);
}
*status_vec_p = 0;
if (s == 0) {
return (res);
}
NSS_STATE_REF_u(s);
res = NSS_UNAVAIL;
int n_loop = 0;
int no_backoff = 19;
do {
/*
* Backend operation may take a while;
* drop the lock so we don't serialize
* more than necessary.
*/
/* After several tries, backoff... */
if (n_loop > no_backoff) {
if (__nss_debug_eng_loop > 1)
(void) fprintf(__nss_debug_file,
"NSS: loop: sleeping %d ...\n",
}
if (__nss_debug_eng_loop)
NSS_RELOCK(&rootp, s);
n_loop++;
if (__nss_debug_eng_loop)
}
}
if (__nss_debug_eng_loop)
(void) fprintf(__nss_debug_file,
"NSS: '%s': return.\n",
break;
} else
if (__nss_debug_eng_loop)
(void) fprintf(__nss_debug_file,
"NSS: '%s': continue ...\n",
}
NSS_UNREF_UNLOCK(rootp, s);
return (res);
}
/*
* Start of nss_setent()/nss_getent()/nss_endent()
*/
/*
* In principle there could be multiple contexts active for a single
* database; in practice, since Posix and UI have helpfully said that
* getent() state is global rather than, say, per-thread or user-supplied,
* we have at most one of these per nss_db_state.
*/
struct nss_getent_context {
int n_src; /* >= max_src ==> end of sequence */
struct nss_db_state *s;
/*
* XXX ?? Should contain enough cross-check info that we can detect an
* nss_context that missed an nss_delete() or similar.
*/
};
static void nss_setent_u(nss_db_root_t *,
nss_getent_t *);
nss_getent_t *,
void *);
static void nss_endent_u(nss_db_root_t *,
nss_getent_t *);
void
{
if (contextpp == 0) {
return;
}
}
void *args)
{
if (contextpp == 0) {
return (NSS_UNAVAIL);
}
return (status);
}
void
{
if (contextpp == 0) {
return;
}
}
/*
* Each of the _u versions of the nss interfaces assume that the context
* lock is held.
*/
static void
{
struct nss_db_state *s;
int n_src;
s = contextp->s;
if (s != 0) {
NSS_RELOCK(&rootp, s);
NSS_UNREF_UNLOCK(rootp, s);
}
contextp->s = 0;
}
}
static void
{
struct nss_db_state *s;
struct nss_getent_context *contextp;
int n_src;
return;
}
s = 0;
} else {
s = contextp->s;
}
if (s == 0) {
if (s == 0) {
/* Couldn't set up state, so quit */
/* ==== is there any danger of not having done an */
/* end_iter() here, and hence of losing backends? */
return;
}
NSS_STATE_REF_u(s);
contextp->s = s;
} else {
s = contextp->s;
/*
* Optimization: don't do endent, don't change
* backends, just do the setent. Look Ma, no locks
* (nor any context that needs updating).
*/
return;
}
NSS_RELOCK(&rootp, s);
} else {
NSS_RELOCK(&rootp, s);
}
}
;
}
if (be == 0) {
return;
}
}
static nss_status_t
{
struct nss_db_state *s;
struct nss_getent_context *contextp;
int n_src;
/* Give up */
return (NSS_UNAVAIL);
}
}
s = contextp->s;
if (s == 0) {
/*
* We've done an end_iter() and haven't done nss_setent()
* or nss_endent() since; we should stick in this state
* until the caller invokes one of those two routines.
*/
return (NSS_SUCCESS);
}
if (be == 0) {
/* If it's null it's a bug, but let's play safe */
res = NSS_UNAVAIL;
} else {
}
if (res != __NSW_SUCCESS) {
}
return (res);
}
NSS_RELOCK(&rootp, s);
do {
n_src++;
if (be == 0) {
/*
* This is the case where we failed to get the backend
* for the last source. We exhausted all sources.
*/
return (NSS_SUCCESS);
}
}
/* Got to the end of the sources without finding another entry */
return (NSS_SUCCESS);
/* success is either a successful entry or end of the sources */
}
/*ARGSUSED*/
static void
{
struct nss_getent_context *contextp;
/* nss_endent() on an unused context is a no-op */
return;
}
/*
* Existing code (BSD, SunOS) works in such a way that getXXXent()
* following an endXXXent() behaves as though the user had invoked
* setXXXent(), i.e. it iterates properly from the beginning.
* We'd better not break this, so our choices are
* (1) leave the context structure around, and do nss_setent or
* something equivalent,
* or (2) free the context completely, and rely on the code in
* nss_getent() that makes getXXXent() do the right thing
* even without a preceding setXXXent().
* The code below does (2), which frees up resources nicely but will
* cost more if the user then does more getXXXent() operations.
* Moral: for efficiency, don't call endXXXent() prematurely.
*/
}