Merge branch 'vendor/TNFTP'
[dragonfly.git] / contrib / amd / amd / info_nis.c
1 /*
2  * Copyright (c) 1997-1999 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  *      %W% (Berkeley) %G%
40  *
41  * $Id: info_nis.c,v 1.5 1999/08/22 05:12:51 ezk Exp $
42  *
43  */
44
45 /*
46  * Get info from NIS map
47  */
48
49 #ifdef HAVE_CONFIG_H
50 # include <config.h>
51 #endif /* HAVE_CONFIG_H */
52 #include <am_defs.h>
53 #include <amd.h>
54
55
56 /*
57  * NIS+ servers in NIS compat mode don't have yp_order()
58  *
59  *      has_yp_order = 1        NIS server
60  *                   = 0        NIS+ server
61  *                   = -1       server is down
62  */
63 static int has_yp_order = -1;
64
65 /* forward declarations */
66 int nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *));
67 int nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp);
68 int nis_init(mnt_map *m, char *map, time_t *tp);
69 int nis_isup(mnt_map *m, char *map);
70 int nis_mtime(mnt_map *m, char *map, time_t *tp);
71
72 /* typedefs */
73 typedef void (*nis_callback_fxn_t)(mnt_map *, char *, char *);
74 #ifndef DEFINED_YPALL_CALLBACK_FXN_T
75 typedef int (*ypall_callback_fxn_t)();
76 #endif /* DEFINED_YPALL_CALLBACK_FXN_T */
77
78 struct nis_callback_data {
79   mnt_map *ncd_m;
80   char *ncd_map;
81   nis_callback_fxn_t ncd_fn;
82 };
83
84 /* Map to the right version of yp_all */
85 #ifdef HAVE_BAD_YP_ALL
86 # define yp_all am_yp_all
87 static int am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback);
88 #endif /* HAVE_BAD_YP_ALL */
89
90
91 /*
92  * Figure out the nis domain name
93  */
94 static int
95 determine_nis_domain(void)
96 {
97   static int nis_not_running = 0;
98   char default_domain[YPMAXDOMAIN];
99
100   if (nis_not_running)
101     return ENOENT;
102
103   if (getdomainname(default_domain, sizeof(default_domain)) < 0) {
104     nis_not_running = 1;
105     plog(XLOG_ERROR, "getdomainname: %m");
106     return EIO;
107   }
108   if (!*default_domain) {
109     nis_not_running = 1;
110 #ifdef DEBUG
111     plog(XLOG_WARNING, "NIS domain name is not set.  NIS ignored.");
112 #endif /* DEBUG */
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 #ifdef DEBUG
148       plog(XLOG_ERROR, "yp enumeration of %s: %s, status=%d, e=%d",
149            ncdp->ncd_map, yperr_string(e), status, e);
150 #else /* not DEBUG */
151       plog(XLOG_ERROR, "yp enumeration of %s: %s", ncdp->ncd_map, yperr_string(e));
152 #endif /* not DEBUG */
153     }
154     return TRUE;
155   }
156 }
157
158
159 int
160 nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *))
161 {
162   int error;
163   struct nis_callback_data data;
164   struct ypall_callback cbinfo;
165
166   if (!gopt.nis_domain) {
167     error = determine_nis_domain();
168     if (error)
169       return error;
170   }
171   data.ncd_m = m;
172   data.ncd_map = map;
173   data.ncd_fn = fn;
174   cbinfo.data = (voidp) &data;
175   cbinfo.foreach = (ypall_callback_fxn_t) callback;
176
177   /*
178    * If you are using NIS and your yp_all function is "broken", you have to
179    * get it fixed.  The bug in yp_all() is that it does not close a TCP
180    * connection to ypserv, and this ypserv runs out of open file descriptors,
181    * getting into an infinite loop, thus all YP clients eventually unbind
182    * and hang too.
183    */
184   error = yp_all(gopt.nis_domain, map, &cbinfo);
185
186   if (error)
187     plog(XLOG_ERROR, "error grabbing nis map of %s: %s", map, yperr_string(ypprot_err(error)));
188   return error;
189 }
190
191
192 /*
193  * Check if NIS is up, so we can determine if to clear the map or not.
194  * Test it by checking the yp order.
195  * Returns: 0 if NIS is down, 1 if it is up.
196  */
197 int
198 nis_isup(mnt_map *m, char *map)
199 {
200   YP_ORDER_OUTORDER_TYPE order;
201   int error;
202   char *master;
203   static int last_status = 1;   /* assume up by default */
204
205   switch (has_yp_order) {
206   case 1:
207     /*
208      * NIS server with yp_order
209      */
210     error = yp_order(gopt.nis_domain, map, &order);
211     if (error != 0) {
212       plog(XLOG_ERROR,
213            "nis_isup: error getting the order of map %s: %s",
214            map, yperr_string(ypprot_err(error)));
215       last_status = 0;
216       return 0;                 /* NIS is down */
217     }
218     break;
219
220   case 0:
221     /*
222      * NIS+ server without yp_order
223      */
224     error = yp_master(gopt.nis_domain, map, &master);
225     if (error != 0) {
226       plog(XLOG_ERROR,
227            "nis_isup: error getting the master of map %s: %s",
228            map, yperr_string(ypprot_err(error)));
229       last_status = 0;
230       return 0;                 /* NIS+ is down */
231     }
232     break;
233
234   default:
235     /*
236      * server was down
237      */
238     last_status = 0;
239   }
240
241   if (last_status == 0) {       /* reinitialize if was down before */
242     time_t dummy;
243     error = nis_init(m, map, &dummy);
244     if (error)
245       return 0;                 /* still down */
246     plog(XLOG_INFO, "nis_isup: NIS came back up for map %s", map);
247     last_status = 1;
248   }
249   return 1;                     /* NIS is up */
250 }
251
252
253 /*
254  * Try to locate a key using NIS.
255  */
256 int
257 nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp)
258 {
259   int outlen;
260   int res;
261   YP_ORDER_OUTORDER_TYPE order;
262
263   /*
264    * Make sure domain initialized
265    */
266   if (!gopt.nis_domain) {
267     int error = determine_nis_domain();
268     if (error)
269       return error;
270   }
271
272
273   switch (has_yp_order) {
274   case 1:
275     /*
276      * NIS server with yp_order
277      * Check if map has changed
278      */
279     if (yp_order(gopt.nis_domain, map, &order))
280       return EIO;
281     if ((time_t) order > *tp) {
282       *tp = (time_t) order;
283       return -1;
284     }
285     break;
286
287   case 0:
288     /*
289      * NIS+ server without yp_order
290      * Check if timeout has expired to invalidate the cache
291      */
292     order = time(NULL);
293     if ((time_t)order - *tp > gopt.am_timeo) {
294       *tp = (time_t)order;
295       return(-1);
296     }
297     break;
298
299   default:
300     /*
301      * server was down
302      */
303      if (nis_isup(m, map))
304        return -1;
305      return EIO;
306   }
307
308   /*
309    * Lookup key
310    */
311   res = yp_match(gopt.nis_domain, map, key, strlen(key), val, &outlen);
312
313   /*
314    * Do something interesting with the return code
315    */
316   switch (res) {
317   case 0:
318     return 0;
319
320   case YPERR_KEY:
321     return ENOENT;
322
323   default:
324     plog(XLOG_ERROR, "%s: %s", map, yperr_string(res));
325     return EIO;
326   }
327 }
328
329
330 int
331 nis_init(mnt_map *m, char *map, time_t *tp)
332 {
333   YP_ORDER_OUTORDER_TYPE order;
334   int yp_order_result;
335   char *master;
336
337   if (!gopt.nis_domain) {
338     int error = determine_nis_domain();
339     if (error)
340       return error;
341   }
342
343   /*
344    * To see if the map exists, try to find
345    * a master for it.
346    */
347   yp_order_result = yp_order(gopt.nis_domain, map, &order);
348   switch (yp_order_result) {
349   case 0:
350     /* NIS server found */
351     has_yp_order = 1;
352     *tp = (time_t) order;
353 #ifdef DEBUG
354     dlog("NIS master for %s@%s has order %lu", map, gopt.nis_domain, (unsigned long) order);
355 #endif /* DEBUG */
356     break;
357   case YPERR_YPERR:
358     /* NIS+ server found ! */
359     has_yp_order = 0;
360     /* try yp_master() instead */
361     if (yp_master(gopt.nis_domain, map, &master)) {
362       return ENOENT;
363     } else {
364 #ifdef DEBUG
365       dlog("NIS master for %s@%s is a NIS+ server", map, gopt.nis_domain);
366 #endif /* DEBUG */
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 #ifdef DEBUG
446   if (i) {
447     dlog("yp_next() returned error: %s\n", yperr_string(i));
448   }
449 #endif /* DEBUG */
450   if (i == YPERR_NOMORE)
451     return 0;
452   return i;
453 }
454 #endif /* HAVE_BAD_YP_ALL */