mapc.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/mapc.c
37 *
38 */
39
40/*
41 * Mount map cache
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
50/*
51 * Make a duplicate reference to an existing map
52 */
53#define mapc_dup(m) ((m)->refc++, (m))
54
55/*
56 * Map cache types
57 * default, none, incremental, all, regexp
58 * MAPC_RE implies MAPC_ALL and must be numerically
59 * greater.
60 */
61#define	MAPC_DFLT	0x000
62#define	MAPC_NONE	0x001
63#define	MAPC_INC	0x002
64#define	MAPC_ROOT	0x004
65#define	MAPC_ALL	0x010
66#define	MAPC_CACHE_MASK	0x0ff
67#define	MAPC_SYNC	0x100
68
69#ifdef HAVE_REGEXEC
70# define	MAPC_RE		0x020
71# define	MAPC_ISRE(m)	((m)->alloc == MAPC_RE)
72#else /* not HAVE_REGEXEC */
73# define	MAPC_ISRE(m)	FALSE
74#endif /* not HAVE_REGEXEC */
75
76/*
77 * Lookup recursion
78 */
79#define	MREC_FULL	2
80#define	MREC_PART	1
81#define	MREC_NONE	0
82
83static struct opt_tab mapc_opt[] =
84{
85  {"all", MAPC_ALL},
86  {"default", MAPC_DFLT},
87  {"inc", MAPC_INC},
88  {"mapdefault", MAPC_DFLT},
89  {"none", MAPC_NONE},
90#ifdef HAVE_REGEXEC
91  {"re", MAPC_RE},
92  {"regexp", MAPC_RE},
93#endif /* HAVE_REGEXEC */
94  {"sync", MAPC_SYNC},
95  {NULL, 0}
96};
97
98/*
99 * Wildcard key
100 */
101static char wildcard[] = "*";
102
103/*
104 * Map type
105 */
106typedef struct map_type map_type;
107struct map_type {
108  char *name;			/* Name of this map type */
109  init_fn *init;		/* Initialization */
110  reload_fn *reload;		/* Reload or fill */
111  isup_fn *isup;		/* Is service up or not? (1=up, 0=down) */
112  search_fn *search;		/* Search for new entry */
113  mtime_fn *mtime;		/* Find modify time */
114  int def_alloc;		/* Default allocation mode */
115};
116
117/*
118 * Map for root node
119 */
120static mnt_map *root_map;
121
122/*
123 * List of known maps
124 */
125qelem map_list_head = {&map_list_head, &map_list_head};
126
127/*
128 * Configuration
129 */
130
131/* forward definitions */
132static const char *get_full_path(const char *map, const char *path, const char *type);
133static int mapc_meta_search(mnt_map *, char *, char **, int);
134static void mapc_sync(mnt_map *);
135static void mapc_clear(mnt_map *);
136static void mapc_clear_kvhash(kv **);
137
138/* ROOT MAP */
139static int root_init(mnt_map *, char *, time_t *);
140
141/* ERROR MAP */
142static int error_init(mnt_map *, char *, time_t *);
143static int error_reload(mnt_map *, char *, add_fn *);
144static int error_search(mnt_map *, char *, char *, char **, time_t *);
145static int error_mtime(mnt_map *, char *, time_t *);
146
147/* PASSWD MAPS */
148#ifdef HAVE_MAP_PASSWD
149extern int passwd_init(mnt_map *, char *, time_t *);
150extern int passwd_search(mnt_map *, char *, char *, char **, time_t *);
151#endif /* HAVE_MAP_PASSWD */
152
153/* HESIOD MAPS */
154#ifdef HAVE_MAP_HESIOD
155extern int amu_hesiod_init(mnt_map *, char *map, time_t *tp);
156extern int hesiod_isup(mnt_map *, char *);
157extern int hesiod_search(mnt_map *, char *, char *, char **, time_t *);
158#endif /* HAVE_MAP_HESIOD */
159
160/* LDAP MAPS */
161#ifdef HAVE_MAP_LDAP
162extern int amu_ldap_init(mnt_map *, char *map, time_t *tp);
163extern int amu_ldap_search(mnt_map *, char *, char *, char **, time_t *);
164extern int amu_ldap_mtime(mnt_map *, char *, time_t *);
165#endif /* HAVE_MAP_LDAP */
166
167/* UNION MAPS */
168#ifdef HAVE_MAP_UNION
169extern int union_init(mnt_map *, char *, time_t *);
170extern int union_search(mnt_map *, char *, char *, char **, time_t *);
171extern int union_reload(mnt_map *, char *, add_fn *);
172#endif /* HAVE_MAP_UNION */
173
174/* Network Information Service PLUS (NIS+) */
175#ifdef HAVE_MAP_NISPLUS
176extern int nisplus_init(mnt_map *, char *, time_t *);
177extern int nisplus_reload(mnt_map *, char *, add_fn *);
178extern int nisplus_search(mnt_map *, char *, char *, char **, time_t *);
179extern int nisplus_mtime(mnt_map *, char *, time_t *);
180#endif /* HAVE_MAP_NISPLUS */
181
182/* Network Information Service (YP, Yellow Pages) */
183#ifdef HAVE_MAP_NIS
184extern int nis_init(mnt_map *, char *, time_t *);
185extern int nis_reload(mnt_map *, char *, add_fn *);
186extern int nis_isup(mnt_map *, char *);
187extern int nis_search(mnt_map *, char *, char *, char **, time_t *);
188extern int nis_mtime(mnt_map *, char *, time_t *);
189#endif /* HAVE_MAP_NIS */
190
191/* NDBM MAPS */
192#ifdef HAVE_MAP_NDBM
193extern int ndbm_init(mnt_map *, char *, time_t *);
194extern int ndbm_search(mnt_map *, char *, char *, char **, time_t *);
195extern int ndbm_mtime(mnt_map *, char *, time_t *);
196#endif /* HAVE_MAP_NDBM */
197
198/* FILE MAPS */
199#ifdef HAVE_MAP_FILE
200extern int file_init_or_mtime(mnt_map *, char *, time_t *);
201extern int file_reload(mnt_map *, char *, add_fn *);
202extern int file_search(mnt_map *, char *, char *, char **, time_t *);
203#endif /* HAVE_MAP_FILE */
204
205/* EXECUTABLE MAPS */
206#ifdef HAVE_MAP_EXEC
207extern int exec_init(mnt_map *, char *, time_t *);
208extern int exec_search(mnt_map *, char *, char *, char **, time_t *);
209#endif /* HAVE_MAP_EXEC */
210
211/* Sun-syntax MAPS */
212#ifdef HAVE_MAP_SUN
213/* XXX: fill in */
214#endif /* HAVE_MAP_SUN */
215
216/* note that the choice of MAPC_{INC,ALL} will affect browsable_dirs */
217static map_type maptypes[] =
218{
219  {
220    "root",
221    root_init,
222    error_reload,
223    NULL,			/* isup function */
224    error_search,
225    error_mtime,
226    MAPC_ROOT
227  },
228#ifdef HAVE_MAP_PASSWD
229  {
230    "passwd",
231    passwd_init,
232    error_reload,
233    NULL,			/* isup function */
234    passwd_search,
235    error_mtime,
236    MAPC_INC
237  },
238#endif /* HAVE_MAP_PASSWD */
239#ifdef HAVE_MAP_HESIOD
240  {
241    "hesiod",
242    amu_hesiod_init,
243    error_reload,
244    hesiod_isup,		/* is Hesiod up or not? */
245    hesiod_search,
246    error_mtime,
247    MAPC_INC
248  },
249#endif /* HAVE_MAP_HESIOD */
250#ifdef HAVE_MAP_LDAP
251  {
252    "ldap",
253    amu_ldap_init,
254    error_reload,
255    NULL,			/* isup function */
256    amu_ldap_search,
257    amu_ldap_mtime,
258    MAPC_INC
259  },
260#endif /* HAVE_MAP_LDAP */
261#ifdef HAVE_MAP_UNION
262  {
263    "union",
264    union_init,
265    union_reload,
266    NULL,			/* isup function */
267    union_search,
268    error_mtime,
269    MAPC_ALL
270  },
271#endif /* HAVE_MAP_UNION */
272#ifdef HAVE_MAP_NISPLUS
273  {
274    "nisplus",
275    nisplus_init,
276    nisplus_reload,
277    NULL,			/* isup function */
278    nisplus_search,
279    nisplus_mtime,
280    MAPC_INC
281  },
282#endif /* HAVE_MAP_NISPLUS */
283#ifdef HAVE_MAP_NIS
284  {
285    "nis",
286    nis_init,
287    nis_reload,
288    nis_isup,			/* is NIS up or not? */
289    nis_search,
290    nis_mtime,
291    MAPC_ALL
292  },
293#endif /* HAVE_MAP_NIS */
294#ifdef HAVE_MAP_NDBM
295  {
296    "ndbm",
297    ndbm_init,
298    error_reload,
299    NULL,			/* isup function */
300    ndbm_search,
301    ndbm_mtime,
302    MAPC_INC
303  },
304#endif /* HAVE_MAP_NDBM */
305#ifdef HAVE_MAP_FILE
306  {
307    "file",
308    file_init_or_mtime,
309    file_reload,
310    NULL,			/* isup function */
311    file_search,
312    file_init_or_mtime,
313    MAPC_ALL
314  },
315#endif /* HAVE_MAP_FILE */
316#ifdef HAVE_MAP_EXEC
317  {
318    "exec",
319    exec_init,
320    error_reload,
321    NULL,			/* isup function */
322    exec_search,
323    error_mtime,
324    MAPC_INC
325  },
326#endif /* HAVE_MAP_EXEC */
327#ifdef notyet /* probe function needs to be there or SEGV */
328#ifdef HAVE_MAP_SUN
329  {
330    /* XXX: fill in */
331    "sun",
332    NULL,
333    NULL,
334    NULL,			/* isup function */
335    NULL,
336    NULL,
337    0
338  },
339#endif /* HAVE_MAP_SUN */
340#endif
341  {
342    "error",
343    error_init,
344    error_reload,
345    NULL,			/* isup function */
346    error_search,
347    error_mtime,
348    MAPC_NONE
349  },
350};
351
352
353/*
354 * Hash function
355 */
356static u_int
357kvhash_of(char *key)
358{
359  u_int i, j;
360
361  for (i = 0; (j = *key++); i += j) ;
362
363  return i % NKVHASH;
364}
365
366
367void
368mapc_showtypes(char *buf, size_t l)
369{
370  map_type *mt=NULL, *lastmt;
371  int linesize = 0, i;
372
373  i = sizeof(maptypes) / sizeof(maptypes[0]);
374  lastmt = maptypes + i;
375  buf[0] = '\0';
376  for (mt = maptypes; mt < lastmt; mt++) {
377    xstrlcat(buf, mt->name, l);
378    if (mt == (lastmt-1))
379      break;	      /* if last one, don't do xstrlcat's that follows */
380    linesize += strlen(mt->name);
381    if (--i > 0) {
382      xstrlcat(buf, ", ", l);
383      linesize += 2;
384    }
385    if (linesize > 54) {
386      linesize = 0;
387      xstrlcat(buf, "\n\t\t ", l);
388    }
389  }
390}
391
392
393/*
394 * Check if a map of a certain type exists.
395 * Return 1 (true) if exists, 0 (false) if not.
396 */
397int
398mapc_type_exists(const char *type)
399{
400  map_type *mt;
401
402  if (!type)
403    return 0;
404  for (mt = maptypes;
405       mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
406       mt++) {
407    if (STREQ(type, mt->name))
408      return 1;
409  }
410  return 0;			/* not found anywhere */
411}
412
413
414/*
415 * Add key and val to the map m.
416 * key and val are assumed to be safe copies
417 */
418void
419mapc_add_kv(mnt_map *m, char *key, char *val)
420{
421  kv **h;
422  kv *n;
423  int hash = kvhash_of(key);
424#ifdef HAVE_REGEXEC
425  regex_t re;
426#endif /* HAVE_REGEXEC */
427
428  dlog("add_kv: %s -> %s", key, val);
429
430  if (val != NULL && strchr(val, '\n') != NULL) {
431    /*
432     * If the entry value contains multiple lines we need to break
433     * them up and add them recursively.  This is a workaround to
434     * support Sun style multi-mounts.  Amd converts Sun style
435     * mulit-mounts to type:=auto.  The problem is that Sun packs all
436     * the entries on one line.  When Amd does the conversion it puts
437     * each type:=auto entry on the same line separated by '\n'.
438     */
439    char *entry, *tok;
440
441    /*
442     * The first line should contain the first entry.  The key for
443     * this entry is the key passed into this function.
444     */
445    if ((tok = strtok(val, "\n")) != NULL) {
446      mapc_add_kv(m, key, xstrdup(tok));
447    }
448
449    /*
450     * For the rest of the entries we need to tokenize them by '\n'
451     * and separate the keys from there entries.
452     */
453    while ((tok = strtok(NULL, "\n")) != NULL) {
454      key = tok;
455      /* find the entry */
456      for (entry = key; *entry && !isspace((unsigned char)*entry); entry++);
457      if (*entry) {
458	*entry++ = '\0';
459      }
460
461      mapc_add_kv(m, xstrdup(key), xstrdup(entry));
462    }
463
464    XFREE(val);
465    return;
466  }
467
468#ifdef HAVE_REGEXEC
469  if (MAPC_ISRE(m)) {
470    char pattern[MAXPATHLEN];
471    int retval;
472
473    /*
474     * Make sure the string is bound to the start and end
475     */
476    xsnprintf(pattern, sizeof(pattern), "^%s$", key);
477    retval = regcomp(&re, pattern, REG_ICASE);
478    if (retval != 0) {
479      char errstr[256];
480
481      /* XXX: this code was recently ported, and must be tested -Erez */
482      errstr[0] = '\0';
483      regerror(retval, &re, errstr, 256);
484      plog(XLOG_USER, "error compiling RE \"%s\": %s", pattern, errstr);
485      return;
486    }
487  } else
488    memset(&re, 0, sizeof(re));
489#endif /* HAVE_REGEXEC */
490
491  h = &m->kvhash[hash];
492  n = ALLOC(struct kv);
493  n->key = key;
494#ifdef HAVE_REGEXEC
495  memcpy(&n->re, &re, sizeof(regex_t));
496#endif /* HAVE_REGEXEC */
497  n->val = val;
498  n->next = *h;
499  *h = n;
500  m->nentries++;
501}
502
503
504static void
505mapc_repl_kv(mnt_map *m, char *key, char *val)
506{
507  kv *k;
508
509  /*
510   * Compute the hash table offset
511   */
512  k = m->kvhash[kvhash_of(key)];
513
514  /*
515   * Scan the linked list for the key
516   */
517  while (k && !FSTREQ(k->key, key))
518    k = k->next;
519
520  if (k) {
521    XFREE(k->val);
522    k->val = val;
523  } else {
524    mapc_add_kv(m, key, val);
525  }
526}
527
528
529/*
530 * Search a map for a key.
531 * Calls map specific search routine.
532 * While map is out of date, keep re-syncing.
533 */
534static int
535search_map(mnt_map *m, char *key, char **valp)
536{
537  int rc;
538
539  do {
540    rc = (*m->search) (m, m->map_name, key, valp, &m->modify);
541    if (rc < 0) {
542      plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name);
543      mapc_sync(m);
544    }
545  } while (rc < 0);
546
547  return rc;
548}
549
550
551/*
552 * Do a wildcard lookup in the map and
553 * save the result.
554 */
555static void
556mapc_find_wildcard(mnt_map *m)
557{
558  /*
559   * Attempt to find the wildcard entry
560   */
561  int rc = search_map(m, wildcard, &m->wildcard);
562
563  if (rc != 0)
564    m->wildcard = NULL;
565}
566
567
568/*
569 * Do a map reload.
570 * Attempt to reload without losing current data by switching the hashes
571 * round.
572 * If reloading was needed and succeeded, return 1; else return 0.
573 */
574static int
575mapc_reload_map(mnt_map *m)
576{
577  int error, ret = 0;
578  kv *maphash[NKVHASH];
579  time_t t;
580
581  error = (*m->mtime) (m, m->map_name, &t);
582  if (error) {
583    t = m->modify;
584  }
585
586  /*
587   * skip reloading maps that have not been modified, unless
588   * amq -f was used (do_mapc_reload is 0)
589   */
590  if (m->reloads != 0 && do_mapc_reload != 0) {
591    if (t <= m->modify) {
592      plog(XLOG_INFO, "reload of map %s is not needed (in sync)", m->map_name);
593      dlog("map %s last load time is %d, last modify time is %d",
594	   m->map_name, (int) m->modify, (int) t);
595      return ret;
596    }
597  }
598
599  /* copy the old hash and zero the map */
600  memcpy((voidp) maphash, (voidp) m->kvhash, sizeof(m->kvhash));
601  memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
602
603  dlog("calling map reload on %s", m->map_name);
604  m->nentries = 0;
605  error = (*m->reload) (m, m->map_name, mapc_add_kv);
606  if (error) {
607    if (m->reloads == 0)
608      plog(XLOG_FATAL, "first time load of map %s failed!", m->map_name);
609    else
610      plog(XLOG_ERROR, "reload of map %s failed - using old values",
611	   m->map_name);
612    mapc_clear(m);
613    memcpy((voidp) m->kvhash, (voidp) maphash, sizeof(m->kvhash));
614  } else {
615    if (m->reloads++ == 0)
616      plog(XLOG_INFO, "first time load of map %s succeeded", m->map_name);
617    else
618      plog(XLOG_INFO, "reload #%d of map %s succeeded",
619	   m->reloads, m->map_name);
620    mapc_clear_kvhash(maphash);
621    if (m->wildcard) {
622       XFREE(m->wildcard);
623       m->wildcard = NULL;
624    }
625    m->modify = t;
626    ret = 1;
627  }
628
629  dlog("calling mapc_search for wildcard");
630  error = mapc_search(m, wildcard, &m->wildcard);
631  if (error)
632    m->wildcard = NULL;
633  return ret;
634}
635
636
637/*
638 * Create a new map
639 */
640static mnt_map *
641mapc_create(char *map, char *opt, const char *type, const char *mntpt)
642{
643  mnt_map *m = ALLOC(struct mnt_map);
644  map_type *mt;
645  time_t modify = 0;
646  u_int alloc = 0;
647
648  cmdoption(opt, mapc_opt, &alloc);
649
650  /*
651   * If using a configuration file, and the map_type is defined, then look
652   * for it, in the maptypes array.  If found, initialize the map using that
653   * map_type.  If not found, return error.  If no map_type was defined,
654   * default to cycling through all maptypes.
655   */
656  if (use_conf_file && type) {
657    /* find what type of map this one is */
658    for (mt = maptypes;
659	 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
660	 mt++) {
661      if (STREQ(type, mt->name)) {
662	plog(XLOG_INFO, "initializing amd.conf map %s of type %s", map, type);
663	if ((*mt->init) (m, map, &modify) == 0) {
664	  break;
665	} else {
666	  plog(XLOG_ERROR, "failed to initialize map %s", map);
667	  error_init(m, map, &modify);
668	  break;
669	}
670      }
671    } /* end of "for (mt =" loop */
672
673  } else {			/* cycle through all known maptypes */
674
675    /*
676     * not using amd conf file or using it by w/o specifying map type
677     */
678    for (mt = maptypes;
679	 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
680	 mt++) {
681      dlog("trying to initialize map %s of type %s ...", map, mt->name);
682      if ((*mt->init) (m, map, &modify) == 0) {
683	break;
684      }
685    }
686  } /* end of "if (use_conf_file && (colpos = strchr ..." statement */
687
688  /* assert: mt in maptypes */
689
690  m->flags = alloc & ~MAPC_CACHE_MASK;
691  m->nentries = 0;
692  alloc &= MAPC_CACHE_MASK;
693
694  if (alloc == MAPC_DFLT)
695    alloc = mt->def_alloc;
696
697  switch (alloc) {
698  default:
699    plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt);
700    alloc = MAPC_INC;
701    /* fall-through... */
702  case MAPC_NONE:
703  case MAPC_INC:
704  case MAPC_ROOT:
705    break;
706
707  case MAPC_ALL:
708    /*
709     * If there is no support for reload and it was requested
710     * then back off to incremental instead.
711     */
712    if (mt->reload == error_reload) {
713      plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name);
714      alloc = MAPC_INC;
715    }
716    break;
717
718#ifdef HAVE_REGEXEC
719  case MAPC_RE:
720    if (mt->reload == error_reload) {
721      plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name);
722      mt = &maptypes[sizeof(maptypes) / sizeof(maptypes[0]) - 1];
723      /* assert: mt->name == "error" */
724    }
725    break;
726#endif /* HAVE_REGEXEC */
727  }
728
729  dlog("Map for %s coming from maptype %s", map, mt->name);
730
731  m->alloc = alloc;
732  m->reload = mt->reload;
733  m->isup = mt->isup;
734  m->modify = modify;
735  m->search = alloc >= MAPC_ALL ? error_search : mt->search;
736  m->mtime = mt->mtime;
737  memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
738  m->map_name = xstrdup(map);
739  m->refc = 1;
740  m->wildcard = NULL;
741  m->reloads = 0;
742  /* initialize per-map information (flags, etc.) */
743  m->cfm = find_cf_map(mntpt);
744
745  /*
746   * synchronize cache with reality
747   */
748  mapc_sync(m);
749
750  return m;
751}
752
753
754/*
755 * Free the cached data in a map hash
756 */
757static void
758mapc_clear_kvhash(kv **kvhash)
759{
760  int i;
761
762  /*
763   * For each of the hash slots, chain
764   * along free'ing the data.
765   */
766  for (i = 0; i < NKVHASH; i++) {
767    kv *k = kvhash[i];
768    while (k) {
769      kv *n = k->next;
770      XFREE(k->key);
771      XFREE(k->val);
772      XFREE(k);
773      k = n;
774    }
775  }
776}
777
778
779/*
780 * Free the cached data in a map
781 */
782static void
783mapc_clear(mnt_map *m)
784{
785  mapc_clear_kvhash(m->kvhash);
786
787  /*
788   * Zero the hash slots
789   */
790  memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
791
792  /*
793   * Free the wildcard if it exists
794   */
795  XFREE(m->wildcard);
796  m->wildcard = NULL;
797
798  m->nentries = 0;
799}
800
801
802/*
803 * Find a map, or create one if it does not exist
804 */
805mnt_map *
806mapc_find(char *map, char *opt, const char *maptype, const char *mntpt)
807{
808  mnt_map *m;
809
810  /*
811   * Search the list of known maps to see if
812   * it has already been loaded.  If it is found
813   * then return a duplicate reference to it.
814   * Otherwise make a new map as required and
815   * add it to the list of maps
816   */
817  ITER(m, mnt_map, &map_list_head)
818    if (STREQ(m->map_name, map))
819      return mapc_dup(m);
820  m = mapc_create(map, opt, maptype, mntpt);
821  ins_que(&m->hdr, &map_list_head);
822
823  return m;
824}
825
826
827/*
828 * Free a map.
829 */
830void
831mapc_free(opaque_t arg)
832{
833  mnt_map *m = (mnt_map *) arg;
834
835  /*
836   * Decrement the reference count.
837   * If the reference count hits zero
838   * then throw the map away.
839   */
840  if (m && --m->refc == 0) {
841    mapc_clear(m);
842    XFREE(m->map_name);
843    rem_que(&m->hdr);
844    XFREE(m);
845  }
846}
847
848
849/*
850 * Search the map for the key.  Put a safe (malloc'ed) copy in *pval or
851 * return an error code
852 */
853static int
854mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse)
855{
856  int error = 0;
857  kv *k = NULL;
858
859  /*
860   * Firewall
861   */
862  if (!m) {
863    plog(XLOG_ERROR, "Null map request for %s", key);
864    return ENOENT;
865  }
866
867  if (m->flags & MAPC_SYNC) {
868    /*
869     * Get modify time...
870     */
871    time_t t;
872    error = (*m->mtime) (m, m->map_name, &t);
873    if (error || t > m->modify) {
874      plog(XLOG_INFO, "Map %s is out of date", m->map_name);
875      mapc_sync(m);
876    }
877  }
878
879  if (!MAPC_ISRE(m)) {
880    /*
881     * Compute the hash table offset
882     */
883    k = m->kvhash[kvhash_of(key)];
884
885    /*
886     * Scan the linked list for the key
887     */
888    while (k && !FSTREQ(k->key, key))
889      k = k->next;
890
891  }
892
893#ifdef HAVE_REGEXEC
894  else if (recurse == MREC_FULL) {
895    /*
896     * Try for an RE match against the entire map.
897     * Note that this will be done in a "random"
898     * order.
899     */
900    int i;
901
902    for (i = 0; i < NKVHASH; i++) {
903      k = m->kvhash[i];
904      while (k) {
905	int retval;
906
907	/* XXX: this code was recently ported, and must be tested -Erez */
908	retval = regexec(&k->re, key, 0, NULL, 0);
909	if (retval == 0) {	/* succeeded */
910	  break;
911	} else {		/* failed to match, log error */
912	  char errstr[256];
913
914	  errstr[0] = '\0';
915	  regerror(retval, &k->re, errstr, 256);
916	  plog(XLOG_USER, "error matching RE \"%s\" against \"%s\": %s",
917	       key, k->key, errstr);
918	}
919	k = k->next;
920      }
921      if (k)
922	break;
923    }
924  }
925#endif /* HAVE_REGEXEC */
926
927  /*
928   * If found then take a copy
929   */
930  if (k) {
931    if (k->val)
932      *pval = xstrdup(k->val);
933    else
934      error = ENOENT;
935  } else if (m->alloc >= MAPC_ALL) {
936    /*
937     * If the entire map is cached then this
938     * key does not exist.
939     */
940    error = ENOENT;
941  } else {
942    /*
943     * Otherwise search the map.  If we are
944     * in incremental mode then add the key
945     * to the cache.
946     */
947    error = search_map(m, key, pval);
948    if (!error && m->alloc == MAPC_INC)
949      mapc_add_kv(m, xstrdup(key), xstrdup(*pval));
950  }
951
952  /*
953   * If an error, and a wildcard exists,
954   * and the key is not internal then
955   * return a copy of the wildcard.
956   */
957  if (error > 0) {
958    if (recurse == MREC_FULL && !MAPC_ISRE(m)) {
959      char wildname[MAXPATHLEN];
960      char *subp;
961      if (*key == '/')
962	return error;
963      /*
964       * Keep chopping sub-directories from the RHS
965       * and replacing with "/ *" and repeat the lookup.
966       * For example:
967       * "src/gnu/gcc" -> "src / gnu / *" -> "src / *"
968       */
969      xstrlcpy(wildname, key, sizeof(wildname));
970      while (error && (subp = strrchr(wildname, '/'))) {
971	/*
972	 * sizeof space left in subp is sizeof wildname minus what's left
973	 * after the strchr above returned a pointer inside wildname into
974	 * subp.
975	 */
976	xstrlcpy(subp, "/*", sizeof(wildname) - (subp - wildname));
977	dlog("mapc recurses on %s", wildname);
978	error = mapc_meta_search(m, wildname, pval, MREC_PART);
979	if (error)
980	  *subp = '\0';
981      }
982
983      if (error > 0 && m->wildcard) {
984	*pval = xstrdup(m->wildcard);
985	error = 0;
986      }
987    }
988  }
989  return error;
990}
991
992
993int
994mapc_search(mnt_map *m, char *key, char **pval)
995{
996  return mapc_meta_search(m, key, pval, MREC_FULL);
997}
998
999
1000/*
1001 * Get map cache in sync with physical representation
1002 */
1003static void
1004mapc_sync(mnt_map *m)
1005{
1006  int need_mtime_update = 0;
1007
1008  if (m->alloc == MAPC_ROOT)
1009    return;			/* nothing to do */
1010
1011  /* do not clear map if map service is down */
1012  if (m->isup) {
1013    if (!((*m->isup)(m, m->map_name))) {
1014      plog(XLOG_ERROR, "mapc_sync: map %s is down: not clearing map", m->map_name);
1015      return;
1016    }
1017  }
1018
1019  if (m->alloc >= MAPC_ALL) {
1020    /* mapc_reload_map() always works */
1021    need_mtime_update = mapc_reload_map(m);
1022  } else {
1023    mapc_clear(m);
1024    /*
1025     * Attempt to find the wildcard entry
1026     */
1027    mapc_find_wildcard(m);
1028    need_mtime_update = 1;	/* because mapc_clear always works */
1029  }
1030
1031  /*
1032   * To be safe, update the mtime of the mnt_map's own node, so that the
1033   * kernel will flush all of its cached entries.
1034   */
1035  if (need_mtime_update && m->cfm) {
1036    am_node *mp = find_ap(m->cfm->cfm_dir);
1037    if (mp) {
1038      clocktime(&mp->am_fattr.na_mtime);
1039    } else {
1040      plog(XLOG_ERROR, "cannot find map %s to update its mtime",
1041	   m->cfm->cfm_dir);
1042    }
1043  }
1044}
1045
1046
1047/*
1048 * Reload all the maps
1049 * Called when Amd gets hit by a SIGHUP.
1050 */
1051void
1052mapc_reload(void)
1053{
1054  mnt_map *m;
1055
1056  /*
1057   * For all the maps,
1058   * Throw away the existing information.
1059   * Do a reload
1060   * Find the wildcard
1061   */
1062  ITER(m, mnt_map, &map_list_head)
1063    mapc_sync(m);
1064}
1065
1066
1067/*
1068 * Root map.
1069 * The root map is used to bootstrap amd.
1070 * All the require top-level mounts are added
1071 * into the root map and then the map is iterated
1072 * and a lookup is done on all the mount points.
1073 * This causes the top level mounts to be automounted.
1074 */
1075static int
1076root_init(mnt_map *m, char *map, time_t *tp)
1077{
1078  *tp = clocktime(NULL);
1079  return STREQ(map, ROOT_MAP) ? 0 : ENOENT;
1080}
1081
1082
1083/*
1084 * Add a new entry to the root map
1085 *
1086 * dir - directory (key)
1087 * opts - mount options
1088 * map - map name
1089 * cfm - optional amd configuration file map section structure
1090 */
1091void
1092root_newmap(const char *dir, const char *opts, const char *map, const cf_map_t *cfm)
1093{
1094  char str[MAXPATHLEN];
1095
1096  /*
1097   * First make sure we have a root map to talk about...
1098   */
1099  if (!root_map)
1100    root_map = mapc_find(ROOT_MAP, "mapdefault", NULL, NULL);
1101
1102  /*
1103   * Then add the entry...
1104   */
1105
1106  /*
1107   * Here I plug in the code to process other amd.conf options like
1108   * map_type, search_path, and flags (browsable_dirs, mount_type).
1109   */
1110
1111  if (cfm) {
1112    if (map) {
1113      xsnprintf(str, sizeof(str),
1114		"cache:=mapdefault;type:=toplvl;mount_type:=%s;fs:=\"%s\"",
1115		cfm->cfm_flags & CFM_MOUNT_TYPE_AUTOFS ? "autofs" : "nfs",
1116		get_full_path(map, cfm->cfm_search_path, cfm->cfm_type));
1117      if (opts && opts[0] != '\0') {
1118	xstrlcat(str, ";", sizeof(str));
1119	xstrlcat(str, opts, sizeof(str));
1120      }
1121      if (cfm->cfm_flags & CFM_BROWSABLE_DIRS_FULL)
1122	xstrlcat(str, ";opts:=rw,fullybrowsable", sizeof(str));
1123      if (cfm->cfm_flags & CFM_BROWSABLE_DIRS)
1124	xstrlcat(str, ";opts:=rw,browsable", sizeof(str));
1125      if (cfm->cfm_type) {
1126	xstrlcat(str, ";maptype:=", sizeof(str));
1127	xstrlcat(str, cfm->cfm_type, sizeof(str));
1128      }
1129    } else {
1130      xstrlcpy(str, opts, sizeof(str));
1131    }
1132  } else {
1133    if (map)
1134      xsnprintf(str, sizeof(str),
1135		"cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
1136		map, opts ? opts : "");
1137    else
1138      xstrlcpy(str, opts, sizeof(str));
1139  }
1140  mapc_repl_kv(root_map, xstrdup(dir), xstrdup(str));
1141}
1142
1143
1144int
1145mapc_keyiter(mnt_map *m, key_fun *fn, opaque_t arg)
1146{
1147  int i;
1148  int c = 0;
1149
1150  for (i = 0; i < NKVHASH; i++) {
1151    kv *k = m->kvhash[i];
1152    while (k) {
1153      (*fn) (k->key, arg);
1154      k = k->next;
1155      c++;
1156    }
1157  }
1158
1159  return c;
1160}
1161
1162
1163/*
1164 * Iterate on the root map and call (*fn)() on the key of all the nodes.
1165 * Returns the number of entries in the root map.
1166 */
1167int
1168root_keyiter(key_fun *fn, opaque_t arg)
1169{
1170  if (root_map) {
1171    int c = mapc_keyiter(root_map, fn, arg);
1172    return c;
1173  }
1174
1175  return 0;
1176}
1177
1178
1179/*
1180 * Error map
1181 */
1182static int
1183error_init(mnt_map *m, char *map, time_t *tp)
1184{
1185  plog(XLOG_USER, "No source data for map %s", map);
1186  *tp = 0;
1187
1188  return 0;
1189}
1190
1191
1192static int
1193error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
1194{
1195  return ENOENT;
1196}
1197
1198
1199static int
1200error_reload(mnt_map *m, char *map, add_fn *fn)
1201{
1202  return ENOENT;
1203}
1204
1205
1206static int
1207error_mtime(mnt_map *m, char *map, time_t *tp)
1208{
1209  *tp = 0;
1210
1211  return 0;
1212}
1213
1214
1215/*
1216 * Return absolute path of map, searched in a type-specific path.
1217 * Note: uses a static buffer for returned data.
1218 */
1219static const char *
1220get_full_path(const char *map, const char *path, const char *type)
1221{
1222  char component[MAXPATHLEN], *str;
1223  static char full_path[MAXPATHLEN];
1224  int len;
1225
1226  /* for now, only file-type search paths are implemented */
1227  if (type && !STREQ(type, "file"))
1228    return map;
1229
1230  /* if null map, return it */
1231  if (!map)
1232    return map;
1233
1234  /* if map includes a '/', return it (absolute or relative path) */
1235  if (strchr(map, '/'))
1236    return map;
1237
1238  /* if path is empty, return map */
1239  if (!path)
1240    return map;
1241
1242  /* now break path into components, and search in each */
1243  xstrlcpy(component, path, sizeof(component));
1244
1245  str = strtok(component, ":");
1246  do {
1247    xstrlcpy(full_path, str, sizeof(full_path));
1248    len = strlen(full_path);
1249    if (full_path[len - 1] != '/') /* add trailing "/" if needed */
1250      xstrlcat(full_path, "/", sizeof(full_path));
1251    xstrlcat(full_path, map, sizeof(full_path));
1252    if (access(full_path, R_OK) == 0)
1253      return full_path;
1254    str = strtok(NULL, ":");
1255  } while (str);
1256
1257  return map;			/* if found nothing, return map */
1258}
1259