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