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