autofs-5.0.6 - add hup signal handling to hosts map
From: Ian Kent <ikent@redhat.com>
Add HUP signal handling to the internal hosts lookup module.
---
CHANGELOG | 1
daemon/direct.c | 4 -
include/mounts.h | 1
lib/mounts.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++-
man/auto.master.5.in | 5 +
man/autofs.5 | 8 +-
modules/lookup_hosts.c | 97 ++++++++++++++++++++++++-----
modules/parse_sun.c | 9 ++
8 files changed, 262 insertions(+), 25 deletions(-)
--- autofs-5.0.6.orig/CHANGELOG
+++ autofs-5.0.6/CHANGELOG
@@ -57,6 +57,7 @@
- fix offset mount point directory removal.
- fix remount of multi mount.
- fix devce ioctl alloc path check.
+- add hup signal handling to hosts map.
28/06/2011 autofs-5.0.6
-----------------------
--- autofs-5.0.6.orig/daemon/direct.c
+++ autofs-5.0.6/daemon/direct.c
@@ -654,7 +654,9 @@ int mount_autofs_offset(struct autofs_po
ret = try_remount(ap, me, t_offset);
if (ret == 1)
return MOUNT_OFFSET_OK;
- return MOUNT_OFFSET_FAIL;
+ /* Offset mount not found, fall thru and try to mount it */
+ if (!(ret == -1 && errno == ENOENT))
+ return MOUNT_OFFSET_FAIL;
} else {
/*
if (is_mounted(_PROC_MOUNTS, me->key, MNTS_AUTOFS)) {
--- autofs-5.0.6.orig/include/mounts.h
+++ autofs-5.0.6/include/mounts.h
@@ -112,5 +112,6 @@ int try_remount(struct autofs_point *, s
int umount_ent(struct autofs_point *, const char *);
int mount_multi_triggers(struct autofs_point *, struct mapent *, const char *, unsigned int, const char *);
int umount_multi_triggers(struct autofs_point *, struct mapent *, char *, const char *);
+int clean_stale_multi_triggers(struct autofs_point *, struct mapent *, char *, const char *);
#endif
--- autofs-5.0.6.orig/lib/mounts.c
+++ autofs-5.0.6/lib/mounts.c
@@ -1436,6 +1436,7 @@ static int remount_active_mount(struct a
/* Re-reading the map, set timeout and return */
if (ap->state == ST_READMAP) {
+ debug(ap->logopt, "already mounted, update timeout");
ops->timeout(ap->logopt, fd, timeout);
ops->close(ap->logopt, fd);
return REMOUNT_READ_MAP;
@@ -1550,7 +1551,9 @@ int try_remount(struct autofs_point *ap,
* record that in the mount point struct. Otherwise we're
* re-reading the map.
*/
- if (ret == REMOUNT_SUCCESS || ret == REMOUNT_READ_MAP) {
+ if (ret == REMOUNT_READ_MAP)
+ return 1;
+ else if (ret == REMOUNT_SUCCESS) {
if (fd != -1) {
if (type == t_indirect)
ap->ioctlfd = fd;
@@ -1781,5 +1784,162 @@ int umount_multi_triggers(struct autofs_
}
return left;
+}
+
+int clean_stale_multi_triggers(struct autofs_point *ap,
+ struct mapent *me, char *top, const char *base)
+{
+ char *root;
+ char mm_top[PATH_MAX + 1];
+ char path[PATH_MAX + 1];
+ char buf[MAX_ERR_BUF];
+ char *offset;
+ struct mapent *oe;
+ struct list_head *mm_root, *pos;
+ const char o_root[] = "/";
+ const char *mm_base;
+ int left, start;
+ time_t age;
+
+ if (top)
+ root = top;
+ else {
+ if (!strchr(me->multi->key, '/'))
+ /* Indirect multi-mount root */
+ /* sprintf okay - if it's mounted, it's
+ * PATH_MAX or less bytes */
+ sprintf(mm_top, "%s/%s", ap->path, me->multi->key);
+ else
+ strcpy(mm_top, me->multi->key);
+ root = mm_top;
+ }
+
+ left = 0;
+ start = strlen(root);
+
+ mm_root = &me->multi->multi_list;
+
+ if (!base)
+ mm_base = o_root;
+ else
+ mm_base = base;
+
+ pos = NULL;
+ offset = path;
+ age = me->multi->age;
+
+ while ((offset = cache_get_offset(mm_base, offset, start, mm_root, &pos))) {
+ char *oe_base;
+ char *key;
+ int ret;
+
+ oe = cache_lookup_offset(mm_base, offset, start, &me->multi_list);
+ /* root offset is a special case */
+ if (!oe || !oe->mapent || (strlen(oe->key) - start) == 1)
+ continue;
+
+ /* Check for and umount stale subtree offsets */
+ oe_base = oe->key + strlen(root);
+ ret = clean_stale_multi_triggers(ap, oe, root, oe_base);
+ left =+ ret;
+ if (ret)
+ continue;
+
+ if (oe->age == age)
+ continue;
+
+ /*
+ * If an offset that has an active mount has been removed
+ * from the multi-mount we don't want to attempt to trigger
+ * mounts for it. Obviously this is because it has been
+ * removed, but less obvious is the potential strange
+ * behaviour that can result if we do try and mount it
+ * again after it's been expired. For example, if an NFS
+ * file system is no longer exported and is later umounted
+ * it can be mounted again without any error message but
+ * shows as an empty directory. That's going to confuse
+ * people for sure.
+ *
+ * If the mount cannot be umounted (the process is now
+ * using a stale mount) the offset needs to be invalidated
+ * so no further mounts will be attempted but the offset
+ * cache entry must remain so expires can continue to
+ * attempt to umount it. If the mount can be umounted and
+ * the offset is removed, at least for NFS we will get
+ * ESTALE errors when attempting list the directory.
+ */
+ if (oe->ioctlfd != -1 ||
+ is_mounted(_PROC_MOUNTS, oe->key, MNTS_REAL)) {
+ if (umount_ent(ap, oe->key)) {
+ debug(ap->logopt,
+ "offset %s has active mount, invalidate",
+ oe->key);
+ if (oe->mapent) {
+ free(oe->mapent);
+ oe->mapent = NULL;
+ }
+ left++;
+ continue;
+ }
+ }
+
+ key = strdup(oe->key);
+ if (!key) {
+ char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+ error(ap->logopt, "malloc: %s", estr);
+ left++;
+ continue;
+ }
+
+ debug(ap->logopt, "umount offset %s", oe->key);
+
+ if (umount_autofs_offset(ap, oe)) {
+ warn(ap->logopt, "failed to umount offset %s", key);
+ left++;
+ } else {
+ struct stat st;
+
+ /* Mount point not ours to delete ? */
+ if (!(oe->flags & MOUNT_FLAG_DIR_CREATED)) {
+ debug(ap->logopt, "delete offset key %s", key);
+ if (cache_delete_offset(oe->mc, key) == CHE_FAIL)
+ error(ap->logopt,
+ "failed to delete offset key %s", key);
+ free(key);
+ continue;
+ }
+
+ /*
+ * An error due to partial directory removal is
+ * ok so only try and remount the offset if the
+ * actual mount point still exists.
+ */
+ ret = rmdir_path(ap, oe->key, ap->dev);
+ if (ret == -1 && !stat(oe->key, &st)) {
+ ret = do_mount_autofs_offset(ap, oe, root, offset);
+ if (ret) {
+ left++;
+ free(key);
+ continue;
+ }
+ /*
+ * Fall through if the trigger can't be mounted
+ * again, since there is no offset there can't
+ * be any mount requests so remove the map
+ * entry from the cache. There's now a dead
+ * offset mount, but what else can we do ....
+ */
+ }
+
+ debug(ap->logopt, "delete offset key %s", key);
+
+ if (cache_delete_offset(oe->mc, key) == CHE_FAIL)
+ error(ap->logopt,
+ "failed to delete offset key %s", key);
+ }
+ free(key);
+ }
+
+ return left;
}
--- autofs-5.0.6.orig/man/auto.master.5.in
+++ autofs-5.0.6/man/auto.master.5.in
@@ -220,7 +220,10 @@ set default log level "none", "verbose"
.SH BUILTIN MAP -hosts
If "-hosts" is given as the map then accessing a key under the mount point
which corresponds to a hostname will allow access to the exports of that
-host.
+host. The hosts map cannot be dynamically updated and requires a HUP signal
+to be sent to the daemon for it to check hosts for an update. Due to possible
+hierarchic dependencies within a mount tree, it might not be completely
+updated during the HUP signal processing.
.P
For example, with an entry in the master map of
.nh
--- autofs-5.0.6.orig/man/autofs.5
+++ autofs-5.0.6/man/autofs.5
@@ -13,10 +13,10 @@ These maps describe how file systems bel
map format; if another map format is specified (e.g. \fBhesiod\fP),
this documentation does not apply.
-Indirect maps can be changed on the fly and the automouter will recognize
-those changes on the next operation it performs on that map. Direct maps
-require a HUP signal be sent to the daemon to refresh their contents as does
-the master map.
+Indirect maps, except for the internal hosts map, can be changed on the fly
+and the automouter will recognize those changes on the next operation it
+performs on that map. Direct maps require a HUP signal be sent to the
+daemon to refresh their contents as does the master map.
.SH "FORMAT"
This is a description of the text file format. Other methods of specifying
these files may exist. All empty lines or lines beginning with # are
--- autofs-5.0.6.orig/modules/lookup_hosts.c
+++ autofs-5.0.6/modules/lookup_hosts.c
@@ -80,10 +80,11 @@ int lookup_read_master(struct master *ma
static char *get_exports(struct autofs_point *ap, const char *host)
{
- char *mapent = NULL;
+ char buf[MAX_ERR_BUF];
+ char *mapent;
exports exp;
- debug(ap->logopt, MODPREFIX "fetchng export list for %s", name);
+ debug(ap->logopt, MODPREFIX "fetchng export list for %s", host);
exp = rpc_get_exports(host, 10, 0, RPC_CLOSE_NOLINGER);
@@ -92,20 +93,20 @@ static char *get_exports(struct autofs_p
if (mapent) {
int len = strlen(mapent) + 1;
- len += strlen(name) + 2*(strlen(exp->ex_dir) + 2) + 3;
+ len += strlen(host) + 2*(strlen(exp->ex_dir) + 2) + 3;
mapent = realloc(mapent, len);
if (!mapent) {
char *estr;
estr = strerror_r(errno, buf, MAX_ERR_BUF);
error(ap->logopt, MODPREFIX "malloc: %s", estr);
rpc_exports_free(exp);
- return NSS_STATUS_UNAVAIL;
+ return NULL;
}
strcat(mapent, " \"");
strcat(mapent, exp->ex_dir);
strcat(mapent, "\"");
} else {
- int len = 2*(strlen(exp->ex_dir) + 2) + strlen(name) + 3;
+ int len = 2*(strlen(exp->ex_dir) + 2) + strlen(host) + 3;
mapent = malloc(len);
if (!mapent) {
@@ -113,14 +114,14 @@ static char *get_exports(struct autofs_p
estr = strerror_r(errno, buf, MAX_ERR_BUF);
error(ap->logopt, MODPREFIX "malloc: %s", estr);
rpc_exports_free(exp);
- return NSS_STATUS_UNAVAIL;
+ return NULL;
}
strcpy(mapent, "\"");
strcat(mapent, exp->ex_dir);
strcat(mapent, "\"");
}
strcat(mapent, " \"");
- strcat(mapent, name);
+ strcat(mapent, host);
strcat(mapent, ":");
strcat(mapent, exp->ex_dir);
strcat(mapent, "\"");
@@ -130,14 +131,14 @@ static char *get_exports(struct autofs_p
rpc_exports_free(exp);
if (!mapent)
- error(ap->logopt, "exports lookup failed for %s", name);
+ error(ap->logopt, MODPREFIX "exports lookup failed for %s", host);
return mapent;
}
-static int do_parse_mount(struct autofs_point *ap,
+static int do_parse_mount(struct autofs_point *ap, struct map_source *source,
const char *name, int name_len, char *mapent,
- void *context)
+ struct lookup_context *ctxt)
{
int ret;
@@ -166,8 +167,68 @@ static int do_parse_mount(struct autofs_
return NSS_STATUS_SUCCESS;
}
+static int update_hosts_mounts(struct autofs_point *ap,
+ struct map_source *source, time_t age,
+ struct lookup_context *ctxt)
+{
+ struct mapent_cache *mc;
+ struct mapent *me;
+ char *mapent;
+ int ret;
+
+ mc = source->mc;
+
+ pthread_cleanup_push(cache_lock_cleanup, mc);
+ cache_writelock(mc);
+ me = cache_lookup_first(mc);
+ while (me) {
+ /* Hosts map entry not yet expanded or already expired */
+ if (!me->multi)
+ goto next;
+
+ debug(ap->logopt, MODPREFIX "get list of exports for %s", me->key);
+
+ mapent = get_exports(ap, me->key);
+ if (mapent) {
+ cache_update(mc, source, me->key, mapent, age);
+ free(mapent);
+ }
+next:
+ me = cache_lookup_next(mc, me);
+ }
+ pthread_cleanup_pop(1);
+
+ pthread_cleanup_push(cache_lock_cleanup, mc);
+ cache_readlock(mc);
+ me = cache_lookup_first(mc);
+ while (me) {
+ /*
+ * Hosts map entry not yet expanded, already expired
+ * or not the base of the tree
+ */
+ if (!me->multi || me->multi != me)
+ goto cont;
+
+ debug(ap->logopt, MODPREFIX
+ "attempt to update exports for exports for %s", me->key);
+
+ master_source_current_wait(ap->entry);
+ ap->entry->current = source;
+ ap->flags |= MOUNT_FLAG_REMOUNT;
+ ret = ctxt->parse->parse_mount(ap, me->key, strlen(me->key),
+ me->mapent, ctxt->parse->context);
+ ap->flags &= ~MOUNT_FLAG_REMOUNT;
+cont:
+ me = cache_lookup_next(mc, me);
+ }
+ pthread_cleanup_pop(1);
+
+ return NSS_STATUS_SUCCESS;
+}
+
int lookup_read_map(struct autofs_point *ap, time_t age, void *context)
{
+ struct lookup_context *ctxt = (struct lookup_context *) context;
struct map_source *source;
struct mapent_cache *mc;
struct hostent *host;
@@ -177,18 +238,23 @@ int lookup_read_map(struct autofs_point
ap->entry->current = NULL;
master_source_current_signal(ap->entry);
+ mc = source->mc;
+
+ debug(ap->logopt, MODPREFIX "read hosts map");
+
/*
* If we don't need to create directories then there's no use
* reading the map. We always need to read the whole map for
* direct mounts in order to mount the triggers.
*/
if (!(ap->flags & MOUNT_FLAG_GHOST) && ap->type != LKP_DIRECT) {
- debug(ap->logopt, "map read not needed, so not done");
+ debug(ap->logopt, MODPREFIX
+ "map not browsable, update existing host entries only");
+ update_hosts_mounts(ap, source, age, ctxt);
+ source->age = age;
return NSS_STATUS_SUCCESS;
}
- mc = source->mc;
-
status = pthread_mutex_lock(&hostent_mutex);
if (status) {
error(ap->logopt, MODPREFIX "failed to lock hostent mutex");
@@ -209,6 +275,7 @@ int lookup_read_map(struct autofs_point
if (status)
error(ap->logopt, MODPREFIX "failed to unlock hostent mutex");
+ update_hosts_mounts(ap, source, age, ctxt);
source->age = age;
return NSS_STATUS_SUCCESS;
@@ -220,11 +287,9 @@ int lookup_mount(struct autofs_point *ap
struct map_source *source;
struct mapent_cache *mc;
struct mapent *me;
- char buf[MAX_ERR_BUF];
char *mapent = NULL;
int mapent_len;
time_t now = time(NULL);
- exports exp;
int ret;
source = ap->entry->current;
@@ -320,7 +385,7 @@ done:
cache_unlock(mc);
}
- ret = do_parse_mount(ap, name, name_len, mapent, ctxt->parse->context);
+ ret = do_parse_mount(ap, source, name, name_len, mapent, ctxt);
free(mapent);
--- autofs-5.0.6.orig/modules/parse_sun.c
+++ autofs-5.0.6/modules/parse_sun.c
@@ -1355,7 +1355,7 @@ int parse_mount(struct autofs_point *ap,
if (check_is_multi(p)) {
char *m_root = NULL;
int m_root_len;
- time_t age = time(NULL);
+ time_t age;
int l;
/* If name starts with "/" it's a direct mount */
@@ -1399,6 +1399,8 @@ int parse_mount(struct autofs_point *ap,
return 1;
}
+ age = me->age;
+
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
cache_multi_writelock(me);
/* It's a multi-mount; deal with it */
@@ -1472,8 +1474,11 @@ int parse_mount(struct autofs_point *ap,
/*
* We've got the ordered list of multi-mount entries so go
- * through and set the parent entry of each
+ * through and remove any stale entries if this is the top
+ * of the multi-mount and set the parent entry of each.
*/
+ if (me == me->multi)
+ clean_stale_multi_triggers(ap, me, NULL, NULL);
cache_set_parents(me);
rv = mount_subtree(ap, me, name, NULL, options, ctxt);