|
Packit |
8480eb |
/* ----------------------------------------------------------------------- *
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* alarm.c - alarm queue handling module.
|
|
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 <stdlib.h>
|
|
Packit |
8480eb |
#include "automount.h"
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
struct alarm {
|
|
Packit |
8480eb |
time_t time;
|
|
Packit |
8480eb |
unsigned int cancel;
|
|
Packit |
8480eb |
struct autofs_point *ap;
|
|
Packit |
8480eb |
struct list_head list;
|
|
Packit |
8480eb |
};
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
Packit |
8480eb |
static pthread_cond_t cond;
|
|
Packit |
8480eb |
static LIST_HEAD(alarms);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
#define alarm_lock() \
|
|
Packit |
8480eb |
do { \
|
|
Packit |
8480eb |
int _alm_lock = pthread_mutex_lock(&mutex); \
|
|
Packit |
8480eb |
if (_alm_lock) \
|
|
Packit |
8480eb |
fatal(_alm_lock); \
|
|
Packit |
8480eb |
} while (0)
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
#define alarm_unlock() \
|
|
Packit |
8480eb |
do { \
|
|
Packit |
8480eb |
int _alm_unlock = pthread_mutex_unlock(&mutex); \
|
|
Packit |
8480eb |
if (_alm_unlock) \
|
|
Packit |
8480eb |
fatal(_alm_unlock); \
|
|
Packit |
8480eb |
} while (0)
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Insert alarm entry on ordered list. */
|
|
Packit |
8480eb |
int alarm_add(struct autofs_point *ap, time_t seconds)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *head;
|
|
Packit |
8480eb |
struct list_head *p;
|
|
Packit |
8480eb |
struct alarm *new;
|
|
Packit |
8480eb |
time_t now = monotonic_time(NULL);
|
|
Packit |
8480eb |
time_t next_alarm = 0;
|
|
Packit |
8480eb |
unsigned int empty = 1;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!seconds)
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
new = malloc(sizeof(struct alarm));
|
|
Packit |
8480eb |
if (!new)
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
new->ap = ap;
|
|
Packit |
8480eb |
new->cancel = 0;
|
|
Packit |
8480eb |
new->time = now + seconds;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
alarm_lock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &alarms;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Check if we have a pending alarm */
|
|
Packit |
8480eb |
if (!list_empty(head)) {
|
|
Packit |
8480eb |
struct alarm *current;
|
|
Packit |
8480eb |
current = list_entry(head->next, struct alarm, list);
|
|
Packit |
8480eb |
next_alarm = current->time;
|
|
Packit |
8480eb |
empty = 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, head) {
|
|
Packit |
8480eb |
struct alarm *this;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
this = list_entry(p, struct alarm, list);
|
|
Packit |
8480eb |
if (this->time >= new->time) {
|
|
Packit |
8480eb |
list_add_tail(&new->list, p);
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
if (p == head)
|
|
Packit |
8480eb |
list_add_tail(&new->list, p);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Wake the alarm thread if it is not busy (ie. if the
|
|
Packit |
8480eb |
* alarms list was empty) or if the new alarm comes before
|
|
Packit |
8480eb |
* the alarm we are currently waiting on.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
if (empty || new->time < next_alarm) {
|
|
Packit |
8480eb |
status = pthread_cond_signal(&cond;;
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
alarm_unlock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void alarm_delete(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *head;
|
|
Packit |
8480eb |
struct list_head *p;
|
|
Packit |
8480eb |
struct alarm *current;
|
|
Packit |
8480eb |
unsigned int signal_cancel = 0;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
alarm_lock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &alarms;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (list_empty(head)) {
|
|
Packit |
8480eb |
alarm_unlock();
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
current = list_entry(head->next, struct alarm, list);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
p = head->next;
|
|
Packit |
8480eb |
while (p != head) {
|
|
Packit |
8480eb |
struct alarm *this;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
this = list_entry(p, struct alarm, list);
|
|
Packit |
8480eb |
p = p->next;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ap == this->ap) {
|
|
Packit |
8480eb |
if (current != this) {
|
|
Packit |
8480eb |
list_del_init(&this->list);
|
|
Packit |
8480eb |
free(this);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
/* Mark as canceled */
|
|
Packit |
8480eb |
this->cancel = 1;
|
|
Packit |
8480eb |
this->time = 0;
|
|
Packit |
8480eb |
signal_cancel = 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (signal_cancel) {
|
|
Packit |
8480eb |
status = pthread_cond_signal(&cond;;
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
alarm_unlock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void *alarm_handler(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct list_head *head;
|
|
Packit |
8480eb |
struct timespec expire;
|
|
Packit |
8480eb |
struct alarm *first;
|
|
Packit |
8480eb |
time_t now;
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
alarm_lock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
head = &alarms;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
while (1) {
|
|
Packit |
8480eb |
if (list_empty(head)) {
|
|
Packit |
8480eb |
/* No alarms, wait for one to be added */
|
|
Packit |
8480eb |
status = pthread_cond_wait(&cond, &mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
first = list_entry(head->next, struct alarm, list);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
now = monotonic_time(NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (first->time > now) {
|
|
Packit |
8480eb |
struct timespec nsecs;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Wait for alarm to trigger or a new alarm
|
|
Packit |
8480eb |
* to be added.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
clock_gettime(CLOCK_MONOTONIC, &nsecs);
|
|
Packit |
8480eb |
expire.tv_sec = first->time;
|
|
Packit |
8480eb |
expire.tv_nsec = nsecs.tv_nsec;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_cond_timedwait(&cond, &mutex, &expire);
|
|
Packit |
8480eb |
if (status && status != ETIMEDOUT)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
/* First alarm has triggered, run it */
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_del(&first->list);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!first->cancel) {
|
|
Packit |
8480eb |
struct autofs_point *ap = first->ap;
|
|
Packit |
8480eb |
alarm_unlock();
|
|
Packit |
8480eb |
st_add_task(ap, ST_EXPIRE);
|
|
Packit |
8480eb |
alarm_lock();
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
free(first);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
/* Will never come here, so alarm_unlock is not necessary */
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int alarm_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 |
pthread_condattr_t condattrs;
|
|
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_condattr_init(&condattrs);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_condattr_setclock(&condattrs, CLOCK_MONOTONIC);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_cond_init(&cond, &condattrs);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_create(&thid, pattrs, alarm_handler, NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_condattr_destroy(&condattrs);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (pattrs)
|
|
Packit |
8480eb |
pthread_attr_destroy(pattrs);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return !status;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|