NetBSD/dist/am-utils/amd/info_nis.c
2005-09-20 17:12:54 +00:00

444 lines
11 KiB
C

/* $NetBSD: info_nis.c,v 1.1.1.9 2005/09/20 17:14:57 rpaulo Exp $ */
/*
* Copyright (c) 1997-2005 Erez Zadok
* Copyright (c) 1989 Jan-Simon Pendry
* Copyright (c) 1989 Imperial College of Science, Technology & Medicine
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry at Imperial College, London.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgment:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* File: am-utils/amd/info_nis.c
*
*/
/*
* Get info from NIS map
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */
#include <am_defs.h>
#include <amd.h>
/*
* NIS+ servers in NIS compat mode don't have yp_order()
*
* has_yp_order = 1 NIS server
* = 0 NIS+ server
* = -1 server is down
*/
static int has_yp_order = -1;
/* forward declarations */
int nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *));
int nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp);
int nis_init(mnt_map *m, char *map, time_t *tp);
int nis_isup(mnt_map *m, char *map);
int nis_mtime(mnt_map *m, char *map, time_t *tp);
/* typedefs */
typedef void (*nis_callback_fxn_t)(mnt_map *, char *, char *);
#ifndef DEFINED_YPALL_CALLBACK_FXN_T
typedef int (*ypall_callback_fxn_t)();
#endif /* DEFINED_YPALL_CALLBACK_FXN_T */
struct nis_callback_data {
mnt_map *ncd_m;
char *ncd_map;
nis_callback_fxn_t ncd_fn;
};
/* Map to the right version of yp_all */
#ifdef HAVE_BAD_YP_ALL
# define yp_all am_yp_all
static int am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback);
#endif /* HAVE_BAD_YP_ALL */
/*
* Figure out the nis domain name
*/
static int
determine_nis_domain(void)
{
static int nis_not_running = 0;
char default_domain[YPMAXDOMAIN];
if (nis_not_running)
return ENOENT;
if (getdomainname(default_domain, sizeof(default_domain)) < 0) {
nis_not_running = 1;
plog(XLOG_ERROR, "getdomainname: %m");
return EIO;
}
if (!*default_domain) {
nis_not_running = 1;
plog(XLOG_WARNING, "NIS domain name is not set. NIS ignored.");
return ENOENT;
}
gopt.nis_domain = strdup(default_domain);
return 0;
}
/*
* Callback from yp_all
*/
static int
callback(int status, char *key, int kl, char *val, int vl, char *data)
{
struct nis_callback_data *ncdp = (struct nis_callback_data *) data;
if (status == YP_TRUE) {
/* add to list of maps */
char *kp = strnsave(key, kl);
char *vp = strnsave(val, vl);
(*ncdp->ncd_fn) (ncdp->ncd_m, kp, vp);
/* we want more ... */
return FALSE;
} else {
/* NOMORE means end of map - otherwise log error */
if (status != YP_NOMORE) {
/* check what went wrong */
int e = ypprot_err(status);
plog(XLOG_ERROR, "yp enumeration of %s: %s, status=%d, e=%d",
ncdp->ncd_map, yperr_string(e), status, e);
}
return TRUE;
}
}
int
nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *))
{
int error;
struct nis_callback_data data;
struct ypall_callback cbinfo;
if (!gopt.nis_domain) {
error = determine_nis_domain();
if (error)
return error;
}
data.ncd_m = m;
data.ncd_map = map;
data.ncd_fn = fn;
cbinfo.data = (voidp) &data;
cbinfo.foreach = (ypall_callback_fxn_t) callback;
/*
* If you are using NIS and your yp_all function is "broken", you have to
* get it fixed. The bug in yp_all() is that it does not close a TCP
* connection to ypserv, and this ypserv runs out of open file descriptors,
* getting into an infinite loop, thus all YP clients eventually unbind
* and hang too.
*/
error = yp_all(gopt.nis_domain, map, &cbinfo);
if (error)
plog(XLOG_ERROR, "error grabbing nis map of %s: %s", map, yperr_string(ypprot_err(error)));
return error;
}
/*
* Check if NIS is up, so we can determine if to clear the map or not.
* Test it by checking the yp order.
* Returns: 0 if NIS is down, 1 if it is up.
*/
int
nis_isup(mnt_map *m, char *map)
{
YP_ORDER_OUTORDER_TYPE order;
int error;
char *master;
static int last_status = 1; /* assume up by default */
switch (has_yp_order) {
case 1:
/*
* NIS server with yp_order
*/
error = yp_order(gopt.nis_domain, map, &order);
if (error != 0) {
plog(XLOG_ERROR,
"nis_isup: error getting the order of map %s: %s",
map, yperr_string(ypprot_err(error)));
last_status = 0;
return 0; /* NIS is down */
}
break;
case 0:
/*
* NIS+ server without yp_order
*/
error = yp_master(gopt.nis_domain, map, &master);
if (error != 0) {
plog(XLOG_ERROR,
"nis_isup: error getting the master of map %s: %s",
map, yperr_string(ypprot_err(error)));
last_status = 0;
return 0; /* NIS+ is down */
}
break;
default:
/*
* server was down
*/
last_status = 0;
}
if (last_status == 0) { /* reinitialize if was down before */
time_t dummy;
error = nis_init(m, map, &dummy);
if (error)
return 0; /* still down */
plog(XLOG_INFO, "nis_isup: NIS came back up for map %s", map);
last_status = 1;
}
return 1; /* NIS is up */
}
/*
* Try to locate a key using NIS.
*/
int
nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp)
{
int outlen;
int res;
YP_ORDER_OUTORDER_TYPE order;
/*
* Make sure domain initialized
*/
if (!gopt.nis_domain) {
int error = determine_nis_domain();
if (error)
return error;
}
switch (has_yp_order) {
case 1:
/*
* NIS server with yp_order
* Check if map has changed
*/
if (yp_order(gopt.nis_domain, map, &order))
return EIO;
if ((time_t) order > *tp) {
*tp = (time_t) order;
return -1;
}
break;
case 0:
/*
* NIS+ server without yp_order
* Check if timeout has expired to invalidate the cache
*/
order = time(NULL);
if ((time_t)order - *tp > gopt.am_timeo) {
*tp = (time_t)order;
return(-1);
}
break;
default:
/*
* server was down
*/
if (nis_isup(m, map))
return -1;
return EIO;
}
/*
* Lookup key
*/
res = yp_match(gopt.nis_domain, map, key, strlen(key), val, &outlen);
/*
* Do something interesting with the return code
*/
switch (res) {
case 0:
return 0;
case YPERR_KEY:
return ENOENT;
default:
plog(XLOG_ERROR, "nis_search: %s: %s", map, yperr_string(res));
return EIO;
}
}
int
nis_init(mnt_map *m, char *map, time_t *tp)
{
YP_ORDER_OUTORDER_TYPE order;
int yp_order_result;
char *master;
if (!gopt.nis_domain) {
int error = determine_nis_domain();
if (error)
return error;
}
/*
* To see if the map exists, try to find
* a master for it.
*/
yp_order_result = yp_order(gopt.nis_domain, map, &order);
switch (yp_order_result) {
case 0:
/* NIS server found */
has_yp_order = 1;
*tp = (time_t) order;
dlog("NIS master for %s@%s has order %lu", map, gopt.nis_domain, (unsigned long) order);
break;
case YPERR_YPERR:
/* NIS+ server found ! */
has_yp_order = 0;
/* try yp_master() instead */
if (yp_master(gopt.nis_domain, map, &master)) {
return ENOENT;
} else {
dlog("NIS master for %s@%s is a NIS+ server", map, gopt.nis_domain);
/* Use fake timestamps */
*tp = time(NULL);
}
break;
default:
/* server is down */
has_yp_order = -1;
return ENOENT;
}
return 0;
}
int
nis_mtime(mnt_map *m, char *map, time_t *tp)
{
return nis_init(m, map, tp);
}
#ifdef HAVE_BAD_YP_ALL
/*
* If you are using NIS and your yp_all function is "broken", use an
* alternate code which avoids a bug in yp_all(). The bug in yp_all() is
* that it does not close a TCP connection to ypserv, and this ypserv runs
* out of open filedescriptors, getting into an infinite loop, thus all YP
* clients eventually unbind and hang too.
*
* Systems known to be plagued with this bug:
* earlier SunOS 4.x
* all irix systems (at this time, up to 6.4 was checked)
*
* -Erez Zadok <ezk@cs.columbia.edu>
* -James Tanis <jtt@cs.columbia.edu> */
static int
am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback)
{
int i, j;
char *outkey, *outval;
int outkeylen, outvallen;
char *outkey_old;
int outkeylen_old;
plog(XLOG_INFO, "NIS map %s reloading using am_yp_all", inmap);
i = yp_first(indomain, inmap, &outkey, &outkeylen, &outval, &outvallen);
if (i) {
plog(XLOG_ERROR, "yp_first() returned error: %s\n", yperr_string(i));
}
do {
j = (incallback->foreach)(YP_TRUE,
outkey,
outkeylen,
outval,
outvallen,
incallback->data);
if (j != FALSE) /* terminate loop */
break;
/*
* We have to manually free all char ** arguments to yp_first/yp_next
* outval must be freed *before* calling yp_next again, outkey can be
* freed as outkey_old *after* the call (this saves one call to
* strnsave).
*/
XFREE(outval);
outkey_old = outkey;
outkeylen_old = outkeylen;
i = yp_next(indomain,
inmap,
outkey_old,
outkeylen_old,
&outkey,
&outkeylen,
&outval,
&outvallen);
XFREE(outkey_old);
} while (!i);
if (i) {
dlog("yp_next() returned error: %s\n", yperr_string(i));
}
if (i == YPERR_NOMORE)
return 0;
return i;
}
#endif /* HAVE_BAD_YP_ALL */