static enum event_type events = ALL_ERRORS; /* All until we can distinguish. */
static char default_dso_name[] = "noop"; /* default DSO is noop */
static int default_reg = 1; /* default action is register */
+static uint32_t timeout;
/* Display help. */
static void print_usage(char *name)
" -h Print this usage.\n"
" -l List registered devices.\n"
" -r Register for event (default).\n"
+ " -t <timeout> (un)register for timeout event.\n"
" -u Unregister for event.\n"
"\n", cmd);
}
char **device_arg, int *reg, int *list)
{
int c;
- const char *options = "d:hlru";
+ const char *options = "d:hlrt:u";
while ((c = getopt(argc, argv, options)) != -1) {
switch (c) {
case 'r':
*reg = 1;
break;
+ case 't':
+ events = TIMEOUT;
+ if (sscanf(optarg, "%"SCNu32, &timeout) != 1){
+ fprintf(stderr, "invalid timeout '%s'\n",
+ optarg);
+ timeout = 0;
+ }
+ break;
case 'u':
*reg = 0;
break;
multilog_init_verbose(standard, _LOG_DEBUG);
if (list) {
- do {
- if (!(ret= dm_get_registered_device(&dso_name,
- &device,
- &events, next))) {
- printf("%s %s 0x%x\n",
- dso_name, device, events);
-
- if (device_arg)
- break;
-
- next = 1;
- }
- } while (!ret);
+ while (1) {
+ if ((ret= dm_get_registered_device(&dso_name, &device,
+ &events, next)))
+ break;
+ printf("%s %s 0x%x", dso_name, device, events);
+ if (events & TIMEOUT){
+ if ((ret = dm_get_event_timeout(device,
+ &timeout))) {
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+ printf(" %"PRIu32"\n", timeout);
+ } else
+ printf("\n");
+ if (device_arg)
+ break;
+
+ next = 1;
+ }
ret = (ret && device_arg) ? EXIT_FAILURE : EXIT_SUCCESS;
goto out;
reg ? "": "un", device, strerror(-ret));
ret = EXIT_FAILURE;
} else {
- printf("%s %sregistered successfully.\n",
- device, reg ? "" : "un");
- ret = EXIT_SUCCESS;
+ if (reg && (events & TIMEOUT) &&
+ ((ret = dm_set_event_timeout(device, timeout)))){
+ fprintf(stderr, "Failed to set timeout for %s: %s\n",
+ device, strerror(-ret));
+ ret = EXIT_FAILURE;
+ } else {
+ printf("%s %sregistered successfully.\n",
+ device, reg ? "" : "un");
+ ret = EXIT_SUCCESS;
+ }
}
out:
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
-
+#include <stdarg.h>
#define dbg_malloc(x...) malloc(x)
char *str; /* Events string as fetched from message. */
enum event_type field; /* Events bitfield. */
} events;
+ union {
+ char *str;
+ uint32_t secs;
+ } timeout;
struct daemon_message *msg; /* Pointer to message buffer. */
};
enum event_type events; /* bitfield for event filter. */
enum event_type current_events;/* bitfield for occured events. */
enum event_type processed_events;/* bitfield for processed events. */
+ time_t next_time;
+ uint32_t timeout;
+ struct list timeout_list;
};
static LIST_INIT(thread_registry);
+static int timeout_running;
+static LIST_INIT(timeout_registry);
+static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t timeout_cond = PTHREAD_COND_INITIALIZER;
+
/* Allocate/free the status structure for a monitoring thread. */
static struct thread_status *alloc_thread_status(struct message_data *data,
struct dso_data *dso_data)
} else {
ret->dso_data = dso_data;
ret->events = data->events.field;
+ ret->timeout = data->timeout.secs;
+ list_init(&ret->timeout_list);
}
}
*/
if (fetch_string(&message_data->dso_name, &p) &&
fetch_string(&message_data->device_path, &p) &&
- fetch_string(&message_data->events.str, &p)) {
+ fetch_string(&message_data->events.str, &p) &&
+ fetch_string(&message_data->timeout.str, &p)) {
if (message_data->events.str) {
enum event_type i = atoi(message_data->events.str);
dbg_free(message_data->events.str);
message_data->events.field = i;
}
+ if (message_data->timeout.str) {
+ uint32_t secs = atoi(message_data->timeout.str);
+ dbg_free(message_data->timeout.str);
+ message_data->timeout.secs = secs ? secs :
+ DEFAULT_TIMEOUT;
+ }
return 1;
}
return 0;
}
+static void exit_timeout(void *unused)
+{
+ timeout_running = 0;
+ pthread_mutex_unlock(&timeout_mutex);
+}
+
+/* wake up monitor threads every so often */
+static void *timeout_thread(void *unused)
+{
+ struct timespec timeout;
+ time_t curr_time;
+
+ timeout.tv_nsec = 0;
+ pthread_cleanup_push(exit_timeout, NULL);
+ pthread_mutex_lock(&timeout_mutex);
+ while(1) {
+ struct thread_status *thread;
+ timeout.tv_sec = (time_t)-1;
+ curr_time = time(NULL);
+ if (list_empty(&timeout_registry))
+ break;
+ list_iterate_items_gen(thread, &timeout_registry,
+ timeout_list) {
+ if (thread->next_time < curr_time) {
+ thread->next_time = curr_time + thread->timeout;
+ pthread_kill(thread->thread, SIGALRM);
+ }
+ if (thread->next_time < timeout.tv_sec)
+ timeout.tv_sec = thread->next_time;
+ }
+ pthread_cond_timedwait(&timeout_cond, &timeout_mutex, &timeout);
+ }
+ timeout_running = 0;
+ pthread_mutex_unlock(&timeout_mutex);
+ pthread_cleanup_pop(0);
+
+ return NULL;
+}
+
+static int register_for_timeout(struct thread_status *thread)
+{
+ int ret = 0;
+ pthread_mutex_lock(&timeout_mutex);
+ thread->next_time = time(NULL) + thread->timeout;
+ if (list_empty(&thread->timeout_list)) {
+ list_add(&timeout_registry, &thread->timeout_list);
+ if (timeout_running)
+ pthread_cond_signal(&timeout_cond);
+ }
+ if (!timeout_running) {
+ pthread_t timeout_id;
+ ret = -pthread_create(&timeout_id, NULL, timeout_thread, NULL);
+ if (ret)
+ goto out;
+ timeout_running = 1;
+ }
+ out:
+ pthread_mutex_unlock(&timeout_mutex);
+ return ret;
+}
+
+static void unregister_for_timeout(struct thread_status *thread)
+{
+ pthread_mutex_lock(&timeout_mutex);
+ if (!list_empty(&thread->timeout_list)) {
+ list_del(&thread->timeout_list);
+ list_init(&thread->timeout_list);
+ }
+ pthread_mutex_unlock(&timeout_mutex);
+}
+
+static void no_intr_log(int level, const char *file, int line,
+ const char *f, ...)
+{
+ va_list ap;
+
+ if (errno == EINTR)
+ return;
+ if (level > _LOG_WARN)
+ return;
+
+ va_start(ap, f);
+
+ if (level < _LOG_WARN)
+ vfprintf(stderr, f, ap);
+ else
+ vprintf(f, ap);
+
+ va_end(ap);
+
+ if (level < _LOG_WARN)
+ fprintf(stderr, "\n");
+ else
+ fprintf(stdout, "\n");
+}
+
+static sigset_t unblock_sigalrm(void)
+{
+ sigset_t set, old;
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+ pthread_sigmask(SIG_UNBLOCK, &set, &old);
+ return old;
+}
+
/* Wait on a device until an event occurs. */
static int event_wait(struct thread_status *thread)
{
+ sigset_t set;
int ret = 0;
/*
void *next = NULL;
if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
return 0;
- if ((ret = dm_task_set_name(dmt, dm_basename(thread->device_path))) &&
- (ret = dm_task_set_event_nr(dmt, thread->event_nr)) &&
- (ret = dm_task_run(dmt))) {
+ if (!(ret = dm_task_set_name(dmt, dm_basename(thread->device_path))) ||
+ !(ret = dm_task_set_event_nr(dmt, thread->event_nr)))
+ goto out;
+ /* This is so that you can break out of waiting on an event,
+ either for a timeout event, or to cancel the thread */
+ set = unblock_sigalrm();
+ dm_log_init(no_intr_log);
+ errno = 0;
+ if ((ret = dm_task_run(dmt))) {
/*
do {
params = NULL;
if ((ret = dm_task_get_info(dmt, &info)))
thread->event_nr = info.event_nr;
+ } else if (thread->events & TIMEOUT && errno == EINTR) {
+ thread->current_events |= TIMEOUT;
+ ret = 1;
+ thread->processed_events = 0;
}
+ pthread_sigmask(SIG_SETMASK, &set, NULL);
+ dm_log_init(NULL);
+ out:
dm_task_destroy(dmt);
return ret;
static int terminate_thread(struct thread_status *thread)
{
- return pthread_cancel(thread->thread);
+ int ret;
+ if ((ret = pthread_cancel(thread->thread)))
+ return ret;
+ ret = pthread_kill(thread->thread, SIGALRM);
+ return ret;
}
/* DSO reference counting. */
static int register_for_event(struct message_data *message_data)
{
int ret = 0;
- struct thread_status *thread, *thread_new;
+ struct thread_status *thread, *thread_new = NULL;
struct dso_data *dso_data;
if (!device_exists(message_data->device_path)) {
lock_mutex();
if ((ret = -create_thread(thread))) {
unlock_mutex();
+ do_unregister_device(thread);
free_thread_status(thread);
goto out;
} else
thread->events |= message_data->events.field;
unlock_mutex();
+ /* FIXME - If you fail to register for timeout events, you
+ still monitor all the other events. Is this the right
+ action for newly created devices? Also, you are still
+ on the timeout registry, so if a timeout thread is
+ successfully started up later, you will start receiving
+ TIMEOUT events */
+ if (thread->events & TIMEOUT)
+ ret = -register_for_timeout(thread);
+ out:
/*
* Deallocate thread status after releasing
* the lock in case we haven't used it.
if (thread_new)
free_thread_status(thread_new);
- out:
free_message(message_data);
return ret;
thread->events &= ~message_data->events.field;
+ if (!(thread->events & TIMEOUT))
+ unregister_for_timeout(thread);
/*
* In case there's no events to monitor on this device ->
* unlink and terminate its monitoring thread.
}
out:
+ free_message(message_data);
unlock_mutex();
return -ENOENT;
}
+static int set_timeout(struct message_data *message_data)
+{
+ int ret = 0;
+ struct thread_status *thread;
+
+ lock_mutex();
+ if (!(thread = lookup_thread_status(message_data))) {
+ unlock_mutex();
+ ret = -ENODEV;
+ goto out;
+ }
+ thread->timeout = message_data->timeout.secs;
+ unlock_mutex();
+
+ out:
+ free_message(message_data);
+
+ return ret;
+}
+
+static int get_timeout(struct message_data *message_data)
+{
+ int ret = 0;
+ struct thread_status *thread;
+ struct daemon_message *msg = message_data->msg;
+
+ lock_mutex();
+ if (!(thread = lookup_thread_status(message_data))) {
+ unlock_mutex();
+ ret = -ENODEV;
+ goto out;
+ }
+ snprintf(msg->msg, sizeof(msg->msg), "%"PRIu32, thread->timeout);
+ unlock_mutex();
+ out:
+ free_message(message_data);
+
+ return ret;
+}
+
+
/* Initialize a fifos structure with path names. */
static int init_fifos(struct fifos *fifos)
{
case CMD_GET_NEXT_REGISTERED_DEVICE:
ret = get_registered_device(&message_data, 1);
break;
+ case CMD_SET_TIMEOUT:
+ ret = set_timeout(&message_data);
+ break;
+ case CMD_GET_TIMEOUT:
+ ret = get_timeout(&message_data);
+ break;
default:
ret = -EINVAL;
break;
stack;
}
+static void sig_alarm(int signum)
+{
+ pthread_testcancel();
+}
+
/* Init thread signal handling. */
static void init_thread_signals(void)
{
sigset_t sigset;
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sig_alarm;
+ sigaction(SIGALRM, &act, NULL);
sigfillset(&sigset);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
}
static int daemon_talk(struct fifos *fifos, struct daemon_message *msg,
int cmd, char *dso_name, char *device,
- enum event_type events)
+ enum event_type events, uint32_t timeout)
{
memset(msg, 0, sizeof(*msg));
* into ASCII message string.
*/
msg->opcode.cmd = cmd;
- snprintf(msg->msg, sizeof(msg->msg), "%s %s %u",
- dso_name ? dso_name : "", device ? device : "", events);
+ snprintf(msg->msg, sizeof(msg->msg), "%s %s %u %"PRIu32,
+ dso_name ? dso_name : "", device ? device : "",
+ events, timeout);
/*
* Write command and message to and
/* Handle the event (de)registration call and return negative error codes. */
static int do_event(int cmd, struct daemon_message *msg,
- char *dso_name, char *device, enum event_type events)
+ char *dso_name, char *device, enum event_type events,
+ uint32_t timeout)
{
int ret;
struct fifos fifos;
return -ESRCH;
}
- ret = daemon_talk(&fifos, msg, cmd, dso_name, device, events);
+ ret = daemon_talk(&fifos, msg, cmd, dso_name, device, events, timeout);
/* what is the opposite of init? */
dtr_client(&fifos);
return -ENODEV;
return do_event(CMD_REGISTER_FOR_EVENT, &msg,
- dso_name, device_path, events);
+ dso_name, device_path, events, 0);
}
int dm_unregister_for_event(char *dso_name, char *device_path,
return -ENODEV;
return do_event(CMD_UNREGISTER_FOR_EVENT, &msg,
- dso_name, device_path, events);
+ dso_name, device_path, events, 0);
}
int dm_get_registered_device(char **dso_name, char **device_path,
if (!(ret = do_event(next ? CMD_GET_NEXT_REGISTERED_DEVICE :
CMD_GET_REGISTERED_DEVICE,
- &msg, *dso_name, *device_path, *events)))
+ &msg, *dso_name, *device_path, *events, 0)))
ret = parse_message(&msg, &dso_name_arg, &device_path_arg,
events);
return ret;
}
+int dm_set_event_timeout(char *device_path, uint32_t timeout)
+{
+ struct daemon_message msg;
+
+ if (!device_exists(device_path))
+ return -ENODEV;
+ return do_event(CMD_SET_TIMEOUT, &msg,
+ NULL, device_path, 0, timeout);
+}
+
+int dm_get_event_timeout(char *device_path, uint32_t *timeout)
+{
+ int ret;
+ struct daemon_message msg;
+
+ if (!device_exists(device_path))
+ return -ENODEV;
+ if (!(ret = do_event(CMD_GET_TIMEOUT, &msg, NULL, device_path, 0, 0)))
+ *timeout = atoi(msg.msg);
+ return ret;
+}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically