| /* ----------------------------------------------------------------------- * |
| * |
| * master.c - master map utility routines. |
| * |
| * Copyright 2006 Ian Kent <raven@themaw.net> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, |
| * USA; either version 2 of the License, or (at your option) any later |
| * version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * ----------------------------------------------------------------------- */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <memory.h> |
| #include <limits.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <ctype.h> |
| #include "automount.h" |
| |
| /* The root of the map entry tree */ |
| struct master *master_list = NULL; |
| |
| extern const char *global_options; |
| extern long global_negative_timeout; |
| |
| /* Attribute to create a joinable thread */ |
| extern pthread_attr_t th_attr; |
| |
| extern struct startup_cond suc; |
| |
| static struct map_source * |
| __master_find_map_source(struct master_mapent *, |
| const char *, const char *, int, const char **); |
| |
| static pthread_mutex_t master_mutex = PTHREAD_MUTEX_INITIALIZER; |
| static pthread_mutex_t instance_mutex = PTHREAD_MUTEX_INITIALIZER; |
| |
| void master_mutex_lock(void) |
| { |
| int status = pthread_mutex_lock(&master_mutex); |
| if (status) |
| fatal(status); |
| } |
| |
| void master_mutex_unlock(void) |
| { |
| int status = pthread_mutex_unlock(&master_mutex); |
| if (status) |
| fatal(status); |
| } |
| |
| void master_mutex_lock_cleanup(void *arg) |
| { |
| master_mutex_unlock(); |
| return; |
| } |
| |
| int master_add_autofs_point(struct master_mapent *entry, unsigned logopt, |
| unsigned nobind, unsigned ghost, int submount) |
| { |
| struct autofs_point *ap; |
| int status; |
| |
| ap = malloc(sizeof(struct autofs_point)); |
| if (!ap) |
| return 0; |
| |
| ap->state = ST_INIT; |
| |
| ap->state_pipe[0] = -1; |
| ap->state_pipe[1] = -1; |
| ap->logpri_fifo = -1; |
| |
| ap->path = strdup(entry->path); |
| if (!ap->path) { |
| free(ap); |
| return 0; |
| } |
| |
| ap->entry = entry; |
| ap->exp_thread = 0; |
| ap->readmap_thread = 0; |
| /* |
| * Program command line option overrides config. |
| * We can't use 0 negative timeout so use default. |
| */ |
| if (global_negative_timeout <= 0) |
| ap->negative_timeout = defaults_get_negative_timeout(); |
| else |
| ap->negative_timeout = global_negative_timeout; |
| ap->exp_runfreq = 0; |
| ap->flags = 0; |
| if (ghost) |
| ap->flags = MOUNT_FLAG_GHOST; |
| |
| if (nobind) |
| ap->flags |= MOUNT_FLAG_NOBIND; |
| |
| if (ap->path[1] == '-') |
| ap->type = LKP_DIRECT; |
| else |
| ap->type = LKP_INDIRECT; |
| |
| ap->logopt = logopt; |
| |
| ap->parent = NULL; |
| ap->thid = 0; |
| ap->submnt_count = 0; |
| ap->submount = submount; |
| INIT_LIST_HEAD(&ap->mounts); |
| INIT_LIST_HEAD(&ap->submounts); |
| ap->shutdown = 0; |
| |
| status = pthread_mutex_init(&ap->mounts_mutex, NULL); |
| if (status) { |
| free(ap->path); |
| free(ap); |
| return 0; |
| } |
| |
| entry->ap = ap; |
| |
| return 1; |
| } |
| |
| void master_free_autofs_point(struct autofs_point *ap) |
| { |
| int status; |
| |
| if (!ap) |
| return; |
| |
| status = pthread_mutex_destroy(&ap->mounts_mutex); |
| if (status) |
| fatal(status); |
| |
| free(ap->path); |
| free(ap); |
| } |
| |
| struct map_source * |
| master_add_map_source(struct master_mapent *entry, |
| char *type, char *format, time_t age, |
| int argc, const char **argv) |
| { |
| struct map_source *source; |
| char *ntype, *nformat; |
| const char **tmpargv; |
| |
| source = malloc(sizeof(struct map_source)); |
| if (!source) |
| return NULL; |
| memset(source, 0, sizeof(struct map_source)); |
| |
| if (type) { |
| ntype = strdup(type); |
| if (!ntype) { |
| master_free_map_source(source, 0); |
| return NULL; |
| } |
| source->type = ntype; |
| } |
| |
| if (format) { |
| nformat = strdup(format); |
| if (!nformat) { |
| master_free_map_source(source, 0); |
| return NULL; |
| } |
| source->format = nformat; |
| } |
| |
| source->age = age; |
| source->stale = 1; |
| |
| tmpargv = copy_argv(argc, argv); |
| if (!tmpargv) { |
| master_free_map_source(source, 0); |
| return NULL; |
| } |
| source->argc = argc; |
| source->argv = tmpargv; |
| |
| master_source_writelock(entry); |
| |
| if (!entry->maps) { |
| source->mc = cache_init(entry->ap, source); |
| if (!source->mc) { |
| master_free_map_source(source, 0); |
| master_source_unlock(entry); |
| return NULL; |
| } |
| entry->maps = source; |
| } else { |
| struct map_source *this, *last, *next; |
| |
| /* Typically there only a few map sources */ |
| |
| this = __master_find_map_source(entry, type, format, argc, tmpargv); |
| if (this) { |
| this->age = age; |
| master_free_map_source(source, 0); |
| master_source_unlock(entry); |
| return this; |
| } |
| |
| source->mc = cache_init(entry->ap, source); |
| if (!source->mc) { |
| master_free_map_source(source, 0); |
| master_source_unlock(entry); |
| return NULL; |
| } |
| |
| last = NULL; |
| next = entry->maps; |
| while (next) { |
| last = next; |
| next = next->next; |
| } |
| if (last) |
| last->next = source; |
| else |
| entry->maps = source; |
| } |
| |
| master_source_unlock(entry); |
| |
| return source; |
| } |
| |
| static int compare_source_type_and_format(struct map_source *map, const char *type, const char *format) |
| { |
| int res = 0; |
| |
| if (type) { |
| if (!map->type) |
| goto done; |
| |
| if (strcmp(map->type, type)) |
| goto done; |
| } else if (map->type) |
| goto done; |
| |
| if (format) { |
| if (!map->format) |
| goto done; |
| |
| if (strcmp(map->format, format)) |
| goto done; |
| } else if (map->format) |
| goto done; |
| |
| res = 1; |
| done: |
| return res; |
| } |
| |
| static struct map_source * |
| __master_find_map_source(struct master_mapent *entry, |
| const char *type, const char *format, |
| int argc, const char **argv) |
| { |
| struct map_source *map; |
| struct map_source *source = NULL; |
| int res; |
| |
| map = entry->maps; |
| while (map) { |
| res = compare_source_type_and_format(map, type, format); |
| if (!res) |
| goto next; |
| |
| res = compare_argv(map->argc, map->argv, argc, argv); |
| if (!res) |
| goto next; |
| |
| source = map; |
| break; |
| next: |
| map = map->next; |
| } |
| |
| return source; |
| } |
| |
| struct map_source *master_find_map_source(struct master_mapent *entry, |
| const char *type, const char *format, |
| int argc, const char **argv) |
| { |
| struct map_source *source = NULL; |
| |
| master_source_readlock(entry); |
| source = __master_find_map_source(entry, type, format, argc, argv); |
| master_source_unlock(entry); |
| |
| return source; |
| } |
| |
| static void __master_free_map_source(struct map_source *source, unsigned int free_cache) |
| { |
| if (source->type) |
| free(source->type); |
| if (source->format) |
| free(source->format); |
| if (free_cache && source->mc) |
| cache_release(source); |
| if (source->lookup) { |
| struct map_source *instance; |
| |
| instance = source->instance; |
| while (instance) { |
| if (instance->lookup) |
| close_lookup(instance->lookup); |
| instance = instance->next; |
| } |
| close_lookup(source->lookup); |
| } |
| if (source->argv) |
| free_argv(source->argc, source->argv); |
| if (source->instance) { |
| struct map_source *instance, *next; |
| |
| instance = source->instance; |
| while (instance) { |
| next = instance->next; |
| __master_free_map_source(instance, 0); |
| instance = next; |
| } |
| } |
| |
| free(source); |
| |
| return; |
| } |
| |
| void master_free_map_source(struct map_source *source, unsigned int free_cache) |
| { |
| int status; |
| |
| status = pthread_mutex_lock(&instance_mutex); |
| if (status) |
| fatal(status); |
| |
| __master_free_map_source(source, free_cache); |
| |
| status = pthread_mutex_unlock(&instance_mutex); |
| if (status) |
| fatal(status); |
| } |
| |
| struct map_source *master_find_source_instance(struct map_source *source, const char *type, const char *format, int argc, const char **argv) |
| { |
| struct map_source *map; |
| struct map_source *instance = NULL; |
| int status, res; |
| |
| status = pthread_mutex_lock(&instance_mutex); |
| if (status) |
| fatal(status); |
| |
| map = source->instance; |
| while (map) { |
| res = compare_source_type_and_format(map, type, format); |
| if (!res) |
| goto next; |
| |
| if (!argv) { |
| instance = map; |
| break; |
| } |
| |
| res = compare_argv(map->argc, map->argv, argc, argv); |
| if (!res) |
| goto next; |
| |
| instance = map; |
| break; |
| next: |
| map = map->next; |
| } |
| |
| status = pthread_mutex_unlock(&instance_mutex); |
| if (status) |
| fatal(status); |
| |
| return instance; |
| } |
| |
| struct map_source * |
| master_add_source_instance(struct map_source *source, const char *type, const char *format, time_t age, int argc, const char **argv) |
| { |
| struct map_source *instance; |
| struct map_source *new; |
| char *ntype, *nformat; |
| const char **tmpargv; |
| int status; |
| |
| instance = master_find_source_instance(source, type, format, argc, argv); |
| if (instance) |
| return instance; |
| |
| new = malloc(sizeof(struct map_source)); |
| if (!new) |
| return NULL; |
| memset(new, 0, sizeof(struct map_source)); |
| |
| if (type) { |
| ntype = strdup(type); |
| if (!ntype) { |
| master_free_map_source(new, 0); |
| return NULL; |
| } |
| new->type = ntype; |
| } |
| |
| if (format) { |
| nformat = strdup(format); |
| if (!nformat) { |
| master_free_map_source(new, 0); |
| return NULL; |
| } |
| new->format = nformat; |
| } |
| |
| new->age = age; |
| new->master_line = 0; |
| new->mc = source->mc; |
| new->exp_timeout = source->exp_timeout; |
| new->stale = 1; |
| |
| tmpargv = copy_argv(argc, argv); |
| if (!tmpargv) { |
| master_free_map_source(new, 0); |
| return NULL; |
| } |
| new->argc = argc; |
| new->argv = tmpargv; |
| |
| status = pthread_mutex_lock(&instance_mutex); |
| if (status) |
| fatal(status); |
| |
| if (!source->instance) |
| source->instance = new; |
| else { |
| /* |
| * We know there's no other instance of this |
| * type so just add to head of list |
| */ |
| new->next = source->instance; |
| source->instance = new; |
| } |
| |
| status = pthread_mutex_unlock(&instance_mutex); |
| if (status) |
| fatal(status); |
| |
| return new; |
| } |
| |
| static int check_stale_instances(struct map_source *source) |
| { |
| struct map_source *map; |
| |
| if (!source) |
| return 0; |
| |
| map = source->instance; |
| while (map) { |
| if (map->stale) |
| return 1; |
| if (check_stale_instances(map)) |
| return 1; |
| map = map->next; |
| } |
| |
| return 0; |
| } |
| |
| void clear_stale_instances(struct map_source *source) |
| { |
| struct map_source *map; |
| |
| if (!source) |
| return; |
| |
| map = source->instance; |
| while (map) { |
| clear_stale_instances(map); |
| if (map->stale) |
| map->stale = 0; |
| map = map->next; |
| } |
| |
| return; |
| } |
| |
| void send_map_update_request(struct autofs_point *ap) |
| { |
| struct map_source *map; |
| int status, need_update = 0; |
| |
| status = pthread_mutex_lock(&instance_mutex); |
| if (status) |
| fatal(status); |
| |
| map = ap->entry->maps; |
| while (map) { |
| if (check_stale_instances(map)) |
| map->stale = 1; |
| if (map->stale) { |
| need_update = 1; |
| break; |
| } |
| map = map->next; |
| } |
| |
| status = pthread_mutex_unlock(&instance_mutex); |
| if (status) |
| fatal(status); |
| |
| if (!need_update) |
| return; |
| |
| st_add_task(ap, ST_READMAP); |
| |
| return; |
| } |
| |
| void master_source_writelock(struct master_mapent *entry) |
| { |
| int status; |
| |
| status = pthread_rwlock_wrlock(&entry->source_lock); |
| if (status) { |
| logmsg("master_mapent source write lock failed"); |
| fatal(status); |
| } |
| return; |
| } |
| |
| void master_source_readlock(struct master_mapent *entry) |
| { |
| int retries = 25; |
| int status; |
| |
| while (retries--) { |
| status = pthread_rwlock_rdlock(&entry->source_lock); |
| if (status != EAGAIN && status != EBUSY) |
| break; |
| else { |
| struct timespec t = { 0, 200000000 }; |
| struct timespec r; |
| |
| if (status == EAGAIN) |
| logmsg("master_mapent source too many readers"); |
| else |
| logmsg("master_mapent source write lock held"); |
| |
| while (nanosleep(&t, &r) == -1 && errno == EINTR) |
| memcpy(&t, &r, sizeof(struct timespec)); |
| } |
| } |
| |
| if (status) { |
| logmsg("master_mapent source read lock failed"); |
| fatal(status); |
| } |
| |
| return; |
| } |
| |
| void master_source_unlock(struct master_mapent *entry) |
| { |
| int status; |
| |
| status = pthread_rwlock_unlock(&entry->source_lock); |
| if (status) { |
| logmsg("master_mapent source unlock failed"); |
| fatal(status); |
| } |
| return; |
| } |
| |
| void master_source_lock_cleanup(void *arg) |
| { |
| struct master_mapent *entry = (struct master_mapent *) arg; |
| |
| master_source_unlock(entry); |
| |
| return; |
| } |
| |
| void master_source_current_wait(struct master_mapent *entry) |
| { |
| int status; |
| |
| status = pthread_mutex_lock(&entry->current_mutex); |
| if (status) { |
| logmsg("entry current source lock failed"); |
| fatal(status); |
| } |
| |
| while (entry->current != NULL) { |
| status = pthread_cond_wait( |
| &entry->current_cond, &entry->current_mutex); |
| if (status) { |
| logmsg("entry current source condition wait failed"); |
| fatal(status); |
| } |
| } |
| |
| return; |
| } |
| |
| void master_source_current_signal(struct master_mapent *entry) |
| { |
| int status; |
| |
| status = pthread_cond_signal(&entry->current_cond); |
| if (status) { |
| logmsg("entry current source condition signal failed"); |
| fatal(status); |
| } |
| |
| status = pthread_mutex_unlock(&entry->current_mutex); |
| if (status) { |
| logmsg("entry current source unlock failed"); |
| fatal(status); |
| } |
| |
| return; |
| } |
| |
| struct master_mapent *master_find_mapent(struct master *master, const char *path) |
| { |
| struct list_head *head, *p; |
| |
| head = &master->mounts; |
| list_for_each(p, head) { |
| struct master_mapent *entry; |
| |
| entry = list_entry(p, struct master_mapent, list); |
| |
| if (!strcmp(entry->path, path)) |
| return entry; |
| } |
| |
| return NULL; |
| } |
| |
| struct autofs_point *__master_find_submount(struct autofs_point *ap, const char *path) |
| { |
| struct list_head *head, *p; |
| |
| head = &ap->submounts; |
| list_for_each(p, head) { |
| struct autofs_point *submount; |
| |
| submount = list_entry(p, struct autofs_point, mounts); |
| |
| if (!strcmp(submount->path, path)) |
| return submount; |
| } |
| |
| return NULL; |
| } |
| |
| struct autofs_point *master_find_submount(struct autofs_point *ap, const char *path) |
| { |
| struct autofs_point *submount; |
| |
| mounts_mutex_lock(ap); |
| submount = __master_find_submount(ap, path); |
| mounts_mutex_unlock(ap); |
| |
| return submount; |
| } |
| |
| struct master_mapent *master_new_mapent(struct master *master, const char *path, time_t age) |
| { |
| struct master_mapent *entry; |
| int status; |
| char *tmp; |
| |
| entry = malloc(sizeof(struct master_mapent)); |
| if (!entry) |
| return NULL; |
| |
| memset(entry, 0, sizeof(struct master_mapent)); |
| |
| tmp = strdup(path); |
| if (!tmp) { |
| free(entry); |
| return NULL; |
| } |
| entry->path = tmp; |
| |
| entry->thid = 0; |
| entry->age = age; |
| entry->master = master; |
| entry->current = NULL; |
| entry->maps = NULL; |
| entry->ap = NULL; |
| |
| status = pthread_rwlock_init(&entry->source_lock, NULL); |
| if (status) |
| fatal(status); |
| |
| status = pthread_mutex_init(&entry->current_mutex, NULL); |
| if (status) |
| fatal(status); |
| |
| status = pthread_cond_init(&entry->current_cond, NULL); |
| if (status) |
| fatal(status); |
| |
| INIT_LIST_HEAD(&entry->list); |
| |
| return entry; |
| } |
| |
| void master_add_mapent(struct master *master, struct master_mapent *entry) |
| { |
| list_add_tail(&entry->list, &master->mounts); |
| return; |
| } |
| |
| void master_remove_mapent(struct master_mapent *entry) |
| { |
| struct master *master = entry->master; |
| |
| if (entry->ap->submount) |
| return; |
| |
| if (!list_empty(&entry->list)) { |
| list_del_init(&entry->list); |
| list_add(&entry->join, &master->completed); |
| } |
| |
| return; |
| } |
| |
| void master_free_mapent_sources(struct master_mapent *entry, unsigned int free_cache) |
| { |
| if (entry->maps) { |
| struct map_source *m, *n; |
| |
| m = entry->maps; |
| while (m) { |
| n = m->next; |
| master_free_map_source(m, free_cache); |
| m = n; |
| } |
| entry->maps = NULL; |
| } |
| |
| return; |
| } |
| |
| void master_free_mapent(struct master_mapent *entry) |
| { |
| int status; |
| |
| if (entry->path) |
| free(entry->path); |
| |
| master_free_autofs_point(entry->ap); |
| |
| status = pthread_rwlock_destroy(&entry->source_lock); |
| if (status) |
| fatal(status); |
| |
| status = pthread_mutex_destroy(&entry->current_mutex); |
| if (status) |
| fatal(status); |
| |
| status = pthread_cond_destroy(&entry->current_cond); |
| if (status) |
| fatal(status); |
| |
| free(entry); |
| |
| return; |
| } |
| |
| struct master *master_new(const char *name, unsigned int timeout, unsigned int ghost) |
| { |
| struct master *master; |
| char *tmp; |
| |
| master = malloc(sizeof(struct master)); |
| if (!master) |
| return NULL; |
| |
| if (!name) |
| tmp = (char *) defaults_get_master_map(); |
| else |
| tmp = strdup(name); |
| |
| if (!tmp) { |
| free(master); |
| return NULL; |
| } |
| |
| master->name = tmp; |
| master->nc = NULL; |
| |
| master->recurse = 0; |
| master->depth = 0; |
| master->reading = 0; |
| master->read_fail = 0; |
| master->default_ghost = ghost; |
| master->default_timeout = timeout; |
| master->default_logging = defaults_get_logging(); |
| master->logopt = master->default_logging; |
| |
| INIT_LIST_HEAD(&master->mounts); |
| INIT_LIST_HEAD(&master->completed); |
| |
| return master; |
| } |
| |
| int master_read_master(struct master *master, time_t age, int readall) |
| { |
| unsigned int logopt = master->logopt; |
| struct mapent_cache *nc; |
| |
| /* |
| * We need to clear and re-populate the null map entry cache |
| * before alowing anyone else to use it. |
| */ |
| master_mutex_lock(); |
| if (master->nc) { |
| cache_writelock(master->nc); |
| nc = master->nc; |
| cache_clean_null_cache(nc); |
| } else { |
| nc = cache_init_null_cache(master); |
| if (!nc) { |
| error(logopt, |
| "failed to init null map cache for %s", |
| master->name); |
| return 0; |
| } |
| cache_writelock(nc); |
| master->nc = nc; |
| } |
| master_init_scan(); |
| lookup_nss_read_master(master, age); |
| cache_unlock(nc); |
| master_mutex_unlock(); |
| |
| if (!master->read_fail) |
| master_mount_mounts(master, age, readall); |
| else { |
| master->read_fail = 0; |
| if (!readall) |
| master_mount_mounts(master, age, readall); |
| } |
| |
| master_mutex_lock(); |
| |
| if (list_empty(&master->mounts)) |
| warn(logopt, "no mounts in table"); |
| |
| master_mutex_unlock(); |
| |
| return 1; |
| } |
| |
| int master_submount_list_empty(struct autofs_point *ap) |
| { |
| int res = 0; |
| |
| mounts_mutex_lock(ap); |
| if (list_empty(&ap->submounts)) |
| res = 1; |
| mounts_mutex_unlock(ap); |
| |
| return res; |
| } |
| |
| int master_notify_submount(struct autofs_point *ap, const char *path, enum states state) |
| { |
| struct list_head *head, *p; |
| struct autofs_point *this = NULL; |
| int ret = 1; |
| |
| mounts_mutex_lock(ap); |
| |
| head = &ap->submounts; |
| p = head->prev; |
| while (p != head) { |
| this = list_entry(p, struct autofs_point, mounts); |
| p = p->prev; |
| |
| /* path not the same */ |
| if (strcmp(this->path, path)) |
| continue; |
| |
| if (!master_submount_list_empty(this)) { |
| char *this_path = strdup(this->path); |
| if (this_path) { |
| mounts_mutex_unlock(ap); |
| master_notify_submount(this, path, state); |
| mounts_mutex_lock(ap); |
| if (!__master_find_submount(ap, this_path)) { |
| free(this_path); |
| continue; |
| } |
| free(this_path); |
| } |
| } |
| |
| /* Now we have found the submount we want to expire */ |
| |
| st_mutex_lock(); |
| |
| if (this->state == ST_SHUTDOWN) { |
| this = NULL; |
| st_mutex_unlock(); |
| break; |
| } |
| |
| this->shutdown = ap->shutdown; |
| |
| __st_add_task(this, state); |
| |
| st_mutex_unlock(); |
| mounts_mutex_unlock(ap); |
| |
| st_wait_task(this, state, 0); |
| |
| /* |
| * If our submount gets to state ST_SHUTDOWN, ST_SHUTDOWN_PENDING or |
| * ST_SHUTDOWN_FORCE we need to wait until it goes away or changes |
| * to ST_READY. |
| */ |
| mounts_mutex_lock(ap); |
| st_mutex_lock(); |
| while ((this = __master_find_submount(ap, path))) { |
| struct timespec t = { 0, 300000000 }; |
| struct timespec r; |
| |
| if (this->state != ST_SHUTDOWN && |
| this->state != ST_SHUTDOWN_PENDING && |
| this->state != ST_SHUTDOWN_FORCE) { |
| ret = 0; |
| break; |
| } |
| |
| st_mutex_unlock(); |
| mounts_mutex_unlock(ap); |
| while (nanosleep(&t, &r) == -1 && errno == EINTR) |
| memcpy(&t, &r, sizeof(struct timespec)); |
| mounts_mutex_lock(ap); |
| st_mutex_lock(); |
| } |
| st_mutex_unlock(); |
| break; |
| } |
| |
| mounts_mutex_unlock(ap); |
| |
| return ret; |
| } |
| |
| void master_notify_state_change(struct master *master, int sig) |
| { |
| struct master_mapent *entry; |
| struct autofs_point *ap; |
| struct list_head *p; |
| int cur_state; |
| unsigned int logopt; |
| |
| pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state); |
| master_mutex_lock(); |
| |
| list_for_each(p, &master->mounts) { |
| enum states next = ST_INVAL; |
| |
| entry = list_entry(p, struct master_mapent, list); |
| |
| ap = entry->ap; |
| logopt = ap->logopt; |
| |
| st_mutex_lock(); |
| |
| if (ap->state == ST_SHUTDOWN) |
| goto next; |
| |
| switch (sig) { |
| case SIGTERM: |
| case SIGINT: |
| if (ap->state != ST_SHUTDOWN_PENDING && |
| ap->state != ST_SHUTDOWN_FORCE) { |
| next = ST_SHUTDOWN_PENDING; |
| ap->shutdown = 1; |
| __st_add_task(ap, next); |
| } |
| break; |
| #ifdef ENABLE_FORCED_SHUTDOWN |
| case SIGUSR2: |
| if (ap->state != ST_SHUTDOWN_FORCE && |
| ap->state != ST_SHUTDOWN_PENDING) { |
| next = ST_SHUTDOWN_FORCE; |
| ap->shutdown = 1; |
| __st_add_task(ap, next); |
| } |
| break; |
| #endif |
| case SIGUSR1: |
| assert(ap->state == ST_READY); |
| next = ST_PRUNE; |
| __st_add_task(ap, next); |
| break; |
| } |
| next: |
| if (next != ST_INVAL) |
| debug(logopt, |
| "sig %d switching %s from %d to %d", |
| sig, ap->path, ap->state, next); |
| |
| st_mutex_unlock(); |
| } |
| |
| master_mutex_unlock(); |
| pthread_setcancelstate(cur_state, NULL); |
| |
| return; |
| } |
| |
| static int master_do_mount(struct master_mapent *entry) |
| { |
| struct startup_cond suc; |
| struct autofs_point *ap; |
| pthread_t thid; |
| int status; |
| |
| ap = entry->ap; |
| |
| if (handle_mounts_startup_cond_init(&suc)) { |
| crit(ap->logopt, |
| "failed to init startup cond for mount %s", entry->path); |
| return 0; |
| } |
| |
| suc.ap = ap; |
| suc.root = ap->path; |
| suc.done = 0; |
| suc.status = 0; |
| |
| debug(ap->logopt, "mounting %s", entry->path); |
| |
| status = pthread_create(&thid, &th_attr, handle_mounts, &suc); |
| if (status) { |
| crit(ap->logopt, |
| "failed to create mount handler thread for %s", |
| entry->path); |
| handle_mounts_startup_cond_destroy(&suc); |
| return 0; |
| } |
| |
| while (!suc.done) { |
| status = pthread_cond_wait(&suc.cond, &suc.mutex); |
| if (status) |
| fatal(status); |
| } |
| |
| if (suc.status) { |
| error(ap->logopt, "failed to startup mount"); |
| handle_mounts_startup_cond_destroy(&suc); |
| return 0; |
| } |
| entry->thid = thid; |
| |
| handle_mounts_startup_cond_destroy(&suc); |
| |
| return 1; |
| } |
| |
| static void check_update_map_sources(struct master_mapent *entry, int readall) |
| { |
| struct map_source *source, *last; |
| struct autofs_point *ap; |
| int map_stale = 0; |
| |
| if (readall) |
| map_stale = 1; |
| |
| ap = entry->ap; |
| |
| master_source_writelock(entry); |
| |
| last = NULL; |
| source = entry->maps; |
| while (source) { |
| if (readall) |
| source->stale = 1; |
| |
| /* |
| * If a map source is no longer valid and all it's |
| * entries have expired away we can get rid of it. |
| */ |
| if (entry->age > source->age) { |
| struct mapent *me; |
| cache_readlock(source->mc); |
| me = cache_lookup_first(source->mc); |
| if (!me) { |
| struct map_source *next = source->next; |
| |
| cache_unlock(source->mc); |
| |
| if (!last) |
| entry->maps = next; |
| else |
| last->next = next; |
| |
| if (entry->maps == source) |
| entry->maps = next; |
| |
| master_free_map_source(source, 1); |
| |
| source = next; |
| continue; |
| } else { |
| source->stale = 1; |
| map_stale = 1; |
| } |
| cache_unlock(source->mc); |
| } |
| last = source; |
| source = source->next; |
| } |
| |
| master_source_unlock(entry); |
| |
| /* The map sources have changed */ |
| if (map_stale) |
| st_add_task(ap, ST_READMAP); |
| |
| return; |
| } |
| |
| int master_mount_mounts(struct master *master, time_t age, int readall) |
| { |
| struct mapent_cache *nc = master->nc; |
| struct list_head *p, *head; |
| int cur_state; |
| |
| pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state); |
| master_mutex_lock(); |
| |
| head = &master->mounts; |
| p = head->next; |
| while (p != head) { |
| struct master_mapent *this; |
| struct autofs_point *ap; |
| struct mapent *ne, *nested; |
| struct stat st; |
| int state_pipe, save_errno; |
| int ret; |
| |
| this = list_entry(p, struct master_mapent, list); |
| p = p->next; |
| |
| ap = this->ap; |
| |
| /* A master map entry has gone away */ |
| if (this->age < age) { |
| st_add_task(ap, ST_SHUTDOWN_PENDING); |
| continue; |
| } |
| |
| cache_readlock(nc); |
| ne = cache_lookup_distinct(nc, this->path); |
| /* |
| * If this path matched a nulled entry the master map entry |
| * must be an indirect mount so the master map entry line |
| * number may be obtained from this->maps. |
| */ |
| if (ne) { |
| int lineno = ne->age; |
| cache_unlock(nc); |
| |
| /* null entry appears after map entry */ |
| if (this->maps->master_line < lineno) { |
| warn(ap->logopt, |
| "ignoring null entry that appears after " |
| "existing entry for %s", this->path); |
| goto cont; |
| } |
| if (ap->state != ST_INIT) { |
| st_add_task(ap, ST_SHUTDOWN_PENDING); |
| continue; |
| } |
| /* |
| * The map entry hasn't been started yet and we've |
| * seen a preceeding null map entry for it so just |
| * delete it from the master map entry list so it |
| * doesn't get in the road. |
| */ |
| list_del_init(&this->list); |
| master_free_mapent_sources(ap->entry, 1); |
| master_free_mapent(ap->entry); |
| continue; |
| } |
| nested = cache_partial_match(nc, this->path); |
| if (nested) { |
| error(ap->logopt, |
| "removing invalid nested null entry %s", |
| nested->key); |
| nested = cache_partial_match(nc, this->path); |
| if (nested) |
| cache_delete(nc, nested->key); |
| } |
| cache_unlock(nc); |
| cont: |
| st_mutex_lock(); |
| |
| state_pipe = this->ap->state_pipe[1]; |
| |
| /* No pipe so mount is needed */ |
| ret = fstat(state_pipe, &st); |
| save_errno = errno; |
| |
| st_mutex_unlock(); |
| |
| if (!ret) |
| check_update_map_sources(this, readall); |
| else if (ret == -1 && save_errno == EBADF) { |
| if (!master_do_mount(this)) { |
| list_del_init(&this->list); |
| master_free_mapent_sources(ap->entry, 1); |
| master_free_mapent(ap->entry); |
| } |
| } |
| } |
| |
| master_mutex_unlock(); |
| pthread_setcancelstate(cur_state, NULL); |
| |
| return 1; |
| } |
| |
| /* The nss source instances end up in reverse order. */ |
| static void list_source_instances(struct map_source *source, struct map_source *instance) |
| { |
| if (!source || !instance) { |
| printf("none"); |
| return; |
| } |
| |
| if (instance->next) |
| list_source_instances(source, instance->next); |
| |
| /* |
| * For convienience we map nss instance type "files" to "file". |
| * Check for that and report corrected instance type. |
| */ |
| if (strcmp(instance->type, "file")) |
| printf("%s ", instance->type); |
| else { |
| if (source->argv && *(source->argv[0]) != '/') |
| printf("files "); |
| else |
| printf("%s ", instance->type); |
| } |
| |
| return; |
| } |
| |
| static void print_map_info(struct map_source *source) |
| { |
| int argc = source->argc; |
| int i, multi, map_num; |
| |
| multi = (source->type && !strcmp(source->type, "multi")); |
| map_num = 1; |
| for (i = 0; i < argc; i++) { |
| if (source->argv[i] && *source->argv[i] != '-') { |
| if (!multi) |
| printf(" map: %s\n", source->argv[i]); |
| else |
| printf(" map[%i]: %s\n", map_num, source->argv[i]); |
| i++; |
| } |
| |
| if (i >= argc) |
| return; |
| |
| if (!strcmp(source->argv[i], "--")) |
| continue; |
| |
| if (source->argv[i]) { |
| int need_newline = 0; |
| int j; |
| |
| if (!multi) |
| printf(" arguments:"); |
| else |
| printf(" arguments[%i]:", map_num); |
| |
| for (j = i; j < source->argc; j++) { |
| if (!strcmp(source->argv[j], "--")) |
| break; |
| printf(" %s", source->argv[j]); |
| i++; |
| need_newline = 1; |
| } |
| if (need_newline) |
| printf("\n"); |
| } |
| if (multi) |
| map_num++; |
| } |
| |
| return; |
| } |
| |
| static int match_type(const char *source, const char *type) |
| { |
| if (!strcmp(source, type)) |
| return 1; |
| /* Sources file and files are synonymous */ |
| if (!strncmp(source, type, 4) && (strlen(source) <= 5)) |
| return 1; |
| return 0; |
| } |
| |
| static char *get_map_name(const char *string) |
| { |
| char *name, *tmp; |
| char *start, *end, *base; |
| |
| tmp = strdup(string); |
| if (!tmp) { |
| printf("error: allocation failure: %s\n", strerror(errno)); |
| return NULL; |
| } |
| |
| base = basename(tmp); |
| end = strchr(base, ','); |
| if (end) |
| *end = '\0'; |
| start = strchr(tmp, '='); |
| if (start) |
| start++; |
| else { |
| char *colon = strrchr(base, ':'); |
| if (colon) |
| start = ++colon; |
| else |
| start = base; |
| } |
| |
| name = strdup(start); |
| if (!name) |
| printf("error: allocation failure: %s\n", strerror(errno)); |
| free(tmp); |
| |
| return name; |
| } |
| |
| static int match_name(struct map_source *source, const char *name) |
| { |
| int argc = source->argc; |
| int ret = 0; |
| int i; |
| |
| /* |
| * This can't work for old style "multi" type sources since |
| * there's no way to know from which map the cache entry came |
| * from and duplicate entries are ignored at map read time. |
| * All we can really do is list all the entries for the given |
| * multi map if one of its map names matches. |
| */ |
| for (i = 0; i < argc; i++) { |
| if (i == 0 || !strcmp(source->argv[i], "--")) { |
| if (i != 0) { |
| i++; |
| if (i >= argc) |
| break; |
| } |
| |
| if (source->argv[i] && *source->argv[i] != '-') { |
| char *map = get_map_name(source->argv[i]); |
| if (!map) |
| break; |
| if (!strcmp(map, name)) { |
| ret = 1; |
| free(map); |
| break; |
| } |
| free(map); |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| int dump_map(struct master *master, const char *type, const char *name) |
| { |
| struct list_head *p, *head; |
| |
| if (list_empty(&master->mounts)) { |
| printf("no master map entries found\n"); |
| return 1; |
| } |
| |
| head = &master->mounts; |
| p = head->next; |
| while (p != head) { |
| struct map_source *source; |
| struct master_mapent *this; |
| struct autofs_point *ap; |
| time_t now = time(NULL); |
| |
| this = list_entry(p, struct master_mapent, list); |
| p = p->next; |
| |
| ap = this->ap; |
| |
| /* |
| * Ensure we actually read indirect map entries so we can |
| * list them. The map reads won't read any indirect map |
| * entries (other than those in a file map) unless the |
| * browse option is set. |
| */ |
| if (ap->type == LKP_INDIRECT) |
| ap->flags |= MOUNT_FLAG_GHOST; |
| |
| /* Read the map content into the cache */ |
| if (lookup_nss_read_map(ap, NULL, now)) |
| lookup_prune_cache(ap, now); |
| else { |
| printf("failed to read map\n"); |
| lookup_close_lookup(ap); |
| continue; |
| } |
| |
| if (!this->maps) { |
| printf("no map sources found for %s\n", ap->path); |
| lookup_close_lookup(ap); |
| continue; |
| } |
| |
| source = this->maps; |
| while (source) { |
| struct map_source *instance; |
| struct mapent *me; |
| |
| instance = NULL; |
| if (source->type) { |
| if (!match_type(source->type, type)) { |
| source = source->next; |
| continue; |
| } |
| if (!match_name(source, name)) { |
| source = source->next; |
| continue; |
| } |
| instance = source; |
| } else { |
| struct map_source *map; |
| |
| map = source->instance; |
| while (map) { |
| if (!match_type(map->type, type)) { |
| map = map->next; |
| continue; |
| } |
| if (!match_name(map, name)) { |
| map = map->next; |
| continue; |
| } |
| instance = map; |
| break; |
| } |
| } |
| |
| if (!instance) { |
| source = source->next; |
| lookup_close_lookup(ap); |
| continue; |
| } |
| |
| me = cache_lookup_first(source->mc); |
| if (!me) |
| printf("no keys found in map\n"); |
| else { |
| do { |
| if (me->source == instance) |
| printf("%s\t%s\n", me->key, me->mapent); |
| } while ((me = cache_lookup_next(source->mc, me))); |
| } |
| |
| lookup_close_lookup(ap); |
| return 1; |
| } |
| lookup_close_lookup(ap); |
| } |
| |
| return 0; |
| } |
| |
| int master_show_mounts(struct master *master) |
| { |
| struct list_head *p, *head; |
| |
| printf("\nautofs dump map information\n" |
| "===========================\n\n"); |
| |
| printf("global options: "); |
| if (!global_options) |
| printf("none configured\n"); |
| else { |
| printf("%s\n", global_options); |
| unsigned int append_options = defaults_get_append_options(); |
| const char *append = append_options ? "will" : "will not"; |
| printf("global options %s be appended to map entries\n", append); |
| } |
| |
| if (list_empty(&master->mounts)) { |
| printf("no master map entries found\n\n"); |
| return 1; |
| } |
| |
| head = &master->mounts; |
| p = head->next; |
| while (p != head) { |
| struct map_source *source; |
| struct master_mapent *this; |
| struct autofs_point *ap; |
| time_t now = time(NULL); |
| unsigned int count = 0; |
| |
| this = list_entry(p, struct master_mapent, list); |
| p = p->next; |
| |
| ap = this->ap; |
| |
| printf("\nMount point: %s\n", ap->path); |
| |
| printf("\nsource(s):\n"); |
| |
| /* |
| * Ensure we actually read indirect map entries so we can |
| * list them. The map reads won't read any indirect map |
| * entries (other than those in a file map) unless the |
| * browse option is set. |
| */ |
| if (ap->type == LKP_INDIRECT) |
| ap->flags |= MOUNT_FLAG_GHOST; |
| |
| /* Read the map content into the cache */ |
| if (lookup_nss_read_map(ap, NULL, now)) |
| lookup_prune_cache(ap, now); |
| else { |
| printf(" failed to read map\n\n"); |
| continue; |
| } |
| |
| if (!this->maps) { |
| printf(" no map sources found\n\n"); |
| continue; |
| } |
| |
| source = this->maps; |
| while (source) { |
| struct mapent *me; |
| |
| if (source->type) |
| printf("\n type: %s\n", source->type); |
| else { |
| printf("\n instance type(s): "); |
| list_source_instances(source, source->instance); |
| printf("\n"); |
| } |
| |
| if (source->argc >= 1) { |
| print_map_info(source); |
| if (count && ap->type == LKP_INDIRECT) |
| printf(" duplicate indirect map entry" |
| " will be ignored at run time\n"); |
| } |
| |
| printf("\n"); |
| |
| me = cache_lookup_first(source->mc); |
| if (!me) |
| printf(" no keys found in map\n"); |
| else { |
| do { |
| printf(" %s | %s\n", me->key, me->mapent); |
| } while ((me = cache_lookup_next(source->mc, me))); |
| } |
| |
| count++; |
| |
| source = source->next; |
| } |
| |
| lookup_close_lookup(ap); |
| |
| printf("\n"); |
| } |
| |
| return 1; |
| } |
| |
| int master_list_empty(struct master *master) |
| { |
| int res = 0; |
| |
| master_mutex_lock(); |
| if (list_empty(&master->mounts)) |
| res = 1; |
| master_mutex_unlock(); |
| |
| return res; |
| } |
| |
| int master_done(struct master *master) |
| { |
| struct list_head *head, *p; |
| struct master_mapent *entry; |
| int res = 0; |
| |
| head = &master->completed; |
| p = head->next; |
| while (p != head) { |
| entry = list_entry(p, struct master_mapent, join); |
| p = p->next; |
| list_del(&entry->join); |
| pthread_join(entry->thid, NULL); |
| master_free_mapent_sources(entry, 1); |
| master_free_mapent(entry); |
| } |
| if (list_empty(&master->mounts)) |
| res = 1; |
| |
| return res; |
| } |
| |
| inline unsigned int master_get_logopt(void) |
| { |
| return master_list ? master_list->logopt : LOGOPT_NONE; |
| } |
| |
| int master_kill(struct master *master) |
| { |
| if (!list_empty(&master->mounts)) |
| return 0; |
| |
| if (master->name) |
| free(master->name); |
| |
| cache_release_null_cache(master); |
| free(master); |
| |
| return 1; |
| } |
| |
| void dump_master(struct master *master) |
| { |
| struct list_head *p, *head; |
| |
| head = &master->mounts; |
| list_for_each(p, head) { |
| struct master_mapent *this = list_entry(p, struct master_mapent, list); |
| logmsg("path %s", this->path); |
| } |
| } |