mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-30 17:18:21 +03:00
Lots of dmeventd-related changes.
This commit is contained in:
parent
7cc6c5d79a
commit
3165248642
@ -1,8 +1,9 @@
|
||||
Version 1.02.14 -
|
||||
=============================
|
||||
Add dm_saprintf().
|
||||
Use CFLAGS when linking so mixed sparc builds can supply -m64.
|
||||
Add dm_tree_use_no_flush_suspend().
|
||||
Lots of dmevent changes.
|
||||
Lots of dmevent changes including revised interface.
|
||||
Export dm_basename().
|
||||
Cope with a trailing space when comparing tables prior to possible reload.
|
||||
Fix dmeventd to cope if monitored device disappears.
|
||||
|
@ -1,5 +1,16 @@
|
||||
dm_event_handler_create
|
||||
dm_event_handler_destroy
|
||||
dm_event_handler_set_dso
|
||||
dm_event_handler_set_name
|
||||
dm_event_handler_set_uuid
|
||||
dm_event_handler_set_major
|
||||
dm_event_handler_set_minor
|
||||
dm_event_handler_set_events
|
||||
dm_event_handler_get_dso
|
||||
dm_event_handler_get_name
|
||||
dm_event_handler_get_uuid
|
||||
dm_event_handler_get_major
|
||||
dm_event_handler_get_minor
|
||||
dm_event_handler_get_events
|
||||
dm_event_register
|
||||
dm_event_unregister
|
||||
dm_event_get_registered_device
|
||||
dm_event_set_timeout
|
||||
dm_event_get_timeout
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define _GNU_SOURCE
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "configure.h"
|
||||
#include "libdevmapper.h"
|
||||
#include "libdevmapper-event.h"
|
||||
#include "list.h"
|
||||
@ -50,7 +51,7 @@
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
/* We must use syslog for now, because multilog is not yet implemented */
|
||||
/* FIXME We use syslog for now, because multilog is not yet implemented */
|
||||
#include <syslog.h>
|
||||
|
||||
static volatile sig_atomic_t _exit_now = 0; /* set to '1' when signal is given to exit */
|
||||
@ -86,11 +87,16 @@ struct dso_data {
|
||||
/*
|
||||
* Event processing.
|
||||
*
|
||||
* The DSO can do whatever appropriate steps if an event happens
|
||||
* such as changing the mapping in case a mirror fails, update
|
||||
* the application metadata etc.
|
||||
* The DSO can do whatever appropriate steps if an event
|
||||
* happens such as changing the mapping in case a mirror
|
||||
* fails, update the application metadata etc.
|
||||
*
|
||||
* This function gets a dm_task that is a result of
|
||||
* DM_DEVICE_WAITEVENT ioctl (results equivalent to
|
||||
* DM_DEVICE_STATUS). It should not destroy it.
|
||||
* The caller must dispose of the task.
|
||||
*/
|
||||
void (*process_event)(const char *device, enum dm_event_type event);
|
||||
void (*process_event)(struct dm_task *dmt, enum dm_event_type event);
|
||||
|
||||
/*
|
||||
* Device registration.
|
||||
@ -100,7 +106,8 @@ struct dso_data {
|
||||
* the process_event() function is sane (eg, read metadata
|
||||
* and activate a mapping).
|
||||
*/
|
||||
int (*register_device)(const char *device);
|
||||
int (*register_device)(const char *device, const char *uuid, int major,
|
||||
int minor);
|
||||
|
||||
/*
|
||||
* Device unregistration.
|
||||
@ -109,14 +116,15 @@ struct dso_data {
|
||||
* for events, the DSO can recognize this and carry out appropriate
|
||||
* steps (eg, deactivate mapping, metadata update).
|
||||
*/
|
||||
int (*unregister_device)(const char *device);
|
||||
int (*unregister_device)(const char *device, const char *uuid,
|
||||
int major, int minor);
|
||||
};
|
||||
static LIST_INIT(_dso_registry);
|
||||
|
||||
/* Structure to keep parsed register variables from client message. */
|
||||
struct message_data {
|
||||
char *dso_name; /* Name of DSO. */
|
||||
char *device_path; /* Mapped device path. */
|
||||
char *device_uuid; /* Mapped device path. */
|
||||
union {
|
||||
char *str; /* Events string as fetched from message. */
|
||||
enum dm_event_type field; /* Events bitfield. */
|
||||
@ -139,15 +147,19 @@ struct thread_status {
|
||||
|
||||
pthread_t thread;
|
||||
|
||||
struct dso_data *dso_data;/* DSO this thread accesses. */
|
||||
struct dso_data *dso_data; /* DSO this thread accesses. */
|
||||
|
||||
char *device_path; /* Mapped device path. */
|
||||
struct {
|
||||
char *uuid;
|
||||
char *name;
|
||||
int major, minor;
|
||||
} device;
|
||||
uint32_t event_nr; /* event number */
|
||||
int processing; /* Set when event is being processed */
|
||||
int status; /* running/shutdown/done */
|
||||
enum dm_event_type events; /* bitfield for event filter. */
|
||||
enum dm_event_type current_events;/* bitfield for occured events. */
|
||||
enum dm_event_type processed_events;/* bitfield for processed events. */
|
||||
enum dm_event_type current_events; /* bitfield for occured events. */
|
||||
struct dm_task *current_task;
|
||||
time_t next_time;
|
||||
uint32_t timeout;
|
||||
struct list timeout_list;
|
||||
@ -168,10 +180,13 @@ static struct thread_status *alloc_thread_status(struct message_data *data,
|
||||
|
||||
if (ret) {
|
||||
if (!memset(ret, 0, sizeof(*ret)) ||
|
||||
!(ret->device_path = dm_strdup(data->device_path))) {
|
||||
!(ret->device.uuid = dm_strdup(data->device_uuid))) {
|
||||
dm_free(ret);
|
||||
ret = NULL;
|
||||
} else {
|
||||
ret->current_task = NULL;
|
||||
ret->device.name = NULL;
|
||||
ret->device.major = ret->device.minor = 0;
|
||||
ret->dso_data = dso_data;
|
||||
ret->events = data->events.field;
|
||||
ret->timeout = data->timeout.secs;
|
||||
@ -184,7 +199,8 @@ static struct thread_status *alloc_thread_status(struct message_data *data,
|
||||
|
||||
static void free_thread_status(struct thread_status *thread)
|
||||
{
|
||||
dm_free(thread->device_path);
|
||||
dm_free(thread->device.uuid);
|
||||
dm_free(thread->device.name);
|
||||
dm_free(thread);
|
||||
}
|
||||
|
||||
@ -213,9 +229,10 @@ static void free_dso_data(struct dso_data *data)
|
||||
|
||||
/*
|
||||
* Fetch a string off src and duplicate it into *ptr.
|
||||
* Pay attention to 0 lenght strings.
|
||||
* Pay attention to zero-length strings.
|
||||
*/
|
||||
/* FIXME: move to separate module to share with the client lib. */
|
||||
/* FIXME? move to libdevmapper to share with the client lib (need to
|
||||
make delimiter a parameter then) */
|
||||
static const char delimiter = ' ';
|
||||
static int fetch_string(char **ptr, char **src)
|
||||
{
|
||||
@ -250,8 +267,8 @@ static void free_message(struct message_data *message_data)
|
||||
if (message_data->dso_name)
|
||||
dm_free(message_data->dso_name);
|
||||
|
||||
if (message_data->device_path)
|
||||
dm_free(message_data->device_path);
|
||||
if (message_data->device_uuid)
|
||||
dm_free(message_data->device_uuid);
|
||||
|
||||
}
|
||||
|
||||
@ -270,7 +287,7 @@ static int parse_message(struct message_data *message_data)
|
||||
* path and events # string from message.
|
||||
*/
|
||||
if (fetch_string(&message_data->dso_name, &p) &&
|
||||
fetch_string(&message_data->device_path, &p) &&
|
||||
fetch_string(&message_data->device_uuid, &p) &&
|
||||
fetch_string(&message_data->events.str, &p) &&
|
||||
fetch_string(&message_data->timeout.str, &p)) {
|
||||
if (message_data->events.str) {
|
||||
@ -330,23 +347,43 @@ static int storepid(int lf)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* FIXME This is unreliable: should use DM_DEVICE_INFO ioctl instead. */
|
||||
/* Check, if a device exists. */
|
||||
static int device_exists(char *device)
|
||||
static int fill_device_data(struct thread_status *ts)
|
||||
{
|
||||
struct stat st_buf;
|
||||
char path2[PATH_MAX];
|
||||
struct dm_task *dmt;
|
||||
struct dm_info dmi;
|
||||
|
||||
if (!device || !*device)
|
||||
if (!ts->device.uuid)
|
||||
return 0;
|
||||
|
||||
if (device[0] == '/') /* absolute path */
|
||||
return !stat(device, &st_buf) && S_ISBLK(st_buf.st_mode);
|
||||
ts->device.name = NULL;
|
||||
ts->device.major = ts->device.minor = 0;
|
||||
|
||||
if (PATH_MAX <= snprintf(path2, PATH_MAX, "%s/%s", dm_dir(), device))
|
||||
dmt = dm_task_create(DM_DEVICE_INFO);
|
||||
if (!dmt)
|
||||
return 0;
|
||||
|
||||
return !stat(path2, &st_buf) && S_ISBLK(st_buf.st_mode);
|
||||
dm_task_set_uuid(dmt, ts->device.uuid);
|
||||
if (!dm_task_run(dmt))
|
||||
goto fail;
|
||||
|
||||
ts->device.name = dm_strdup(dm_task_get_name(dmt));
|
||||
if (!ts->device.name)
|
||||
goto fail;
|
||||
|
||||
if (!dm_task_get_info(dmt, &dmi))
|
||||
goto fail;
|
||||
|
||||
ts->device.major = dmi.major;
|
||||
ts->device.minor = dmi.minor;
|
||||
|
||||
dm_task_destroy(dmt);
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
dm_task_destroy(dmt);
|
||||
dm_free(ts->device.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -359,13 +396,12 @@ static struct thread_status *lookup_thread_status(struct message_data *data)
|
||||
struct thread_status *thread;
|
||||
|
||||
list_iterate_items(thread, &_thread_registry)
|
||||
if (!strcmp(data->device_path, thread->device_path))
|
||||
if (!strcmp(data->device_uuid, thread->device.uuid))
|
||||
return thread;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Cleanup at exit. */
|
||||
static void exit_dm_lib(void)
|
||||
{
|
||||
@ -392,11 +428,10 @@ static void *timeout_thread(void *unused)
|
||||
while (!list_empty(&timeout_registry)) {
|
||||
struct thread_status *thread;
|
||||
|
||||
timeout.tv_sec = (time_t)-1;
|
||||
timeout.tv_sec = (time_t) -1;
|
||||
curr_time = time(NULL);
|
||||
|
||||
list_iterate_items_gen(thread, &timeout_registry,
|
||||
timeout_list) {
|
||||
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);
|
||||
@ -406,7 +441,8 @@ static void *timeout_thread(void *unused)
|
||||
timeout.tv_sec = thread->next_time;
|
||||
}
|
||||
|
||||
pthread_cond_timedwait(&_timeout_cond, &_timeout_mutex, &timeout);
|
||||
pthread_cond_timedwait(&_timeout_cond, &_timeout_mutex,
|
||||
&timeout);
|
||||
}
|
||||
|
||||
pthread_cleanup_pop(1);
|
||||
@ -491,23 +527,22 @@ static sigset_t unblock_sigalrm(void)
|
||||
#define DM_WAIT_FATAL 2
|
||||
|
||||
/* Wait on a device until an event occurs. */
|
||||
static int event_wait(struct thread_status *thread)
|
||||
static int event_wait(struct thread_status *thread, struct dm_task **task)
|
||||
{
|
||||
sigset_t set;
|
||||
int ret = DM_WAIT_RETRY;
|
||||
/*
|
||||
void *next = NULL;
|
||||
char *params, *target_type;
|
||||
uint64_t start, length;
|
||||
*/
|
||||
struct dm_task *dmt;
|
||||
struct dm_info info;
|
||||
|
||||
*task = 0;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
|
||||
return DM_WAIT_RETRY;
|
||||
|
||||
if (!(ret = dm_task_set_name(dmt, dm_basename(thread->device_path))) ||
|
||||
!(ret = dm_task_set_event_nr(dmt, thread->event_nr)))
|
||||
thread->current_task = dmt;
|
||||
|
||||
if (!dm_task_set_uuid(dmt, thread->device.uuid) ||
|
||||
!dm_task_set_event_nr(dmt, thread->event_nr))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
@ -517,31 +552,21 @@ static int event_wait(struct thread_status *thread)
|
||||
set = unblock_sigalrm();
|
||||
dm_log_init(no_intr_log);
|
||||
errno = 0;
|
||||
if ((ret = dm_task_run(dmt))) {
|
||||
if (dm_task_run(dmt)) {
|
||||
thread->current_events |= DM_EVENT_DEVICE_ERROR;
|
||||
ret = DM_WAIT_INTR;
|
||||
|
||||
/*
|
||||
* FIXME: I am setting processed_events to zero here
|
||||
* because it is causing problems. for example, the
|
||||
* mirror target emits a signal for INSYNC, then
|
||||
* subsequent events (device failures) are not handled
|
||||
*/
|
||||
thread->processed_events = 0;
|
||||
|
||||
if ((ret = dm_task_get_info(dmt, &info)))
|
||||
thread->event_nr = info.event_nr;
|
||||
} else if (thread->events & DM_EVENT_TIMEOUT && errno == EINTR) {
|
||||
thread->current_events |= DM_EVENT_TIMEOUT;
|
||||
ret = DM_WAIT_INTR;
|
||||
thread->processed_events = 0;
|
||||
} else {
|
||||
/* FIXME replace with log_* macro */
|
||||
syslog(LOG_NOTICE, "dm_task_run failed, errno = %d, %s",
|
||||
errno, strerror(errno));
|
||||
if (errno == ENXIO) {
|
||||
/* FIXME replace with log_* macro */
|
||||
syslog(LOG_ERR, "%s disappeared, detaching", thread->device_path);
|
||||
syslog(LOG_ERR, "%s disappeared, detaching",
|
||||
thread->device.name);
|
||||
ret = DM_WAIT_FATAL;
|
||||
}
|
||||
}
|
||||
@ -550,7 +575,11 @@ static int event_wait(struct thread_status *thread)
|
||||
dm_log_init(NULL);
|
||||
|
||||
out:
|
||||
if (ret == DM_WAIT_FATAL || ret == DM_WAIT_RETRY) {
|
||||
dm_task_destroy(dmt);
|
||||
thread->current_task = NULL;
|
||||
} else
|
||||
*task = dmt;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -558,20 +587,25 @@ static int event_wait(struct thread_status *thread)
|
||||
/* Register a device with the DSO. */
|
||||
static int do_register_device(struct thread_status *thread)
|
||||
{
|
||||
return thread->dso_data->register_device(thread->device_path);
|
||||
return thread->dso_data->register_device(thread->device.name,
|
||||
thread->device.uuid,
|
||||
thread->device.major,
|
||||
thread->device.minor);
|
||||
}
|
||||
|
||||
/* Unregister a device with the DSO. */
|
||||
static int do_unregister_device(struct thread_status *thread)
|
||||
{
|
||||
return thread->dso_data->unregister_device(thread->device_path);
|
||||
return thread->dso_data->unregister_device(thread->device.name,
|
||||
thread->device.uuid,
|
||||
thread->device.major,
|
||||
thread->device.minor);
|
||||
}
|
||||
|
||||
/* Process an event in the DSO. */
|
||||
static void do_process_event(struct thread_status *thread)
|
||||
static void do_process_event(struct thread_status *thread, struct dm_task *task)
|
||||
{
|
||||
thread->dso_data->process_event(thread->device_path,
|
||||
thread->current_events);
|
||||
thread->dso_data->process_event(task, thread->current_events);
|
||||
}
|
||||
|
||||
/* Thread cleanup handler to unregister device. */
|
||||
@ -581,7 +615,10 @@ static void monitor_unregister(void *arg)
|
||||
|
||||
if (!do_unregister_device(thread))
|
||||
syslog(LOG_ERR, "%s: %s unregister failed\n", __func__,
|
||||
thread->device_path);
|
||||
thread->device.name);
|
||||
if (thread->current_task)
|
||||
dm_task_destroy(thread->current_task);
|
||||
thread->current_task = NULL;
|
||||
}
|
||||
|
||||
/* Device monitoring thread. */
|
||||
@ -589,6 +626,7 @@ static void *monitor_thread(void *arg)
|
||||
{
|
||||
struct thread_status *thread = arg;
|
||||
int wait_error = 0;
|
||||
struct dm_task *task;
|
||||
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
|
||||
pthread_cleanup_push(monitor_unregister, thread);
|
||||
@ -602,14 +640,18 @@ static void *monitor_thread(void *arg)
|
||||
while (1) {
|
||||
thread->current_events = 0;
|
||||
|
||||
wait_error = event_wait(thread);
|
||||
wait_error = event_wait(thread, &task);
|
||||
if (wait_error == DM_WAIT_RETRY)
|
||||
continue;
|
||||
|
||||
/* FIXME Give a DSO a chance to clean up. */
|
||||
if (wait_error == DM_WAIT_FATAL)
|
||||
break;
|
||||
|
||||
/*
|
||||
* We know that wait succeeded and stored a
|
||||
* pointer to dm_task with device status into task.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Check against filter.
|
||||
*
|
||||
@ -617,10 +659,6 @@ static void *monitor_thread(void *arg)
|
||||
* the device got registered for those events AND
|
||||
* those events haven't been processed yet, call
|
||||
* the DSO's process_event() handler.
|
||||
*
|
||||
* FIXME: when does processed_events get cleared? What if
|
||||
* the same type of event happens later... after the first
|
||||
* was handled properly?
|
||||
*/
|
||||
lock_mutex();
|
||||
if (thread->status == DM_THREAD_SHUTDOWN) {
|
||||
@ -629,17 +667,21 @@ static void *monitor_thread(void *arg)
|
||||
}
|
||||
unlock_mutex();
|
||||
|
||||
if (thread->events &
|
||||
thread->current_events &
|
||||
~thread->processed_events) {
|
||||
if (thread->events & thread->current_events) {
|
||||
lock_mutex();
|
||||
thread->processing = 1;
|
||||
unlock_mutex();
|
||||
do_process_event(thread);
|
||||
thread->processed_events |= thread->current_events;
|
||||
|
||||
do_process_event(thread, task);
|
||||
dm_task_destroy(task);
|
||||
thread->current_task = NULL;
|
||||
|
||||
lock_mutex();
|
||||
thread->processing = 0;
|
||||
unlock_mutex();
|
||||
} else {
|
||||
dm_task_destroy(task);
|
||||
thread->current_task = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,12 +689,12 @@ static void *monitor_thread(void *arg)
|
||||
thread->status = DM_THREAD_DONE;
|
||||
unlock_mutex();
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create a device monitoring thread. */
|
||||
/* FIXME: call this with mutex hold ? */
|
||||
static int create_thread(struct thread_status *thread)
|
||||
{
|
||||
return pthread_create(&thread->thread, NULL, monitor_thread, thread);
|
||||
@ -714,11 +756,11 @@ static int lookup_symbol(void *dl, struct dso_data *data,
|
||||
|
||||
static int lookup_symbols(void *dl, struct dso_data *data)
|
||||
{
|
||||
return lookup_symbol(dl, data, (void*) &data->process_event,
|
||||
return lookup_symbol(dl, data, (void *) &data->process_event,
|
||||
"process_event") &&
|
||||
lookup_symbol(dl, data, (void*) &data->register_device,
|
||||
lookup_symbol(dl, data, (void *) &data->register_device,
|
||||
"register_device") &&
|
||||
lookup_symbol(dl, data, (void*) &data->unregister_device,
|
||||
lookup_symbol(dl, data, (void *) &data->unregister_device,
|
||||
"unregister_device");
|
||||
}
|
||||
|
||||
@ -728,13 +770,13 @@ static struct dso_data *load_dso(struct message_data *data)
|
||||
void *dl;
|
||||
struct dso_data *ret = NULL;
|
||||
|
||||
if (!(dl = dlopen(data->dso_name, RTLD_NOW))){
|
||||
if (!(dl = dlopen(data->dso_name, RTLD_NOW))) {
|
||||
const char *dlerr = dlerror();
|
||||
syslog(LOG_ERR, "dmeventd %s dlopen failed: %s", data->dso_name, dlerr);
|
||||
char buf[1024]; /* FIXME */
|
||||
snprintf(buf, 1024, "%s dlopen failed: %s", data->dso_name, dlerr);
|
||||
data->msg->size = strlen(buf) + 1;
|
||||
data->msg->data = dm_strdup(buf);
|
||||
syslog(LOG_ERR, "dmeventd %s dlopen failed: %s", data->dso_name,
|
||||
dlerr);
|
||||
data->msg->size =
|
||||
dm_saprintf(&(data->msg->data), "%s dlopen failed: %s",
|
||||
data->dso_name, dlerr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -763,7 +805,6 @@ static struct dso_data *load_dso(struct message_data *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Return success on daemon active check. */
|
||||
static int active(struct message_data *message_data)
|
||||
{
|
||||
@ -782,16 +823,9 @@ static int register_for_event(struct message_data *message_data)
|
||||
struct thread_status *thread, *thread_new = NULL;
|
||||
struct dso_data *dso_data;
|
||||
|
||||
if (!device_exists(message_data->device_path)) {
|
||||
stack;
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(dso_data = lookup_dso(message_data)) &&
|
||||
!(dso_data = load_dso(message_data))) {
|
||||
stack;
|
||||
/* FIXME */
|
||||
#ifdef ELIBACC
|
||||
ret = -ELIBACC;
|
||||
#else
|
||||
@ -807,15 +841,17 @@ static int register_for_event(struct message_data *message_data)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!fill_device_data(thread_new)) {
|
||||
stack;
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
lock_mutex();
|
||||
|
||||
if (!(thread = lookup_thread_status(message_data))) {
|
||||
unlock_mutex();
|
||||
|
||||
/*
|
||||
* FIXME: better do this asynchronously in the
|
||||
* monitoring thread ?
|
||||
*/
|
||||
if (!(ret = do_register_device(thread_new)))
|
||||
goto out;
|
||||
|
||||
@ -906,42 +942,40 @@ static int unregister_for_event(struct message_data *message_data)
|
||||
static int registered_device(struct message_data *message_data,
|
||||
struct thread_status *thread)
|
||||
{
|
||||
char test[1];
|
||||
struct dm_event_daemon_message *msg = message_data->msg;
|
||||
|
||||
const char *fmt = "%s %s %u";
|
||||
const char *dso = thread->dso_data->dso_name;
|
||||
const char *dev = thread->device_path;
|
||||
unsigned events = ((thread->status == DM_THREAD_RUNNING) && (thread->events)) ?
|
||||
thread->events : thread->events | DM_EVENT_REGISTRATION_PENDING;
|
||||
const char *dev = thread->device.uuid;
|
||||
unsigned events = ((thread->status == DM_THREAD_RUNNING)
|
||||
&& (thread->events)) ? thread->events : thread->
|
||||
events | DM_EVENT_REGISTRATION_PENDING;
|
||||
|
||||
if (msg->data)
|
||||
dm_free(msg->data);
|
||||
|
||||
msg->size = snprintf(test, 1, fmt, dso, dev, events);
|
||||
msg->data = dm_malloc(msg->size);
|
||||
snprintf(msg->data, msg->size, fmt, dso, dev, events);
|
||||
msg->size = dm_saprintf(&(msg->data), fmt, dso, dev, events);
|
||||
|
||||
unlock_mutex();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int want_registered_device(char *dso_name, char *device_path,
|
||||
static int want_registered_device(char *dso_name, char *device_uuid,
|
||||
struct thread_status *thread)
|
||||
{
|
||||
/* If DSO names and device paths are equal. */
|
||||
if (dso_name && device_path)
|
||||
if (dso_name && device_uuid)
|
||||
return !strcmp(dso_name, thread->dso_data->dso_name) &&
|
||||
!strcmp(device_path, thread->device_path);
|
||||
!strcmp(device_uuid, thread->device.uuid);
|
||||
|
||||
/* If DSO names are equal. */
|
||||
if (dso_name)
|
||||
return !strcmp(dso_name, thread->dso_data->dso_name);
|
||||
|
||||
/* If device paths are equal. */
|
||||
if (device_path)
|
||||
return !strcmp(device_path, thread->device_path);
|
||||
if (device_uuid)
|
||||
return !strcmp(device_uuid, thread->device.uuid);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -956,7 +990,7 @@ static int _get_registered_device(struct message_data *message_data, int next)
|
||||
/* Iterate list of threads checking if we want a particular one. */
|
||||
list_iterate_items(thread, &_thread_registry)
|
||||
if ((hit = want_registered_device(message_data->dso_name,
|
||||
message_data->device_path,
|
||||
message_data->device_uuid,
|
||||
thread)))
|
||||
break;
|
||||
|
||||
@ -971,10 +1005,8 @@ static int _get_registered_device(struct message_data *message_data, int next)
|
||||
if (list_end(&_thread_registry, &thread->list))
|
||||
goto out;
|
||||
|
||||
thread = list_item(thread->list.n,
|
||||
struct thread_status);
|
||||
} while (!want_registered_device(message_data->dso_name,
|
||||
NULL, thread));
|
||||
thread = list_item(thread->list.n, struct thread_status);
|
||||
} while (!want_registered_device(message_data->dso_name, NULL, thread));
|
||||
|
||||
return registered_device(message_data, thread);
|
||||
|
||||
@ -1016,9 +1048,8 @@ static int get_timeout(struct message_data *message_data)
|
||||
|
||||
lock_mutex();
|
||||
if ((thread = lookup_thread_status(message_data))) {
|
||||
msg->data = dm_malloc(8*sizeof(uint32_t)); /* FIXME */
|
||||
msg->size = snprintf(msg->data, 8*sizeof(uint32_t),
|
||||
"%"PRIu32, thread->timeout);
|
||||
msg->size =
|
||||
dm_saprintf(&(msg->data), "%" PRIu32, thread->timeout);
|
||||
} else {
|
||||
msg->data = NULL;
|
||||
msg->size = 0;
|
||||
@ -1028,7 +1059,6 @@ static int get_timeout(struct message_data *message_data)
|
||||
return thread ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize a fifos structure with path names. */
|
||||
static void init_fifos(struct dm_event_fifos *fifos)
|
||||
{
|
||||
@ -1049,7 +1079,17 @@ static int open_fifos(struct dm_event_fifos *fifos)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* FIXME Warn/abort if perms are wrong - not something to fix silently. */
|
||||
struct stat st;
|
||||
|
||||
/* Warn about wrong permissions if applicable */
|
||||
if ((!stat(fifos->client_path, &st)) && (st.st_mode & 0777) != 0600)
|
||||
syslog(LOG_WARNING, "Fixing wrong permissions on %s",
|
||||
fifos->client_path);
|
||||
|
||||
if ((!stat(fifos->server_path, &st)) && (st.st_mode & 0777) != 0600)
|
||||
syslog(LOG_WARNING, "Fixing wrong permissions on %s",
|
||||
fifos->server_path);
|
||||
|
||||
/* If they were already there, make sure permissions are ok. */
|
||||
if (chmod(fifos->client_path, 0600)) {
|
||||
syslog(LOG_ERR, "Unable to set correct file permissions on %s",
|
||||
@ -1083,7 +1123,8 @@ static int open_fifos(struct dm_event_fifos *fifos)
|
||||
* Read message from client making sure that data is available
|
||||
* and a complete message is read. Must not block indefinitely.
|
||||
*/
|
||||
static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg)
|
||||
static int client_read(struct dm_event_fifos *fifos,
|
||||
struct dm_event_daemon_message *msg)
|
||||
{
|
||||
struct timeval t;
|
||||
unsigned bytes = 0;
|
||||
@ -1102,7 +1143,7 @@ static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
FD_SET(fifos->client, &fds);
|
||||
t.tv_sec = 1;
|
||||
t.tv_usec = 0;
|
||||
ret = select(fifos->client+1, &fds, NULL, NULL, &t);
|
||||
ret = select(fifos->client + 1, &fds, NULL, NULL, &t);
|
||||
|
||||
if (!ret && !bytes) /* nothing to read */
|
||||
return 0;
|
||||
@ -1115,9 +1156,9 @@ static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
|
||||
ret = read(fifos->client, buf + bytes, size - bytes);
|
||||
bytes += ret > 0 ? ret : 0;
|
||||
if (bytes == 2*sizeof(uint32_t) && header) {
|
||||
msg->cmd = ntohl(*((uint32_t *)buf));
|
||||
msg->size = ntohl(*((uint32_t *)buf + 1));
|
||||
if (bytes == 2 * sizeof(uint32_t) && header) {
|
||||
msg->cmd = ntohl(*((uint32_t *) buf));
|
||||
msg->size = ntohl(*((uint32_t *) buf + 1));
|
||||
buf = msg->data = dm_malloc(msg->size);
|
||||
size = msg->size;
|
||||
bytes = 0;
|
||||
@ -1129,6 +1170,7 @@ static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
if (msg->data)
|
||||
dm_free(msg->data);
|
||||
msg->data = NULL;
|
||||
msg->size = 0;
|
||||
}
|
||||
|
||||
return bytes == size;
|
||||
@ -1137,18 +1179,20 @@ static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
/*
|
||||
* Write a message to the client making sure that it is ready to write.
|
||||
*/
|
||||
static int client_write(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg)
|
||||
static int client_write(struct dm_event_fifos *fifos,
|
||||
struct dm_event_daemon_message *msg)
|
||||
{
|
||||
unsigned bytes = 0;
|
||||
int ret = 0;
|
||||
fd_set fds;
|
||||
|
||||
size_t size = 2*sizeof(uint32_t) + msg->size;
|
||||
size_t size = 2 * sizeof(uint32_t) + msg->size;
|
||||
char *buf = alloca(size);
|
||||
|
||||
*((uint32_t *)buf) = htonl(msg->cmd);
|
||||
*((uint32_t *)buf + 1) = htonl(msg->size);
|
||||
memcpy(buf + 2*sizeof(uint32_t), msg->data, msg->size);
|
||||
if (msg->data)
|
||||
memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
|
||||
|
||||
errno = 0;
|
||||
while (bytes < size && errno != EIO) {
|
||||
@ -1156,7 +1200,8 @@ static int client_write(struct dm_event_fifos *fifos, struct dm_event_daemon_mes
|
||||
/* Watch client write FIFO to be ready for output. */
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fifos->server, &fds);
|
||||
} while (select(fifos->server +1, NULL, &fds, NULL, NULL) != 1);
|
||||
} while (select(fifos->server + 1, NULL, &fds, NULL, NULL) !=
|
||||
1);
|
||||
|
||||
ret = write(fifos->server, buf + bytes, size - bytes);
|
||||
bytes += ret > 0 ? ret : 0;
|
||||
@ -1176,15 +1221,16 @@ static int handle_request(struct dm_event_daemon_message *msg,
|
||||
{
|
||||
static struct {
|
||||
unsigned int cmd;
|
||||
int (*f)(struct message_data*);
|
||||
int (*f)(struct message_data *);
|
||||
} requests[] = {
|
||||
{ DM_EVENT_CMD_REGISTER_FOR_EVENT, register_for_event },
|
||||
{ DM_EVENT_CMD_UNREGISTER_FOR_EVENT, unregister_for_event },
|
||||
{ DM_EVENT_CMD_GET_REGISTERED_DEVICE, get_registered_device },
|
||||
{ DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE, get_next_registered_device },
|
||||
{ DM_EVENT_CMD_SET_TIMEOUT, set_timeout },
|
||||
{ DM_EVENT_CMD_GET_TIMEOUT, get_timeout },
|
||||
{ DM_EVENT_CMD_ACTIVE, active },
|
||||
{ DM_EVENT_CMD_REGISTER_FOR_EVENT, register_for_event},
|
||||
{ DM_EVENT_CMD_UNREGISTER_FOR_EVENT, unregister_for_event},
|
||||
{ DM_EVENT_CMD_GET_REGISTERED_DEVICE, get_registered_device},
|
||||
{ DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
|
||||
get_next_registered_device},
|
||||
{ DM_EVENT_CMD_SET_TIMEOUT, set_timeout},
|
||||
{ DM_EVENT_CMD_GET_TIMEOUT, get_timeout},
|
||||
{ DM_EVENT_CMD_ACTIVE, active},
|
||||
}, *req;
|
||||
|
||||
for (req = requests; req < requests + sizeof(requests); req++)
|
||||
@ -1203,8 +1249,7 @@ static int do_process_request(struct dm_event_daemon_message *msg)
|
||||
/* Parse the message. */
|
||||
memset(&message_data, 0, sizeof(message_data));
|
||||
message_data.msg = msg;
|
||||
if (msg->cmd != DM_EVENT_CMD_ACTIVE &&
|
||||
!parse_message(&message_data)) {
|
||||
if (msg->cmd != DM_EVENT_CMD_ACTIVE && !parse_message(&message_data)) {
|
||||
stack;
|
||||
ret = -EINVAL;
|
||||
} else
|
||||
@ -1220,15 +1265,11 @@ static void process_request(struct dm_event_fifos *fifos)
|
||||
{
|
||||
struct dm_event_daemon_message msg;
|
||||
|
||||
/* FIXME: better error handling */
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
/*
|
||||
* Read the request from the client.
|
||||
* Of course, it's tough to tell what to do when
|
||||
* we use fucking retarded return codes like
|
||||
* 0 for error.
|
||||
* Read the request from the client (client_read, client_write
|
||||
* give true on success and false on failure).
|
||||
*/
|
||||
if (!client_read(fifos, &msg))
|
||||
return;
|
||||
@ -1236,7 +1277,12 @@ static void process_request(struct dm_event_fifos *fifos)
|
||||
msg.cmd = do_process_request(&msg);
|
||||
if (!msg.data) {
|
||||
msg.data = dm_strdup(strerror(-msg.cmd));
|
||||
if (msg.data)
|
||||
msg.size = strlen(msg.data) + 1;
|
||||
else {
|
||||
msg.size = 0;
|
||||
stack;
|
||||
}
|
||||
}
|
||||
|
||||
if (!client_write(fifos, &msg))
|
||||
@ -1270,14 +1316,16 @@ static void cleanup_unused_threads(void)
|
||||
if (ret == ESRCH) {
|
||||
thread->status = DM_THREAD_DONE;
|
||||
} else if (ret) {
|
||||
syslog(LOG_ERR, "Unable to terminate thread: %s\n",
|
||||
syslog(LOG_ERR,
|
||||
"Unable to terminate thread: %s\n",
|
||||
strerror(-ret));
|
||||
stack;
|
||||
}
|
||||
goto out;
|
||||
} else {
|
||||
list_del(l);
|
||||
syslog(LOG_ERR, "thread can't be on unused list unless !thread->events");
|
||||
syslog(LOG_ERR,
|
||||
"thread can't be on unused list unless !thread->events");
|
||||
thread->status = DM_THREAD_RUNNING;
|
||||
LINK_THREAD(thread);
|
||||
}
|
||||
@ -1288,7 +1336,7 @@ static void cleanup_unused_threads(void)
|
||||
free_thread_status(thread);
|
||||
}
|
||||
}
|
||||
out:
|
||||
out:
|
||||
unlock_mutex();
|
||||
}
|
||||
|
||||
@ -1344,7 +1392,7 @@ static void exit_handler(int sig)
|
||||
static int lock_pidfile(void)
|
||||
{
|
||||
int lf;
|
||||
char pidfile[] = "/var/run/dmeventd.pid"; /* FIXME Must be configurable at compile-time! */
|
||||
char pidfile[] = DMEVENTD_PIDFILE;
|
||||
|
||||
if ((lf = open(pidfile, O_CREAT | O_RDWR, 0644)) < 0)
|
||||
exit(EXIT_OPEN_PID_FAILURE);
|
||||
@ -1472,7 +1520,8 @@ int main(int argc, char *argv[])
|
||||
while (!_exit_now) {
|
||||
process_request(&fifos);
|
||||
cleanup_unused_threads();
|
||||
if (!list_empty(&_thread_registry) || !list_empty(&_thread_registry_unused))
|
||||
if (!list_empty(&_thread_registry)
|
||||
|| !list_empty(&_thread_registry_unused))
|
||||
_thread_registries_empty = 0;
|
||||
else
|
||||
_thread_registries_empty = 1;
|
||||
|
@ -30,43 +30,110 @@
|
||||
#include <sys/wait.h>
|
||||
#include <arpa/inet.h> /* for htonl, ntohl */
|
||||
|
||||
/* Set by any of the external fxns the first time one of them is called */
|
||||
/* FIXME Unused */
|
||||
// static int _logging = 0;
|
||||
struct dm_event_handler {
|
||||
const char *dso;
|
||||
const char *device;
|
||||
const char *uuid;
|
||||
int major;
|
||||
int minor;
|
||||
enum dm_event_type events;
|
||||
};
|
||||
|
||||
/* Fetch a string off src and duplicate it into *dest. */
|
||||
/* FIXME: move to seperate module to share with the daemon. */
|
||||
static const char delimiter = ' ';
|
||||
static char *fetch_string(char **src)
|
||||
static void dm_event_handler_clear_device(struct dm_event_handler *h)
|
||||
{
|
||||
char *p, *ret;
|
||||
h->device = h->uuid = NULL;
|
||||
h->major = h->minor = 0;
|
||||
}
|
||||
|
||||
if ((p = strchr(*src, delimiter)))
|
||||
*p = 0;
|
||||
struct dm_event_handler *dm_event_handler_create(void)
|
||||
{
|
||||
struct dm_event_handler *ret = 0;
|
||||
|
||||
if ((ret = dm_strdup(*src)))
|
||||
*src += strlen(ret) + 1;
|
||||
if (!(ret = dm_malloc(sizeof(*ret))))
|
||||
return NULL;
|
||||
|
||||
if (p)
|
||||
*p = delimiter;
|
||||
ret->dso = ret->device = ret->uuid = NULL;
|
||||
ret->major = ret->minor = 0;
|
||||
ret->events = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse a device message from the daemon. */
|
||||
static int parse_message(struct dm_event_daemon_message *msg, char **dso_name,
|
||||
char **device, enum dm_event_type *events)
|
||||
void dm_event_handler_destroy(struct dm_event_handler *h)
|
||||
{
|
||||
char *p = msg->data;
|
||||
dm_free(h);
|
||||
}
|
||||
|
||||
if ((*dso_name = fetch_string(&p)) &&
|
||||
(*device = fetch_string(&p))) {
|
||||
*events = atoi(p);
|
||||
void dm_event_handler_set_dso(struct dm_event_handler *h, const char *path)
|
||||
{
|
||||
h->dso = path;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
void dm_event_handler_set_name(struct dm_event_handler *h, const char *name)
|
||||
{
|
||||
dm_event_handler_clear_device(h);
|
||||
h->device = name;
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
void dm_event_handler_set_uuid(struct dm_event_handler *h, const char *uuid)
|
||||
{
|
||||
dm_event_handler_clear_device(h);
|
||||
h->uuid = uuid;
|
||||
}
|
||||
|
||||
void dm_event_handler_set_major(struct dm_event_handler *h, int major)
|
||||
{
|
||||
int minor = h->minor;
|
||||
|
||||
dm_event_handler_clear_device(h);
|
||||
h->major = major;
|
||||
h->minor = minor;
|
||||
}
|
||||
|
||||
void dm_event_handler_set_minor(struct dm_event_handler *h, int minor)
|
||||
{
|
||||
int major = h->major;
|
||||
|
||||
dm_event_handler_clear_device(h);
|
||||
|
||||
h->major = major;
|
||||
h->minor = minor;
|
||||
}
|
||||
|
||||
void dm_event_handler_set_events(struct dm_event_handler *h,
|
||||
enum dm_event_type event)
|
||||
{
|
||||
h->events = event;
|
||||
}
|
||||
|
||||
const char *dm_event_handler_get_dso(const struct dm_event_handler *h)
|
||||
{
|
||||
return h->dso;
|
||||
}
|
||||
|
||||
const char *dm_event_handler_get_name(const struct dm_event_handler *h)
|
||||
{
|
||||
return h->device;
|
||||
}
|
||||
|
||||
const char *dm_event_handler_get_uuid(const struct dm_event_handler *h)
|
||||
{
|
||||
return h->uuid;
|
||||
}
|
||||
|
||||
int dm_event_handler_get_major(const struct dm_event_handler *h)
|
||||
{
|
||||
return h->major;
|
||||
}
|
||||
|
||||
int dm_event_handler_get_minor(const struct dm_event_handler *h)
|
||||
{
|
||||
return h->minor;
|
||||
}
|
||||
|
||||
enum dm_event_type dm_event_handler_get_events(const struct dm_event_handler *h)
|
||||
{
|
||||
return h->events;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -78,13 +145,14 @@ static int parse_message(struct dm_event_daemon_message *msg, char **dso_name,
|
||||
*
|
||||
* Returns: 0 on failure, 1 on success
|
||||
*/
|
||||
static int daemon_read(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg)
|
||||
static int daemon_read(struct dm_event_fifos *fifos,
|
||||
struct dm_event_daemon_message *msg)
|
||||
{
|
||||
unsigned bytes = 0;
|
||||
int ret, i;
|
||||
fd_set fds;
|
||||
struct timeval tval = {0, 0};
|
||||
size_t size = 2 * sizeof(uint32_t); // status + size
|
||||
struct timeval tval = { 0, 0 };
|
||||
size_t size = 2 * sizeof(uint32_t); /* status + size */
|
||||
char *buf = alloca(size);
|
||||
int header = 1;
|
||||
|
||||
@ -94,7 +162,8 @@ static int daemon_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fifos->server, &fds);
|
||||
tval.tv_sec = 1;
|
||||
ret = select(fifos->server+1, &fds, NULL, NULL, &tval);
|
||||
ret = select(fifos->server + 1, &fds, NULL, NULL,
|
||||
&tval);
|
||||
if (ret < 0 && errno != EINTR) {
|
||||
log_error("Unable to read from event server");
|
||||
return 0;
|
||||
@ -116,7 +185,7 @@ static int daemon_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
}
|
||||
|
||||
bytes += ret;
|
||||
if (bytes == 2*sizeof(uint32_t) && header) {
|
||||
if (bytes == 2 * sizeof(uint32_t) && header) {
|
||||
msg->cmd = ntohl(*((uint32_t *)buf));
|
||||
msg->size = ntohl(*((uint32_t *)buf + 1));
|
||||
buf = msg->data = dm_malloc(msg->size);
|
||||
@ -136,32 +205,34 @@ static int daemon_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
}
|
||||
|
||||
/* Write message to daemon. */
|
||||
static int daemon_write(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg)
|
||||
static int daemon_write(struct dm_event_fifos *fifos,
|
||||
struct dm_event_daemon_message *msg)
|
||||
{
|
||||
unsigned bytes = 0;
|
||||
int ret = 0;
|
||||
fd_set fds;
|
||||
|
||||
size_t size = 2*sizeof(uint32_t) + msg->size;
|
||||
size_t size = 2 * sizeof(uint32_t) + msg->size;
|
||||
char *buf = alloca(size);
|
||||
|
||||
*((uint32_t *)buf) = htonl(msg->cmd);
|
||||
*((uint32_t *)buf + 1) = htonl(msg->size);
|
||||
memcpy(buf + 2*sizeof(uint32_t), msg->data, msg->size);
|
||||
memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
|
||||
|
||||
while (bytes < size) {
|
||||
do {
|
||||
/* Watch daemon write FIFO to be ready for output. */
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fifos->client, &fds);
|
||||
ret = select(fifos->client +1, NULL, &fds, NULL, NULL);
|
||||
ret = select(fifos->client + 1, NULL, &fds, NULL, NULL);
|
||||
if ((ret < 0) && (errno != EINTR)) {
|
||||
log_error("Unable to talk to event daemon");
|
||||
return 0;
|
||||
}
|
||||
} while (ret < 1);
|
||||
|
||||
ret = write(fifos->client, ((char *) buf) + bytes, size - bytes);
|
||||
ret = write(fifos->client, ((char *) buf) + bytes,
|
||||
size - bytes);
|
||||
if (ret < 0) {
|
||||
if ((errno == EINTR) || (errno == EAGAIN))
|
||||
continue;
|
||||
@ -177,14 +248,14 @@ static int daemon_write(struct dm_event_fifos *fifos, struct dm_event_daemon_mes
|
||||
return bytes == size;
|
||||
}
|
||||
|
||||
static int daemon_talk(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg,
|
||||
int cmd, const char *dso_name, const char *device,
|
||||
static int daemon_talk(struct dm_event_fifos *fifos,
|
||||
struct dm_event_daemon_message *msg, int cmd,
|
||||
const char *dso_name, const char *device,
|
||||
enum dm_event_type events, uint32_t timeout)
|
||||
{
|
||||
char test[1];
|
||||
const char *dso = dso_name ? dso_name : "";
|
||||
const char *dev = device ? device : "";
|
||||
const char *fmt = "%s %s %u %"PRIu32;
|
||||
const char *fmt = "%s %s %u %" PRIu32;
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
/*
|
||||
@ -192,10 +263,7 @@ static int daemon_talk(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
* into ASCII message string.
|
||||
*/
|
||||
msg->cmd = cmd;
|
||||
/* FIXME depends on glibc 2.1+ */
|
||||
msg->size = snprintf(test, 1, fmt, dso, dev, events, timeout);
|
||||
msg->data = alloca(msg->size);
|
||||
snprintf(msg->data, msg->size, fmt, dso, dev, events, timeout);
|
||||
msg->size = dm_saprintf(&(msg->data), fmt, dso, dev, events, timeout);
|
||||
|
||||
/*
|
||||
* Write command and message to and
|
||||
@ -256,7 +324,7 @@ static int start_daemon(struct dm_event_fifos *fifos)
|
||||
return 0;
|
||||
}
|
||||
|
||||
start_server:
|
||||
start_server:
|
||||
/* server is not running */
|
||||
pid = fork();
|
||||
|
||||
@ -264,11 +332,13 @@ start_server:
|
||||
log_error("Unable to fork.");
|
||||
|
||||
else if (!pid) {
|
||||
execvp("dmeventd", NULL); /* security risk if admin has bad PATH */
|
||||
/* FIXME configure path (cf. lvm2 modprobe) */
|
||||
execvp("dmeventd", NULL);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
if (waitpid(pid, &status, 0) < 0)
|
||||
log_error("Unable to start dmeventd: %s", strerror(errno));
|
||||
log_error("Unable to start dmeventd: %s",
|
||||
strerror(errno));
|
||||
else if (WEXITSTATUS(status))
|
||||
log_error("Unable to start dmeventd.");
|
||||
else
|
||||
@ -281,8 +351,8 @@ start_server:
|
||||
/* Initialize client. */
|
||||
static int init_client(struct dm_event_fifos *fifos)
|
||||
{
|
||||
/* FIXME Is fifo the most suitable method? */
|
||||
/* FIXME Why not share comms/daemon code with something else e.g. multipath? */
|
||||
/* FIXME? Is fifo the most suitable method? Why not share
|
||||
comms/daemon code with something else e.g. multipath? */
|
||||
|
||||
/* init fifos */
|
||||
memset(fifos, 0, sizeof(*fifos));
|
||||
@ -303,14 +373,13 @@ static int init_client(struct dm_event_fifos *fifos)
|
||||
}
|
||||
|
||||
/* Lock out anyone else trying to do communication with the daemon. */
|
||||
if (flock(fifos->server, LOCK_EX) < 0){
|
||||
if (flock(fifos->server, LOCK_EX) < 0) {
|
||||
log_error("%s: flock %s", __func__, fifos->server_path);
|
||||
close(fifos->server);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if ((fifos->client = open(fifos->client_path,
|
||||
O_WRONLY | O_NONBLOCK)) < 0) {*/
|
||||
/* if ((fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK)) < 0) {*/
|
||||
if ((fifos->client = open(fifos->client_path, O_RDWR | O_NONBLOCK)) < 0) {
|
||||
log_error("%s: Can't open client fifo %s: %s",
|
||||
__func__, fifos->client_path, strerror(errno));
|
||||
@ -331,40 +400,45 @@ static void dtr_client(struct dm_event_fifos *fifos)
|
||||
close(fifos->server);
|
||||
}
|
||||
|
||||
/* Check, if a block device exists. */
|
||||
static int device_exists(const char *device)
|
||||
/* Get uuid of a device, if it exists (otherwise NULL). */
|
||||
static struct dm_task *get_device_info(const struct dm_event_handler *h)
|
||||
{
|
||||
struct stat st_buf;
|
||||
char path2[PATH_MAX];
|
||||
struct dm_task *dmt = dm_task_create(DM_DEVICE_INFO);
|
||||
struct dm_task *ret;
|
||||
|
||||
if (!device)
|
||||
return 0;
|
||||
if (!dmt)
|
||||
return NULL;
|
||||
|
||||
if (device[0] == '/') /* absolute path */
|
||||
return !stat(device, &st_buf) && S_ISBLK(st_buf.st_mode);
|
||||
if (h->uuid)
|
||||
dm_task_set_uuid(dmt, h->uuid);
|
||||
else if (h->device)
|
||||
dm_task_set_name(dmt, h->device);
|
||||
else if (h->major && h->minor) {
|
||||
dm_task_set_major(dmt, h->major);
|
||||
dm_task_set_minor(dmt, h->minor);
|
||||
}
|
||||
|
||||
if (PATH_MAX <= snprintf(path2, PATH_MAX, "%s/%s", dm_dir(), device))
|
||||
return 0;
|
||||
if (!dm_task_run(dmt))
|
||||
ret = NULL;
|
||||
else
|
||||
ret = dmt;
|
||||
|
||||
return !stat(path2, &st_buf) && S_ISBLK(st_buf.st_mode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Handle the event (de)registration call and return negative error codes. */
|
||||
static int do_event(int cmd, struct dm_event_daemon_message *msg,
|
||||
const char *dso_name, const char *device, enum dm_event_type events,
|
||||
uint32_t timeout)
|
||||
const char *dso_name, const char *device,
|
||||
enum dm_event_type events, uint32_t timeout)
|
||||
{
|
||||
int ret;
|
||||
struct dm_event_fifos fifos;
|
||||
|
||||
/* FIXME Start the daemon here if it's not running e.g. exclusive lock file */
|
||||
/* FIXME Move this to separate 'dm_event_register_handler' - if no daemon here, fail */
|
||||
if (!init_client(&fifos)) {
|
||||
stack;
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
/* FIXME Use separate 'dm_event_register_handler' function to pass in dso? */
|
||||
ret = daemon_talk(&fifos, msg, cmd, dso_name, device, events, timeout);
|
||||
|
||||
/* what is the opposite of init? */
|
||||
@ -373,23 +447,25 @@ static int do_event(int cmd, struct dm_event_daemon_message *msg,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* FIXME remove dso_name - use handle instead */
|
||||
/* FIXME Use uuid not path! */
|
||||
/* External library interface. */
|
||||
int dm_event_register(const char *dso_name, const char *device_path,
|
||||
enum dm_event_type events)
|
||||
int dm_event_register(const struct dm_event_handler *h)
|
||||
{
|
||||
int ret, err;
|
||||
const char *uuid;
|
||||
struct dm_task *dmt;
|
||||
struct dm_event_daemon_message msg;
|
||||
|
||||
if (!device_exists(device_path)) {
|
||||
log_error("%s: device not found", device_path);
|
||||
if (!(dmt = get_device_info(h))) {
|
||||
log_error("%s: device not found", h->device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uuid = dm_task_get_uuid(dmt);
|
||||
|
||||
if ((err = do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, &msg,
|
||||
dso_name, device_path, events, 0)) < 0) {
|
||||
log_error("%s: event registration failed: %s", device_path,
|
||||
h->dso, uuid, h->events, 0)) < 0) {
|
||||
log_error("%s: event registration failed: %s",
|
||||
dm_task_get_name(dmt),
|
||||
msg.data ? msg.data : strerror(-err));
|
||||
ret = 0;
|
||||
} else
|
||||
@ -398,23 +474,29 @@ int dm_event_register(const char *dso_name, const char *device_path,
|
||||
if (msg.data)
|
||||
dm_free(msg.data);
|
||||
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dm_event_unregister(const char *dso_name, const char *device_path,
|
||||
enum dm_event_type events)
|
||||
int dm_event_unregister(const struct dm_event_handler *h)
|
||||
{
|
||||
int ret, err;
|
||||
const char *uuid;
|
||||
struct dm_task *dmt;
|
||||
struct dm_event_daemon_message msg;
|
||||
|
||||
if (!device_exists(device_path)) {
|
||||
log_error("%s: device not found", device_path);
|
||||
if (!(dmt = get_device_info(h))) {
|
||||
log_error("%s: device not found", dm_task_get_name(dmt));
|
||||
return 0;
|
||||
}
|
||||
|
||||
uuid = dm_task_get_uuid(dmt);
|
||||
|
||||
if ((err = do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, &msg,
|
||||
dso_name, device_path, events, 0)) < 0) {
|
||||
log_error("%s: event deregistration failed: %s", device_path,
|
||||
h->dso, uuid, h->events, 0)) < 0) {
|
||||
log_error("%s: event deregistration failed: %s",
|
||||
dm_task_get_name(dmt),
|
||||
msg.data ? msg.data : strerror(-err));
|
||||
ret = 0;
|
||||
} else
|
||||
@ -422,9 +504,48 @@ int dm_event_unregister(const char *dso_name, const char *device_path,
|
||||
|
||||
if (msg.data)
|
||||
dm_free(msg.data);
|
||||
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0 /* left out for now */
|
||||
|
||||
/* Fetch a string off src and duplicate it into *dest. */
|
||||
/* FIXME: move to seperate module to share with the daemon. */
|
||||
static const char delimiter = ' ';
|
||||
static char *fetch_string(char **src)
|
||||
{
|
||||
char *p, *ret;
|
||||
|
||||
if ((p = strchr(*src, delimiter)))
|
||||
*p = 0;
|
||||
|
||||
if ((ret = dm_strdup(*src)))
|
||||
*src += strlen(ret) + 1;
|
||||
|
||||
if (p)
|
||||
*p = delimiter;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse a device message from the daemon. */
|
||||
static int parse_message(struct dm_event_daemon_message *msg, char **dso_name,
|
||||
char **device, enum dm_event_type *events)
|
||||
{
|
||||
char *p = msg->data;
|
||||
|
||||
if ((*dso_name = fetch_string(&p)) && (*device = fetch_string(&p))) {
|
||||
*events = atoi(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* dm_event_get_registered_device
|
||||
* @dso_name
|
||||
@ -488,9 +609,11 @@ int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
|
||||
|
||||
if (!device_exists(device_path))
|
||||
return -ENODEV;
|
||||
if (!(ret = do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path, 0, 0)))
|
||||
if (!(ret = do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
|
||||
0, 0)))
|
||||
*timeout = atoi(msg.data);
|
||||
if (msg.data)
|
||||
dm_free(msg.data);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
@ -24,50 +24,70 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/* Event type definitions. */
|
||||
/* FIXME Use masks to separate the types and provide for extension. */
|
||||
enum dm_event_type {
|
||||
DM_EVENT_SINGLE = 0x01, /* Report multiple errors just once. */
|
||||
DM_EVENT_MULTI = 0x02, /* Report all of them. */
|
||||
DM_EVENT_SETTINGS_MASK = 0x0000FF,
|
||||
DM_EVENT_SINGLE = 0x000001, /* Report multiple errors just once. */
|
||||
DM_EVENT_MULTI = 0x000002, /* Report all of them. */
|
||||
|
||||
DM_EVENT_SECTOR_ERROR = 0x04, /* Failure on a particular sector. */
|
||||
DM_EVENT_DEVICE_ERROR = 0x08, /* Device failure. */
|
||||
DM_EVENT_PATH_ERROR = 0x10, /* Failure on an io path. */
|
||||
DM_EVENT_ADAPTOR_ERROR = 0x20, /* Failure off a host adaptor. */
|
||||
DM_EVENT_ERROR_MASK = 0x00FF00,
|
||||
DM_EVENT_SECTOR_ERROR = 0x000100, /* Failure on a particular sector. */
|
||||
DM_EVENT_DEVICE_ERROR = 0x000200, /* Device failure. */
|
||||
DM_EVENT_PATH_ERROR = 0x000400, /* Failure on an io path. */
|
||||
DM_EVENT_ADAPTOR_ERROR = 0x000800, /* Failure off a host adaptor. */
|
||||
|
||||
DM_EVENT_SYNC_STATUS = 0x40, /* Mirror synchronization completed/failed. */
|
||||
DM_EVENT_TIMEOUT = 0x80, /* Timeout has occured */
|
||||
DM_EVENT_REGISTRATION_PENDING = 0X100, /* Monitor thread is setting-up/shutting-down */
|
||||
DM_EVENT_STATUS_MASK = 0xFF0000,
|
||||
DM_EVENT_SYNC_STATUS = 0x010000, /* Mirror synchronization completed/failed. */
|
||||
DM_EVENT_TIMEOUT = 0x020000, /* Timeout has occured */
|
||||
|
||||
DM_EVENT_REGISTRATION_PENDING = 0x1000000, /* Monitor thread is setting-up/shutting-down */
|
||||
};
|
||||
|
||||
/* FIXME Use a mask. */
|
||||
#define DM_EVENT_ALL_ERRORS (DM_EVENT_SECTOR_ERROR | DM_EVENT_DEVICE_ERROR | \
|
||||
DM_EVENT_PATH_ERROR | DM_EVENT_ADAPTOR_ERROR)
|
||||
#define DM_EVENT_ALL_ERRORS DM_EVENT_ERROR_MASK
|
||||
|
||||
/* Prototypes for event lib interface. */
|
||||
|
||||
/* FIXME Replace device with standard name/uuid/devno choice */
|
||||
/* Interface changes:
|
||||
First register a handler, passing in a unique ref for the device. */
|
||||
struct dm_event_handler;
|
||||
|
||||
// int dm_event_register_handler(const char *dso_name, const char *device);
|
||||
// int dm_event_register(const char *dso_name, const char *name, const char *uuid, uint32_t major, uint32_t minor, enum dm_event_type events);
|
||||
/* Create and destroy dm_event_handler struct, which is passed to
|
||||
register/unregister functions below */
|
||||
struct dm_event_handler *dm_event_handler_create(void);
|
||||
void dm_event_handler_destroy(struct dm_event_handler *h);
|
||||
|
||||
/* Or (better?) add to task structure and use existing functions - run
|
||||
a task to register/unregister events - we may need to run task
|
||||
withe that with the new event mechanism anyway, then the dso calls
|
||||
just hook in. */
|
||||
/* Set parameters of a handler:
|
||||
- dso - shared library path to handle the events
|
||||
(only one of the following three needs to be set)
|
||||
- name - device name or path
|
||||
- uuid - device uuid
|
||||
- major and minor - device major/minor numbers
|
||||
- events - a bitfield defining which events to handle (see
|
||||
enum dm_event_type above)
|
||||
*/
|
||||
void dm_event_handler_set_dso(struct dm_event_handler *h, const char *path);
|
||||
void dm_event_handler_set_name(struct dm_event_handler *h, const char *name);
|
||||
void dm_event_handler_set_uuid(struct dm_event_handler *h, const char *uuid);
|
||||
void dm_event_handler_set_major(struct dm_event_handler *h, int major);
|
||||
void dm_event_handler_set_minor(struct dm_event_handler *h, int minor);
|
||||
void dm_event_handler_set_events(struct dm_event_handler *h,
|
||||
enum dm_event_type event);
|
||||
|
||||
int dm_event_register(const char *dso_name, const char *device, enum dm_event_type events);
|
||||
int dm_event_unregister(const char *dso_name, const char *device,
|
||||
enum dm_event_type events);
|
||||
int dm_event_get_registered_device(char **dso_name, char **device,
|
||||
enum dm_event_type *events, int next);
|
||||
int dm_event_set_timeout(const char *device, uint32_t timeout);
|
||||
int dm_event_get_timeout(const char *device, uint32_t *timeout);
|
||||
/* Get parameters of a handler, same as above */
|
||||
const char *dm_event_handler_get_dso(const struct dm_event_handler *h);
|
||||
const char *dm_event_handler_get_name(const struct dm_event_handler *h);
|
||||
const char *dm_event_handler_get_uuid(const struct dm_event_handler *h);
|
||||
int dm_event_handler_get_major(const struct dm_event_handler *h);
|
||||
int dm_event_handler_get_minor(const struct dm_event_handler *h);
|
||||
enum dm_event_type dm_event_handler_get_events(const struct dm_event_handler *h);
|
||||
|
||||
/* Prototypes for DSO interface. */
|
||||
void process_event(const char *device, enum dm_event_type event);
|
||||
int register_device(const char *device);
|
||||
int unregister_device(const char *device);
|
||||
/* Call out to dmeventd to register or unregister a handler. If
|
||||
dmeventd is not running, it is spawned first. */
|
||||
int dm_event_register(const struct dm_event_handler *h);
|
||||
int dm_event_unregister(const struct dm_event_handler *h);
|
||||
|
||||
/* Prototypes for DSO interface, see dmeventd.c, struct dso_data for
|
||||
detailed descriptions. */
|
||||
void process_event(struct dm_task *dmt, enum dm_event_type event);
|
||||
int register_device(const char *device, const char *uuid, int major, int minor);
|
||||
int unregister_device(const char *device, const char *uuid, int major,
|
||||
int minor);
|
||||
|
||||
#endif
|
||||
|
@ -115,3 +115,4 @@ dm_split_lvm_name
|
||||
dm_split_words
|
||||
dm_snprintf
|
||||
dm_basename
|
||||
dm_saprintf
|
||||
|
@ -623,4 +623,10 @@ int dm_snprintf(char *buf, size_t bufsize, const char *format, ...);
|
||||
*/
|
||||
char *dm_basename(const char *path);
|
||||
|
||||
/*
|
||||
* Returns size of a buffer which is allocated with dm_malloc.
|
||||
* Pointer to the buffer is stored in *buf.
|
||||
*/
|
||||
int dm_saprintf(char **buf, const char *format, ...);
|
||||
|
||||
#endif /* LIB_DEVICE_MAPPER_H */
|
||||
|
@ -129,3 +129,33 @@ char *dm_basename(const char *path)
|
||||
return p ? p + 1 : (char *) path;
|
||||
}
|
||||
|
||||
int dm_saprintf(char **result, const char *format, ...)
|
||||
{
|
||||
int n, ok = 0, size = 32;
|
||||
va_list ap;
|
||||
char *buf = dm_malloc(size);
|
||||
|
||||
*result = 0;
|
||||
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
while (!ok) {
|
||||
va_start(ap, format);
|
||||
n = vsnprintf(buf, size, format, ap);
|
||||
if (0 <= n && n < size)
|
||||
ok = 1;
|
||||
else {
|
||||
dm_free(buf);
|
||||
size *= 2;
|
||||
buf = dm_malloc(size);
|
||||
if (!buf)
|
||||
return -1;
|
||||
};
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
*result = dm_strdup(buf);
|
||||
dm_free(buf);
|
||||
return n + 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user