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