info_nis.c revision 310490
1/* 2 * Copyright (c) 1997-2014 Erez Zadok 3 * Copyright (c) 1989 Jan-Simon Pendry 4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 5 * Copyright (c) 1989 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry at Imperial College, London. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * 36 * File: am-utils/amd/info_nis.c 37 * 38 */ 39 40/* 41 * Get info from NIS map 42 */ 43 44#ifdef HAVE_CONFIG_H 45# include <config.h> 46#endif /* HAVE_CONFIG_H */ 47#include <am_defs.h> 48#include <amd.h> 49#include <sun_map.h> 50 51 52/* 53 * NIS+ servers in NIS compat mode don't have yp_order() 54 * 55 * has_yp_order = 1 NIS server 56 * = 0 NIS+ server 57 * = -1 server is down 58 */ 59static int has_yp_order = -1; 60 61/* forward declarations */ 62int nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)); 63int nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp); 64int nis_init(mnt_map *m, char *map, time_t *tp); 65int nis_isup(mnt_map *m, char *map); 66int nis_mtime(mnt_map *m, char *map, time_t *tp); 67 68/* typedefs */ 69typedef void (*nis_callback_fxn_t)(mnt_map *, char *, char *); 70#ifndef DEFINED_YPALL_CALLBACK_FXN_T 71typedef int (*ypall_callback_fxn_t)(); 72#endif /* DEFINED_YPALL_CALLBACK_FXN_T */ 73 74struct nis_callback_data { 75 mnt_map *ncd_m; 76 char *ncd_map; 77 nis_callback_fxn_t ncd_fn; 78}; 79 80/* Map to the right version of yp_all */ 81#ifdef HAVE_BAD_YP_ALL 82# define yp_all am_yp_all 83static int am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback); 84#endif /* HAVE_BAD_YP_ALL */ 85 86 87/* 88 * Figure out the nis domain name 89 */ 90static int 91determine_nis_domain(void) 92{ 93 static int nis_not_running = 0; 94 char default_domain[YPMAXDOMAIN]; 95 96 if (nis_not_running) 97 return ENOENT; 98 99 if (getdomainname(default_domain, sizeof(default_domain)) < 0) { 100 nis_not_running = 1; 101 plog(XLOG_ERROR, "getdomainname: %m"); 102 return EIO; 103 } 104 if (!*default_domain) { 105 nis_not_running = 1; 106 plog(XLOG_WARNING, "NIS domain name is not set. NIS ignored."); 107 return ENOENT; 108 } 109 gopt.nis_domain = xstrdup(default_domain); 110 111 return 0; 112} 113 114 115/* 116 * Callback from yp_all 117 */ 118static int 119callback(int status, char *key, int kl, char *val, int vl, char *data) 120{ 121 struct nis_callback_data *ncdp = (struct nis_callback_data *) data; 122 123 if (status == YP_TRUE) { 124 125 /* add to list of maps */ 126 char *kp = strnsave(key, kl); 127 char *vp = strnsave(val, vl); 128 129 (*ncdp->ncd_fn) (ncdp->ncd_m, kp, vp); 130 131 /* we want more ... */ 132 return FALSE; 133 134 } else { 135 136 /* NOMORE means end of map - otherwise log error */ 137 if (status != YP_NOMORE) { 138 /* check what went wrong */ 139 int e = ypprot_err(status); 140 141 plog(XLOG_ERROR, "yp enumeration of %s: %s, status=%d, e=%d", 142 ncdp->ncd_map, yperr_string(e), status, e); 143 } 144 return TRUE; 145 } 146} 147 148 149int 150nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)) 151{ 152 int error; 153 struct nis_callback_data data; 154 struct ypall_callback cbinfo; 155 156 if (!gopt.nis_domain) { 157 error = determine_nis_domain(); 158 if (error) 159 return error; 160 } 161 data.ncd_m = m; 162 data.ncd_map = map; 163 data.ncd_fn = fn; 164 cbinfo.data = (voidp) &data; 165 cbinfo.foreach = (ypall_callback_fxn_t) callback; 166 167 plog(XLOG_INFO, "NIS map %s reloading using yp_all", map); 168 /* 169 * If you are using NIS and your yp_all function is "broken", you have to 170 * get it fixed. The bug in yp_all() is that it does not close a TCP 171 * connection to ypserv, and this ypserv runs out of open file descriptors, 172 * getting into an infinite loop, thus all YP clients eventually unbind 173 * and hang too. 174 */ 175 error = yp_all(gopt.nis_domain, map, &cbinfo); 176 177 if (error) 178 plog(XLOG_ERROR, "error grabbing nis map of %s: %s", map, yperr_string(ypprot_err(error))); 179 return error; 180} 181 182 183/* 184 * Check if NIS is up, so we can determine if to clear the map or not. 185 * Test it by checking the yp order. 186 * Returns: 0 if NIS is down, 1 if it is up. 187 */ 188int 189nis_isup(mnt_map *m, char *map) 190{ 191 YP_ORDER_OUTORDER_TYPE order; 192 int error; 193 char *master; 194 static int last_status = 1; /* assume up by default */ 195 196 switch (has_yp_order) { 197 case 1: 198 /* 199 * NIS server with yp_order 200 */ 201 error = yp_order(gopt.nis_domain, map, &order); 202 if (error != 0) { 203 plog(XLOG_ERROR, 204 "nis_isup: error getting the order of map %s: %s", 205 map, yperr_string(ypprot_err(error))); 206 last_status = 0; 207 return 0; /* NIS is down */ 208 } 209 break; 210 211 case 0: 212 /* 213 * NIS+ server without yp_order 214 */ 215 error = yp_master(gopt.nis_domain, map, &master); 216 if (error != 0) { 217 plog(XLOG_ERROR, 218 "nis_isup: error getting the master of map %s: %s", 219 map, yperr_string(ypprot_err(error))); 220 last_status = 0; 221 return 0; /* NIS+ is down */ 222 } 223 break; 224 225 default: 226 /* 227 * server was down 228 */ 229 last_status = 0; 230 } 231 232 if (last_status == 0) { /* reinitialize if was down before */ 233 time_t dummy; 234 error = nis_init(m, map, &dummy); 235 if (error) 236 return 0; /* still down */ 237 plog(XLOG_INFO, "nis_isup: NIS came back up for map %s", map); 238 last_status = 1; 239 } 240 return 1; /* NIS is up */ 241} 242 243 244/* 245 * Try to locate a key using NIS. 246 */ 247int 248nis_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) 249{ 250 int outlen; 251 int res; 252 YP_ORDER_OUTORDER_TYPE order; 253 254 /* 255 * Make sure domain initialized 256 */ 257 if (!gopt.nis_domain) { 258 int error = determine_nis_domain(); 259 if (error) 260 return error; 261 } 262 263 264 switch (has_yp_order) { 265 case 1: 266 /* 267 * NIS server with yp_order 268 * Check if map has changed 269 */ 270 if (yp_order(gopt.nis_domain, map, &order)) 271 return EIO; 272 if ((time_t) order > *tp) { 273 *tp = (time_t) order; 274 return -1; 275 } 276 break; 277 278 case 0: 279 /* 280 * NIS+ server without yp_order 281 * Check if timeout has expired to invalidate the cache 282 */ 283 order = time(NULL); 284 if ((time_t)order - *tp > gopt.am_timeo) { 285 *tp = (time_t)order; 286 return(-1); 287 } 288 break; 289 290 default: 291 /* 292 * server was down 293 */ 294 if (nis_isup(m, map)) 295 return -1; 296 return EIO; 297 } 298 299 /* 300 * Lookup key 301 */ 302 res = yp_match(gopt.nis_domain, map, key, strlen(key), pval, &outlen); 303 if (m->cfm && (m->cfm->cfm_flags & CFM_SUN_MAP_SYNTAX) && res == 0) { 304 char *oldval = *pval; 305 *pval = sun_entry2amd(key, oldval); 306 /* We always need to free the output of the yp_match call. */ 307 XFREE(oldval); 308 if (*pval == NULL) 309 return -1; /* sun2amd parser error */ 310 } 311 312 /* 313 * Do something interesting with the return code 314 */ 315 switch (res) { 316 case 0: 317 return 0; 318 319 case YPERR_KEY: 320 return ENOENT; 321 322 default: 323 plog(XLOG_ERROR, "nis_search: %s: %s", map, yperr_string(res)); 324 return EIO; 325 } 326} 327 328 329int 330nis_init(mnt_map *m, char *map, time_t *tp) 331{ 332 YP_ORDER_OUTORDER_TYPE order; 333 int yp_order_result; 334 char *master; 335 336 if (!gopt.nis_domain) { 337 int error = determine_nis_domain(); 338 if (error) 339 return error; 340 } 341 342 /* 343 * To see if the map exists, try to find 344 * a master for it. 345 */ 346 yp_order_result = yp_order(gopt.nis_domain, map, &order); 347 switch (yp_order_result) { 348 case 0: 349 /* NIS server found */ 350 has_yp_order = 1; 351 *tp = (time_t) order; 352 dlog("NIS master for %s@%s has order %lu", map, gopt.nis_domain, (unsigned long) order); 353 break; 354 case YPERR_YPERR: 355 /* NIS+ server found ! */ 356 has_yp_order = 0; 357 /* try yp_master() instead */ 358 if (yp_master(gopt.nis_domain, map, &master)) { 359 return ENOENT; 360 } else { 361 dlog("NIS master for %s@%s is a NIS+ server", map, gopt.nis_domain); 362 /* Use fake timestamps */ 363 *tp = time(NULL); 364 } 365 break; 366 default: 367 /* server is down */ 368 has_yp_order = -1; 369 return ENOENT; 370 } 371 return 0; 372} 373 374 375int 376nis_mtime(mnt_map *m, char *map, time_t *tp) 377{ 378 return nis_init(m, map, tp); 379} 380 381 382#ifdef HAVE_BAD_YP_ALL 383/* 384 * If you are using NIS and your yp_all function is "broken", use an 385 * alternate code which avoids a bug in yp_all(). The bug in yp_all() is 386 * that it does not close a TCP connection to ypserv, and this ypserv runs 387 * out of open filedescriptors, getting into an infinite loop, thus all YP 388 * clients eventually unbind and hang too. 389 * 390 * Systems known to be plagued with this bug: 391 * earlier SunOS 4.x 392 * all irix systems (at this time, up to 6.4 was checked) 393 * 394 * -Erez Zadok <ezk@cs.columbia.edu> 395 * -James Tanis <jtt@cs.columbia.edu> */ 396static int 397am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback) 398{ 399 int i, j; 400 char *outkey, *outval; 401 int outkeylen, outvallen; 402 char *outkey_old; 403 int outkeylen_old; 404 405 plog(XLOG_INFO, "NIS map %s reloading using am_yp_all", inmap); 406 407 i = yp_first(indomain, inmap, &outkey, &outkeylen, &outval, &outvallen); 408 if (i) { 409 plog(XLOG_ERROR, "yp_first() returned error: %s\n", yperr_string(i)); 410 } 411 do { 412 j = (incallback->foreach)(YP_TRUE, 413 outkey, 414 outkeylen, 415 outval, 416 outvallen, 417 incallback->data); 418 if (j != FALSE) /* terminate loop */ 419 break; 420 421 /* 422 * We have to manually free all char ** arguments to yp_first/yp_next 423 * outval must be freed *before* calling yp_next again, outkey can be 424 * freed as outkey_old *after* the call (this saves one call to 425 * strnsave). 426 */ 427 XFREE(outval); 428 outkey_old = outkey; 429 outkeylen_old = outkeylen; 430 i = yp_next(indomain, 431 inmap, 432 outkey_old, 433 outkeylen_old, 434 &outkey, 435 &outkeylen, 436 &outval, 437 &outvallen); 438 XFREE(outkey_old); 439 } while (!i); 440 if (i) { 441 dlog("yp_next() returned error: %s\n", yperr_string(i)); 442 } 443 if (i == YPERR_NOMORE) 444 return 0; 445 return i; 446} 447#endif /* HAVE_BAD_YP_ALL */ 448