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