/* $NetBSD: info_nis.c,v 1.1.1.6 2003/03/09 01:13:15 christos Exp $ */ /* * Copyright (c) 1997-2003 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. * * * Id: info_nis.c,v 1.13 2002/12/27 22:43:49 ezk Exp * */ /* * Get info from NIS map */ #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include #include /* * 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 * -James Tanis */ 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 */