2 * Copyright (c) 1997-1998 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.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
41 * $Id: mapc.c,v 1.1.1.1 1998/11/05 02:04:48 ezk Exp $
51 #endif /* HAVE_CONFIG_H */
56 * Make a duplicate reference to an existing map
58 #define mapc_dup(m) ((m)->refc++, (m))
62 * default, none, incremental, all, regexp
63 * MAPC_RE implies MAPC_ALL and must be numerically
66 #define MAPC_DFLT 0x000
67 #define MAPC_NONE 0x001
68 #define MAPC_INC 0x002
69 #define MAPC_ROOT 0x004
70 #define MAPC_ALL 0x010
71 #define MAPC_CACHE_MASK 0x0ff
72 #define MAPC_SYNC 0x100
75 # define MAPC_RE 0x020
76 # define MAPC_ISRE(m) ((m)->alloc == MAPC_RE)
77 #else /* not HAVE_REGEXEC */
78 # define MAPC_ISRE(m) FALSE
79 #endif /* not HAVE_REGEXEC */
88 #define MAX_CHAIN 2048
90 static struct opt_tab mapc_opt[] =
93 {"default", MAPC_DFLT},
95 {"mapdefault", MAPC_DFLT},
100 #endif /* HAVE_REGEXEC */
108 static char wildcard[] = "*";
113 typedef struct map_type map_type;
115 char *name; /* Name of this map type */
116 init_fn *init; /* Initialization */
117 reload_fn *reload; /* Reload or fill */
118 isup_fn *isup; /* Is service up or not? (1=up, 0=down) */
119 search_fn *search; /* Search for new entry */
120 mtime_fn *mtime; /* Find modify time */
121 int def_alloc; /* Default allocation mode */
127 static mnt_map *root_map;
132 qelem map_list_head = {&map_list_head, &map_list_head};
138 /* forward definitions */
139 static const char *get_full_path(const char *map, const char *path, const char *type);
140 static int mapc_meta_search(mnt_map *, char *, char **, int);
141 static void mapc_sync(mnt_map *);
144 static int root_init(mnt_map *, char *, time_t *);
147 static int error_init(mnt_map *, char *, time_t *);
148 static int error_reload(mnt_map *, char *, add_fn *);
149 static int error_search(mnt_map *, char *, char *, char **, time_t *);
150 static int error_mtime(mnt_map *, char *, time_t *);
153 #ifdef HAVE_MAP_PASSWD
154 extern int passwd_init(mnt_map *, char *, time_t *);
155 extern int passwd_search(mnt_map *, char *, char *, char **, time_t *);
156 #endif /* HAVE_MAP_PASSWD */
159 #ifdef HAVE_MAP_HESIOD
160 extern int amu_hesiod_init(mnt_map *, char *map, time_t *tp);
161 extern int hesiod_search(mnt_map *, char *, char *, char **, time_t *);
162 #endif /* HAVE_MAP_HESIOD */
166 extern int amu_ldap_init(mnt_map *, char *map, time_t *tp);
167 extern int amu_ldap_search(mnt_map *, char *, char *, char **, time_t *);
168 extern int amu_ldap_mtime(mnt_map *, char *, time_t *);
169 #endif /* HAVE_MAP_LDAP */
172 #ifdef HAVE_MAP_UNION
173 extern int union_init(mnt_map *, char *, time_t *);
174 extern int union_search(mnt_map *, char *, char *, char **, time_t *);
175 extern int union_reload(mnt_map *, char *, add_fn *);
176 #endif /* HAVE_MAP_UNION */
178 /* Network Information Service PLUS (NIS+) */
179 #ifdef HAVE_MAP_NISPLUS
180 extern int nisplus_init(mnt_map *, char *, time_t *);
181 extern int nisplus_reload(mnt_map *, char *, add_fn *);
182 extern int nisplus_search(mnt_map *, char *, char *, char **, time_t *);
183 extern int nisplus_mtime(mnt_map *, char *, time_t *);
184 #endif /* HAVE_MAP_NISPLUS */
186 /* Network Information Service (YP, Yellow Pages) */
188 extern int nis_init(mnt_map *, char *, time_t *);
189 extern int nis_reload(mnt_map *, char *, add_fn *);
190 extern int nis_isup(mnt_map *, char *);
191 extern int nis_search(mnt_map *, char *, char *, char **, time_t *);
192 extern int nis_mtime(mnt_map *, char *, time_t *);
193 #endif /* HAVE_MAP_NIS */
197 extern int ndbm_init(mnt_map *, char *, time_t *);
198 extern int ndbm_search(mnt_map *, char *, char *, char **, time_t *);
199 extern int ndbm_mtime(mnt_map *, char *, time_t *);
200 #endif /* HAVE_MAP_NDBM */
204 extern int file_init(mnt_map *, char *, time_t *);
205 extern int file_reload(mnt_map *, char *, add_fn *);
206 extern int file_search(mnt_map *, char *, char *, char **, time_t *);
207 extern int file_mtime(mnt_map *, char *, time_t *);
208 #endif /* HAVE_MAP_FILE */
211 /* note that the choice of MAPC_{INC,ALL} will affect browsable_dirs */
212 static map_type maptypes[] =
218 NULL, /* isup function */
223 #ifdef HAVE_MAP_PASSWD
228 NULL, /* isup function */
233 #endif /* HAVE_MAP_PASSWD */
234 #ifdef HAVE_MAP_HESIOD
239 NULL, /* isup function */
244 #endif /* HAVE_MAP_HESIOD */
250 NULL, /* isup function */
255 #endif /* HAVE_MAP_LDAP */
256 #ifdef HAVE_MAP_UNION
261 NULL, /* isup function */
266 #endif /* HAVE_MAP_UNION */
267 #ifdef HAVE_MAP_NISPLUS
272 NULL, /* isup function */
277 #endif /* HAVE_MAP_NISPLUS */
283 nis_isup, /* is NIS up or not? */
288 #endif /* HAVE_MAP_NIS */
294 NULL, /* isup function */
299 #endif /* HAVE_MAP_NDBM */
305 NULL, /* isup function */
310 #endif /* HAVE_MAP_FILE */
315 NULL, /* isup function */
331 for (i = 0; (j = *key++); i += j) ;
338 mapc_showtypes(char *buf)
344 for (mt = maptypes; mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); mt++) {
346 strcat(buf, mt->name);
353 * Check if a map of a certain type exists.
354 * Return 1 (true) if exists, 0 (false) if not.
357 mapc_type_exists(const char *type)
364 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
366 if (STREQ(type, mt->name))
369 return 0; /* not found anywhere */
374 * Add key and val to the map m.
375 * key and val are assumed to be safe copies
378 mapc_add_kv(mnt_map *m, char *key, char *val)
382 int hash = kvhash_of(key);
385 #endif /* HAVE_REGEXEC */
388 dlog("add_kv: %s -> %s", key, val);
393 char pattern[MAXPATHLEN];
397 * Make sure the string is bound to the start and end
399 sprintf(pattern, "^%s$", key);
400 retval = regcomp(&re, pattern, REG_ICASE);
404 /* XXX: this code was recently ported, and must be tested -Erez */
406 regerror(retval, &re, errstr, 256);
407 plog(XLOG_USER, "error compiling RE \"%s\": %s", pattern, errstr);
411 #endif /* HAVE_REGEXEC */
413 h = &m->kvhash[hash];
414 n = ALLOC(struct kv);
417 memcpy(&n->re, &re, sizeof(regex_t));
418 #endif /* HAVE_REGEXEC */
426 mapc_repl_kv(mnt_map *m, char *key, char *val)
431 * Compute the hash table offset
433 k = m->kvhash[kvhash_of(key)];
436 * Scan the linked list for the key
438 while (k && !FSTREQ(k->key, key))
445 mapc_add_kv(m, key, val);
451 * Search a map for a key.
452 * Calls map specific search routine.
453 * While map is out of date, keep re-syncing.
456 search_map(mnt_map *m, char *key, char **valp)
461 rc = (*m->search) (m, m->map_name, key, valp, &m->modify);
463 plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name);
473 * Do a wildcard lookup in the map and
477 mapc_find_wildcard(mnt_map *m)
480 * Attempt to find the wildcard entry
482 int rc = search_map(m, wildcard, &m->wildcard);
493 mapc_reload_map(mnt_map *m)
498 dlog("calling map reload on %s", m->map_name);
500 error = (*m->reload) (m, m->map_name, mapc_add_kv);
506 dlog("calling mapc_search for wildcard");
508 error = mapc_search(m, wildcard, &m->wildcard);
520 mapc_create(char *map, char *opt, const char *type)
522 mnt_map *m = ALLOC(struct mnt_map);
527 cmdoption(opt, mapc_opt, &alloc);
530 * If using a configuration file, and the map_type is defined, then look
531 * for it, in the maptypes array. If found, initialize the map using that
532 * map_type. If not found, return error. If no map_type was defined,
533 * default to cycling through all maptypes.
535 if (use_conf_file && type) {
536 /* find what type of map this one is */
538 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
540 if (STREQ(type, mt->name)) {
541 plog(XLOG_INFO, "initializing amd conf map %s of type %s", map, type);
542 if ((*mt->init) (m, map, &modify) == 0) {
545 plog(XLOG_ERROR, "failed to initialize map %s", map);
546 error_init(m, map, &modify);
550 } /* end of "for (mt =" loop */
552 } else { /* cycle through all known maptypes */
555 * not using amd conf file or using it by w/o specifying map type
558 mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
561 dlog("trying to initialize map %s of type %s ...", map, mt->name);
563 if ((*mt->init) (m, map, &modify) == 0) {
567 } /* end of "if (use_conf_file && (colpos = strchr ..." statement */
569 /* assert: mt in maptypes */
571 m->flags = alloc & ~MAPC_CACHE_MASK;
572 alloc &= MAPC_CACHE_MASK;
574 if (alloc == MAPC_DFLT)
575 alloc = mt->def_alloc;
579 plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt);
581 /* fall-through... */
589 * If there is no support for reload and it was requested
590 * then back off to incremental instead.
592 if (mt->reload == error_reload) {
593 plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name);
600 if (mt->reload == error_reload) {
601 plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name);
602 mt = &maptypes[sizeof(maptypes) / sizeof(maptypes[0]) - 1];
603 /* assert: mt->name == "error" */
606 #endif /* HAVE_REGEXEC */
610 dlog("Map for %s coming from maptype %s", map, mt->name);
614 m->reload = mt->reload;
617 m->search = alloc >= MAPC_ALL ? error_search : mt->search;
618 m->mtime = mt->mtime;
619 memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
620 m->map_name = strdup(map);
625 * synchronize cache with reality
634 * Free the cached data in a map
637 mapc_clear(mnt_map *m)
642 * For each of the hash slots, chain
643 * along free'ing the data.
645 for (i = 0; i < NKVHASH; i++) {
646 kv *k = m->kvhash[i];
658 * Zero the hash slots
660 memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
663 * Free the wildcard if it exists
673 * Find a map, or create one if it does not exist
676 mapc_find(char *map, char *opt, const char *maptype)
681 * Search the list of known maps to see if
682 * it has already been loaded. If it is found
683 * then return a duplicate reference to it.
684 * Otherwise make a new map as required and
685 * add it to the list of maps
687 ITER(m, mnt_map, &map_list_head)
688 if (STREQ(m->map_name, map))
690 m = mapc_create(map, opt, maptype);
691 ins_que(&m->hdr, &map_list_head);
706 * Decrement the reference count.
707 * If the reference count hits zero
708 * then throw the map away.
710 if (m && --m->refc == 0) {
720 * Search the map for the key. Put a safe (malloc'ed) copy in *pval or
721 * return an error code
724 mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse)
733 plog(XLOG_ERROR, "Null map request for %s", key);
736 if (m->flags & MAPC_SYNC) {
741 error = (*m->mtime) (m, m->map_name, &t);
742 if (error || t > m->modify) {
744 plog(XLOG_INFO, "Map %s is out of date", m->map_name);
751 * Compute the hash table offset
753 k = m->kvhash[kvhash_of(key)];
756 * Scan the linked list for the key
758 while (k && !FSTREQ(k->key, key))
764 else if (recurse == MREC_FULL) {
766 * Try for an RE match against the entire map.
767 * Note that this will be done in a "random"
772 for (i = 0; i < NKVHASH; i++) {
777 /* XXX: this code was recently ported, and must be tested -Erez */
778 retval = regexec(&k->re, key, 0, 0, 0);
779 if (retval == 0) { /* succeeded */
781 } else { /* failed to match, log error */
785 regerror(retval, &k->re, errstr, 256);
786 plog(XLOG_USER, "error matching RE \"%s\" against \"%s\": %s",
787 key, k->key, errstr);
795 #endif /* HAVE_REGEXEC */
798 * If found then take a copy
802 *pval = strdup(k->val);
805 } else if (m->alloc >= MAPC_ALL) {
807 * If the entire map is cached then this
808 * key does not exist.
813 * Otherwise search the map. If we are
814 * in incremental mode then add the key
817 error = search_map(m, key, pval);
818 if (!error && m->alloc == MAPC_INC)
819 mapc_add_kv(m, strdup(key), strdup(*pval));
823 * If an error, and a wildcard exists,
824 * and the key is not internal then
825 * return a copy of the wildcard.
828 if (recurse == MREC_FULL && !MAPC_ISRE(m)) {
829 char wildname[MAXPATHLEN];
834 * Keep chopping sub-directories from the RHS
835 * and replacing with "/ *" and repeat the lookup.
837 * "src/gnu/gcc" -> "src / gnu / *" -> "src / *"
839 strcpy(wildname, key);
840 while (error && (subp = strrchr(wildname, '/'))) {
843 dlog("mapc recurses on %s", wildname);
845 error = mapc_meta_search(m, wildname, pval, MREC_PART);
850 if (error > 0 && m->wildcard) {
851 *pval = strdup(m->wildcard);
861 mapc_search(mnt_map *m, char *key, char **pval)
863 return mapc_meta_search(m, key, pval, MREC_FULL);
868 * Get map cache in sync with physical representation
871 mapc_sync(mnt_map *m)
873 if (m->alloc != MAPC_ROOT) {
875 /* do not clear map if map service is down */
877 if (!((*m->isup)(m, m->map_name))) {
878 plog(XLOG_ERROR, "mapc_sync: map %s is down: not clearing map", m->map_name);
885 if (m->alloc >= MAPC_ALL)
886 if (mapc_reload_map(m))
889 * Attempt to find the wildcard entry
891 if (m->alloc < MAPC_ALL)
892 mapc_find_wildcard(m);
898 * Reload all the maps
899 * Called when Amd gets hit by a SIGHUP.
908 * Throw away the existing information.
912 ITER(m, mnt_map, &map_list_head)
919 * The root map is used to bootstrap amd.
920 * All the require top-level mounts are added
921 * into the root map and then the map is iterated
922 * and a lookup is done on all the mount points.
923 * This causes the top level mounts to be automounted.
926 root_init(mnt_map *m, char *map, time_t *tp)
929 return STREQ(map, ROOT_MAP) ? 0 : ENOENT;
934 * Add a new entry to the root map
936 * dir - directory (key)
937 * opts - mount options
939 * cfm - optional amd configuration file map section structure
942 root_newmap(const char *dir, const char *opts, const char *map, const cf_map_t *cfm)
944 char str[MAXPATHLEN];
947 * First make sure we have a root map to talk about...
950 root_map = mapc_find(ROOT_MAP, "mapdefault", NULL);
953 * Then add the entry...
957 * Here I plug in the code to process other amd.conf options like
958 * map_type, search_path, and flags (browsable_dirs, mount_type).
963 sprintf(str, "cache:=mapdefault;type:=%s;fs:=\"%s\"",
964 cfm->cfm_flags & CFM_MOUNT_TYPE_AUTOFS ? "autofs" : "toplvl",
965 get_full_path(map, cfm->cfm_search_path, cfm->cfm_type));
966 if (opts && opts[0] != '\0') {
970 if (cfm->cfm_flags & CFM_BROWSABLE_DIRS_FULL)
971 strcat(str, ";opts:=rw,fullybrowsable");
972 if (cfm->cfm_flags & CFM_BROWSABLE_DIRS)
973 strcat(str, ";opts:=rw,browsable");
975 strcat(str, ";maptype:=");
976 strcat(str, cfm->cfm_type);
983 sprintf(str, "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
984 map, opts ? opts : "");
988 mapc_repl_kv(root_map, strdup((char *)dir), strdup(str));
993 mapc_keyiter(mnt_map *m, void (*fn) (char *, voidp), voidp arg)
998 for (i = 0; i < NKVHASH; i++) {
999 kv *k = m->kvhash[i];
1001 (*fn) (k->key, arg);
1012 * Iterate on the root map and call (*fn)() on the key of all the nodes.
1013 * Finally throw away the root map.
1016 root_keyiter(void (*fn)(char *, voidp), voidp arg)
1019 int c = mapc_keyiter(root_map, fn, arg);
1028 * Was: NEW_TOPLVL_READDIR
1029 * Search a chain for an entry with some name.
1030 * -Erez Zadok <ezk@cs.columbia.edu>
1033 key_already_in_chain(char *keyname, const nfsentry *chain)
1035 const nfsentry *tmpchain = chain;
1038 if (keyname && tmpchain->ne_name && STREQ(keyname, tmpchain->ne_name))
1040 tmpchain = tmpchain->ne_nextentry;
1048 * Create a chain of entries which are not linked.
1049 * -Erez Zadok <ezk@cs.columbia.edu>
1052 make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable)
1054 static u_int last_cookie = ~(u_int) 0 - 1;
1055 static nfsentry chain[MAX_CHAIN];
1056 static int max_entries = MAX_CHAIN;
1058 int num_entries = 0, preflen = 0, i;
1059 nfsentry *retval = (nfsentry *) NULL;
1064 plog(XLOG_DEBUG, "make_entry_chain: mp is (NULL)");
1069 plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt is (NULL)");
1072 mmp = (mnt_map *) mf->mf_private;
1074 plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt->mf_private is (NULL)");
1078 /* iterate over keys */
1079 for (i = 0; i < NKVHASH; i++) {
1081 for (k = mmp->kvhash[i]; k ; k = k->next) {
1084 * Skip unwanted entries which are either not real entries or
1085 * very difficult to interpret (wildcards...) This test needs
1086 * lots of improvement. Any takers?
1093 if (!fully_browsable && strchr(key, '*'))
1097 * If the map has a prefix-string then check if the key starts with
1098 * this * string, and if it does, skip over this prefix.
1101 if (!NSTREQ(key, mp->am_pref, preflen))
1106 /* no more '/' are allowed, unless browsable_dirs=full was used */
1107 if (!fully_browsable && strchr(key, '/'))
1110 /* no duplicates allowed */
1111 if (key_already_in_chain(key, current_chain))
1114 /* fill in a cell and link the entry */
1115 if (num_entries >= max_entries) {
1117 plog(XLOG_DEBUG, "make_entry_chain: no more space in chain");
1118 if (num_entries > 0) {
1119 chain[num_entries - 1].ne_nextentry = 0;
1125 /* we have space. put entry in next cell */
1127 chain[num_entries].ne_fileid = (u_int) last_cookie;
1128 *(u_int *) chain[num_entries].ne_cookie =
1129 (u_int) last_cookie;
1130 chain[num_entries].ne_name = key;
1131 if (num_entries < max_entries - 1) { /* link to next one */
1132 chain[num_entries].ne_nextentry = &chain[num_entries + 1];
1135 } /* end of "while (k)" */
1136 } /* end of "for (i ... NKVHASH ..." */
1138 /* terminate chain */
1139 if (num_entries > 0) {
1140 chain[num_entries - 1].ne_nextentry = 0;
1152 error_init(mnt_map *m, char *map, time_t *tp)
1154 plog(XLOG_USER, "No source data for map %s", map);
1162 error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
1169 error_reload(mnt_map *m, char *map, add_fn *fn)
1176 error_mtime(mnt_map *m, char *map, time_t *tp)
1185 * Return absolute path of map, searched in a type-specific path.
1186 * Note: uses a static buffer for returned data.
1189 get_full_path(const char *map, const char *path, const char *type)
1191 char component[MAXPATHLEN], *str;
1192 static char full_path[MAXPATHLEN];
1195 /* for now, only file-type search paths are implemented */
1196 if (type && !STREQ(type, "file"))
1199 /* if null map, return it */
1203 /* if map includes a '/', return it (absolute or relative path) */
1204 if (strchr(map, '/'))
1207 /* if path is empty, return map */
1211 /* now break path into components, and search in each */
1212 strcpy(component, path);
1214 str = strtok(component, ":");
1216 strcpy(full_path, str);
1217 len = strlen(full_path);
1218 if (full_path[len - 1] != '/') /* add trailing "/" if needed */
1219 strcat(full_path, "/");
1220 strcat(full_path, map);
1221 if (access(full_path, R_OK) == 0)
1223 str = strtok(NULL, ":");
1226 return map; /* if found nothing, return map */