Blame modules/lookup_hosts.c

Packit 8480eb
/* ----------------------------------------------------------------------- *
Packit 8480eb
 *   
Packit 8480eb
 *  lookup_hosts.c - module for Linux automount to mount the exports
Packit 8480eb
 *                      from a given host
Packit 8480eb
 *
Packit 8480eb
 *   Copyright 2005 Ian Kent <raven@themaw.net>
Packit 8480eb
 *
Packit 8480eb
 *   This program is free software; you can redistribute it and/or modify
Packit 8480eb
 *   it under the terms of the GNU General Public License as published by
Packit 8480eb
 *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
Packit 8480eb
 *   USA; either version 2 of the License, or (at your option) any later
Packit 8480eb
 *   version; incorporated herein by reference.
Packit 8480eb
 *
Packit 8480eb
 * ----------------------------------------------------------------------- */
Packit 8480eb
Packit 8480eb
#include <stdio.h>
Packit 8480eb
#include <malloc.h>
Packit 8480eb
#include <sys/param.h>
Packit 8480eb
#include <sys/types.h>
Packit 8480eb
#include <sys/stat.h>
Packit 8480eb
#include <netdb.h>
Packit 8480eb
Packit 8480eb
#define MODULE_LOOKUP
Packit 8480eb
#include "automount.h"
Packit 8480eb
#include "nsswitch.h"
Packit 8480eb
Packit 8480eb
#define MAPFMT_DEFAULT "sun"
Packit 8480eb
#define MODPREFIX "lookup(hosts): "
Packit 8480eb
Packit 8480eb
pthread_mutex_t hostent_mutex = PTHREAD_MUTEX_INITIALIZER;
Packit 8480eb
Packit 8480eb
struct lookup_context {
Packit 8480eb
	struct parse_mod *parse;
Packit 8480eb
};
Packit 8480eb
Packit 8480eb
int lookup_version = AUTOFS_LOOKUP_VERSION;	/* Required by protocol */
Packit 8480eb
Packit 8480eb
int lookup_init(const char *mapfmt,
Packit 8480eb
		int argc, const char *const *argv, void **context)
Packit 8480eb
{
Packit 8480eb
	struct lookup_context *ctxt;
Packit 8480eb
	char buf[MAX_ERR_BUF];
Packit 8480eb
Packit 8480eb
	*context = NULL;
Packit 8480eb
Packit 8480eb
	ctxt = malloc(sizeof(struct lookup_context));
Packit 8480eb
	if (!ctxt) {
Packit 8480eb
		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Packit 8480eb
		logerr(MODPREFIX "malloc: %s", estr);
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	mapfmt = MAPFMT_DEFAULT;
Packit 8480eb
Packit 8480eb
	ctxt->parse = open_parse(mapfmt, MODPREFIX, argc, argv);
Packit 8480eb
	if (!ctxt->parse) {
Packit 8480eb
		logerr(MODPREFIX "failed to open parse context");
Packit 8480eb
		free(ctxt);
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	*context = ctxt;
Packit 8480eb
Packit 8480eb
	return 0;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int lookup_reinit(const char *mapfmt,
Packit 8480eb
		  int argc, const char *const *argv, void **context)
Packit 8480eb
{
Packit 8480eb
	struct lookup_context *ctxt = (struct lookup_context *) *context;
Packit 8480eb
	int ret;
Packit 8480eb
Packit 8480eb
	mapfmt = MAPFMT_DEFAULT;
Packit 8480eb
Packit 8480eb
	ret = reinit_parse(ctxt->parse, mapfmt, MODPREFIX, argc, argv);
Packit 8480eb
	if (ret)
Packit 8480eb
		return 1;
Packit 8480eb
Packit 8480eb
	return 0;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int lookup_read_master(struct master *master, time_t age, void *context)
Packit 8480eb
{
Packit 8480eb
	return NSS_STATUS_UNKNOWN;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
static char *get_exports(struct autofs_point *ap, const char *host)
Packit 8480eb
{
Packit 8480eb
	char buf[MAX_ERR_BUF];
Packit 8480eb
	char *mapent;
Packit Service 27309f
	struct exportinfo *exp, *this;
Packit Service d5db16
	size_t hostlen = strlen(host);
Packit Service d5db16
	size_t mapent_len;
Packit 8480eb
Packit 8480eb
	debug(ap->logopt, MODPREFIX "fetchng export list for %s", host);
Packit 8480eb
Packit 8480eb
	exp = rpc_get_exports(host, 10, 0, RPC_CLOSE_NOLINGER);
Packit 8480eb
Packit 8480eb
	this = exp;
Packit Service d5db16
	mapent_len = 0;
Packit 8480eb
	while (this) {
Packit Service d5db16
		mapent_len += hostlen + 2*(strlen(this->dir) + 2) + 3;
Packit Service d5db16
		this = this->next;
Packit Service d5db16
	}
Packit Service d5db16
Packit Service d5db16
	mapent = malloc(mapent_len + 1);
Packit Service d5db16
	if (!mapent) {
Packit Service d5db16
		char *estr;
Packit Service d5db16
		estr = strerror_r(errno, buf, MAX_ERR_BUF);
Packit Service d5db16
		error(ap->logopt, MODPREFIX "malloc: %s", estr);
Packit Service d5db16
		error(ap->logopt, MODPREFIX "exports lookup failed for %s", host);
Packit Service d5db16
		rpc_exports_free(exp);
Packit Service d5db16
		return NULL;
Packit Service d5db16
	}
Packit Service d5db16
	*mapent = 0;
Packit Service d5db16
Packit Service d5db16
	this = exp;
Packit Service d5db16
	while (this) {
Packit Service d5db16
		if (!*mapent)
Packit 8480eb
			strcpy(mapent, "\"");
Packit Service d5db16
		else
Packit Service d5db16
			strcat(mapent, " \"");
Packit Service d5db16
		strcat(mapent, this->dir);
Packit Service d5db16
		strcat(mapent, "\"");
Packit Service d5db16
Packit 8480eb
		strcat(mapent, " \"");
Packit 8480eb
		strcat(mapent, host);
Packit 8480eb
		strcat(mapent, ":");
Packit Service 27309f
		strcat(mapent, this->dir);
Packit 8480eb
		strcat(mapent, "\"");
Packit 8480eb
Packit Service 27309f
		this = this->next;
Packit 8480eb
	}
Packit 8480eb
	rpc_exports_free(exp);
Packit 8480eb
Packit 8480eb
	return mapent;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
static int do_parse_mount(struct autofs_point *ap, struct map_source *source,
Packit 8480eb
			  const char *name, int name_len, char *mapent,
Packit 8480eb
			  struct lookup_context *ctxt)
Packit 8480eb
{
Packit 8480eb
	int ret;
Packit 8480eb
Packit 8480eb
	master_source_current_wait(ap->entry);
Packit 8480eb
	ap->entry->current = source;
Packit 8480eb
Packit 8480eb
	ret = ctxt->parse->parse_mount(ap, name, name_len,
Packit 8480eb
				 mapent, ctxt->parse->context);
Packit 8480eb
	if (ret) {
Packit 8480eb
		struct mapent_cache *mc = source->mc;
Packit 8480eb
Packit 8480eb
		/* Don't update negative cache when re-connecting */
Packit 8480eb
		if (ap->flags & MOUNT_FLAG_REMOUNT)
Packit 8480eb
			return NSS_STATUS_TRYAGAIN;
Packit 8480eb
		cache_writelock(mc);
Packit 8480eb
		cache_update_negative(mc, source, name, ap->negative_timeout);
Packit 8480eb
		cache_unlock(mc);
Packit 8480eb
		return NSS_STATUS_TRYAGAIN;
Packit 8480eb
	}
Packit 8480eb
	return NSS_STATUS_SUCCESS;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
static void update_hosts_mounts(struct autofs_point *ap,
Packit 8480eb
				struct map_source *source, time_t age,
Packit 8480eb
				struct lookup_context *ctxt)
Packit 8480eb
{
Packit 8480eb
	struct mapent_cache *mc;
Packit 8480eb
	struct mapent *me;
Packit 8480eb
	char *mapent;
Packit 8480eb
	int ret;
Packit 8480eb
Packit 8480eb
	mc = source->mc;
Packit 8480eb
Packit 8480eb
	pthread_cleanup_push(cache_lock_cleanup, mc);
Packit 8480eb
	cache_writelock(mc);
Packit 8480eb
	me = cache_lookup_first(mc);
Packit 8480eb
	while (me) {
Packit 8480eb
		/* Hosts map entry not yet expanded or already expired */
Packit 8480eb
		if (!me->multi)
Packit 8480eb
			goto next;
Packit 8480eb
Packit 8480eb
		debug(ap->logopt, MODPREFIX "get list of exports for %s", me->key);
Packit 8480eb
Packit 8480eb
		mapent = get_exports(ap, me->key);
Packit 8480eb
		if (mapent) {
Packit 8480eb
			cache_update(mc, source, me->key, mapent, age);
Packit 8480eb
			free(mapent);
Packit 8480eb
		}
Packit 8480eb
next:
Packit 8480eb
		me = cache_lookup_next(mc, me);
Packit 8480eb
	}
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
Packit 8480eb
	pthread_cleanup_push(cache_lock_cleanup, mc);
Packit 8480eb
	cache_readlock(mc);
Packit 8480eb
	me = cache_lookup_first(mc);
Packit 8480eb
	while (me) {
Packit 8480eb
		/*
Packit 8480eb
		 * Hosts map entry not yet expanded, already expired
Packit 8480eb
		 * or not the base of the tree
Packit 8480eb
		 */
Packit 8480eb
		if (!me->multi || me->multi != me)
Packit 8480eb
			goto cont;
Packit 8480eb
Packit 8480eb
		debug(ap->logopt, MODPREFIX
Packit 8480eb
		      "attempt to update exports for %s", me->key);
Packit 8480eb
Packit 8480eb
		master_source_current_wait(ap->entry);
Packit 8480eb
		ap->entry->current = source;
Packit 8480eb
		ap->flags |= MOUNT_FLAG_REMOUNT;
Packit 8480eb
		ret = ctxt->parse->parse_mount(ap, me->key, strlen(me->key),
Packit 8480eb
					       me->mapent, ctxt->parse->context);
Packit 8480eb
		if (ret)
Packit 8480eb
			warn(ap->logopt, MODPREFIX
Packit 8480eb
			     "failed to parse mount %s", me->mapent);
Packit 8480eb
		ap->flags &= ~MOUNT_FLAG_REMOUNT;
Packit 8480eb
cont:
Packit 8480eb
		me = cache_lookup_next(mc, me);
Packit 8480eb
	}
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int lookup_read_map(struct autofs_point *ap, time_t age, void *context)
Packit 8480eb
{
Packit 8480eb
	struct lookup_context *ctxt = (struct lookup_context *) context;
Packit 8480eb
	struct map_source *source;
Packit 8480eb
	struct mapent_cache *mc;
Packit 8480eb
	struct hostent *host;
Packit 8480eb
	int status;
Packit 8480eb
Packit 8480eb
	source = ap->entry->current;
Packit 8480eb
	ap->entry->current = NULL;
Packit 8480eb
	master_source_current_signal(ap->entry);
Packit 8480eb
Packit 8480eb
	mc = source->mc;
Packit 8480eb
Packit 8480eb
	debug(ap->logopt, MODPREFIX "read hosts map");
Packit 8480eb
Packit 8480eb
	/*
Packit 8480eb
	 * If we don't need to create directories then there's no use
Packit 8480eb
	 * reading the map. We always need to read the whole map for
Packit 8480eb
	 * direct mounts in order to mount the triggers.
Packit 8480eb
	 */
Packit 8480eb
	if (!(ap->flags & MOUNT_FLAG_GHOST) && ap->type != LKP_DIRECT) {
Packit 8480eb
		debug(ap->logopt, MODPREFIX
Packit 8480eb
		      "map not browsable, update existing host entries only");
Packit 8480eb
		update_hosts_mounts(ap, source, age, ctxt);
Packit 8480eb
		source->age = age;
Packit 8480eb
		return NSS_STATUS_SUCCESS;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	status = pthread_mutex_lock(&hostent_mutex);
Packit 8480eb
	if (status) {
Packit 8480eb
		error(ap->logopt, MODPREFIX "failed to lock hostent mutex");
Packit 8480eb
		return NSS_STATUS_UNAVAIL;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	sethostent(0);
Packit 8480eb
	while ((host = gethostent()) != NULL) {
Packit 8480eb
		pthread_cleanup_push(cache_lock_cleanup, mc);
Packit 8480eb
		cache_writelock(mc);
Packit 8480eb
		cache_update(mc, source, host->h_name, NULL, age);
Packit 8480eb
		cache_unlock(mc);
Packit 8480eb
		pthread_cleanup_pop(0);
Packit 8480eb
	}
Packit 8480eb
	endhostent();
Packit 8480eb
Packit 8480eb
	status = pthread_mutex_unlock(&hostent_mutex);
Packit 8480eb
	if (status)
Packit 8480eb
		error(ap->logopt, MODPREFIX "failed to unlock hostent mutex");
Packit 8480eb
Packit 8480eb
	update_hosts_mounts(ap, source, age, ctxt);
Packit 8480eb
	source->age = age;
Packit 8480eb
Packit 8480eb
	return NSS_STATUS_SUCCESS;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *context)
Packit 8480eb
{
Packit 8480eb
	struct lookup_context *ctxt = (struct lookup_context *) context;
Packit 8480eb
	struct map_source *source;
Packit 8480eb
	struct mapent_cache *mc;
Packit 8480eb
	struct mapent *me;
Packit 8480eb
	char *mapent = NULL;
Packit 8480eb
	int mapent_len;
Packit 8480eb
	time_t now = monotonic_time(NULL);
Packit 8480eb
	int ret;
Packit 8480eb
Packit 8480eb
	source = ap->entry->current;
Packit 8480eb
	ap->entry->current = NULL;
Packit 8480eb
	master_source_current_signal(ap->entry);
Packit 8480eb
Packit 8480eb
	mc = source->mc;
Packit 8480eb
Packit 8480eb
	/* Check if we recorded a mount fail for this key anywhere */
Packit 8480eb
	me = lookup_source_mapent(ap, name, LKP_DISTINCT);
Packit 8480eb
	if (me) {
Packit 8480eb
		if (me->status >= monotonic_time(NULL)) {
Packit 8480eb
			cache_unlock(me->mc);
Packit 8480eb
			return NSS_STATUS_NOTFOUND;
Packit 8480eb
		} else {
Packit 8480eb
			struct mapent_cache *smc = me->mc;
Packit 8480eb
			struct mapent *sme;
Packit 8480eb
Packit 8480eb
			if (me->mapent)
Packit 8480eb
				cache_unlock(smc);
Packit 8480eb
			else {
Packit 8480eb
				cache_unlock(smc);
Packit 8480eb
				cache_writelock(smc);
Packit 8480eb
				sme = cache_lookup_distinct(smc, name);
Packit 8480eb
				/* Negative timeout expired for non-existent entry. */
Packit 8480eb
				if (sme && !sme->mapent) {
Packit 8480eb
					if (cache_pop_mapent(sme) == CHE_FAIL)
Packit 8480eb
						cache_delete(smc, name);
Packit 8480eb
				}
Packit 8480eb
				cache_unlock(smc);
Packit 8480eb
			}
Packit 8480eb
		}
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	cache_readlock(mc);
Packit 8480eb
	me = cache_lookup_distinct(mc, name);
Packit 8480eb
	if (!me) {
Packit 8480eb
		cache_unlock(mc);
Packit 8480eb
		/*
Packit 8480eb
		 * We haven't read the list of hosts into the
Packit 8480eb
		 * cache so go straight to the lookup.
Packit 8480eb
		 */
Packit 8480eb
		if (!(ap->flags & MOUNT_FLAG_GHOST)) {
Packit 8480eb
			/*
Packit 8480eb
			 * If name contains a '/' we're searching for an
Packit 8480eb
			 * offset that doesn't exist in the export list
Packit 8480eb
			 * so it's NOTFOUND otherwise this could be a
Packit 8480eb
			 * lookup for a new host.
Packit 8480eb
			 */
Packit 8480eb
			if (*name != '/' && strchr(name, '/'))
Packit 8480eb
				return NSS_STATUS_NOTFOUND;
Packit 8480eb
			goto done;
Packit 8480eb
		}
Packit 8480eb
Packit 8480eb
		if (*name == '/')
Packit 8480eb
			info(ap->logopt, MODPREFIX
Packit 8480eb
			      "can't find path in hosts map %s", name);
Packit 8480eb
		else
Packit 8480eb
			info(ap->logopt, MODPREFIX
Packit 8480eb
			      "can't find path in hosts map %s/%s",
Packit 8480eb
			      ap->path, name);
Packit 8480eb
Packit 8480eb
		debug(ap->logopt,
Packit 8480eb
		      MODPREFIX "lookup failed - update exports list");
Packit 8480eb
		goto done;
Packit 8480eb
	}
Packit 8480eb
	/*
Packit 8480eb
	 * Host map export entries are added to the cache as
Packit 8480eb
	 * direct mounts. If the name we seek starts with a slash
Packit 8480eb
	 * it must be a mount request for one of the exports.
Packit 8480eb
	 */
Packit 8480eb
	if (*name == '/') {
Packit 8480eb
		pthread_cleanup_push(cache_lock_cleanup, mc);
Packit 8480eb
		mapent_len = strlen(me->mapent);
Packit 8480eb
		mapent = malloc(mapent_len + 1);
Packit 8480eb
		if (mapent)
Packit 8480eb
			strcpy(mapent, me->mapent);
Packit 8480eb
		pthread_cleanup_pop(0);
Packit 8480eb
	}
Packit 8480eb
	cache_unlock(mc);
Packit 8480eb
Packit 8480eb
done:
Packit 8480eb
	debug(ap->logopt, MODPREFIX "%s -> %s", name, mapent);
Packit 8480eb
Packit 8480eb
	if (!mapent) {
Packit 8480eb
		/* We need to get the exports list and update the cache. */
Packit 8480eb
		mapent = get_exports(ap, name);
Packit 8480eb
Packit 8480eb
		/* Exports lookup failed so we're outa here */
Packit 8480eb
		if (!mapent)
Packit 8480eb
			return NSS_STATUS_UNAVAIL;
Packit 8480eb
Packit 8480eb
		cache_writelock(mc);
Packit 8480eb
		cache_update(mc, source, name, mapent, now);
Packit 8480eb
		cache_unlock(mc);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	ret = do_parse_mount(ap, source, name, name_len, mapent, ctxt);
Packit 8480eb
Packit 8480eb
	free(mapent);
Packit 8480eb
Packit 8480eb
	return ret;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int lookup_done(void *context)
Packit 8480eb
{
Packit 8480eb
	struct lookup_context *ctxt = (struct lookup_context *) context;
Packit 8480eb
	int rv = close_parse(ctxt->parse);
Packit 8480eb
	free(ctxt);
Packit 8480eb
	return rv;
Packit 8480eb
}