1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-01-02 01:18:26 +03:00

Lots of dmeventd-related changes.

This commit is contained in:
Alasdair Kergon 2007-01-11 21:54:53 +00:00
parent 7cc6c5d79a
commit 3165248642
8 changed files with 559 additions and 318 deletions

View File

@ -1,8 +1,9 @@
Version 1.02.14 - Version 1.02.14 -
============================= =============================
Add dm_saprintf().
Use CFLAGS when linking so mixed sparc builds can supply -m64. Use CFLAGS when linking so mixed sparc builds can supply -m64.
Add dm_tree_use_no_flush_suspend(). Add dm_tree_use_no_flush_suspend().
Lots of dmevent changes. Lots of dmevent changes including revised interface.
Export dm_basename(). Export dm_basename().
Cope with a trailing space when comparing tables prior to possible reload. Cope with a trailing space when comparing tables prior to possible reload.
Fix dmeventd to cope if monitored device disappears. Fix dmeventd to cope if monitored device disappears.

View File

@ -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_register
dm_event_unregister dm_event_unregister
dm_event_get_registered_device
dm_event_set_timeout
dm_event_get_timeout

View File

@ -19,6 +19,7 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64 #define _FILE_OFFSET_BITS 64
#include "configure.h"
#include "libdevmapper.h" #include "libdevmapper.h"
#include "libdevmapper-event.h" #include "libdevmapper-event.h"
#include "list.h" #include "list.h"
@ -50,7 +51,7 @@
#include <malloc.h> #include <malloc.h>
#endif #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> #include <syslog.h>
static volatile sig_atomic_t _exit_now = 0; /* set to '1' when signal is given to exit */ 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. * Event processing.
* *
* The DSO can do whatever appropriate steps if an event happens * The DSO can do whatever appropriate steps if an event
* such as changing the mapping in case a mirror fails, update * happens such as changing the mapping in case a mirror
* the application metadata etc. * 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. * Device registration.
@ -100,7 +106,8 @@ struct dso_data {
* the process_event() function is sane (eg, read metadata * the process_event() function is sane (eg, read metadata
* and activate a mapping). * 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. * Device unregistration.
@ -109,14 +116,15 @@ struct dso_data {
* for events, the DSO can recognize this and carry out appropriate * for events, the DSO can recognize this and carry out appropriate
* steps (eg, deactivate mapping, metadata update). * 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); static LIST_INIT(_dso_registry);
/* Structure to keep parsed register variables from client message. */ /* Structure to keep parsed register variables from client message. */
struct message_data { struct message_data {
char *dso_name; /* Name of DSO. */ char *dso_name; /* Name of DSO. */
char *device_path; /* Mapped device path. */ char *device_uuid; /* Mapped device path. */
union { union {
char *str; /* Events string as fetched from message. */ char *str; /* Events string as fetched from message. */
enum dm_event_type field; /* Events bitfield. */ enum dm_event_type field; /* Events bitfield. */
@ -139,15 +147,19 @@ struct thread_status {
pthread_t thread; 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 */ uint32_t event_nr; /* event number */
int processing; /* Set when event is being processed */ int processing; /* Set when event is being processed */
int status; /* running/shutdown/done */ int status; /* running/shutdown/done */
enum dm_event_type events; /* bitfield for event filter. */ enum dm_event_type events; /* bitfield for event filter. */
enum dm_event_type current_events;/* bitfield for occured events. */ enum dm_event_type current_events; /* bitfield for occured events. */
enum dm_event_type processed_events;/* bitfield for processed events. */ struct dm_task *current_task;
time_t next_time; time_t next_time;
uint32_t timeout; uint32_t timeout;
struct list timeout_list; struct list timeout_list;
@ -168,10 +180,13 @@ static struct thread_status *alloc_thread_status(struct message_data *data,
if (ret) { if (ret) {
if (!memset(ret, 0, sizeof(*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); dm_free(ret);
ret = NULL; ret = NULL;
} else { } else {
ret->current_task = NULL;
ret->device.name = NULL;
ret->device.major = ret->device.minor = 0;
ret->dso_data = dso_data; ret->dso_data = dso_data;
ret->events = data->events.field; ret->events = data->events.field;
ret->timeout = data->timeout.secs; 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) 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); 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. * 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 const char delimiter = ' ';
static int fetch_string(char **ptr, char **src) 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) if (message_data->dso_name)
dm_free(message_data->dso_name); dm_free(message_data->dso_name);
if (message_data->device_path) if (message_data->device_uuid)
dm_free(message_data->device_path); 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. * path and events # string from message.
*/ */
if (fetch_string(&message_data->dso_name, &p) && 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->events.str, &p) &&
fetch_string(&message_data->timeout.str, &p)) { fetch_string(&message_data->timeout.str, &p)) {
if (message_data->events.str) { if (message_data->events.str) {
@ -330,23 +347,43 @@ static int storepid(int lf)
return 1; return 1;
} }
/* FIXME This is unreliable: should use DM_DEVICE_INFO ioctl instead. */
/* Check, if a device exists. */ /* Check, if a device exists. */
static int device_exists(char *device) static int fill_device_data(struct thread_status *ts)
{ {
struct stat st_buf; struct dm_task *dmt;
char path2[PATH_MAX]; struct dm_info dmi;
if (!device || !*device) if (!ts->device.uuid)
return 0; return 0;
if (device[0] == '/') /* absolute path */ ts->device.name = NULL;
return !stat(device, &st_buf) && S_ISBLK(st_buf.st_mode); 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 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; struct thread_status *thread;
list_iterate_items(thread, &_thread_registry) 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 thread;
return NULL; return NULL;
} }
/* Cleanup at exit. */ /* Cleanup at exit. */
static void exit_dm_lib(void) static void exit_dm_lib(void)
{ {
@ -392,11 +428,10 @@ static void *timeout_thread(void *unused)
while (!list_empty(&timeout_registry)) { while (!list_empty(&timeout_registry)) {
struct thread_status *thread; struct thread_status *thread;
timeout.tv_sec = (time_t)-1; timeout.tv_sec = (time_t) -1;
curr_time = time(NULL); curr_time = time(NULL);
list_iterate_items_gen(thread, &timeout_registry, list_iterate_items_gen(thread, &timeout_registry, timeout_list) {
timeout_list) {
if (thread->next_time < curr_time) { if (thread->next_time < curr_time) {
thread->next_time = curr_time + thread->timeout; thread->next_time = curr_time + thread->timeout;
pthread_kill(thread->thread, SIGALRM); pthread_kill(thread->thread, SIGALRM);
@ -406,7 +441,8 @@ static void *timeout_thread(void *unused)
timeout.tv_sec = thread->next_time; 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); pthread_cleanup_pop(1);
@ -491,23 +527,22 @@ static sigset_t unblock_sigalrm(void)
#define DM_WAIT_FATAL 2 #define DM_WAIT_FATAL 2
/* Wait on a device until an event occurs. */ /* 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; sigset_t set;
int ret = DM_WAIT_RETRY; int ret = DM_WAIT_RETRY;
/*
void *next = NULL;
char *params, *target_type;
uint64_t start, length;
*/
struct dm_task *dmt; struct dm_task *dmt;
struct dm_info info; struct dm_info info;
*task = 0;
if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT))) if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
return DM_WAIT_RETRY; return DM_WAIT_RETRY;
if (!(ret = dm_task_set_name(dmt, dm_basename(thread->device_path))) || thread->current_task = dmt;
!(ret = dm_task_set_event_nr(dmt, thread->event_nr)))
if (!dm_task_set_uuid(dmt, thread->device.uuid) ||
!dm_task_set_event_nr(dmt, thread->event_nr))
goto out; goto out;
/* /*
@ -517,31 +552,21 @@ static int event_wait(struct thread_status *thread)
set = unblock_sigalrm(); set = unblock_sigalrm();
dm_log_init(no_intr_log); dm_log_init(no_intr_log);
errno = 0; errno = 0;
if ((ret = dm_task_run(dmt))) { if (dm_task_run(dmt)) {
thread->current_events |= DM_EVENT_DEVICE_ERROR; thread->current_events |= DM_EVENT_DEVICE_ERROR;
ret = DM_WAIT_INTR; 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))) if ((ret = dm_task_get_info(dmt, &info)))
thread->event_nr = info.event_nr; thread->event_nr = info.event_nr;
} else if (thread->events & DM_EVENT_TIMEOUT && errno == EINTR) { } else if (thread->events & DM_EVENT_TIMEOUT && errno == EINTR) {
thread->current_events |= DM_EVENT_TIMEOUT; thread->current_events |= DM_EVENT_TIMEOUT;
ret = DM_WAIT_INTR; ret = DM_WAIT_INTR;
thread->processed_events = 0;
} else { } else {
/* FIXME replace with log_* macro */
syslog(LOG_NOTICE, "dm_task_run failed, errno = %d, %s", syslog(LOG_NOTICE, "dm_task_run failed, errno = %d, %s",
errno, strerror(errno)); errno, strerror(errno));
if (errno == ENXIO) { if (errno == ENXIO) {
/* FIXME replace with log_* macro */ syslog(LOG_ERR, "%s disappeared, detaching",
syslog(LOG_ERR, "%s disappeared, detaching", thread->device_path); thread->device.name);
ret = DM_WAIT_FATAL; ret = DM_WAIT_FATAL;
} }
} }
@ -550,7 +575,11 @@ static int event_wait(struct thread_status *thread)
dm_log_init(NULL); dm_log_init(NULL);
out: out:
if (ret == DM_WAIT_FATAL || ret == DM_WAIT_RETRY) {
dm_task_destroy(dmt); dm_task_destroy(dmt);
thread->current_task = NULL;
} else
*task = dmt;
return ret; return ret;
} }
@ -558,20 +587,25 @@ static int event_wait(struct thread_status *thread)
/* Register a device with the DSO. */ /* Register a device with the DSO. */
static int do_register_device(struct thread_status *thread) 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. */ /* Unregister a device with the DSO. */
static int do_unregister_device(struct thread_status *thread) 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. */ /* 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->dso_data->process_event(task, thread->current_events);
thread->current_events);
} }
/* Thread cleanup handler to unregister device. */ /* Thread cleanup handler to unregister device. */
@ -581,7 +615,10 @@ static void monitor_unregister(void *arg)
if (!do_unregister_device(thread)) if (!do_unregister_device(thread))
syslog(LOG_ERR, "%s: %s unregister failed\n", __func__, 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. */ /* Device monitoring thread. */
@ -589,6 +626,7 @@ static void *monitor_thread(void *arg)
{ {
struct thread_status *thread = arg; struct thread_status *thread = arg;
int wait_error = 0; int wait_error = 0;
struct dm_task *task;
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
pthread_cleanup_push(monitor_unregister, thread); pthread_cleanup_push(monitor_unregister, thread);
@ -602,14 +640,18 @@ static void *monitor_thread(void *arg)
while (1) { while (1) {
thread->current_events = 0; thread->current_events = 0;
wait_error = event_wait(thread); wait_error = event_wait(thread, &task);
if (wait_error == DM_WAIT_RETRY) if (wait_error == DM_WAIT_RETRY)
continue; continue;
/* FIXME Give a DSO a chance to clean up. */
if (wait_error == DM_WAIT_FATAL) if (wait_error == DM_WAIT_FATAL)
break; break;
/*
* We know that wait succeeded and stored a
* pointer to dm_task with device status into task.
*/
/* /*
* Check against filter. * Check against filter.
* *
@ -617,10 +659,6 @@ static void *monitor_thread(void *arg)
* the device got registered for those events AND * the device got registered for those events AND
* those events haven't been processed yet, call * those events haven't been processed yet, call
* the DSO's process_event() handler. * 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(); lock_mutex();
if (thread->status == DM_THREAD_SHUTDOWN) { if (thread->status == DM_THREAD_SHUTDOWN) {
@ -629,17 +667,21 @@ static void *monitor_thread(void *arg)
} }
unlock_mutex(); unlock_mutex();
if (thread->events & if (thread->events & thread->current_events) {
thread->current_events &
~thread->processed_events) {
lock_mutex(); lock_mutex();
thread->processing = 1; thread->processing = 1;
unlock_mutex(); 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(); lock_mutex();
thread->processing = 0; thread->processing = 0;
unlock_mutex(); 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; thread->status = DM_THREAD_DONE;
unlock_mutex(); unlock_mutex();
pthread_cleanup_pop(0); pthread_cleanup_pop(1);
return NULL; return NULL;
} }
/* Create a device monitoring thread. */ /* Create a device monitoring thread. */
/* FIXME: call this with mutex hold ? */
static int create_thread(struct thread_status *thread) static int create_thread(struct thread_status *thread)
{ {
return pthread_create(&thread->thread, NULL, monitor_thread, 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) 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") && "process_event") &&
lookup_symbol(dl, data, (void*) &data->register_device, lookup_symbol(dl, data, (void *) &data->register_device,
"register_device") && "register_device") &&
lookup_symbol(dl, data, (void*) &data->unregister_device, lookup_symbol(dl, data, (void *) &data->unregister_device,
"unregister_device"); "unregister_device");
} }
@ -728,13 +770,13 @@ static struct dso_data *load_dso(struct message_data *data)
void *dl; void *dl;
struct dso_data *ret = NULL; 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(); const char *dlerr = dlerror();
syslog(LOG_ERR, "dmeventd %s dlopen failed: %s", data->dso_name, dlerr); syslog(LOG_ERR, "dmeventd %s dlopen failed: %s", data->dso_name,
char buf[1024]; /* FIXME */ dlerr);
snprintf(buf, 1024, "%s dlopen failed: %s", data->dso_name, dlerr); data->msg->size =
data->msg->size = strlen(buf) + 1; dm_saprintf(&(data->msg->data), "%s dlopen failed: %s",
data->msg->data = dm_strdup(buf); data->dso_name, dlerr);
return NULL; return NULL;
} }
@ -763,7 +805,6 @@ static struct dso_data *load_dso(struct message_data *data)
return ret; return ret;
} }
/* Return success on daemon active check. */ /* Return success on daemon active check. */
static int active(struct message_data *message_data) 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 thread_status *thread, *thread_new = NULL;
struct dso_data *dso_data; struct dso_data *dso_data;
if (!device_exists(message_data->device_path)) {
stack;
ret = -ENODEV;
goto out;
}
if (!(dso_data = lookup_dso(message_data)) && if (!(dso_data = lookup_dso(message_data)) &&
!(dso_data = load_dso(message_data))) { !(dso_data = load_dso(message_data))) {
stack; stack;
/* FIXME */
#ifdef ELIBACC #ifdef ELIBACC
ret = -ELIBACC; ret = -ELIBACC;
#else #else
@ -807,15 +841,17 @@ static int register_for_event(struct message_data *message_data)
goto out; goto out;
} }
if (!fill_device_data(thread_new)) {
stack;
ret = -ENODEV;
goto out;
}
lock_mutex(); lock_mutex();
if (!(thread = lookup_thread_status(message_data))) { if (!(thread = lookup_thread_status(message_data))) {
unlock_mutex(); unlock_mutex();
/*
* FIXME: better do this asynchronously in the
* monitoring thread ?
*/
if (!(ret = do_register_device(thread_new))) if (!(ret = do_register_device(thread_new)))
goto out; 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, static int registered_device(struct message_data *message_data,
struct thread_status *thread) struct thread_status *thread)
{ {
char test[1];
struct dm_event_daemon_message *msg = message_data->msg; struct dm_event_daemon_message *msg = message_data->msg;
const char *fmt = "%s %s %u"; const char *fmt = "%s %s %u";
const char *dso = thread->dso_data->dso_name; const char *dso = thread->dso_data->dso_name;
const char *dev = thread->device_path; const char *dev = thread->device.uuid;
unsigned events = ((thread->status == DM_THREAD_RUNNING) && (thread->events)) ? unsigned events = ((thread->status == DM_THREAD_RUNNING)
thread->events : thread->events | DM_EVENT_REGISTRATION_PENDING; && (thread->events)) ? thread->events : thread->
events | DM_EVENT_REGISTRATION_PENDING;
if (msg->data) if (msg->data)
dm_free(msg->data); dm_free(msg->data);
msg->size = snprintf(test, 1, fmt, dso, dev, events); msg->size = dm_saprintf(&(msg->data), fmt, dso, dev, events);
msg->data = dm_malloc(msg->size);
snprintf(msg->data, msg->size, fmt, dso, dev, events);
unlock_mutex(); unlock_mutex();
return 0; 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) struct thread_status *thread)
{ {
/* If DSO names and device paths are equal. */ /* 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) && 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 names are equal. */
if (dso_name) if (dso_name)
return !strcmp(dso_name, thread->dso_data->dso_name); return !strcmp(dso_name, thread->dso_data->dso_name);
/* If device paths are equal. */ /* If device paths are equal. */
if (device_path) if (device_uuid)
return !strcmp(device_path, thread->device_path); return !strcmp(device_uuid, thread->device.uuid);
return 1; 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. */ /* Iterate list of threads checking if we want a particular one. */
list_iterate_items(thread, &_thread_registry) list_iterate_items(thread, &_thread_registry)
if ((hit = want_registered_device(message_data->dso_name, if ((hit = want_registered_device(message_data->dso_name,
message_data->device_path, message_data->device_uuid,
thread))) thread)))
break; break;
@ -971,10 +1005,8 @@ static int _get_registered_device(struct message_data *message_data, int next)
if (list_end(&_thread_registry, &thread->list)) if (list_end(&_thread_registry, &thread->list))
goto out; goto out;
thread = list_item(thread->list.n, thread = list_item(thread->list.n, struct thread_status);
struct thread_status); } while (!want_registered_device(message_data->dso_name, NULL, thread));
} while (!want_registered_device(message_data->dso_name,
NULL, thread));
return registered_device(message_data, thread); return registered_device(message_data, thread);
@ -1016,9 +1048,8 @@ static int get_timeout(struct message_data *message_data)
lock_mutex(); lock_mutex();
if ((thread = lookup_thread_status(message_data))) { if ((thread = lookup_thread_status(message_data))) {
msg->data = dm_malloc(8*sizeof(uint32_t)); /* FIXME */ msg->size =
msg->size = snprintf(msg->data, 8*sizeof(uint32_t), dm_saprintf(&(msg->data), "%" PRIu32, thread->timeout);
"%"PRIu32, thread->timeout);
} else { } else {
msg->data = NULL; msg->data = NULL;
msg->size = 0; msg->size = 0;
@ -1028,7 +1059,6 @@ static int get_timeout(struct message_data *message_data)
return thread ? 0 : -ENODEV; return thread ? 0 : -ENODEV;
} }
/* Initialize a fifos structure with path names. */ /* Initialize a fifos structure with path names. */
static void init_fifos(struct dm_event_fifos *fifos) static void init_fifos(struct dm_event_fifos *fifos)
{ {
@ -1049,7 +1079,17 @@ static int open_fifos(struct dm_event_fifos *fifos)
return -errno; 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 they were already there, make sure permissions are ok. */
if (chmod(fifos->client_path, 0600)) { if (chmod(fifos->client_path, 0600)) {
syslog(LOG_ERR, "Unable to set correct file permissions on %s", 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 * Read message from client making sure that data is available
* and a complete message is read. Must not block indefinitely. * 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; struct timeval t;
unsigned bytes = 0; 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); FD_SET(fifos->client, &fds);
t.tv_sec = 1; t.tv_sec = 1;
t.tv_usec = 0; 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 */ if (!ret && !bytes) /* nothing to read */
return 0; 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); ret = read(fifos->client, buf + bytes, size - bytes);
bytes += ret > 0 ? ret : 0; bytes += ret > 0 ? ret : 0;
if (bytes == 2*sizeof(uint32_t) && header) { if (bytes == 2 * sizeof(uint32_t) && header) {
msg->cmd = ntohl(*((uint32_t *)buf)); msg->cmd = ntohl(*((uint32_t *) buf));
msg->size = ntohl(*((uint32_t *)buf + 1)); msg->size = ntohl(*((uint32_t *) buf + 1));
buf = msg->data = dm_malloc(msg->size); buf = msg->data = dm_malloc(msg->size);
size = msg->size; size = msg->size;
bytes = 0; bytes = 0;
@ -1129,6 +1170,7 @@ static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
if (msg->data) if (msg->data)
dm_free(msg->data); dm_free(msg->data);
msg->data = NULL; msg->data = NULL;
msg->size = 0;
} }
return bytes == size; 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. * 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; unsigned bytes = 0;
int ret = 0; int ret = 0;
fd_set fds; 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); char *buf = alloca(size);
*((uint32_t *)buf) = htonl(msg->cmd); *((uint32_t *)buf) = htonl(msg->cmd);
*((uint32_t *)buf + 1) = htonl(msg->size); *((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; errno = 0;
while (bytes < size && errno != EIO) { 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. */ /* Watch client write FIFO to be ready for output. */
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(fifos->server, &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); ret = write(fifos->server, buf + bytes, size - bytes);
bytes += ret > 0 ? ret : 0; bytes += ret > 0 ? ret : 0;
@ -1176,15 +1221,16 @@ static int handle_request(struct dm_event_daemon_message *msg,
{ {
static struct { static struct {
unsigned int cmd; unsigned int cmd;
int (*f)(struct message_data*); int (*f)(struct message_data *);
} requests[] = { } requests[] = {
{ DM_EVENT_CMD_REGISTER_FOR_EVENT, register_for_event }, { DM_EVENT_CMD_REGISTER_FOR_EVENT, register_for_event},
{ DM_EVENT_CMD_UNREGISTER_FOR_EVENT, unregister_for_event }, { DM_EVENT_CMD_UNREGISTER_FOR_EVENT, unregister_for_event},
{ DM_EVENT_CMD_GET_REGISTERED_DEVICE, get_registered_device }, { DM_EVENT_CMD_GET_REGISTERED_DEVICE, get_registered_device},
{ DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE, get_next_registered_device }, { DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
{ DM_EVENT_CMD_SET_TIMEOUT, set_timeout }, get_next_registered_device},
{ DM_EVENT_CMD_GET_TIMEOUT, get_timeout }, { DM_EVENT_CMD_SET_TIMEOUT, set_timeout},
{ DM_EVENT_CMD_ACTIVE, active }, { DM_EVENT_CMD_GET_TIMEOUT, get_timeout},
{ DM_EVENT_CMD_ACTIVE, active},
}, *req; }, *req;
for (req = requests; req < requests + sizeof(requests); 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. */ /* Parse the message. */
memset(&message_data, 0, sizeof(message_data)); memset(&message_data, 0, sizeof(message_data));
message_data.msg = msg; message_data.msg = msg;
if (msg->cmd != DM_EVENT_CMD_ACTIVE && if (msg->cmd != DM_EVENT_CMD_ACTIVE && !parse_message(&message_data)) {
!parse_message(&message_data)) {
stack; stack;
ret = -EINVAL; ret = -EINVAL;
} else } else
@ -1220,15 +1265,11 @@ static void process_request(struct dm_event_fifos *fifos)
{ {
struct dm_event_daemon_message msg; struct dm_event_daemon_message msg;
/* FIXME: better error handling */
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
/* /*
* Read the request from the client. * Read the request from the client (client_read, client_write
* Of course, it's tough to tell what to do when * give true on success and false on failure).
* we use fucking retarded return codes like
* 0 for error.
*/ */
if (!client_read(fifos, &msg)) if (!client_read(fifos, &msg))
return; return;
@ -1236,7 +1277,12 @@ static void process_request(struct dm_event_fifos *fifos)
msg.cmd = do_process_request(&msg); msg.cmd = do_process_request(&msg);
if (!msg.data) { if (!msg.data) {
msg.data = dm_strdup(strerror(-msg.cmd)); msg.data = dm_strdup(strerror(-msg.cmd));
if (msg.data)
msg.size = strlen(msg.data) + 1; msg.size = strlen(msg.data) + 1;
else {
msg.size = 0;
stack;
}
} }
if (!client_write(fifos, &msg)) if (!client_write(fifos, &msg))
@ -1270,14 +1316,16 @@ static void cleanup_unused_threads(void)
if (ret == ESRCH) { if (ret == ESRCH) {
thread->status = DM_THREAD_DONE; thread->status = DM_THREAD_DONE;
} else if (ret) { } else if (ret) {
syslog(LOG_ERR, "Unable to terminate thread: %s\n", syslog(LOG_ERR,
"Unable to terminate thread: %s\n",
strerror(-ret)); strerror(-ret));
stack; stack;
} }
goto out; goto out;
} else { } else {
list_del(l); 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; thread->status = DM_THREAD_RUNNING;
LINK_THREAD(thread); LINK_THREAD(thread);
} }
@ -1288,7 +1336,7 @@ static void cleanup_unused_threads(void)
free_thread_status(thread); free_thread_status(thread);
} }
} }
out: out:
unlock_mutex(); unlock_mutex();
} }
@ -1344,7 +1392,7 @@ static void exit_handler(int sig)
static int lock_pidfile(void) static int lock_pidfile(void)
{ {
int lf; 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) if ((lf = open(pidfile, O_CREAT | O_RDWR, 0644)) < 0)
exit(EXIT_OPEN_PID_FAILURE); exit(EXIT_OPEN_PID_FAILURE);
@ -1472,7 +1520,8 @@ int main(int argc, char *argv[])
while (!_exit_now) { while (!_exit_now) {
process_request(&fifos); process_request(&fifos);
cleanup_unused_threads(); 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; _thread_registries_empty = 0;
else else
_thread_registries_empty = 1; _thread_registries_empty = 1;

View File

@ -30,43 +30,110 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <arpa/inet.h> /* for htonl, ntohl */ #include <arpa/inet.h> /* for htonl, ntohl */
/* Set by any of the external fxns the first time one of them is called */ struct dm_event_handler {
/* FIXME Unused */ const char *dso;
// static int _logging = 0; 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. */ static void dm_event_handler_clear_device(struct dm_event_handler *h)
/* FIXME: move to seperate module to share with the daemon. */
static const char delimiter = ' ';
static char *fetch_string(char **src)
{ {
char *p, *ret; h->device = h->uuid = NULL;
h->major = h->minor = 0;
}
if ((p = strchr(*src, delimiter))) struct dm_event_handler *dm_event_handler_create(void)
*p = 0; {
struct dm_event_handler *ret = 0;
if ((ret = dm_strdup(*src))) if (!(ret = dm_malloc(sizeof(*ret))))
*src += strlen(ret) + 1; return NULL;
if (p) ret->dso = ret->device = ret->uuid = NULL;
*p = delimiter; ret->major = ret->minor = 0;
ret->events = 0;
return ret; return ret;
} }
/* Parse a device message from the daemon. */ void dm_event_handler_destroy(struct dm_event_handler *h)
static int parse_message(struct dm_event_daemon_message *msg, char **dso_name,
char **device, enum dm_event_type *events)
{ {
char *p = msg->data; dm_free(h);
}
if ((*dso_name = fetch_string(&p)) && void dm_event_handler_set_dso(struct dm_event_handler *h, const char *path)
(*device = fetch_string(&p))) { {
*events = atoi(p); 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 * 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; unsigned bytes = 0;
int ret, i; int ret, i;
fd_set fds; fd_set fds;
struct timeval tval = {0, 0}; struct timeval tval = { 0, 0 };
size_t size = 2 * sizeof(uint32_t); // status + size size_t size = 2 * sizeof(uint32_t); /* status + size */
char *buf = alloca(size); char *buf = alloca(size);
int header = 1; 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_ZERO(&fds);
FD_SET(fifos->server, &fds); FD_SET(fifos->server, &fds);
tval.tv_sec = 1; 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) { if (ret < 0 && errno != EINTR) {
log_error("Unable to read from event server"); log_error("Unable to read from event server");
return 0; return 0;
@ -116,7 +185,7 @@ static int daemon_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
} }
bytes += ret; bytes += ret;
if (bytes == 2*sizeof(uint32_t) && header) { if (bytes == 2 * sizeof(uint32_t) && header) {
msg->cmd = ntohl(*((uint32_t *)buf)); msg->cmd = ntohl(*((uint32_t *)buf));
msg->size = ntohl(*((uint32_t *)buf + 1)); msg->size = ntohl(*((uint32_t *)buf + 1));
buf = msg->data = dm_malloc(msg->size); 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. */ /* 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; unsigned bytes = 0;
int ret = 0; int ret = 0;
fd_set fds; 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); char *buf = alloca(size);
*((uint32_t *)buf) = htonl(msg->cmd); *((uint32_t *)buf) = htonl(msg->cmd);
*((uint32_t *)buf + 1) = htonl(msg->size); *((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) { while (bytes < size) {
do { do {
/* Watch daemon write FIFO to be ready for output. */ /* Watch daemon write FIFO to be ready for output. */
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(fifos->client, &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)) { if ((ret < 0) && (errno != EINTR)) {
log_error("Unable to talk to event daemon"); log_error("Unable to talk to event daemon");
return 0; return 0;
} }
} while (ret < 1); } 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 (ret < 0) {
if ((errno == EINTR) || (errno == EAGAIN)) if ((errno == EINTR) || (errno == EAGAIN))
continue; continue;
@ -177,14 +248,14 @@ static int daemon_write(struct dm_event_fifos *fifos, struct dm_event_daemon_mes
return bytes == size; return bytes == size;
} }
static int daemon_talk(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg, static int daemon_talk(struct dm_event_fifos *fifos,
int cmd, const char *dso_name, const char *device, struct dm_event_daemon_message *msg, int cmd,
const char *dso_name, const char *device,
enum dm_event_type events, uint32_t timeout) enum dm_event_type events, uint32_t timeout)
{ {
char test[1];
const char *dso = dso_name ? dso_name : ""; const char *dso = dso_name ? dso_name : "";
const char *dev = device ? device : ""; const char *dev = device ? device : "";
const char *fmt = "%s %s %u %"PRIu32; const char *fmt = "%s %s %u %" PRIu32;
memset(msg, 0, sizeof(*msg)); 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. * into ASCII message string.
*/ */
msg->cmd = cmd; msg->cmd = cmd;
/* FIXME depends on glibc 2.1+ */ msg->size = dm_saprintf(&(msg->data), fmt, dso, dev, events, timeout);
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);
/* /*
* Write command and message to and * Write command and message to and
@ -256,7 +324,7 @@ static int start_daemon(struct dm_event_fifos *fifos)
return 0; return 0;
} }
start_server: start_server:
/* server is not running */ /* server is not running */
pid = fork(); pid = fork();
@ -264,11 +332,13 @@ start_server:
log_error("Unable to fork."); log_error("Unable to fork.");
else if (!pid) { 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); exit(EXIT_FAILURE);
} else { } else {
if (waitpid(pid, &status, 0) < 0) 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)) else if (WEXITSTATUS(status))
log_error("Unable to start dmeventd."); log_error("Unable to start dmeventd.");
else else
@ -281,8 +351,8 @@ start_server:
/* Initialize client. */ /* Initialize client. */
static int init_client(struct dm_event_fifos *fifos) static int init_client(struct dm_event_fifos *fifos)
{ {
/* FIXME Is fifo the most suitable method? */ /* FIXME? Is fifo the most suitable method? Why not share
/* FIXME Why not share comms/daemon code with something else e.g. multipath? */ comms/daemon code with something else e.g. multipath? */
/* init fifos */ /* init fifos */
memset(fifos, 0, sizeof(*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. */ /* 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); log_error("%s: flock %s", __func__, fifos->server_path);
close(fifos->server); close(fifos->server);
return 0; return 0;
} }
/* if ((fifos->client = open(fifos->client_path, /* if ((fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK)) < 0) {*/
O_WRONLY | O_NONBLOCK)) < 0) {*/
if ((fifos->client = open(fifos->client_path, O_RDWR | 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", log_error("%s: Can't open client fifo %s: %s",
__func__, fifos->client_path, strerror(errno)); __func__, fifos->client_path, strerror(errno));
@ -331,40 +400,45 @@ static void dtr_client(struct dm_event_fifos *fifos)
close(fifos->server); close(fifos->server);
} }
/* Check, if a block device exists. */ /* Get uuid of a device, if it exists (otherwise NULL). */
static int device_exists(const char *device) static struct dm_task *get_device_info(const struct dm_event_handler *h)
{ {
struct stat st_buf; struct dm_task *dmt = dm_task_create(DM_DEVICE_INFO);
char path2[PATH_MAX]; struct dm_task *ret;
if (!device) if (!dmt)
return 0; return NULL;
if (device[0] == '/') /* absolute path */ if (h->uuid)
return !stat(device, &st_buf) && S_ISBLK(st_buf.st_mode); 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)) if (!dm_task_run(dmt))
return 0; 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. */ /* Handle the event (de)registration call and return negative error codes. */
static int do_event(int cmd, struct dm_event_daemon_message *msg, static int do_event(int cmd, struct dm_event_daemon_message *msg,
const char *dso_name, const char *device, enum dm_event_type events, const char *dso_name, const char *device,
uint32_t timeout) enum dm_event_type events, uint32_t timeout)
{ {
int ret; int ret;
struct dm_event_fifos fifos; 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)) { if (!init_client(&fifos)) {
stack; stack;
return -ESRCH; 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); ret = daemon_talk(&fifos, msg, cmd, dso_name, device, events, timeout);
/* what is the opposite of init? */ /* what is the opposite of init? */
@ -373,23 +447,25 @@ static int do_event(int cmd, struct dm_event_daemon_message *msg,
return ret; return ret;
} }
/* FIXME remove dso_name - use handle instead */
/* FIXME Use uuid not path! */
/* External library interface. */ /* External library interface. */
int dm_event_register(const char *dso_name, const char *device_path, int dm_event_register(const struct dm_event_handler *h)
enum dm_event_type events)
{ {
int ret, err; int ret, err;
const char *uuid;
struct dm_task *dmt;
struct dm_event_daemon_message msg; struct dm_event_daemon_message msg;
if (!device_exists(device_path)) { if (!(dmt = get_device_info(h))) {
log_error("%s: device not found", device_path); log_error("%s: device not found", h->device);
return 0; return 0;
} }
uuid = dm_task_get_uuid(dmt);
if ((err = do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, &msg, if ((err = do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, &msg,
dso_name, device_path, events, 0)) < 0) { h->dso, uuid, h->events, 0)) < 0) {
log_error("%s: event registration failed: %s", device_path, log_error("%s: event registration failed: %s",
dm_task_get_name(dmt),
msg.data ? msg.data : strerror(-err)); msg.data ? msg.data : strerror(-err));
ret = 0; ret = 0;
} else } else
@ -398,23 +474,29 @@ int dm_event_register(const char *dso_name, const char *device_path,
if (msg.data) if (msg.data)
dm_free(msg.data); dm_free(msg.data);
dm_task_destroy(dmt);
return ret; return ret;
} }
int dm_event_unregister(const char *dso_name, const char *device_path, int dm_event_unregister(const struct dm_event_handler *h)
enum dm_event_type events)
{ {
int ret, err; int ret, err;
const char *uuid;
struct dm_task *dmt;
struct dm_event_daemon_message msg; struct dm_event_daemon_message msg;
if (!device_exists(device_path)) { if (!(dmt = get_device_info(h))) {
log_error("%s: device not found", device_path); log_error("%s: device not found", dm_task_get_name(dmt));
return 0; return 0;
} }
uuid = dm_task_get_uuid(dmt);
if ((err = do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, &msg, if ((err = do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, &msg,
dso_name, device_path, events, 0)) < 0) { h->dso, uuid, h->events, 0)) < 0) {
log_error("%s: event deregistration failed: %s", device_path, log_error("%s: event deregistration failed: %s",
dm_task_get_name(dmt),
msg.data ? msg.data : strerror(-err)); msg.data ? msg.data : strerror(-err));
ret = 0; ret = 0;
} else } else
@ -422,9 +504,48 @@ int dm_event_unregister(const char *dso_name, const char *device_path,
if (msg.data) if (msg.data)
dm_free(msg.data); dm_free(msg.data);
dm_task_destroy(dmt);
return ret; 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 * dm_event_get_registered_device
* @dso_name * @dso_name
@ -488,9 +609,11 @@ int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
if (!device_exists(device_path)) if (!device_exists(device_path))
return -ENODEV; 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); *timeout = atoi(msg.data);
if (msg.data) if (msg.data)
dm_free(msg.data); dm_free(msg.data);
return ret; return ret;
} }
#endif

View File

@ -24,50 +24,70 @@
#include <stdint.h> #include <stdint.h>
/* Event type definitions. */ /* Event type definitions. */
/* FIXME Use masks to separate the types and provide for extension. */
enum dm_event_type { enum dm_event_type {
DM_EVENT_SINGLE = 0x01, /* Report multiple errors just once. */ DM_EVENT_SETTINGS_MASK = 0x0000FF,
DM_EVENT_MULTI = 0x02, /* Report all of them. */ 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_ERROR_MASK = 0x00FF00,
DM_EVENT_DEVICE_ERROR = 0x08, /* Device failure. */ DM_EVENT_SECTOR_ERROR = 0x000100, /* Failure on a particular sector. */
DM_EVENT_PATH_ERROR = 0x10, /* Failure on an io path. */ DM_EVENT_DEVICE_ERROR = 0x000200, /* Device failure. */
DM_EVENT_ADAPTOR_ERROR = 0x20, /* Failure off a host adaptor. */ 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_STATUS_MASK = 0xFF0000,
DM_EVENT_TIMEOUT = 0x80, /* Timeout has occured */ DM_EVENT_SYNC_STATUS = 0x010000, /* Mirror synchronization completed/failed. */
DM_EVENT_REGISTRATION_PENDING = 0X100, /* Monitor thread is setting-up/shutting-down */ 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_ERROR_MASK
#define DM_EVENT_ALL_ERRORS (DM_EVENT_SECTOR_ERROR | DM_EVENT_DEVICE_ERROR | \
DM_EVENT_PATH_ERROR | DM_EVENT_ADAPTOR_ERROR)
/* Prototypes for event lib interface. */ /* Prototypes for event lib interface. */
/* FIXME Replace device with standard name/uuid/devno choice */ struct dm_event_handler;
/* Interface changes:
First register a handler, passing in a unique ref for the device. */
// int dm_event_register_handler(const char *dso_name, const char *device); /* Create and destroy dm_event_handler struct, which is passed to
// 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); 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 /* Set parameters of a handler:
a task to register/unregister events - we may need to run task - dso - shared library path to handle the events
withe that with the new event mechanism anyway, then the dso calls (only one of the following three needs to be set)
just hook in. */ - 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); /* Get parameters of a handler, same as above */
int dm_event_unregister(const char *dso_name, const char *device, const char *dm_event_handler_get_dso(const struct dm_event_handler *h);
enum dm_event_type events); const char *dm_event_handler_get_name(const struct dm_event_handler *h);
int dm_event_get_registered_device(char **dso_name, char **device, const char *dm_event_handler_get_uuid(const struct dm_event_handler *h);
enum dm_event_type *events, int next); int dm_event_handler_get_major(const struct dm_event_handler *h);
int dm_event_set_timeout(const char *device, uint32_t timeout); int dm_event_handler_get_minor(const struct dm_event_handler *h);
int dm_event_get_timeout(const char *device, uint32_t *timeout); enum dm_event_type dm_event_handler_get_events(const struct dm_event_handler *h);
/* Prototypes for DSO interface. */ /* Call out to dmeventd to register or unregister a handler. If
void process_event(const char *device, enum dm_event_type event); dmeventd is not running, it is spawned first. */
int register_device(const char *device); int dm_event_register(const struct dm_event_handler *h);
int unregister_device(const char *device); 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 #endif

View File

@ -115,3 +115,4 @@ dm_split_lvm_name
dm_split_words dm_split_words
dm_snprintf dm_snprintf
dm_basename dm_basename
dm_saprintf

View File

@ -623,4 +623,10 @@ int dm_snprintf(char *buf, size_t bufsize, const char *format, ...);
*/ */
char *dm_basename(const char *path); 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 */ #endif /* LIB_DEVICE_MAPPER_H */

View File

@ -129,3 +129,33 @@ char *dm_basename(const char *path)
return p ? p + 1 : (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;
}