1
0
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:
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 -
=============================
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.

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_unregister
dm_event_get_registered_device
dm_event_set_timeout
dm_event_get_timeout

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -115,3 +115,4 @@ dm_split_lvm_name
dm_split_words
dm_snprintf
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);
/*
* 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 */

View File

@ -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;
}