|
Packit |
8480eb |
/* ----------------------------------------------------------------------- *
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* state.c - state machine functions.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* Copyright 2006 Ian Kent <raven@themaw.net> - All Rights Reserved
|
|
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 <sys/ioctl.h>
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
#include "automount.h"
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Attribute to create detached thread */
|
|
Packit |
8480eb |
extern pthread_attr_t th_attr_detached;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
struct state_queue {
|
|
Packit |
8480eb |
pthread_t thid;
|
|
Packit |
8480eb |
struct list_head list;
|
|
Packit |
8480eb |
struct list_head pending;
|
|
Packit |
8480eb |
struct autofs_point *ap;
|
|
Packit |
8480eb |
enum states state;
|
|
Packit |
8480eb |
unsigned int busy;
|
|
Packit |
8480eb |
unsigned int done;
|
|
Packit |
8480eb |
unsigned int cancel;
|
|
Packit |
8480eb |
};
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
Packit |
8480eb |
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
|
Packit |
8480eb |
static unsigned int signaled = 0;
|
|
Packit |
8480eb |
static LIST_HEAD(state_queue);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void st_set_thid(struct autofs_point *, pthread_t);
|
|
Packit |
8480eb |
static void st_set_done(struct autofs_point *ap);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void st_mutex_lock(void)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status = pthread_mutex_lock(&mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void st_mutex_unlock(void)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status = pthread_mutex_unlock(&mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void dump_state_queue(void)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *head = &state_queue;
|
|
Packit |
8480eb |
struct list_head *p, *q;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
logmsg("dumping queue");
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, head) {
|
|
Packit |
8480eb |
struct state_queue *entry;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
entry = list_entry(p, struct state_queue, list);
|
|
Packit |
8480eb |
logmsg("queue list head path %s state %d busy %d",
|
|
Packit |
8480eb |
entry->ap->path, entry->state, entry->busy);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(q, &entry->pending) {
|
|
Packit |
8480eb |
struct state_queue *this;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
this = list_entry(q, struct state_queue, pending);
|
|
Packit |
8480eb |
logmsg("queue list entry path %s state %d busy %d",
|
|
Packit |
8480eb |
this->ap->path, this->state, this->busy);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void nextstate(int statefd, enum states next)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
char buf[MAX_ERR_BUF];
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (write(statefd, &next, sizeof(next)) != sizeof(next)) {
|
|
Packit |
8480eb |
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
Packit |
8480eb |
logerr("write failed %s", estr);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Handle expire thread cleanup and return the next state the system
|
|
Packit |
8480eb |
* should enter as a result.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
void expire_cleanup(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
pthread_t thid = pthread_self();
|
|
Packit |
8480eb |
struct expire_args *ec;
|
|
Packit |
8480eb |
struct autofs_point *ap;
|
|
Packit |
8480eb |
int success;
|
|
Packit |
8480eb |
enum states next = ST_INVAL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ec = (struct expire_args *) arg;
|
|
Packit |
8480eb |
ap = ec->ap;
|
|
Packit |
8480eb |
success = ec->status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_lock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt,
|
|
Packit |
8480eb |
"got thid %lu path %s stat %d",
|
|
Packit |
8480eb |
(unsigned long) thid, ap->path, success);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Check to see if expire process finished */
|
|
Packit |
8480eb |
if (thid == ap->exp_thread) {
|
|
Packit |
8480eb |
unsigned int idle;
|
|
Packit |
8480eb |
int rv;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap->exp_thread = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
switch (ap->state) {
|
|
Packit |
8480eb |
case ST_EXPIRE:
|
|
Packit |
8480eb |
/* FALLTHROUGH */
|
|
Packit |
8480eb |
case ST_PRUNE:
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* If we're a submount and we've just pruned or
|
|
Packit |
8480eb |
* expired everything away, try to shut down.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* Since we use the the fact that a mount will not
|
|
Packit |
8480eb |
* expire for at least ap->exp_timeout to avoid a
|
|
Packit |
8480eb |
* mount <-> expire race we need to wait before
|
|
Packit |
8480eb |
* letting a submount expire away. We also need
|
|
Packit |
8480eb |
* them to go away fairly quickly so the owner
|
|
Packit |
8480eb |
* mount expires in a reasonable time. Just skip
|
|
Packit |
8480eb |
* one expire check after it's no longer busy before
|
|
Packit |
8480eb |
* allowing it to shutdown.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* But if this mount point is an amd format map it
|
|
Packit |
8480eb |
* is better to keep the mount around longer. This
|
|
Packit |
8480eb |
* is because of the common heavy reuse of maps in
|
|
Packit |
8480eb |
* amd maps and we want to try and avoid constantly
|
|
Packit |
8480eb |
* re-reading large maps.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
if (ap->submount && !success) {
|
|
Packit |
8480eb |
rv = ops->askumount(ap->logopt, ap->ioctlfd, &idle);
|
|
Packit |
8480eb |
if (!rv && idle && ap->submount > 1) {
|
|
Packit |
8480eb |
struct map_source *map = ap->entry->maps;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ap->submount > 4 ||
|
|
Packit |
8480eb |
!(map->flags & MAP_FLAG_FORMAT_AMD)) {
|
|
Packit |
8480eb |
next = ST_SHUTDOWN_PENDING;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ap->submount++;
|
|
Packit |
8480eb |
} else if (ap->submount > 1)
|
|
Packit |
8480eb |
ap->submount = 1;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ap->state == ST_EXPIRE && !ap->submount)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* FALLTHROUGH */
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case ST_READY:
|
|
Packit |
8480eb |
next = ST_READY;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case ST_SHUTDOWN_PENDING:
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* If we reveive a mount request while trying to
|
|
Packit |
8480eb |
* shutdown return to ready state unless we have
|
|
Packit |
8480eb |
* been signaled to shutdown.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
rv = ops->askumount(ap->logopt, ap->ioctlfd, &idle);
|
|
Packit |
8480eb |
if (!rv && !idle && !ap->shutdown) {
|
|
Packit |
8480eb |
next = ST_READY;
|
|
Packit |
8480eb |
if (!ap->submount)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq);
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
next = ST_SHUTDOWN;
|
|
Packit |
8480eb |
#ifdef ENABLE_IGNORE_BUSY_MOUNTS
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
#else
|
|
Packit |
8480eb |
if (success == 0)
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Failed shutdown returns to ready */
|
|
Packit |
8480eb |
warn(ap->logopt, "filesystem %s still busy", ap->path);
|
|
Packit |
8480eb |
if (!ap->submount)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq);
|
|
Packit |
8480eb |
next = ST_READY;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
#endif
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case ST_SHUTDOWN_FORCE:
|
|
Packit |
8480eb |
next = ST_SHUTDOWN;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
default:
|
|
Packit |
8480eb |
error(ap->logopt, "bad state %d", ap->state);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (next != ST_INVAL) {
|
|
Packit |
8480eb |
debug(ap->logopt,
|
|
Packit |
8480eb |
"sigchld: exp %lu finished, switching from %d to %d",
|
|
Packit |
8480eb |
(unsigned long) thid, ap->state, next);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_set_done(ap);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (next != ST_INVAL)
|
|
Packit |
8480eb |
__st_add_task(ap, next);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_unlock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static unsigned int st_ready(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
debug(ap->logopt,
|
|
Packit |
8480eb |
"st_ready(): state = %d path %s", ap->state, ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap->shutdown = 0;
|
|
Packit |
8480eb |
ap->state = ST_READY;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
enum expire {
|
|
Packit |
8480eb |
EXP_ERROR,
|
|
Packit |
8480eb |
EXP_STARTED,
|
|
Packit |
8480eb |
EXP_PARTIAL
|
|
Packit |
8480eb |
};
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Generate expiry messages. If "now" is true, timeouts are ignored.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* Returns: ERROR - error
|
|
Packit |
8480eb |
* STARTED - expiry process started
|
|
Packit |
8480eb |
* DONE - nothing to expire
|
|
Packit |
8480eb |
* PARTIAL - partial expire
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void expire_proc_cleanup(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct expire_args *ea;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ea = (struct expire_args *) arg;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_unlock(&ea->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_cond_destroy(&ea->cond);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_destroy(&ea->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
free(ea);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit Service |
467561 |
static enum expire expire_proc(struct autofs_point *ap, int how)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
pthread_t thid;
|
|
Packit |
8480eb |
struct expire_args *ea;
|
|
Packit |
8480eb |
void *(*expire)(void *);
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
assert(ap->exp_thread == 0);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ea = malloc(sizeof(struct expire_args));
|
|
Packit |
8480eb |
if (!ea) {
|
|
Packit |
8480eb |
error(ap->logopt, "failed to malloc expire cond struct");
|
|
Packit |
8480eb |
return EXP_ERROR;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_init(&ea->mutex, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_cond_init(&ea->cond, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_lock(&ea->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ea->ap = ap;
|
|
Packit Service |
467561 |
ea->how = how;
|
|
Packit |
8480eb |
ea->status = 1;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ap->type == LKP_INDIRECT)
|
|
Packit |
8480eb |
expire = expire_proc_indirect;
|
|
Packit |
8480eb |
else
|
|
Packit |
8480eb |
expire = expire_proc_direct;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_create(&thid, &th_attr_detached, expire, ea);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"expire thread create for %s failed", ap->path);
|
|
Packit |
8480eb |
expire_proc_cleanup((void *) ea);
|
|
Packit |
8480eb |
return EXP_ERROR;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ap->exp_thread = thid;
|
|
Packit |
8480eb |
st_set_thid(ap, thid);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_push(expire_proc_cleanup, ea);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt, "exp_proc = %lu path %s",
|
|
Packit |
8480eb |
(unsigned long) ap->exp_thread, ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ea->signaled = 0;
|
|
Packit |
8480eb |
while (!ea->signaled) {
|
|
Packit |
8480eb |
status = pthread_cond_wait(&ea->cond, &ea->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return EXP_STARTED;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void do_readmap_cleanup(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct readmap_args *ra;
|
|
Packit |
8480eb |
struct autofs_point *ap;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ra = (struct readmap_args *) arg;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap = ra->ap;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_lock();
|
|
Packit |
8480eb |
ap->readmap_thread = 0;
|
|
Packit |
8480eb |
st_set_done(ap);
|
|
Packit |
8480eb |
st_ready(ap);
|
|
Packit |
8480eb |
st_mutex_unlock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
free(ra);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit Service |
c59270 |
static void tree_mnts_cleanup(void *arg)
|
|
Packit Service |
c59270 |
{
|
|
Packit Service |
c59270 |
struct mnt_list *mnts = (struct mnt_list *) arg;
|
|
Packit Service |
c59270 |
tree_free_mnt_tree(mnts);
|
|
Packit Service |
c59270 |
return;
|
|
Packit Service |
c59270 |
}
|
|
Packit Service |
c59270 |
|
|
Packit Service |
c59270 |
static void do_readmap_mount(struct autofs_point *ap, struct mnt_list *mnts,
|
|
Packit |
8480eb |
struct map_source *map, struct mapent *me, time_t now)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mapent_cache *nc;
|
|
Packit |
8480eb |
struct mapent *ne, *nested, *valid;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
nc = ap->entry->master->nc;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ne = cache_lookup_distinct(nc, me->key);
|
|
Packit |
8480eb |
if (!ne) {
|
|
Packit |
8480eb |
nested = cache_partial_match(nc, me->key);
|
|
Packit |
8480eb |
if (nested) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"removing invalid nested null entry %s",
|
|
Packit |
8480eb |
nested->key);
|
|
Packit |
8480eb |
nested = cache_partial_match(nc, me->key);
|
|
Packit |
8480eb |
if (nested)
|
|
Packit |
8480eb |
cache_delete(nc, nested->key);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->age < now || (ne && map->master_line > ne->age)) {
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* The map instance may have changed, such as the map name or
|
|
Packit |
8480eb |
* the mount options, but the direct map entry may still exist
|
|
Packit |
8480eb |
* in one of the other maps. If so then update the new cache
|
|
Packit |
8480eb |
* entry device and inode so we can find it at lookup. Later,
|
|
Packit |
8480eb |
* the mount for the new cache entry will just update the
|
|
Packit |
8480eb |
* timeout.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* TODO: how do we recognise these orphaned map instances. We
|
|
Packit |
8480eb |
* can't just delete these instances when the cache becomes
|
|
Packit |
8480eb |
* empty because that is a valid state for a master map entry.
|
|
Packit |
8480eb |
* This is becuase of the requirement to continue running with
|
|
Packit |
8480eb |
* an empty cache awaiting a map re-load.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
valid = lookup_source_valid_mapent(ap, me->key, LKP_DISTINCT);
|
|
Packit |
8480eb |
if (valid && valid->mc == me->mc) {
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* We've found a map entry that has been removed from
|
|
Packit |
8480eb |
* the current cache so there is no need to update it.
|
|
Packit |
8480eb |
* The stale entry will be dealt with when we prune the
|
|
Packit |
8480eb |
* cache later.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
cache_unlock(valid->mc);
|
|
Packit |
8480eb |
valid = NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
if (valid) {
|
|
Packit |
8480eb |
struct mapent_cache *vmc = valid->mc;
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
time_t timeout;
|
|
Packit |
8480eb |
time_t runfreq;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_unlock(vmc);
|
|
Packit |
8480eb |
debug(ap->logopt,
|
|
Packit |
8480eb |
"updating cache entry for valid direct trigger %s",
|
|
Packit |
8480eb |
me->key);
|
|
Packit |
8480eb |
cache_writelock(vmc);
|
|
Packit |
8480eb |
valid = cache_lookup_distinct(vmc, me->key);
|
|
Packit |
8480eb |
if (!valid) {
|
|
Packit |
8480eb |
cache_unlock(vmc);
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"failed to find expected existing valid map entry");
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
/* Take over the mount if there is one */
|
|
Packit |
8480eb |
valid->ioctlfd = me->ioctlfd;
|
|
Packit |
8480eb |
me->ioctlfd = -1;
|
|
Packit |
8480eb |
/* Set device and inode number of the new mapent */
|
|
Packit |
8480eb |
cache_set_ino_index(vmc, me->key, me->dev, me->ino);
|
|
Packit |
8480eb |
cache_unlock(vmc);
|
|
Packit |
8480eb |
/* Set timeout and calculate the expire run frequency */
|
|
Packit |
8480eb |
timeout = get_exp_timeout(ap, map);
|
|
Packit |
8480eb |
ops->timeout(ap->logopt, valid->ioctlfd, timeout);
|
|
Packit |
8480eb |
if (timeout) {
|
|
Packit |
8480eb |
runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO;
|
|
Packit |
8480eb |
if (ap->exp_runfreq)
|
|
Packit |
8480eb |
ap->exp_runfreq = min(ap->exp_runfreq, runfreq);
|
|
Packit |
8480eb |
else
|
|
Packit |
8480eb |
ap->exp_runfreq = runfreq;
|
|
Packit |
8480eb |
}
|
|
Packit Service |
c59270 |
} else if (!tree_is_mounted(mnts, me->key, MNTS_REAL))
|
|
Packit Service |
c59270 |
do_umount_autofs_direct(ap, mnts, me);
|
|
Packit |
8480eb |
else
|
|
Packit |
8480eb |
debug(ap->logopt,
|
|
Packit |
8480eb |
"%s is mounted", me->key);
|
|
Packit |
8480eb |
} else
|
|
Packit Service |
c59270 |
do_mount_autofs_direct(ap, mnts, me, get_exp_timeout(ap, map));
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void *do_readmap(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct autofs_point *ap;
|
|
Packit |
8480eb |
struct map_source *map;
|
|
Packit |
8480eb |
struct mapent_cache *nc, *mc;
|
|
Packit |
8480eb |
struct readmap_args *ra;
|
|
Packit Service |
c59270 |
struct mnt_list *mnts;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
time_t now;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ra = (struct readmap_args *) arg;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_lock(&ra->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap = ra->ap;
|
|
Packit |
8480eb |
now = ra->now;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ra->signaled = 1;
|
|
Packit |
8480eb |
status = pthread_cond_signal(&ra->cond);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
error(ap->logopt, "failed to signal expire condition");
|
|
Packit |
8480eb |
pthread_mutex_unlock(&ra->mutex);
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_unlock(&ra->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_push(do_readmap_cleanup, ra);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
info(ap->logopt, "re-reading map for %s", ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = lookup_nss_read_map(ap, NULL, now);
|
|
Packit |
8480eb |
if (!status)
|
|
Packit |
8480eb |
pthread_exit(NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ap->type == LKP_INDIRECT) {
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
time_t timeout = get_exp_timeout(ap, ap->entry->maps);
|
|
Packit |
8480eb |
ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO;
|
|
Packit |
8480eb |
ops->timeout(ap->logopt, ap->ioctlfd, timeout);
|
|
Packit |
8480eb |
lookup_prune_cache(ap, now);
|
|
Packit |
8480eb |
status = lookup_ghost(ap, ap->path);
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
struct mapent *me;
|
|
Packit |
8480eb |
unsigned int append_alarm = !ap->exp_runfreq;
|
|
Packit |
8480eb |
|
|
Packit Service |
c59270 |
mnts = tree_make_mnt_tree(_PROC_MOUNTS, "/");
|
|
Packit Service |
c59270 |
pthread_cleanup_push(tree_mnts_cleanup, mnts);
|
|
Packit |
8480eb |
nc = ap->entry->master->nc;
|
|
Packit |
8480eb |
cache_readlock(nc);
|
|
Packit |
8480eb |
pthread_cleanup_push(cache_lock_cleanup, nc);
|
|
Packit |
8480eb |
master_source_readlock(ap->entry);
|
|
Packit |
8480eb |
pthread_cleanup_push(master_source_lock_cleanup, ap->entry);
|
|
Packit |
8480eb |
map = ap->entry->maps;
|
|
Packit |
8480eb |
while (map) {
|
|
Packit |
8480eb |
/* Is map source up to date or no longer valid */
|
|
Packit |
8480eb |
if (!map->stale && !check_stale_instances(map)) {
|
|
Packit |
8480eb |
map = map->next;
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
mc = map->mc;
|
|
Packit |
8480eb |
pthread_cleanup_push(cache_lock_cleanup, mc);
|
|
Packit |
8480eb |
cache_readlock(mc);
|
|
Packit |
8480eb |
me = cache_enumerate(mc, NULL);
|
|
Packit |
8480eb |
while (me) {
|
|
Packit Service |
c59270 |
do_readmap_mount(ap, mnts, map, me, now);
|
|
Packit |
8480eb |
me = cache_enumerate(mc, me);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
lookup_prune_one_cache(ap, map->mc, now);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
clear_stale_instances(map);
|
|
Packit |
8480eb |
map->stale = 0;
|
|
Packit |
8480eb |
map = map->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* If the direct mount map was empty at startup no expire
|
|
Packit |
8480eb |
* alarm will have been added. So add it here if there are
|
|
Packit |
8480eb |
* now map entries.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
if (append_alarm && ap->exp_runfreq)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq +
|
|
Packit |
8480eb |
rand() % ap->exp_runfreq);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit Service |
c59270 |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void st_readmap_cleanup(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct readmap_args *ra;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ra = (struct readmap_args *) arg;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_unlock(&ra->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_cond_destroy(&ra->cond);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_destroy(&ra->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static unsigned int st_readmap(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
pthread_t thid;
|
|
Packit |
8480eb |
struct readmap_args *ra;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
int now = monotonic_time(NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt, "state %d path %s", ap->state, ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
assert(ap->state == ST_READY);
|
|
Packit |
8480eb |
assert(ap->readmap_thread == 0);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap->state = ST_READMAP;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ra = malloc(sizeof(struct readmap_args));
|
|
Packit |
8480eb |
if (!ra) {
|
|
Packit |
8480eb |
error(ap->logopt, "failed to malloc readmap cond struct");
|
|
Packit |
8480eb |
/* It didn't work: return to ready */
|
|
Packit |
8480eb |
st_ready(ap);
|
|
Packit |
8480eb |
if (!ap->submount)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_init(&ra->mutex, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_cond_init(&ra->cond, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_lock(&ra->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ra->ap = ap;
|
|
Packit |
8480eb |
ra->now = now;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_create(&thid, &th_attr_detached, do_readmap, ra);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
error(ap->logopt, "readmap thread create failed");
|
|
Packit |
8480eb |
st_readmap_cleanup(ra);
|
|
Packit |
8480eb |
free(ra);
|
|
Packit |
8480eb |
/* It didn't work: return to ready */
|
|
Packit |
8480eb |
st_ready(ap);
|
|
Packit |
8480eb |
if (!ap->submount)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ap->readmap_thread = thid;
|
|
Packit |
8480eb |
st_set_thid(ap, thid);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_push(st_readmap_cleanup, ra);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ra->signaled = 0;
|
|
Packit |
8480eb |
while (!ra->signaled) {
|
|
Packit |
8480eb |
status = pthread_cond_wait(&ra->cond, &ra->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static unsigned int st_prepare_shutdown(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int exp;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt, "state %d path %s", ap->state, ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
assert(ap->state == ST_READY || ap->state == ST_EXPIRE);
|
|
Packit |
8480eb |
ap->state = ST_SHUTDOWN_PENDING;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Unmount everything */
|
|
Packit |
8480eb |
exp = expire_proc(ap, 1);
|
|
Packit |
8480eb |
switch (exp) {
|
|
Packit |
8480eb |
case EXP_ERROR:
|
|
Packit |
8480eb |
case EXP_PARTIAL:
|
|
Packit |
8480eb |
/* It didn't work: return to ready */
|
|
Packit |
8480eb |
if (!ap->submount)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq);
|
|
Packit |
8480eb |
st_ready(ap);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case EXP_STARTED:
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static unsigned int st_force_shutdown(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int exp;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt, "state %d path %s", ap->state, ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
assert(ap->state == ST_READY || ap->state == ST_EXPIRE);
|
|
Packit |
8480eb |
ap->state = ST_SHUTDOWN_FORCE;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Unmount everything */
|
|
Packit |
8480eb |
exp = expire_proc(ap, 1);
|
|
Packit |
8480eb |
switch (exp) {
|
|
Packit |
8480eb |
case EXP_ERROR:
|
|
Packit |
8480eb |
case EXP_PARTIAL:
|
|
Packit |
8480eb |
/* It didn't work: return to ready */
|
|
Packit |
8480eb |
if (!ap->submount)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq);
|
|
Packit |
8480eb |
st_ready(ap);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case EXP_STARTED:
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static unsigned int st_shutdown(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
debug(ap->logopt, "state %d path %s", ap->state, ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
assert(ap->state == ST_SHUTDOWN_PENDING || ap->state == ST_SHUTDOWN_FORCE);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap->state = ST_SHUTDOWN;
|
|
Packit |
8480eb |
nextstate(ap->state_pipe[1], ST_SHUTDOWN);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static unsigned int st_prune(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
debug(ap->logopt, "state %d path %s", ap->state, ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
assert(ap->state == ST_READY);
|
|
Packit |
8480eb |
ap->state = ST_PRUNE;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
switch (expire_proc(ap, 1)) {
|
|
Packit |
8480eb |
case EXP_ERROR:
|
|
Packit |
8480eb |
case EXP_PARTIAL:
|
|
Packit |
8480eb |
if (!ap->submount)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq);
|
|
Packit |
8480eb |
st_ready(ap);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case EXP_STARTED:
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static unsigned int st_expire(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
debug(ap->logopt, "state %d path %s", ap->state, ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
assert(ap->state == ST_READY);
|
|
Packit |
8480eb |
ap->state = ST_EXPIRE;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
switch (expire_proc(ap, 0)) {
|
|
Packit |
8480eb |
case EXP_ERROR:
|
|
Packit |
8480eb |
case EXP_PARTIAL:
|
|
Packit |
8480eb |
if (!ap->submount)
|
|
Packit |
8480eb |
alarm_add(ap, ap->exp_runfreq);
|
|
Packit |
8480eb |
st_ready(ap);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case EXP_STARTED:
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static struct state_queue *st_alloc_task(struct autofs_point *ap, enum states state)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct state_queue *task;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
task = malloc(sizeof(struct state_queue));
|
|
Packit |
8480eb |
if (!task)
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
memset(task, 0, sizeof(struct state_queue));
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
task->ap = ap;
|
|
Packit |
8480eb |
task->state = state;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
INIT_LIST_HEAD(&task->list);
|
|
Packit |
8480eb |
INIT_LIST_HEAD(&task->pending);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return task;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Insert alarm entry on ordered list.
|
|
Packit |
8480eb |
* State queue mutex and ap state mutex, in that order, must be held.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
int __st_add_task(struct autofs_point *ap, enum states state)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *head;
|
|
Packit |
8480eb |
struct list_head *p, *q;
|
|
Packit |
8480eb |
struct state_queue *new;
|
|
Packit |
8480eb |
unsigned int empty = 1;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Task termination marker, poke state machine */
|
|
Packit |
8480eb |
if (state == ST_READY) {
|
|
Packit |
8480eb |
st_ready(ap);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
signaled = 1;
|
|
Packit |
8480eb |
status = pthread_cond_signal(&cond;;
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ap->state == ST_SHUTDOWN)
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (state == ST_SHUTDOWN)
|
|
Packit |
8480eb |
return st_shutdown(ap);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &state_queue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Add to task queue for autofs_point ? */
|
|
Packit |
8480eb |
list_for_each(p, head) {
|
|
Packit |
8480eb |
struct state_queue *task;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
task = list_entry(p, struct state_queue, list);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (task->ap != ap)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
empty = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Don't add duplicate tasks */
|
|
Packit |
8480eb |
if ((task->state == state && !task->done) ||
|
|
Packit |
8480eb |
(ap->state == ST_SHUTDOWN_PENDING ||
|
|
Packit |
8480eb |
ap->state == ST_SHUTDOWN_FORCE))
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* No pending tasks */
|
|
Packit |
8480eb |
if (list_empty(&task->pending)) {
|
|
Packit |
8480eb |
new = st_alloc_task(ap, state);
|
|
Packit |
8480eb |
if (new)
|
|
Packit |
8480eb |
list_add_tail(&new->pending, &task->pending);
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(q, &task->pending) {
|
|
Packit |
8480eb |
struct state_queue *p_task;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
p_task = list_entry(q, struct state_queue, pending);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (p_task->state == state ||
|
|
Packit |
8480eb |
(ap->state == ST_SHUTDOWN_PENDING ||
|
|
Packit |
8480eb |
ap->state == ST_SHUTDOWN_FORCE))
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
new = st_alloc_task(ap, state);
|
|
Packit |
8480eb |
if (new)
|
|
Packit |
8480eb |
list_add_tail(&new->pending, &task->pending);
|
|
Packit |
8480eb |
done:
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (empty) {
|
|
Packit |
8480eb |
new = st_alloc_task(ap, state);
|
|
Packit |
8480eb |
if (new)
|
|
Packit |
8480eb |
list_add(&new->list, head);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
signaled = 1;
|
|
Packit |
8480eb |
status = pthread_cond_signal(&cond;;
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int st_add_task(struct autofs_point *ap, enum states state)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int ret;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_lock();
|
|
Packit |
8480eb |
ret = __st_add_task(ap, state);
|
|
Packit |
8480eb |
st_mutex_unlock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return ret;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Remove state queue tasks for ap.
|
|
Packit |
8480eb |
* State queue mutex and ap state mutex, in that order, must be held.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
void st_remove_tasks(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *head;
|
|
Packit |
8480eb |
struct list_head *p, *q;
|
|
Packit |
8480eb |
struct state_queue *task, *waiting;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_lock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &state_queue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (list_empty(head)) {
|
|
Packit |
8480eb |
st_mutex_unlock();
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
p = head->next;
|
|
Packit |
8480eb |
while (p != head) {
|
|
Packit |
8480eb |
task = list_entry(p, struct state_queue, list);
|
|
Packit |
8480eb |
p = p->next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (task->ap != ap)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (task->busy) {
|
|
Packit |
8480eb |
/* We only cancel readmap, prune and expire */
|
|
Packit |
8480eb |
if (task->state == ST_EXPIRE ||
|
|
Packit |
8480eb |
task->state == ST_PRUNE ||
|
|
Packit |
8480eb |
task->state == ST_READMAP)
|
|
Packit |
8480eb |
task->cancel = 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
q = (&task->pending)->next;
|
|
Packit |
8480eb |
while(q != &task->pending) {
|
|
Packit |
8480eb |
waiting = list_entry(q, struct state_queue, pending);
|
|
Packit |
8480eb |
q = q->next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Don't remove existing shutdown task */
|
|
Packit |
8480eb |
if (waiting->state != ST_SHUTDOWN_PENDING &&
|
|
Packit |
8480eb |
waiting->state != ST_SHUTDOWN_FORCE) {
|
|
Packit |
8480eb |
list_del(&waiting->pending);
|
|
Packit |
8480eb |
free(waiting);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
signaled = 1;
|
|
Packit |
8480eb |
status = pthread_cond_signal(&cond;;
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_unlock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static int st_task_active(struct autofs_point *ap, enum states state)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *head;
|
|
Packit |
8480eb |
struct list_head *p, *q;
|
|
Packit |
8480eb |
struct state_queue *task, *waiting;
|
|
Packit |
8480eb |
unsigned int active = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_lock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &state_queue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, head) {
|
|
Packit |
8480eb |
task = list_entry(p, struct state_queue, list);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (task->ap != ap)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (task->state == state) {
|
|
Packit |
8480eb |
active = 1;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (state == ST_ANY) {
|
|
Packit |
8480eb |
active = 1;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(q, &task->pending) {
|
|
Packit |
8480eb |
waiting = list_entry(q, struct state_queue, pending);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (waiting->state == state) {
|
|
Packit |
8480eb |
active = 1;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (state == ST_ANY) {
|
|
Packit |
8480eb |
active = 1;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_unlock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return active;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int st_wait_task(struct autofs_point *ap, enum states state, unsigned int seconds)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
unsigned int wait = 0;
|
|
Packit |
8480eb |
unsigned int duration = 0;
|
|
Packit |
8480eb |
int ret = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (1) {
|
|
Packit |
8480eb |
struct timespec t = { 0, 200000000 };
|
|
Packit |
8480eb |
struct timespec r;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (nanosleep(&t, &r) == -1 && errno == EINTR)
|
|
Packit |
8480eb |
memcpy(&t, &r, sizeof(struct timespec));
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (wait++ == 4) {
|
|
Packit |
8480eb |
wait = 0;
|
|
Packit |
8480eb |
duration++;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!st_task_active(ap, state)) {
|
|
Packit |
8480eb |
ret = 1;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (seconds && duration >= seconds)
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return ret;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int st_wait_state(struct autofs_point *ap, enum states state)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
while (1) {
|
|
Packit |
8480eb |
struct timespec t = { 0, 200000000 };
|
|
Packit |
8480eb |
struct timespec r;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (nanosleep(&t, &r) == -1 && errno == EINTR)
|
|
Packit |
8480eb |
memcpy(&t, &r, sizeof(struct timespec));
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_lock();
|
|
Packit |
8480eb |
if (ap->state == state) {
|
|
Packit |
8480eb |
st_mutex_unlock();
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
st_mutex_unlock();
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static int run_state_task(struct state_queue *task)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct autofs_point *ap;
|
|
Packit |
8480eb |
enum states next_state, state;
|
|
Packit |
8480eb |
unsigned long ret = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap = task->ap;
|
|
Packit |
8480eb |
next_state = task->state;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
state = ap->state;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (next_state != state) {
|
|
Packit |
8480eb |
switch (next_state) {
|
|
Packit |
8480eb |
case ST_PRUNE:
|
|
Packit |
8480eb |
ret = st_prune(ap);
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case ST_EXPIRE:
|
|
Packit |
8480eb |
ret = st_expire(ap);
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case ST_READMAP:
|
|
Packit |
8480eb |
ret = st_readmap(ap);
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case ST_SHUTDOWN_PENDING:
|
|
Packit |
8480eb |
ret = st_prepare_shutdown(ap);
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
case ST_SHUTDOWN_FORCE:
|
|
Packit |
8480eb |
ret = st_force_shutdown(ap);
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
default:
|
|
Packit |
8480eb |
error(ap->logopt, "bad next state %d", next_state);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return ret;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void st_set_thid(struct autofs_point *ap, pthread_t thid)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *p, *head = &state_queue;
|
|
Packit |
8480eb |
struct state_queue *task;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, head) {
|
|
Packit |
8480eb |
task = list_entry(p, struct state_queue, list);
|
|
Packit |
8480eb |
if (task->ap == ap) {
|
|
Packit |
8480eb |
task->thid = thid;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Requires state mutex to be held */
|
|
Packit |
8480eb |
static void st_set_done(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *p, *head;
|
|
Packit |
8480eb |
struct state_queue *task;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &state_queue;
|
|
Packit |
8480eb |
list_for_each(p, head) {
|
|
Packit |
8480eb |
task = list_entry(p, struct state_queue, list);
|
|
Packit |
8480eb |
if (task->ap == ap) {
|
|
Packit |
8480eb |
task->done = 1;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void *st_queue_handler(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *head;
|
|
Packit |
8480eb |
struct list_head *p;
|
|
Packit |
8480eb |
int status, ret;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
st_mutex_lock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (1) {
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* If the state queue list is empty, wait until an
|
|
Packit |
8480eb |
* entry is added.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
head = &state_queue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (list_empty(head)) {
|
|
Packit |
8480eb |
status = pthread_cond_wait(&cond, &mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
p = head->next;
|
|
Packit |
8480eb |
while(p != head) {
|
|
Packit |
8480eb |
struct state_queue *task;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
task = list_entry(p, struct state_queue, list);
|
|
Packit |
8480eb |
p = p->next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (task->cancel) {
|
|
Packit |
8480eb |
list_del(&task->list);
|
|
Packit |
8480eb |
free(task);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
task->busy = 1;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ret = run_state_task(task);
|
|
Packit |
8480eb |
if (!ret) {
|
|
Packit |
8480eb |
list_del(&task->list);
|
|
Packit |
8480eb |
free(task);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (1) {
|
|
Packit |
8480eb |
signaled = 0;
|
|
Packit |
8480eb |
while (!signaled) {
|
|
Packit |
8480eb |
status = pthread_cond_wait(&cond, &mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &state_queue;
|
|
Packit |
8480eb |
p = head->next;
|
|
Packit |
8480eb |
while (p != head) {
|
|
Packit |
8480eb |
struct state_queue *task, *next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
task = list_entry(p, struct state_queue, list);
|
|
Packit |
8480eb |
p = p->next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Task may have been canceled before it started */
|
|
Packit |
8480eb |
if (!task->thid && task->cancel)
|
|
Packit |
8480eb |
goto remove;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!task->busy) {
|
|
Packit |
8480eb |
/* Start a new task */
|
|
Packit |
8480eb |
task->busy = 1;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ret = run_state_task(task);
|
|
Packit |
8480eb |
if (!ret)
|
|
Packit |
8480eb |
goto remove;
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Still starting up */
|
|
Packit |
8480eb |
if (!task->thid)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (task->cancel) {
|
|
Packit |
8480eb |
pthread_cancel(task->thid);
|
|
Packit |
8480eb |
task->cancel = 0;
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Still busy */
|
|
Packit |
8480eb |
if (!task->done)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
remove:
|
|
Packit |
8480eb |
/* No more tasks for this queue */
|
|
Packit |
8480eb |
if (list_empty(&task->pending)) {
|
|
Packit |
8480eb |
list_del(&task->list);
|
|
Packit |
8480eb |
free(task);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Next task */
|
|
Packit |
8480eb |
next = list_entry((&task->pending)->next,
|
|
Packit |
8480eb |
struct state_queue, pending);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_del(&task->list);
|
|
Packit |
8480eb |
list_del_init(&next->pending);
|
|
Packit |
8480eb |
free(task);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_add_tail(&next->list, head);
|
|
Packit |
8480eb |
if (p == head)
|
|
Packit |
8480eb |
p = head->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (list_empty(head))
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int st_start_handler(void)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
pthread_t thid;
|
|
Packit |
8480eb |
pthread_attr_t attrs;
|
|
Packit |
8480eb |
pthread_attr_t *pattrs = &attrs;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_attr_init(pattrs);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
pattrs = NULL;
|
|
Packit |
8480eb |
else {
|
|
Packit |
8480eb |
pthread_attr_setdetachstate(pattrs, PTHREAD_CREATE_DETACHED);
|
|
Packit |
8480eb |
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
|
|
Packit |
8480eb |
pthread_attr_setstacksize(pattrs, PTHREAD_STACK_MIN*4);
|
|
Packit |
8480eb |
#endif
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_create(&thid, pattrs, st_queue_handler, NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (pattrs)
|
|
Packit |
8480eb |
pthread_attr_destroy(pattrs);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return !status;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|