xref: /netbsd-src/external/bsd/am-utils/dist/amd/info_nis.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
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