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