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