mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-30 17:18:21 +03:00
Lots of dmevent changes.
Export dm_basename(). Cope with a trailing space when comparing tables prior to possible reload.
This commit is contained in:
parent
6032a223f4
commit
5b95f17814
@ -1,6 +1,8 @@
|
||||
Version 1.02.14 -
|
||||
=============================
|
||||
Some dmevent cleanups.
|
||||
Lots of dmevent changes.
|
||||
Export dm_basename().
|
||||
Cope with a trailing space when comparing tables prior to possible reload.
|
||||
Fix dmeventd to cope if monitored device disappears.
|
||||
|
||||
Version 1.02.13 - 28 Nov 2006
|
||||
|
@ -15,8 +15,7 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
SOURCES = libdevmapper-event.c \
|
||||
dmeventd.c
|
||||
SOURCES = libdevmapper-event.c
|
||||
|
||||
LIB_STATIC = libdevmapper-event.a
|
||||
|
||||
@ -26,12 +25,20 @@ else
|
||||
LIB_SHARED = libdevmapper-event.so
|
||||
endif
|
||||
|
||||
TARGETS = dmeventd
|
||||
CLEAN_TARGETS = dmeventd.o
|
||||
|
||||
include ../make.tmpl
|
||||
|
||||
LDFLAGS += -ldl -ldevmapper -lpthread
|
||||
CLDFLAGS += -ldl -ldevmapper -lpthread
|
||||
|
||||
dmeventd: $(LIB_SHARED) dmeventd.o
|
||||
$(CC) -o $@ dmeventd.o $(LDFLAGS) \
|
||||
-L. -ldevmapper-event $(LIBS) -rdynamic
|
||||
|
||||
.PHONY: install_dynamic install_static install_include \
|
||||
install_pkgconfig
|
||||
install_pkgconfig install_dmeventd
|
||||
|
||||
INSTALL_TYPE = install_dynamic
|
||||
|
||||
@ -43,7 +50,7 @@ ifeq ("@PKGCONFIG@", "yes")
|
||||
INSTALL_TYPE += install_pkgconfig
|
||||
endif
|
||||
|
||||
install: $(INSTALL_TYPE) install_include
|
||||
install: $(INSTALL_TYPE) install_include install_dmeventd
|
||||
|
||||
install_include:
|
||||
$(INSTALL) -D $(OWNER) $(GROUP) -m 444 libdevmapper-event.h \
|
||||
@ -55,6 +62,9 @@ install_dynamic: libdevmapper-event.$(LIB_SUFFIX)
|
||||
$(LN_S) -f libdevmapper-event.$(LIB_SUFFIX).$(LIB_VERSION) \
|
||||
$(libdir)/libdevmapper-event.$(LIB_SUFFIX)
|
||||
|
||||
install_dmeventd: dmeventd
|
||||
$(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) $< $(sbindir)/$<
|
||||
|
||||
install_pkgconfig:
|
||||
$(INSTALL) -D $(OWNER) $(GROUP) -m 444 libdevmapper-event.pc \
|
||||
$(usrlibdir)/pkgconfig/devmapper-event.pc
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2005-2007 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
@ -42,15 +42,22 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <arpa/inet.h> /* for htonl, ntohl */
|
||||
|
||||
#ifdef linux
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
/* We must 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 */
|
||||
static volatile sig_atomic_t _thread_registries_empty = 1; /* registries are empty initially */
|
||||
|
||||
/* List (un)link macros. */
|
||||
#define LINK(x, head) list_add(head, &(x)->list)
|
||||
#define LINK_DSO(dso) LINK(dso, &dso_registry)
|
||||
#define LINK_THREAD(thread) LINK(thread, &thread_registry)
|
||||
#define LINK_DSO(dso) LINK(dso, &_dso_registry)
|
||||
#define LINK_THREAD(thread) LINK(thread, &_thread_registry)
|
||||
|
||||
#define UNLINK(x) list_del(&(x)->list)
|
||||
#define UNLINK_DSO(x) UNLINK(x)
|
||||
@ -59,7 +66,11 @@
|
||||
#define DAEMON_NAME "dmeventd"
|
||||
|
||||
/* Global mutex for list accesses. */
|
||||
static pthread_mutex_t mutex;
|
||||
static pthread_mutex_t _global_mutex;
|
||||
|
||||
#define DM_THREAD_RUNNING 0
|
||||
#define DM_THREAD_SHUTDOWN 1
|
||||
#define DM_THREAD_DONE 2
|
||||
|
||||
/* Data kept about a DSO. */
|
||||
struct dso_data {
|
||||
@ -98,7 +109,7 @@ struct dso_data {
|
||||
*/
|
||||
int (*unregister_device)(const char *device);
|
||||
};
|
||||
static LIST_INIT(dso_registry);
|
||||
static LIST_INIT(_dso_registry);
|
||||
|
||||
/* Structure to keep parsed register variables from client message. */
|
||||
struct message_data {
|
||||
@ -131,6 +142,7 @@ struct thread_status {
|
||||
char *device_path; /* Mapped device path. */
|
||||
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. */
|
||||
@ -138,13 +150,13 @@ struct thread_status {
|
||||
uint32_t timeout;
|
||||
struct list timeout_list;
|
||||
};
|
||||
static LIST_INIT(thread_registry);
|
||||
static LIST_INIT(thread_registry_unused);
|
||||
static LIST_INIT(_thread_registry);
|
||||
static LIST_INIT(_thread_registry_unused);
|
||||
|
||||
static int timeout_running;
|
||||
static int _timeout_running;
|
||||
static LIST_INIT(timeout_registry);
|
||||
static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t timeout_cond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_mutex_t _timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t _timeout_cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
/* Allocate/free the status structure for a monitoring thread. */
|
||||
static struct thread_status *alloc_thread_status(struct message_data *data,
|
||||
@ -197,14 +209,6 @@ static void free_dso_data(struct dso_data *data)
|
||||
dm_free(data);
|
||||
}
|
||||
|
||||
/* FIXME: Factor out. */
|
||||
static char *dm_basename(char *str)
|
||||
{
|
||||
char *p = strrchr(str, '/');
|
||||
|
||||
return p ? p + 1 : str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch a string off src and duplicate it into *ptr.
|
||||
* Pay attention to 0 lenght strings.
|
||||
@ -246,12 +250,18 @@ static void free_message(struct message_data *message_data)
|
||||
|
||||
if (message_data->device_path)
|
||||
dm_free(message_data->device_path);
|
||||
|
||||
}
|
||||
|
||||
/* Parse a register message from the client. */
|
||||
static int parse_message(struct message_data *message_data)
|
||||
{
|
||||
char *p = message_data->msg->msg;
|
||||
int ret = 0;
|
||||
char *p = message_data->msg->data;
|
||||
struct dm_event_daemon_message *msg = message_data->msg;
|
||||
|
||||
if (!msg->data)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Retrieve application identifier, mapped device
|
||||
@ -278,21 +288,24 @@ static int parse_message(struct message_data *message_data)
|
||||
DM_EVENT_DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
return 1;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
dm_free(msg->data);
|
||||
msg->data = NULL;
|
||||
msg->size = 0;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/* Global mutex to lock access to lists et al. */
|
||||
static int lock_mutex(void)
|
||||
{
|
||||
return pthread_mutex_lock(&mutex);
|
||||
return pthread_mutex_lock(&_global_mutex);
|
||||
}
|
||||
|
||||
static int unlock_mutex(void)
|
||||
{
|
||||
return pthread_mutex_unlock(&mutex);
|
||||
return pthread_mutex_unlock(&_global_mutex);
|
||||
}
|
||||
|
||||
/* Store pid in pidfile. */
|
||||
@ -343,7 +356,7 @@ static struct thread_status *lookup_thread_status(struct message_data *data)
|
||||
{
|
||||
struct thread_status *thread;
|
||||
|
||||
list_iterate_items(thread, &thread_registry)
|
||||
list_iterate_items(thread, &_thread_registry)
|
||||
if (!strcmp(data->device_path, thread->device_path))
|
||||
return thread;
|
||||
|
||||
@ -358,29 +371,10 @@ static void exit_dm_lib(void)
|
||||
dm_lib_exit();
|
||||
}
|
||||
|
||||
/* Derive error case from target parameter string. */
|
||||
/* FIXME Remove? */
|
||||
static int error_detected(struct thread_status *thread, char *params) __attribute__ ((unused));
|
||||
static int error_detected(struct thread_status *thread, char *params)
|
||||
{
|
||||
size_t len;
|
||||
/*
|
||||
Leave it to the DSO to decide how to interpret the status info
|
||||
if ((len = strlen(params)) &&
|
||||
params[len - 1] == 'F') {
|
||||
*/
|
||||
if (params && (len = strlen(params))) {
|
||||
thread->current_events |= DM_EVENT_DEVICE_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exit_timeout(void *unused)
|
||||
{
|
||||
timeout_running = 0;
|
||||
pthread_mutex_unlock(&timeout_mutex);
|
||||
_timeout_running = 0;
|
||||
pthread_mutex_unlock(&_timeout_mutex);
|
||||
}
|
||||
|
||||
/* Wake up monitor threads every so often. */
|
||||
@ -391,7 +385,7 @@ static void *timeout_thread(void *unused)
|
||||
|
||||
timeout.tv_nsec = 0;
|
||||
pthread_cleanup_push(exit_timeout, NULL);
|
||||
pthread_mutex_lock(&timeout_mutex);
|
||||
pthread_mutex_lock(&_timeout_mutex);
|
||||
|
||||
while (!list_empty(&timeout_registry)) {
|
||||
struct thread_status *thread;
|
||||
@ -410,7 +404,7 @@ 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);
|
||||
@ -422,37 +416,37 @@ static int register_for_timeout(struct thread_status *thread)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pthread_mutex_lock(&timeout_mutex);
|
||||
pthread_mutex_lock(&_timeout_mutex);
|
||||
|
||||
thread->next_time = time(NULL) + thread->timeout;
|
||||
|
||||
if (list_empty(&thread->timeout_list)) {
|
||||
list_add(&timeout_registry, &thread->timeout_list);
|
||||
if (timeout_running)
|
||||
pthread_cond_signal(&timeout_cond);
|
||||
if (_timeout_running)
|
||||
pthread_cond_signal(&_timeout_cond);
|
||||
}
|
||||
|
||||
if (!timeout_running) {
|
||||
if (!_timeout_running) {
|
||||
pthread_t timeout_id;
|
||||
|
||||
if (!(ret = -pthread_create(&timeout_id, NULL,
|
||||
timeout_thread, NULL)))
|
||||
timeout_running = 1;
|
||||
_timeout_running = 1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&timeout_mutex);
|
||||
pthread_mutex_unlock(&_timeout_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void unregister_for_timeout(struct thread_status *thread)
|
||||
{
|
||||
pthread_mutex_lock(&timeout_mutex);
|
||||
pthread_mutex_lock(&_timeout_mutex);
|
||||
if (!list_empty(&thread->timeout_list)) {
|
||||
list_del(&thread->timeout_list);
|
||||
list_init(&thread->timeout_list);
|
||||
}
|
||||
pthread_mutex_unlock(&timeout_mutex);
|
||||
pthread_mutex_unlock(&_timeout_mutex);
|
||||
}
|
||||
|
||||
static void no_intr_log(int level, const char *file, int line,
|
||||
@ -490,11 +484,15 @@ static sigset_t unblock_sigalrm(void)
|
||||
return old;
|
||||
}
|
||||
|
||||
#define DM_WAIT_RETRY 0
|
||||
#define DM_WAIT_INTR 1
|
||||
#define DM_WAIT_FATAL 2
|
||||
|
||||
/* Wait on a device until an event occurs. */
|
||||
static int event_wait(struct thread_status *thread)
|
||||
{
|
||||
sigset_t set;
|
||||
int ret = 0;
|
||||
int ret = DM_WAIT_RETRY;
|
||||
/*
|
||||
void *next = NULL;
|
||||
char *params, *target_type;
|
||||
@ -504,7 +502,7 @@ static int event_wait(struct thread_status *thread)
|
||||
struct dm_info info;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
|
||||
return 0;
|
||||
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)))
|
||||
@ -518,19 +516,8 @@ static int event_wait(struct thread_status *thread)
|
||||
dm_log_init(no_intr_log);
|
||||
errno = 0;
|
||||
if ((ret = dm_task_run(dmt))) {
|
||||
/*
|
||||
do {
|
||||
params = NULL;
|
||||
next = dm_get_next_target(dmt, next, &start, &length,
|
||||
&target_type, ¶ms);
|
||||
|
||||
log_error("%s: %s\n", __func__, params);
|
||||
if ((ret = error_detected(thread, params)))
|
||||
break;
|
||||
} while(next);
|
||||
*/
|
||||
thread->current_events |= DM_EVENT_DEVICE_ERROR;
|
||||
ret = 1;
|
||||
ret = DM_WAIT_INTR;
|
||||
|
||||
/*
|
||||
* FIXME: I am setting processed_events to zero here
|
||||
@ -544,7 +531,7 @@ static int event_wait(struct thread_status *thread)
|
||||
thread->event_nr = info.event_nr;
|
||||
} else if (thread->events & DM_EVENT_TIMEOUT && errno == EINTR) {
|
||||
thread->current_events |= DM_EVENT_TIMEOUT;
|
||||
ret = 1;
|
||||
ret = DM_WAIT_INTR;
|
||||
thread->processed_events = 0;
|
||||
} else {
|
||||
/* FIXME replace with log_* macro */
|
||||
@ -553,7 +540,7 @@ static int event_wait(struct thread_status *thread)
|
||||
if (errno == ENXIO) {
|
||||
/* FIXME replace with log_* macro */
|
||||
syslog(LOG_ERR, "%s disappeared, detaching", thread->device_path);
|
||||
ret = 2; /* FIXME What does 2 mean? Use macro. */
|
||||
ret = DM_WAIT_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -591,13 +578,11 @@ static void monitor_unregister(void *arg)
|
||||
struct thread_status *thread = arg;
|
||||
|
||||
if (!do_unregister_device(thread))
|
||||
log_error("%s: %s unregister failed\n", __func__,
|
||||
syslog(LOG_ERR, "%s: %s unregister failed\n", __func__,
|
||||
thread->device_path);
|
||||
}
|
||||
|
||||
/* Device monitoring thread. */
|
||||
static void *monitor_thread(void *arg)
|
||||
__attribute((noreturn));
|
||||
static void *monitor_thread(void *arg)
|
||||
{
|
||||
struct thread_status *thread = arg;
|
||||
@ -608,23 +593,19 @@ static void *monitor_thread(void *arg)
|
||||
|
||||
/* Wait for do_process_request() to finish its task. */
|
||||
lock_mutex();
|
||||
thread->status = DM_THREAD_RUNNING;
|
||||
unlock_mutex();
|
||||
|
||||
/* Loop forever awaiting/analyzing device events. */
|
||||
while (1) {
|
||||
thread->current_events = 0;
|
||||
|
||||
/*
|
||||
* FIXME If unrecoverable error (ENODEV) happens
|
||||
* we loop indefinitely. event_wait should return
|
||||
* more than 0/1.
|
||||
*/
|
||||
wait_error = event_wait(thread);
|
||||
if (!wait_error)
|
||||
if (wait_error == DM_WAIT_RETRY)
|
||||
continue;
|
||||
|
||||
/* FIXME Give a DSO a chance to clean up. */
|
||||
if (wait_error == 2)
|
||||
if (wait_error == DM_WAIT_FATAL)
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -639,6 +620,13 @@ static void *monitor_thread(void *arg)
|
||||
* the same type of event happens later... after the first
|
||||
* was handled properly?
|
||||
*/
|
||||
lock_mutex();
|
||||
if (thread->status == DM_THREAD_SHUTDOWN) {
|
||||
unlock_mutex();
|
||||
break;
|
||||
}
|
||||
unlock_mutex();
|
||||
|
||||
if (thread->events &
|
||||
thread->current_events &
|
||||
~thread->processed_events) {
|
||||
@ -653,7 +641,12 @@ static void *monitor_thread(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
lock_mutex();
|
||||
thread->status = DM_THREAD_DONE;
|
||||
unlock_mutex();
|
||||
|
||||
pthread_cleanup_pop(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create a device monitoring thread. */
|
||||
@ -695,7 +688,7 @@ static struct dso_data *lookup_dso(struct message_data *data)
|
||||
|
||||
lock_mutex();
|
||||
|
||||
list_iterate_items(dso_data, &dso_registry)
|
||||
list_iterate_items(dso_data, &_dso_registry)
|
||||
if (!strcmp(data->dso_name, dso_data->dso_name)) {
|
||||
lib_get(dso_data);
|
||||
ret = dso_data;
|
||||
@ -714,8 +707,6 @@ static int lookup_symbol(void *dl, struct dso_data *data,
|
||||
if ((*symbol = dlsym(dl, name)))
|
||||
return 1;
|
||||
|
||||
log_error("looking up %s symbol in %s\n", name, data->dso_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -735,11 +726,13 @@ static struct dso_data *load_dso(struct message_data *data)
|
||||
void *dl;
|
||||
struct dso_data *ret = NULL;
|
||||
|
||||
log_very_verbose("Opening shared library %s", data->dso_name);
|
||||
|
||||
if (!(dl = dlopen(data->dso_name, RTLD_NOW))){
|
||||
log_error("dmeventd %s dlopen failed: %s", data->dso_name,
|
||||
dlerror());
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -895,7 +888,7 @@ static int unregister_for_event(struct message_data *message_data)
|
||||
*/
|
||||
if (!thread->events) {
|
||||
UNLINK_THREAD(thread);
|
||||
LINK(thread, &thread_registry_unused);
|
||||
LINK(thread, &_thread_registry_unused);
|
||||
}
|
||||
unlock_mutex();
|
||||
|
||||
@ -911,11 +904,21 @@ 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;
|
||||
|
||||
snprintf(msg->msg, sizeof(msg->msg), "%s %s %u",
|
||||
thread->dso_data->dso_name, thread->device_path,
|
||||
thread->events);
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
unlock_mutex();
|
||||
|
||||
@ -949,7 +952,7 @@ static int _get_registered_device(struct message_data *message_data, int next)
|
||||
lock_mutex();
|
||||
|
||||
/* 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,
|
||||
message_data->device_path,
|
||||
thread)))
|
||||
@ -963,7 +966,7 @@ static int _get_registered_device(struct message_data *message_data, int next)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
if (list_end(&thread_registry, &thread->list))
|
||||
if (list_end(&_thread_registry, &thread->list))
|
||||
goto out;
|
||||
|
||||
thread = list_item(thread->list.n,
|
||||
@ -1006,10 +1009,18 @@ static int get_timeout(struct message_data *message_data)
|
||||
struct thread_status *thread;
|
||||
struct dm_event_daemon_message *msg = message_data->msg;
|
||||
|
||||
if (msg->data)
|
||||
dm_free(msg->data);
|
||||
|
||||
lock_mutex();
|
||||
if ((thread = lookup_thread_status(message_data)))
|
||||
snprintf(msg->msg, sizeof(msg->msg),
|
||||
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);
|
||||
} else {
|
||||
msg->data = NULL;
|
||||
msg->size = 0;
|
||||
}
|
||||
unlock_mutex();
|
||||
|
||||
return thread ? 0 : -ENODEV;
|
||||
@ -1017,32 +1028,50 @@ static int get_timeout(struct message_data *message_data)
|
||||
|
||||
|
||||
/* Initialize a fifos structure with path names. */
|
||||
static int init_fifos(struct dm_event_fifos *fifos)
|
||||
static void init_fifos(struct dm_event_fifos *fifos)
|
||||
{
|
||||
if (memset(fifos, 0, sizeof(*fifos))) {
|
||||
memset(fifos, 0, sizeof(*fifos));
|
||||
|
||||
fifos->client_path = DM_EVENT_FIFO_CLIENT;
|
||||
fifos->server_path = DM_EVENT_FIFO_SERVER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Open fifos used for client communication. */
|
||||
static int open_fifos(struct dm_event_fifos *fifos)
|
||||
{
|
||||
/* Blocks until client is ready to write. */
|
||||
if ((fifos->server = open(fifos->server_path, O_WRONLY)) < 0) {
|
||||
/* Create fifos */
|
||||
if (((mkfifo(fifos->client_path, 0600) == -1) && errno != EEXIST) ||
|
||||
((mkfifo(fifos->server_path, 0600) == -1) && errno != EEXIST)) {
|
||||
syslog(LOG_ERR, "%s: Failed to create a fifo.\n", __func__);
|
||||
stack;
|
||||
return -EXIT_FIFO_FAILURE;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* FIXME Warn/abort if perms are wrong - not something to fix silently. */
|
||||
/* 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",
|
||||
fifos->client_path);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (chmod(fifos->server_path, 0600)) {
|
||||
syslog(LOG_ERR, "Unable to set correct file permissions on %s",
|
||||
fifos->server_path);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Need to open read+write or we will block or fail */
|
||||
if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
|
||||
stack;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Need to open read+write for select() to work. */
|
||||
if ((fifos->client = open(fifos->client_path, O_RDWR)) < 0) {
|
||||
stack;
|
||||
close(fifos->server);
|
||||
return -EXIT_FIFO_FAILURE;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1058,9 +1087,14 @@ static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
unsigned bytes = 0;
|
||||
int ret = 0;
|
||||
fd_set fds;
|
||||
int header = 1;
|
||||
size_t size = 2 * sizeof(uint32_t); /* status + size */
|
||||
char *buf = alloca(size);
|
||||
|
||||
msg->data = NULL;
|
||||
|
||||
errno = 0;
|
||||
while (bytes < sizeof(*msg) && errno != EOF) {
|
||||
while (bytes < size && errno != EOF) {
|
||||
/* Watch client read FIFO for input. */
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fifos->client, &fds);
|
||||
@ -1077,11 +1111,25 @@ static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
if (ret < 0) /* error */
|
||||
return 0;
|
||||
|
||||
ret = read(fifos->client, msg, sizeof(*msg) - bytes);
|
||||
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));
|
||||
buf = msg->data = dm_malloc(msg->size);
|
||||
size = msg->size;
|
||||
bytes = 0;
|
||||
header = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes == sizeof(*msg);
|
||||
if (bytes != size) {
|
||||
if (msg->data)
|
||||
dm_free(msg->data);
|
||||
msg->data = NULL;
|
||||
}
|
||||
|
||||
return bytes == size;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1093,19 +1141,26 @@ static int client_write(struct dm_event_fifos *fifos, struct dm_event_daemon_mes
|
||||
int ret = 0;
|
||||
fd_set fds;
|
||||
|
||||
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);
|
||||
|
||||
errno = 0;
|
||||
while (bytes < sizeof(*msg) && errno != EIO) {
|
||||
while (bytes < size && errno != EIO) {
|
||||
do {
|
||||
/* 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);
|
||||
|
||||
ret = write(fifos->server, msg, sizeof(*msg) - bytes);
|
||||
ret = write(fifos->server, buf + bytes, size - bytes);
|
||||
bytes += ret > 0 ? ret : 0;
|
||||
}
|
||||
|
||||
return bytes == sizeof(*msg);
|
||||
return bytes == size;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1131,7 +1186,7 @@ static int handle_request(struct dm_event_daemon_message *msg,
|
||||
}, *req;
|
||||
|
||||
for (req = requests; req < requests + sizeof(requests); req++)
|
||||
if (req->cmd == msg->opcode.cmd)
|
||||
if (req->cmd == msg->cmd)
|
||||
return req->f(message_data);
|
||||
|
||||
return -EINVAL;
|
||||
@ -1146,7 +1201,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->opcode.cmd != DM_EVENT_CMD_ACTIVE &&
|
||||
if (msg->cmd != DM_EVENT_CMD_ACTIVE &&
|
||||
!parse_message(&message_data)) {
|
||||
stack;
|
||||
ret = -EINVAL;
|
||||
@ -1176,10 +1231,17 @@ static void process_request(struct dm_event_fifos *fifos)
|
||||
if (!client_read(fifos, &msg))
|
||||
return;
|
||||
|
||||
msg.opcode.status = do_process_request(&msg);
|
||||
msg.cmd = do_process_request(&msg);
|
||||
if (!msg.data) {
|
||||
msg.data = dm_strdup(strerror(-msg.cmd));
|
||||
msg.size = strlen(msg.data) + 1;
|
||||
}
|
||||
|
||||
if (!client_write(fifos, &msg))
|
||||
stack;
|
||||
|
||||
if (msg.data)
|
||||
dm_free(msg.data);
|
||||
}
|
||||
|
||||
static void cleanup_unused_threads(void)
|
||||
@ -1189,27 +1251,40 @@ static void cleanup_unused_threads(void)
|
||||
struct thread_status *thread;
|
||||
|
||||
lock_mutex();
|
||||
while ((l = list_first(&thread_registry_unused))) {
|
||||
while ((l = list_first(&_thread_registry_unused))) {
|
||||
thread = list_item(l, struct thread_status);
|
||||
if (thread->processing) {
|
||||
goto out; /* cleanup on the next round */
|
||||
}
|
||||
list_del(l);
|
||||
|
||||
if (thread->status == DM_THREAD_RUNNING) {
|
||||
thread->status = DM_THREAD_SHUTDOWN;
|
||||
goto out;
|
||||
} else if (thread->status == DM_THREAD_SHUTDOWN) {
|
||||
if (!thread->events) {
|
||||
/* turn codes negative -- should we be returning this? */
|
||||
if ((ret = -terminate_thread(thread)))
|
||||
ret = terminate_thread(thread);
|
||||
|
||||
if (ret == ESRCH) {
|
||||
thread->status = DM_THREAD_DONE;
|
||||
} else if (ret) {
|
||||
syslog(LOG_ERR, "Unable to terminate thread: %s\n",
|
||||
strerror(-ret));
|
||||
stack;
|
||||
else {
|
||||
}
|
||||
goto out;
|
||||
} else {
|
||||
list_del(l);
|
||||
syslog(LOG_ERR, "thread can't be on unused list unless !thread->events");
|
||||
thread->status = DM_THREAD_RUNNING;
|
||||
LINK_THREAD(thread);
|
||||
}
|
||||
} else if (thread->status == DM_THREAD_DONE) {
|
||||
list_del(l);
|
||||
pthread_join(thread->thread, NULL);
|
||||
lib_put(thread->dso_data);
|
||||
free_thread_status(thread);
|
||||
}
|
||||
} else {
|
||||
log_error("thread can't be on unused list unless !thread->events");
|
||||
LINK_THREAD(thread);
|
||||
}
|
||||
|
||||
}
|
||||
out:
|
||||
unlock_mutex();
|
||||
@ -1230,23 +1305,38 @@ static void init_thread_signals(void)
|
||||
act.sa_handler = sig_alarm;
|
||||
sigaction(SIGALRM, &act, NULL);
|
||||
sigfillset(&my_sigset);
|
||||
|
||||
/* These are used for exiting */
|
||||
sigdelset(&my_sigset, SIGTERM);
|
||||
sigdelset(&my_sigset, SIGINT);
|
||||
sigdelset(&my_sigset, SIGHUP);
|
||||
sigdelset(&my_sigset, SIGQUIT);
|
||||
|
||||
pthread_sigmask(SIG_BLOCK, &my_sigset, NULL);
|
||||
}
|
||||
|
||||
static int daemonize(void)
|
||||
{
|
||||
setsid();
|
||||
if (chdir("/"))
|
||||
return -EXIT_CHDIR_FAILURE;
|
||||
|
||||
/* FIXME: activate again after we're done with tracing.
|
||||
if ((close(STDIN_FILENO) < 0) ||
|
||||
(close(STDOUT_FILENO) < 0) ||
|
||||
(close(STDERR_FILENO) < 0))
|
||||
return -EXIT_DESC_CLOSE_FAILURE;
|
||||
/*
|
||||
* exit_handler
|
||||
* @sig
|
||||
*
|
||||
* Set the global variable which the process should
|
||||
* be watching to determine when to exit.
|
||||
*/
|
||||
static void exit_handler(int sig)
|
||||
{
|
||||
/*
|
||||
* We exit when '_exit_now' is set.
|
||||
* That is, when a signal has been received.
|
||||
*
|
||||
* We can not simply set '_exit_now' unless all
|
||||
* threads are done processing.
|
||||
*/
|
||||
if (!_thread_registries_empty) {
|
||||
syslog(LOG_ERR, "There are still devices being monitored.");
|
||||
syslog(LOG_ERR, "Refusing to exit.");
|
||||
} else
|
||||
_exit_now = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lock_pidfile(void)
|
||||
@ -1255,31 +1345,104 @@ static int lock_pidfile(void)
|
||||
char pidfile[] = "/var/run/dmeventd.pid"; /* FIXME Must be configurable at compile-time! */
|
||||
|
||||
if ((lf = open(pidfile, O_CREAT | O_RDWR, 0644)) < 0)
|
||||
return -EXIT_OPEN_PID_FAILURE;
|
||||
exit(EXIT_OPEN_PID_FAILURE);
|
||||
|
||||
if (flock(lf, LOCK_EX | LOCK_NB) < 0)
|
||||
return -EXIT_LOCKFILE_INUSE;
|
||||
exit(EXIT_LOCKFILE_INUSE);
|
||||
|
||||
if (!storepid(lf))
|
||||
return -EXIT_FAILURE;
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dmeventd(void)
|
||||
static void daemonize(void)
|
||||
{
|
||||
int status;
|
||||
int pid;
|
||||
int fd;
|
||||
struct rlimit rlim;
|
||||
struct timeval tval;
|
||||
sigset_t my_sigset;
|
||||
|
||||
sigemptyset(&my_sigset);
|
||||
if (sigprocmask(SIG_SETMASK, &my_sigset, NULL) < 0) {
|
||||
fprintf(stderr, "Unable to restore signals.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
signal(SIGTERM, &exit_handler);
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (pid) {
|
||||
/* Wait for response from child */
|
||||
while (!waitpid(pid, &status, WNOHANG) && !_exit_now) {
|
||||
tval.tv_sec = 0;
|
||||
tval.tv_usec = 250000; /* .25 sec */
|
||||
select(0, NULL, NULL, NULL, &tval);
|
||||
}
|
||||
|
||||
if (_exit_now) /* Child has signaled it is ok - we can exit now */
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
/* Problem with child. Determine what it is by exit code */
|
||||
switch (WEXITSTATUS(status)) {
|
||||
case EXIT_LOCKFILE_INUSE:
|
||||
break;
|
||||
case EXIT_DESC_CLOSE_FAILURE:
|
||||
break;
|
||||
case EXIT_DESC_OPEN_FAILURE:
|
||||
break;
|
||||
case EXIT_OPEN_PID_FAILURE:
|
||||
break;
|
||||
case EXIT_FIFO_FAILURE:
|
||||
break;
|
||||
case EXIT_CHDIR_FAILURE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
exit(EXIT_FAILURE); /* Redundant */
|
||||
}
|
||||
|
||||
setsid();
|
||||
if (chdir("/"))
|
||||
exit(EXIT_CHDIR_FAILURE);
|
||||
|
||||
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
|
||||
fd = 256; /* just have to guess */
|
||||
else
|
||||
fd = rlim.rlim_cur;
|
||||
|
||||
for (--fd; fd >= 0; fd--)
|
||||
close(fd);
|
||||
|
||||
if ((open("/dev/null", O_RDONLY) < 0) ||
|
||||
(open("/dev/null", O_WRONLY) < 0) ||
|
||||
(open("/dev/null", O_WRONLY) < 0))
|
||||
exit(EXIT_DESC_OPEN_FAILURE);
|
||||
|
||||
openlog("dmeventd", LOG_PID, LOG_DAEMON);
|
||||
|
||||
lock_pidfile(); /* exits if failure */
|
||||
|
||||
/* Set the rest of the signals to cause '_exit_now' to be set */
|
||||
signal(SIGINT, &exit_handler);
|
||||
signal(SIGHUP, &exit_handler);
|
||||
signal(SIGQUIT, &exit_handler);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
struct dm_event_fifos fifos;
|
||||
//struct sys_log logdata = {DAEMON_NAME, LOG_DAEMON};
|
||||
|
||||
if ((ret = daemonize()))
|
||||
exit(-ret);
|
||||
|
||||
/* FIXME: set daemon name. */
|
||||
// set_name();
|
||||
|
||||
if ((ret = lock_pidfile()))
|
||||
exit(-ret);
|
||||
daemonize();
|
||||
|
||||
init_thread_signals();
|
||||
|
||||
@ -1288,8 +1451,7 @@ void dmeventd(void)
|
||||
//multilog_init_verbose(std_syslog, _LOG_DEBUG);
|
||||
//multilog_async(1);
|
||||
|
||||
if ((ret = init_fifos(&fifos)))
|
||||
exit(-ret);
|
||||
init_fifos(&fifos);
|
||||
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
|
||||
@ -1299,40 +1461,29 @@ void dmeventd(void)
|
||||
#endif
|
||||
|
||||
if ((ret = open_fifos(&fifos)))
|
||||
exit(-ret);
|
||||
exit(EXIT_FIFO_FAILURE);
|
||||
|
||||
/* Signal parent, letting them know we are ready to go. */
|
||||
kill(getppid(), SIGUSR1);
|
||||
kill(getppid(), SIGTERM);
|
||||
syslog(LOG_INFO, "dmeventd ready for processing.");
|
||||
|
||||
/*
|
||||
* We exit when there are no more devices to watch.
|
||||
* That is, when the last unregister happens.
|
||||
*
|
||||
* We must be careful though. One of our threads which is
|
||||
* watching a device may receive an event and:
|
||||
* 1) Alter the device and unregister it
|
||||
* or
|
||||
* 2) Alter the device, unregister, [alter again,] and reregister
|
||||
*
|
||||
* We must be capable of answering a request to unregister
|
||||
* that comes from the very thread that must be unregistered.
|
||||
* Additionally, if that thread unregisters itself and it was the
|
||||
* only thread being monitored, we must also handle the case where
|
||||
* that thread may perform a register before exiting. (In other
|
||||
* words, we can not simply exit if all threads have been unregistered
|
||||
* unless all threads are done processing.
|
||||
*/
|
||||
do {
|
||||
while (!_exit_now) {
|
||||
process_request(&fifos);
|
||||
cleanup_unused_threads();
|
||||
} while(!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;
|
||||
}
|
||||
|
||||
exit_dm_lib();
|
||||
|
||||
#ifdef MCL_CURRENT
|
||||
munlockall();
|
||||
#endif
|
||||
pthread_mutex_destroy(&mutex);
|
||||
pthread_mutex_destroy(&_mutex);
|
||||
|
||||
syslog(LOG_INFO, "dmeventd shutting down.");
|
||||
closelog();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
@ -1,13 +1,51 @@
|
||||
#ifndef __DMEVENTD_DOT_H__
|
||||
#define __DMEVENTD_DOT_H__
|
||||
|
||||
/* FIXME This stuff must be configurable. */
|
||||
|
||||
#define DM_EVENT_DAEMON "/sbin/dmeventd"
|
||||
#define DM_EVENT_LOCKFILE "/var/lock/dmeventd"
|
||||
#define DM_EVENT_FIFO_CLIENT "/var/run/dmeventd-client"
|
||||
#define DM_EVENT_FIFO_SERVER "/var/run/dmeventd-server"
|
||||
#define DM_EVENT_PIDFILE "/var/run/dmeventd.pid"
|
||||
|
||||
#define DM_EVENT_DEFAULT_TIMEOUT 10
|
||||
|
||||
/* Commands for the daemon passed in the message below. */
|
||||
enum dm_event_command {
|
||||
DM_EVENT_CMD_ACTIVE = 1,
|
||||
DM_EVENT_CMD_REGISTER_FOR_EVENT,
|
||||
DM_EVENT_CMD_UNREGISTER_FOR_EVENT,
|
||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE,
|
||||
DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
|
||||
DM_EVENT_CMD_SET_TIMEOUT,
|
||||
DM_EVENT_CMD_GET_TIMEOUT,
|
||||
};
|
||||
|
||||
/* Message passed between client and daemon. */
|
||||
struct dm_event_daemon_message {
|
||||
uint32_t cmd;
|
||||
uint32_t size;
|
||||
char *data;
|
||||
};
|
||||
|
||||
/* FIXME Is this meant to be exported? I can't see where the
|
||||
interface uses it. */
|
||||
/* Fifos for client/daemon communication. */
|
||||
struct dm_event_fifos {
|
||||
int client;
|
||||
int server;
|
||||
const char *client_path;
|
||||
const char *server_path;
|
||||
};
|
||||
|
||||
/* EXIT_SUCCESS 0 -- stdlib.h */
|
||||
/* EXIT_FAILURE 1 -- stdlib.h */
|
||||
#define EXIT_LOCKFILE_INUSE 2
|
||||
#define EXIT_DESC_CLOSE_FAILURE 3
|
||||
#define EXIT_OPEN_PID_FAILURE 4
|
||||
#define EXIT_FIFO_FAILURE 5
|
||||
#define EXIT_CHDIR_FAILURE 6
|
||||
|
||||
void dmeventd(void)
|
||||
__attribute((noreturn));
|
||||
#define EXIT_DESC_OPEN_FAILURE 4
|
||||
#define EXIT_OPEN_PID_FAILURE 5
|
||||
#define EXIT_FIFO_FAILURE 6
|
||||
#define EXIT_CHDIR_FAILURE 7
|
||||
|
||||
#endif /* __DMEVENTD_DOT_H__ */
|
||||
|
@ -27,8 +27,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#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 */
|
||||
@ -57,7 +57,7 @@ static char *fetch_string(char **src)
|
||||
static int parse_message(struct dm_event_daemon_message *msg, char **dso_name,
|
||||
char **device, enum dm_event_type *events)
|
||||
{
|
||||
char *p = msg->msg;
|
||||
char *p = msg->data;
|
||||
|
||||
if ((*dso_name = fetch_string(&p)) &&
|
||||
(*device = fetch_string(&p))) {
|
||||
@ -81,36 +81,58 @@ static int parse_message(struct dm_event_daemon_message *msg, char **dso_name,
|
||||
static int daemon_read(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg)
|
||||
{
|
||||
unsigned bytes = 0;
|
||||
int ret = 0;
|
||||
int ret, i;
|
||||
fd_set fds;
|
||||
struct timeval tval = {0, 0};
|
||||
size_t size = 2 * sizeof(uint32_t); // status + size
|
||||
char *buf = alloca(size);
|
||||
int header = 1;
|
||||
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
while (bytes < sizeof(*msg)) {
|
||||
do {
|
||||
while (bytes < size) {
|
||||
for (i = 0, ret = 0; (i < 20) && (ret < 1); i++) {
|
||||
/* Watch daemon read FIFO for input. */
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fifos->server, &fds);
|
||||
ret = select(fifos->server+1, &fds, NULL, NULL, NULL);
|
||||
tval.tv_sec = 1;
|
||||
ret = select(fifos->server+1, &fds, NULL, NULL, &tval);
|
||||
if (ret < 0 && errno != EINTR) {
|
||||
/* FIXME Log error */
|
||||
log_error("Unable to read from event server");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (ret < 1) {
|
||||
log_error("Unable to read from event server.");
|
||||
return 0;
|
||||
}
|
||||
} while (ret < 1);
|
||||
|
||||
ret = read(fifos->server, msg, sizeof(*msg) - bytes);
|
||||
ret = read(fifos->server, buf + bytes, size);
|
||||
if (ret < 0) {
|
||||
if ((errno == EINTR) || (errno == EAGAIN))
|
||||
continue;
|
||||
else {
|
||||
/* FIXME Log error */
|
||||
log_error("Unable to read from event server.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bytes += ret;
|
||||
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;
|
||||
header = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes == sizeof(*msg);
|
||||
if (bytes != size) {
|
||||
if (msg->data)
|
||||
dm_free(msg->data);
|
||||
msg->data = NULL;
|
||||
}
|
||||
|
||||
return bytes == size;
|
||||
}
|
||||
|
||||
/* Write message to daemon. */
|
||||
@ -120,24 +142,31 @@ static int daemon_write(struct dm_event_fifos *fifos, struct dm_event_daemon_mes
|
||||
int ret = 0;
|
||||
fd_set fds;
|
||||
|
||||
while (bytes < sizeof(*msg)) {
|
||||
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);
|
||||
|
||||
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);
|
||||
if ((ret < 0) && (errno != EINTR)) {
|
||||
/* FIXME Log error */
|
||||
log_error("Unable to talk to event daemon");
|
||||
return 0;
|
||||
}
|
||||
} while (ret < 1);
|
||||
|
||||
ret = write(fifos->client, msg, sizeof(*msg) - bytes);
|
||||
ret = write(fifos->client, ((char *) buf) + bytes, size - bytes);
|
||||
if (ret < 0) {
|
||||
if ((errno == EINTR) || (errno == EAGAIN))
|
||||
continue;
|
||||
else {
|
||||
/* fixme: log error */
|
||||
log_error("Unable to talk to event daemon");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -145,29 +174,28 @@ static int daemon_write(struct dm_event_fifos *fifos, struct dm_event_daemon_mes
|
||||
bytes += ret;
|
||||
}
|
||||
|
||||
return bytes == sizeof(*msg);
|
||||
return bytes == size;
|
||||
}
|
||||
|
||||
static int daemon_talk(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg,
|
||||
int cmd, char *dso_name, char *device,
|
||||
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;
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
/*
|
||||
* Set command and pack the arguments
|
||||
* into ASCII message string.
|
||||
*/
|
||||
msg->opcode.cmd = cmd;
|
||||
|
||||
if (sizeof(msg->msg) <= (unsigned) snprintf(msg->msg, sizeof(msg->msg),
|
||||
"%s %s %u %"PRIu32,
|
||||
dso_name ? dso_name : "",
|
||||
device ? device : "",
|
||||
events, timeout)) {
|
||||
stack;
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
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);
|
||||
|
||||
/*
|
||||
* Write command and message to and
|
||||
@ -183,98 +211,70 @@ static int daemon_talk(struct dm_event_fifos *fifos, struct dm_event_daemon_mess
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return msg->opcode.status;
|
||||
}
|
||||
|
||||
static volatile sig_atomic_t daemon_running = 0;
|
||||
|
||||
static void daemon_running_signal_handler(int sig)
|
||||
{
|
||||
daemon_running = 1;
|
||||
return (int32_t) msg->cmd;
|
||||
}
|
||||
|
||||
/*
|
||||
* start_daemon
|
||||
*
|
||||
* This function forks off a process (dmeventd) that will handle
|
||||
* the events. A signal must be returned from the child to
|
||||
* indicate when it is ready to handle requests. The parent
|
||||
* (this function) returns 1 if there is a daemon running.
|
||||
* the events. I am currently test opening one of the fifos to
|
||||
* ensure that the daemon is running and listening... I thought
|
||||
* this would be less expensive than fork/exec'ing every time.
|
||||
* Perhaps there is an even quicker/better way (no, checking the
|
||||
* lock file is _not_ a better way).
|
||||
*
|
||||
* Returns: 1 on success, 0 otherwise
|
||||
*/
|
||||
static int start_daemon(void)
|
||||
static int start_daemon(struct dm_event_fifos *fifos)
|
||||
{
|
||||
int pid, ret = 0;
|
||||
void *old_hand;
|
||||
sigset_t set, oset;
|
||||
int status;
|
||||
struct stat statbuf;
|
||||
|
||||
/* Must be able to acquire signal */
|
||||
old_hand = signal(SIGUSR1, &daemon_running_signal_handler);
|
||||
if (old_hand == SIG_ERR) {
|
||||
log_error("Unable to setup signal handler.");
|
||||
if (stat(fifos->client_path, &statbuf))
|
||||
goto start_server;
|
||||
|
||||
if (!S_ISFIFO(statbuf.st_mode)) {
|
||||
log_error("%s is not a fifo.", fifos->client_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sigemptyset(&set) || sigaddset(&set, SIGUSR1)) {
|
||||
log_error("Unable to fill signal set.");
|
||||
} else if (sigprocmask(SIG_UNBLOCK, &set, &oset)) {
|
||||
log_error("Can't unblock the potentially blocked signal SIGUSR1");
|
||||
/* Anyone listening? If not, errno will be ENXIO */
|
||||
fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK);
|
||||
if (fifos->client >= 0) {
|
||||
/* server is running and listening */
|
||||
|
||||
close(fifos->client);
|
||||
return 1;
|
||||
} else if (errno != ENXIO) {
|
||||
/* problem */
|
||||
|
||||
log_error("%s: Can't open client fifo %s: %s",
|
||||
__func__, fifos->client_path, strerror(errno));
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
start_server:
|
||||
/* server is not running */
|
||||
pid = fork();
|
||||
|
||||
if (pid < 0)
|
||||
log_error("Unable to fork.\n");
|
||||
else if (pid) { /* parent waits for child to get ready for requests */
|
||||
int status;
|
||||
log_error("Unable to fork.");
|
||||
|
||||
/* FIXME Better way to do this? */
|
||||
while (!waitpid(pid, &status, WNOHANG) && !daemon_running)
|
||||
sleep(1);
|
||||
|
||||
if (daemon_running) {
|
||||
ret = 1;
|
||||
} else {
|
||||
switch (WEXITSTATUS(status)) {
|
||||
case EXIT_LOCKFILE_INUSE:
|
||||
/*
|
||||
* Note, this is ok... we still have daemon
|
||||
* that we can communicate with...
|
||||
*/
|
||||
log_print("Starting dmeventd failed: "
|
||||
"dmeventd already running.\n");
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
log_error("Unable to start dmeventd.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Sometimes, a single process may perform multiple calls
|
||||
* that result in a daemon starting and exiting. If we
|
||||
* don't reset this, the second (or greater) time the daemon
|
||||
* is started will cause this logic not to work.
|
||||
*/
|
||||
daemon_running = 0;
|
||||
} else {
|
||||
signal(SIGUSR1, SIG_IGN); /* don't care about error */
|
||||
|
||||
/* dmeventd function is responsible for properly setting **
|
||||
** itself up. It must never return - only exit. This is**
|
||||
** why it is followed by an EXIT_FAILURE */
|
||||
dmeventd();
|
||||
else if (!pid) {
|
||||
execvp("dmeventd", NULL); /* security risk if admin has bad PATH */
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
if (waitpid(pid, &status, 0) < 0)
|
||||
log_error("Unable to start dmeventd: %s", strerror(errno));
|
||||
else if (WEXITSTATUS(status))
|
||||
log_error("Unable to start dmeventd.");
|
||||
else
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
/* FIXME What if old_hand is SIG_ERR? */
|
||||
if (signal(SIGUSR1, old_hand) == SIG_ERR)
|
||||
log_error("Unable to reset signal handler.");
|
||||
|
||||
if (sigprocmask(SIG_SETMASK, &oset, NULL))
|
||||
log_error("Unable to reset signal mask.");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -289,79 +289,50 @@ static int init_client(struct dm_event_fifos *fifos)
|
||||
fifos->client_path = DM_EVENT_FIFO_CLIENT;
|
||||
fifos->server_path = DM_EVENT_FIFO_SERVER;
|
||||
|
||||
/* FIXME The server should be responsible for these, not the client. */
|
||||
/* Create fifos */
|
||||
if (((mkfifo(fifos->client_path, 0600) == -1) && errno != EEXIST) ||
|
||||
((mkfifo(fifos->server_path, 0600) == -1) && errno != EEXIST)) {
|
||||
log_error("%s: Failed to create a fifo.\n", __func__);
|
||||
if (!start_daemon(fifos)) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME Warn/abort if perms are wrong - not something to fix silently. */
|
||||
/* If they were already there, make sure permissions are ok. */
|
||||
if (chmod(fifos->client_path, 0600)) {
|
||||
log_error("Unable to set correct file permissions on %s",
|
||||
fifos->client_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (chmod(fifos->server_path, 0600)) {
|
||||
log_error("Unable to set correct file permissions on %s",
|
||||
fifos->server_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the fifo used to read from the daemon.
|
||||
* Allows daemon to create its write fifo...
|
||||
*/
|
||||
/* Open the fifo used to read from the daemon. */
|
||||
if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
|
||||
log_error("%s: open server fifo %s\n",
|
||||
log_error("%s: open server fifo %s",
|
||||
__func__, fifos->server_path);
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lock out anyone else trying to do communication with the daemon. */
|
||||
/* FIXME Why failure not retry? How do multiple processes communicate? */
|
||||
if (flock(fifos->server, LOCK_EX) < 0){
|
||||
log_error("%s: flock %s\n", __func__, fifos->server_path);
|
||||
log_error("%s: flock %s", __func__, fifos->server_path);
|
||||
close(fifos->server);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Anyone listening? If not, errno will be ENXIO */
|
||||
while ((fifos->client = open(fifos->client_path,
|
||||
O_WRONLY | O_NONBLOCK)) < 0) {
|
||||
if (errno != ENXIO) {
|
||||
log_error("%s: Can't open client fifo %s: %s\n",
|
||||
/* 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));
|
||||
close(fifos->server);
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME Unnecessary if daemon was started before calling this */
|
||||
if (!start_daemon()) {
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void dtr_client(struct dm_event_fifos *fifos)
|
||||
{
|
||||
if (flock(fifos->server, LOCK_UN))
|
||||
log_error("flock unlock %s\n", fifos->server_path);
|
||||
log_error("flock unlock %s", fifos->server_path);
|
||||
|
||||
close(fifos->client);
|
||||
close(fifos->server);
|
||||
}
|
||||
|
||||
/* Check, if a block device exists. */
|
||||
static int device_exists(char *device)
|
||||
static int device_exists(const char *device)
|
||||
{
|
||||
struct stat st_buf;
|
||||
char path2[PATH_MAX];
|
||||
@ -380,7 +351,7 @@ static int device_exists(char *device)
|
||||
|
||||
/* Handle the event (de)registration call and return negative error codes. */
|
||||
static int do_event(int cmd, struct dm_event_daemon_message *msg,
|
||||
char *dso_name, char *device, enum dm_event_type events,
|
||||
const char *dso_name, const char *device, enum dm_event_type events,
|
||||
uint32_t timeout)
|
||||
{
|
||||
int ret;
|
||||
@ -405,10 +376,10 @@ static int do_event(int cmd, struct dm_event_daemon_message *msg,
|
||||
/* FIXME remove dso_name - use handle instead */
|
||||
/* FIXME Use uuid not path! */
|
||||
/* External library interface. */
|
||||
int dm_event_register(char *dso_name, char *device_path,
|
||||
int dm_event_register(const char *dso_name, const char *device_path,
|
||||
enum dm_event_type events)
|
||||
{
|
||||
int ret;
|
||||
int ret, err;
|
||||
struct dm_event_daemon_message msg;
|
||||
|
||||
if (!device_exists(device_path)) {
|
||||
@ -416,20 +387,24 @@ int dm_event_register(char *dso_name, char *device_path,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((ret = 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) {
|
||||
log_error("%s: event registration failed: %s", device_path,
|
||||
strerror(-ret));
|
||||
return 0;
|
||||
msg.data ? msg.data : strerror(-err));
|
||||
ret = 0;
|
||||
} else
|
||||
ret = 1;
|
||||
|
||||
if (msg.data)
|
||||
dm_free(msg.data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dm_event_unregister(char *dso_name, char *device_path,
|
||||
int dm_event_unregister(const char *dso_name, const char *device_path,
|
||||
enum dm_event_type events)
|
||||
{
|
||||
int ret;
|
||||
int ret, err;
|
||||
struct dm_event_daemon_message msg;
|
||||
|
||||
if (!device_exists(device_path)) {
|
||||
@ -437,16 +412,30 @@ int dm_event_unregister(char *dso_name, char *device_path,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((ret = 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) {
|
||||
log_error("%s: event deregistration failed: %s", device_path,
|
||||
strerror(-ret));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
msg.data ? msg.data : strerror(-err));
|
||||
ret = 0;
|
||||
} else
|
||||
ret = 1;
|
||||
|
||||
if (msg.data)
|
||||
dm_free(msg.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* dm_event_get_registered_device
|
||||
* @dso_name
|
||||
* @device_path
|
||||
* @events
|
||||
* @next
|
||||
*
|
||||
* FIXME: This function sucks.
|
||||
*
|
||||
* Returns: 1 if device found, 0 otherwise (even on error)
|
||||
*/
|
||||
int dm_event_get_registered_device(char **dso_name, char **device_path,
|
||||
enum dm_event_type *events, int next)
|
||||
{
|
||||
@ -456,9 +445,14 @@ int dm_event_get_registered_device(char **dso_name, char **device_path,
|
||||
|
||||
if (!(ret = do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
|
||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE,
|
||||
&msg, *dso_name, *device_path, *events, 0)))
|
||||
ret = parse_message(&msg, &dso_name_arg, &device_path_arg,
|
||||
&msg, *dso_name, *device_path, *events, 0))) {
|
||||
ret = !parse_message(&msg, &dso_name_arg, &device_path_arg,
|
||||
events);
|
||||
} else /* FIXME: Make sure this is ENOENT */
|
||||
ret = 0;
|
||||
|
||||
if (msg.data)
|
||||
dm_free(msg.data);
|
||||
|
||||
if (next) {
|
||||
if (*dso_name)
|
||||
@ -477,7 +471,7 @@ int dm_event_get_registered_device(char **dso_name, char **device_path,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dm_event_set_timeout(char *device_path, uint32_t timeout)
|
||||
int dm_event_set_timeout(const char *device_path, uint32_t timeout)
|
||||
{
|
||||
struct dm_event_daemon_message msg;
|
||||
|
||||
@ -487,7 +481,7 @@ int dm_event_set_timeout(char *device_path, uint32_t timeout)
|
||||
NULL, device_path, 0, timeout);
|
||||
}
|
||||
|
||||
int dm_event_get_timeout(char *device_path, uint32_t *timeout)
|
||||
int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
|
||||
{
|
||||
int ret;
|
||||
struct dm_event_daemon_message msg;
|
||||
@ -495,16 +489,8 @@ int dm_event_get_timeout(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)))
|
||||
*timeout = atoi(msg.msg);
|
||||
*timeout = atoi(msg.data);
|
||||
if (msg.data)
|
||||
dm_free(msg.data);
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* Overrides for Emacs so that we follow Linus's tabbing style.
|
||||
* Emacs will notice this stuff at the end of the file and automatically
|
||||
* adjust the settings for this buffer only. This must remain at the end
|
||||
* of the file.
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
|
@ -23,45 +23,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* FIXME This stuff must be configurable. */
|
||||
|
||||
#define DM_EVENT_DAEMON "/sbin/dmeventd"
|
||||
#define DM_EVENT_LOCKFILE "/var/lock/dmeventd"
|
||||
#define DM_EVENT_FIFO_CLIENT "/var/run/dmeventd-client"
|
||||
#define DM_EVENT_FIFO_SERVER "/var/run/dmeventd-server"
|
||||
#define DM_EVENT_PIDFILE "/var/run/dmeventd.pid"
|
||||
|
||||
#define DM_EVENT_DEFAULT_TIMEOUT 10
|
||||
|
||||
/* Commands for the daemon passed in the message below. */
|
||||
enum dm_event_command {
|
||||
DM_EVENT_CMD_ACTIVE = 1,
|
||||
DM_EVENT_CMD_REGISTER_FOR_EVENT,
|
||||
DM_EVENT_CMD_UNREGISTER_FOR_EVENT,
|
||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE,
|
||||
DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
|
||||
DM_EVENT_CMD_SET_TIMEOUT,
|
||||
DM_EVENT_CMD_GET_TIMEOUT,
|
||||
};
|
||||
|
||||
/* Message passed between client and daemon. */
|
||||
struct dm_event_daemon_message {
|
||||
union {
|
||||
unsigned int cmd; /* FIXME Use fixed size. */
|
||||
int status; /* FIXME Use fixed size. */
|
||||
} opcode;
|
||||
char msg[252]; /* FIXME Why is this 252 ? */
|
||||
} __attribute__((packed)); /* FIXME Do this properly! */
|
||||
|
||||
/* FIXME Is this meant to be exported? I can't see where the interface uses it. */
|
||||
/* Fifos for client/daemon communication. */
|
||||
struct dm_event_fifos {
|
||||
int client;
|
||||
int server;
|
||||
const char *client_path;
|
||||
const char *server_path;
|
||||
};
|
||||
|
||||
/* Event type definitions. */
|
||||
/* FIXME Use masks to separate the types and provide for extension. */
|
||||
enum dm_event_type {
|
||||
@ -75,6 +36,7 @@ enum dm_event_type {
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
/* FIXME Use a mask. */
|
||||
@ -86,19 +48,22 @@ enum dm_event_type {
|
||||
/* FIXME Replace device with standard name/uuid/devno choice */
|
||||
/* 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);
|
||||
// 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);
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/* FIXME Missing consts? */
|
||||
int dm_event_register(char *dso_name, char *device, enum dm_event_type events);
|
||||
int dm_event_unregister(char *dso_name, char *device,
|
||||
/* 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. */
|
||||
|
||||
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(char *device, uint32_t timeout);
|
||||
int dm_event_get_timeout(char *device, uint32_t *timeout);
|
||||
int dm_event_set_timeout(const char *device, uint32_t timeout);
|
||||
int dm_event_get_timeout(const char *device, uint32_t *timeout);
|
||||
|
||||
/* Prototypes for DSO interface. */
|
||||
void process_event(const char *device, enum dm_event_type event);
|
||||
|
@ -113,3 +113,4 @@ dm_task_set_geometry
|
||||
dm_split_lvm_name
|
||||
dm_split_words
|
||||
dm_snprintf
|
||||
dm_basename
|
||||
|
@ -1506,6 +1506,8 @@ static int _reload_with_suppression_v4(struct dm_task *dmt)
|
||||
t2 = task->head;
|
||||
|
||||
while (t1 && t2) {
|
||||
while (t2->params[strlen(t2->params) - 1] == ' ')
|
||||
t2->params[strlen(t2->params) - 1] = '\0';
|
||||
if ((t1->start != t2->start) ||
|
||||
(t1->length != t2->length) ||
|
||||
(strcmp(t1->type, t2->type)) ||
|
||||
|
@ -608,4 +608,9 @@ int dm_split_words(char *buffer, unsigned max,
|
||||
*/
|
||||
int dm_snprintf(char *buf, size_t bufsize, const char *format, ...);
|
||||
|
||||
/*
|
||||
* Returns pointer to the last component of the path.
|
||||
*/
|
||||
char *dm_basename(const char *path);
|
||||
|
||||
#endif /* LIB_DEVICE_MAPPER_H */
|
||||
|
@ -121,3 +121,11 @@ int dm_snprintf(char *buf, size_t bufsize, const char *format, ...)
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
char *dm_basename(const char *path)
|
||||
{
|
||||
char *p = strrchr(path, '/');
|
||||
|
||||
return p ? p + 1 : path;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user