diff --git a/daemons/dmeventd/.exported_symbols b/daemons/dmeventd/.exported_symbols index 4854e9828..7847409d2 100644 --- a/daemons/dmeventd/.exported_symbols +++ b/daemons/dmeventd/.exported_symbols @@ -1,3 +1,5 @@ -process_event -register_device -unregister_device \ No newline at end of file +dm_register_for_event +dm_unregister_for_event +dm_get_registered_device +dm_set_event_timeout +dm_get_event_timeout diff --git a/daemons/dmeventd/Makefile.in b/daemons/dmeventd/Makefile.in index 394f61e52..369f5e2b3 100644 --- a/daemons/dmeventd/Makefile.in +++ b/daemons/dmeventd/Makefile.in @@ -1,51 +1,56 @@ # -# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. -# Copyright (C) 2004 Red Hat, Inc. All rights reserved. +# Copyright (C) 2005 Red Hat, Inc. All rights reserved. # # This file is part of the device-mapper userspace tools. # # This copyrighted material is made available to anyone wishing to use, # modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. +# of the GNU Lesser General Public License v.2.1. # -# You should have received a copy of the GNU General Public License +# You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ +interface = @interface@ -TARGETS = dmevent dmeventd -INSTALL_TYPE = install_dynamic +SOURCES = libdevmapper-event.c \ + dmeventd.c -SOURCES = noop.c -CLEAN_TARGETS = dmevent.o dmeventd.o +LIB_STATIC = libdevmapper-event.a ifeq ("@LIB_SUFFIX@","dylib") - LIB_SHARED = libdmeventdnoop.dylib + LIB_SHARED = libdevmapper-event.dylib else - LIB_SHARED = libdmeventdnoop.so + LIB_SHARED = libdevmapper-event.so endif - -LDFLAGS += -ldl -ldevmapper -lmultilog + +CLDFLAGS += -ldl -ldevmapper -lpthread include ../make.tmpl -libdmeventdnoop.so: noop.o +.PHONY: install_dynamic install_static -dmevent: dmevent.o $(interfacedir)/libdevmapper.$(LIB_SUFFIX) $(top_srcdir)/lib/event/libdmevent.$(LIB_SUFFIX) - $(CC) -o $@ dmevent.o $(LDFLAGS) \ - -L$(interfacedir) -L$(DESTDIR)/lib -L$(top_srcdir)/lib/event -L$(top_srcdir)/multilog $(LIBS) +INSTALL_TYPE = install_dynamic -dmeventd: dmeventd.o $(interfacedir)/libdevmapper.$(LIB_SUFFIX) $(top_srcdir)/lib/event/libdmevent.$(LIB_SUFFIX) - $(CC) -o $@ dmeventd.o $(LDFLAGS) \ - -L$(interfacedir) -L$(DESTDIR)/lib -L$(top_srcdir)/lib/event -L$(top_srcdir)/multilog -lpthread -ldmevent $(LIBS) +ifeq ("@STATIC_LINK@", "yes") + INSTALL_TYPE += install_static +endif install: $(INSTALL_TYPE) -.PHONY: install_dynamic +install_dynamic: libdevmapper-event.$(LIB_SUFFIX) + $(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) $< \ + $(libdir)/libdevmapper-event.$(LIB_SUFFIX).$(LIB_VERSION) + $(LN_S) -f libdevmapper-event.$(LIB_SUFFIX).$(LIB_VERSION) \ + $(libdir)/libdevmapper-event.$(LIB_SUFFIX) + $(INSTALL) -D $(OWNER) $(GROUP) -m 444 libdevmapper-event.h \ + $(includedir)/libdevmapper-event.h -install_dynamic: dmeventd - $(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) dmeventd $(sbindir)/dmeventd +install_static: libdevmapper-event.a + $(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) $< \ + $(libdir)/libdevmapper-event.a.$(LIB_VERSION) + $(LN_S) -f libdevmapper-event.a.$(LIB_VERSION) $(libdir)/libdevmapper-event.a diff --git a/daemons/dmeventd/dmevent.c b/daemons/dmeventd/dmevent.c deleted file mode 100644 index 6e9eb4601..000000000 --- a/daemons/dmeventd/dmevent.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2005 Red Hat, Inc. All rights reserved. - * - * This file is part of the device-mapper userspace tools. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU Lesser General Public License v.2.1. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "libdevmapper.h" -#include "libdm-event.h" -#include "libmultilog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static enum event_type events = ALL_ERRORS; /* All until we can distinguish. */ -static char default_dso_name[] = "noop"; /* default DSO is noop */ -static int default_reg = 1; /* default action is register */ -static uint32_t timeout; - -struct event_ops { -int (*dm_register_for_event)(char *dso_name, char *device, - enum event_type event_types); -int (*dm_unregister_for_event)(char *dso_name, char *device, - enum event_type event_types); -int (*dm_get_registered_device)(char **dso_name, char **device, - enum event_type *event_types, int next); -int (*dm_set_event_timeout)(char *device, uint32_t time); -int (*dm_get_event_timeout)(char *device, uint32_t *time); -}; - -/* Display help. */ -static void print_usage(char *name) -{ - char *cmd = strrchr(name, '/'); - - cmd = cmd ? cmd + 1 : name; - printf("Usage::\n" - "%s [options] \n" - "\n" - "Options:\n" - " -d Specify the DSO to use.\n" - " -h Print this usage.\n" - " -l List registered devices.\n" - " -r Register for event (default).\n" - " -t (un)register for timeout event.\n" - " -u Unregister for event.\n" - "\n", cmd); -} - -/* Parse command line arguments. */ -static int parse_argv(int argc, char **argv, char **dso_name_arg, - char **device_arg, int *reg, int *list) -{ - int c; - const char *options = "d:hlrt:u"; - - while ((c = getopt(argc, argv, options)) != -1) { - switch (c) { - case 'd': - *dso_name_arg = optarg; - break; - case 'h': - print_usage(argv[0]); - exit(EXIT_SUCCESS); - case 'l': - *list = 1; - break; - case 'r': - *reg = 1; - break; - case 't': - events = TIMEOUT; - if (sscanf(optarg, "%"SCNu32, &timeout) != 1){ - fprintf(stderr, "invalid timeout '%s'\n", - optarg); - timeout = 0; - } - break; - case 'u': - *reg = 0; - break; - default: - fprintf(stderr, "Unknown option '%c'.\n" - "Try '-h' for help.\n", c); - - return 0; - } - } - - if (optind >= argc) { - if (!*list) { - fprintf(stderr, "You need to specify a device.\n"); - return 0; - } - } else - *device_arg = argv[optind]; - - return 1; -} - - -static int lookup_symbol(void *dl, void **symbol, const char *name) -{ - if ((*symbol = dlsym(dl, name))) - return 1; - - fprintf(stderr, "error looking up %s symbol: %s\n", name, dlerror()); - - return 0; -} - -static int lookup_symbols(void *dl, struct event_ops *e) -{ - return lookup_symbol(dl, (void *) &e->dm_register_for_event, - "dm_register_for_event") && - lookup_symbol(dl, (void *) &e->dm_unregister_for_event, - "dm_unregister_for_event") && - lookup_symbol(dl, (void *) &e->dm_get_registered_device, - "dm_get_registered_device") && - lookup_symbol(dl, (void *) &e->dm_set_event_timeout, - "dm_set_event_timeout") && - lookup_symbol(dl, (void *) &e->dm_get_event_timeout, - "dm_get_event_timeout"); -} - -int main(int argc, char **argv) -{ - void *dl; - struct event_ops e; - int list = 0, next = 0, ret, reg = default_reg; - char *device, *device_arg = NULL, *dso_name, *dso_name_arg = NULL; - - if (!parse_argv(argc, argv, &dso_name_arg, &device_arg, ®, &list)) - exit(EXIT_FAILURE); - - if (device_arg) { - if (!(device = strdup(device_arg))) - exit(EXIT_FAILURE); - } else - device = NULL; - - if (dso_name_arg) { - if (!(dso_name = strdup(dso_name_arg))) - exit(EXIT_FAILURE); - } else { - if (!(dso_name = strdup(default_dso_name))) - exit(EXIT_FAILURE); - } - - /* FIXME: use -v/-q options to set this */ - multilog_add_type(standard, NULL); - multilog_init_verbose(standard, _LOG_DEBUG); - - if (!(dl = dlopen("libdmevent.so", RTLD_NOW))){ - fprintf(stderr, "Cannot dlopen libdmevent.so: %s\n", dlerror()); - goto out; - } - if (!(lookup_symbols(dl, &e))) - goto out; - if (list) { - while (1) { - if ((ret= e.dm_get_registered_device(&dso_name, - &device, - &events, next))) - break; - printf("%s %s 0x%x", dso_name, device, events); - if (events & TIMEOUT){ - if ((ret = e.dm_get_event_timeout(device, - &timeout))) { - ret = EXIT_FAILURE; - goto out; - } - printf(" %"PRIu32"\n", timeout); - } else - printf("\n"); - if (device_arg) - break; - - next = 1; - } - - ret = (ret && device_arg) ? EXIT_FAILURE : EXIT_SUCCESS; - goto out; - } - - if ((ret = reg ? e.dm_register_for_event(dso_name, device, events) : - e.dm_unregister_for_event(dso_name, device, events))) { - fprintf(stderr, "Failed to %sregister %s: %s\n", - reg ? "": "un", device, strerror(-ret)); - ret = EXIT_FAILURE; - } else { - if (reg && (events & TIMEOUT) && - ((ret = e.dm_set_event_timeout(device, timeout)))){ - fprintf(stderr, "Failed to set timeout for %s: %s\n", - device, strerror(-ret)); - ret = EXIT_FAILURE; - } else { - printf("%s %sregistered successfully.\n", - device, reg ? "" : "un"); - ret = EXIT_SUCCESS; - } - } - - out: - multilog_del_type(standard); - - if (device) - free(device); - if (dso_name) - free(dso_name); - - exit(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: - */ diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c index 572bce074..8ec2b31fa 100644 --- a/daemons/dmeventd/dmeventd.c +++ b/daemons/dmeventd/dmeventd.c @@ -14,20 +14,19 @@ /* * dmeventd - dm event daemon to monitor active mapped devices - * - * Author - Heinz Mauelshagen, Red Hat GmbH. */ #include "libdevmapper.h" -#include "libdm-event.h" +#include "libdevmapper-event.h" #include "list.h" +#include "dmeventd.h" +//#include "libmultilog.h" +#include "log.h" #include #include #include -#include #include -#include #include #include #include @@ -40,16 +39,19 @@ #include #include #include +#include -#include "libmultilog.h" - +#ifdef linux +#include +#endif +/* FIXME Use dm library */ #define dbg_malloc(x...) malloc(x) #define dbg_strdup(x...) strdup(x) #define dbg_free(x...) free(x) /* List (un)link macros. */ -#define LINK(x, head) list_add(&(x)->list, head) +#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) @@ -57,7 +59,7 @@ #define UNLINK_DSO(x) UNLINK(x) #define UNLINK_THREAD(x) UNLINK(x) -#define DAEMON_NAME "dmeventd" +#define DAEMON_NAME "dmeventd" /* Global mutex for list accesses. */ static pthread_mutex_t mutex; @@ -78,7 +80,7 @@ struct dso_data { * such as changing the mapping in case a mirror fails, update * the application metadata etc. */ - void (*process_event)(char *device, enum event_type event); + void (*process_event)(const char *device, enum dm_event_type event); /* * Device registration. @@ -88,7 +90,7 @@ struct dso_data { * the process_event() function is sane (eg, read metadata * and activate a mapping). */ - int (*register_device)(char *device); + int (*register_device)(const char *device); /* * Device unregistration. @@ -97,7 +99,7 @@ struct dso_data { * for events, the DSO can recognize this and carry out appropriate * steps (eg, deactivate mapping, metadata update). */ - int (*unregister_device)(char *device); + int (*unregister_device)(const char *device); }; static LIST_INIT(dso_registry); @@ -107,9 +109,13 @@ struct message_data { char *device_path; /* Mapped device path. */ union { char *str; /* Events string as fetched from message. */ - enum event_type field; /* Events bitfield. */ + enum dm_event_type field; /* Events bitfield. */ } events; - struct daemon_message *msg; /* Pointer to message buffer. */ + union { + char *str; + uint32_t secs; + } timeout; + struct dm_event_daemon_message *msg; /* Pointer to message buffer. */ }; /* @@ -121,17 +127,26 @@ struct message_data { struct thread_status { struct list list; - pthread_t thread; + pthread_t thread; struct dso_data *dso_data;/* DSO this thread accesses. */ char *device_path; /* Mapped device path. */ - enum event_type events; /* bitfield for event filter. */ - enum event_type current_events;/* bitfield for occured events. */ - enum event_type processed_events;/* bitfield for processed events. */ + int event_nr; /* event number */ + 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. */ + time_t next_time; + uint32_t timeout; + struct list timeout_list; }; static LIST_INIT(thread_registry); +static int timeout_running; +static LIST_INIT(timeout_registry); +static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t timeout_cond = PTHREAD_COND_INITIALIZER; + /* Allocate/free the status structure for a monitoring thread. */ static struct thread_status *alloc_thread_status(struct message_data *data, struct dso_data *dso_data) @@ -139,13 +154,15 @@ static struct thread_status *alloc_thread_status(struct message_data *data, struct thread_status *ret = (typeof(ret)) dbg_malloc(sizeof(*ret)); if (ret) { - memset(ret, 0, sizeof(*ret)); - if ((ret->device_path = dbg_strdup(data->device_path))) { - ret->dso_data = dso_data; - ret->events = data->events.field; - } else { + if (!memset(ret, 0, sizeof(*ret)) || + !(ret->device_path = dbg_strdup(data->device_path))) { dbg_free(ret); ret = NULL; + } else { + ret->dso_data = dso_data; + ret->events = data->events.field; + ret->timeout = data->timeout.secs; + list_init(&ret->timeout_list); } } @@ -164,8 +181,8 @@ static struct dso_data *alloc_dso_data(struct message_data *data) struct dso_data *ret = (typeof(ret)) dbg_malloc(sizeof(*ret)); if (ret) { - memset(ret, 0, sizeof(*ret)); - if (!(ret->dso_name = dbg_strdup(data->dso_name))) { + if (!memset(ret, 0, sizeof(*ret)) || + !(ret->dso_name = dbg_strdup(data->dso_name))) { dbg_free(ret); ret = NULL; } @@ -180,18 +197,40 @@ static void free_dso_data(struct dso_data *data) dbg_free(data); } -/* Fetch a string off src and duplicate it into *dest. */ +/* 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. + */ /* FIXME: move to separate module to share with the client lib. */ static const char delimiter = ' '; -static char *fetch_string(char **src) +static int fetch_string(char **ptr, char **src) { - char *p, *ret; + int ret = 0; + char *p; + size_t len; if ((p = strchr(*src, delimiter))) *p = 0; - if ((ret = strdup(*src))) - *src += strlen(ret) + 1; + if ((*ptr = dbg_strdup(*src))) { + if ((len = strlen(*ptr))) + *src += len; + else { + dbg_free(*ptr); + *ptr = NULL; + } + + (*src)++; + ret = 1; + } if (p) *p = delimiter; @@ -214,19 +253,16 @@ static int parse_message(struct message_data *message_data) { char *p = message_data->msg->msg; -log_print("%s: here\n", __func__); -fflush(stdout); - /* * Retrieve application identifier, mapped device * path and events # string from message. */ - if ((message_data->dso_name = fetch_string(&p)) && - (message_data->device_path = fetch_string(&p)) && - (message_data->events.str = fetch_string(&p))) { -log_print("%s: %s %s %s\n", __func__, message_data->dso_name, message_data->device_path, message_data->events.str); + if (fetch_string(&message_data->dso_name, &p) && + fetch_string(&message_data->device_path, &p) && + fetch_string(&message_data->events.str, &p) && + fetch_string(&message_data->timeout.str, &p)) { if (message_data->events.str) { - enum event_type i = atoi(message_data->events.str); + enum dm_event_type i = atoi(message_data->events.str); /* * Free string representaion of events. @@ -235,12 +271,16 @@ log_print("%s: %s %s %s\n", __func__, message_data->dso_name, message_data->devi dbg_free(message_data->events.str); message_data->events.field = i; } + if (message_data->timeout.str) { + uint32_t secs = atoi(message_data->timeout.str); + dbg_free(message_data->timeout.str); + message_data->timeout.secs = secs ? secs : + DM_EVENT_DEFAULT_TIMEOUT; + } return 1; } - free_message(message_data); - return 0; }; @@ -275,50 +315,6 @@ static int storepid(int lf) return 1; } -/* - * create+flock file. - * - * Used to synchronize daemon startups. - */ -static int lf = -1; -static char pidfile[] = "/var/run/dmeventd.pid"; - -/* Store pid in pidfile. */ -static int lock(void) -{ - /* Already locked. */ - if (lf > -1) - return 1; - - if ((lf = open(pidfile, O_CREAT | O_RDWR, 0644)) == -1) { - log_err("opening pid file\n"); - return 0; - } - - if (flock(lf, LOCK_EX | LOCK_NB) == -1) { - log_err("lock pid file\n"); - close(lf); - lf = -1; - return 0; - } - - return 1; -} - -static void unlock(void) -{ - /* Not locked! */ - if (lf == -1) - return; - - if (flock(lf, LOCK_UN)) - log_err("flock unlock %s\n", pidfile); - - if (close(lf)) - log_err("close %s\n", pidfile); - - lf = -1; -} /* Check, if a device exists. */ static int device_exists(char *device) @@ -354,46 +350,199 @@ static void exit_dm_lib(void) } /* 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') { - thread->current_events |= DEVICE_ERROR; +*/ + 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); +} + +/* Wake up monitor threads every so often. */ +static void *timeout_thread(void *unused) +{ + struct timespec timeout; + time_t curr_time; + + timeout.tv_nsec = 0; + pthread_cleanup_push(exit_timeout, NULL); + pthread_mutex_lock(&timeout_mutex); + + while (!list_empty(&timeout_registry)) { + struct thread_status *thread; + + timeout.tv_sec = (time_t)-1; + curr_time = time(NULL); + + list_iterate_items_gen(thread, &timeout_registry, + timeout_list) { + if (thread->next_time < curr_time) { + thread->next_time = curr_time + thread->timeout; + pthread_kill(thread->thread, SIGALRM); + } + + if (thread->next_time < timeout.tv_sec) + timeout.tv_sec = thread->next_time; + } + + pthread_cond_timedwait(&timeout_cond, &timeout_mutex, &timeout); + } + + pthread_cleanup_pop(1); + + return NULL; +} + +static int register_for_timeout(struct thread_status *thread) +{ + int ret = 0; + + pthread_mutex_lock(&timeout_mutex); + + thread->next_time = time(NULL) + thread->timeout; + + if (list_empty(&thread->timeout_list)) { + list_add(&timeout_registry, &thread->timeout_list); + if (timeout_running) + pthread_cond_signal(&timeout_cond); + } + + if (!timeout_running) { + pthread_t timeout_id; + + if (!(ret = -pthread_create(&timeout_id, NULL, + timeout_thread, NULL))) + timeout_running = 1; + } + + pthread_mutex_unlock(&timeout_mutex); + + return ret; +} + +static void unregister_for_timeout(struct thread_status *thread) +{ + pthread_mutex_lock(&timeout_mutex); + if (!list_empty(&thread->timeout_list)) { + list_del(&thread->timeout_list); + list_init(&thread->timeout_list); + } + pthread_mutex_unlock(&timeout_mutex); +} + +static void no_intr_log(int level, const char *file, int line, + const char *f, ...) +{ + va_list ap; + + if (errno == EINTR) + return; + if (level > _LOG_WARN) + return; + + va_start(ap, f); + + if (level < _LOG_WARN) + vfprintf(stderr, f, ap); + else + vprintf(f, ap); + + va_end(ap); + + if (level < _LOG_WARN) + fprintf(stderr, "\n"); + else + fprintf(stdout, "\n"); +} + +static sigset_t unblock_sigalrm(void) +{ + sigset_t set, old; + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + pthread_sigmask(SIG_UNBLOCK, &set, &old); + return old; +} + /* Wait on a device until an event occurs. */ static int event_wait(struct thread_status *thread) { + sigset_t set; int ret = 0; +/* void *next = NULL; char *params, *target_type; uint64_t start, length; +*/ struct dm_task *dmt; + struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT))) return 0; - if ((ret = dm_task_set_name(dmt, basename(thread->device_path))) && - (ret = dm_task_set_event_nr(dmt, 0)) && - (ret = dm_task_run(dmt))) { + if (!(ret = dm_task_set_name(dmt, dm_basename(thread->device_path))) || + !(ret = dm_task_set_event_nr(dmt, thread->event_nr))) + goto out; + + /* + * This is so that you can break out of waiting on an event, + * either for a timeout event, or to cancel the thread. + */ + set = unblock_sigalrm(); + dm_log_init(no_intr_log); + errno = 0; + if ((ret = dm_task_run(dmt))) { +/* do { - /* Retrieve next target. */ params = NULL; next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); -log_print("%s: %s\n", __func__, params); + 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; + + /* + * 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 = 1; + thread->processed_events = 0; } + pthread_sigmask(SIG_SETMASK, &set, NULL); + dm_log_init(NULL); + + out: dm_task_destroy(dmt); return ret; @@ -411,7 +560,7 @@ static int do_unregister_device(struct thread_status *thread) return thread->dso_data->unregister_device(thread->device_path); } -/* Process an event the DSO. */ +/* Process an event in the DSO. */ static void do_process_event(struct thread_status *thread) { thread->dso_data->process_event(thread->device_path, @@ -424,7 +573,7 @@ static void monitor_unregister(void *arg) struct thread_status *thread = arg; if (!do_unregister_device(thread)) - log_err("%s: %s unregister failed\n", __func__, + log_error("%s: %s unregister failed\n", __func__, thread->device_path); } @@ -436,7 +585,7 @@ static void *monitor_thread(void *arg) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); pthread_cleanup_push(monitor_unregister, thread); - /* Wait for comm_thread() to finish its task. */ + /* Wait for do_process_reques() to finish its task. */ lock_mutex(); unlock_mutex(); @@ -447,9 +596,6 @@ static void *monitor_thread(void *arg) if (!event_wait(thread)) continue; -/* REMOVEME: */ -log_print("%s: cycle on %s\n", __func__, thread->device_path); - /* * Check against filter. * @@ -478,7 +624,12 @@ static int create_thread(struct thread_status *thread) static int terminate_thread(struct thread_status *thread) { - return pthread_cancel(thread->thread); + int ret; + + if ((ret = pthread_cancel(thread->thread))) + return ret; + + return pthread_kill(thread->thread, SIGALRM); } /* DSO reference counting. */ @@ -523,7 +674,7 @@ static int lookup_symbol(void *dl, struct dso_data *data, if ((*symbol = dlsym(dl, name))) return 1; - log_err("looking up %s symbol in %s\n", name, data->dso_name); + log_error("looking up %s symbol in %s\n", name, data->dso_name); return 0; } @@ -550,8 +701,6 @@ static char *create_dso_file_name(char *dso_name) strlen(suffix) + 1))) sprintf(ret, "%s%s%s", prefix, dso_name, suffix); -log_print("%s: \"%s\"\n", __func__, ret); - return ret; } @@ -562,13 +711,13 @@ static struct dso_data *load_dso(struct message_data *data) struct dso_data *ret = NULL; char *dso_file; -log_print("%s: \"%s\"\n", __func__, data->dso_name); - if (!(dso_file = create_dso_file_name(data->dso_name))) return NULL; - if (!(dl = dlopen(dso_file, RTLD_NOW))) + if (!(dl = dlopen(dso_file, RTLD_NOW))){ + log_error("%s\n", dlerror()); goto free_dso_file; + } if (!(ret = alloc_dso_data(data))) goto close; @@ -602,16 +751,22 @@ log_print("%s: \"%s\"\n", __func__, data->dso_name); } +/* Return success on daemon active check. */ +static int active(struct message_data *message_data) +{ + return 0; +} + /* * Register for an event. * - * Only one caller at a time here, because we use a FIFO and lock - * it against multiple accesses. + * Only one caller at a time here, because we use + * a FIFO and lock it against multiple accesses. */ static int register_for_event(struct message_data *message_data) { int ret = 0; - struct thread_status *thread, *thread_new; + struct thread_status *thread, *thread_new = NULL; struct dso_data *dso_data; if (!device_exists(message_data->device_path)) { @@ -619,13 +774,16 @@ static int register_for_event(struct message_data *message_data) ret = -ENODEV; goto out; } -log_print("%s\n", __func__); -fflush(stdout); if (!(dso_data = lookup_dso(message_data)) && !(dso_data = load_dso(message_data))) { stack; +/* FIXME */ +#ifdef ELIBACC ret = -ELIBACC; +#else + ret = -ENODEV; +#endif goto out; } @@ -636,20 +794,26 @@ fflush(stdout); goto out; } - if (!(ret = do_register_device(thread_new))) - goto out; - lock_mutex(); - if ((thread = lookup_thread_status(message_data))) - ret = -EPERM; - else { + 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; + thread = thread_new; thread_new = NULL; /* Try to create the monitoring thread for this device. */ + lock_mutex(); if ((ret = -create_thread(thread))) { unlock_mutex(); + do_unregister_device(thread); free_thread_status(thread); goto out; } else @@ -659,8 +823,18 @@ fflush(stdout); /* Or event # into events bitfield. */ thread->events |= message_data->events.field; - unlock_mutex(); + unlock_mutex(); + /* FIXME - If you fail to register for timeout events, you + still monitor all the other events. Is this the right + action for newly created devices? Also, you are still + on the timeout registry, so if a timeout thread is + successfully started up later, you will start receiving + DM_EVENT_TIMEOUT events */ + if (thread->events & DM_EVENT_TIMEOUT) + ret = -register_for_timeout(thread); + + out: /* * Deallocate thread status after releasing * the lock in case we haven't used it. @@ -668,9 +842,6 @@ fflush(stdout); if (thread_new) free_thread_status(thread_new); - out: - free_message(message_data); - return ret; } @@ -692,15 +863,17 @@ static int unregister_for_event(struct message_data *message_data) if (!(thread = lookup_thread_status(message_data))) { unlock_mutex(); - ret = -ESRCH; + ret = -ENODEV; goto out; } thread->events &= ~message_data->events.field; + if (!(thread->events & DM_EVENT_TIMEOUT)) + unregister_for_timeout(thread); /* - * In case there's no events to monitor on this - * device -> unlink and terminate its monitoring thread. + * In case there's no events to monitor on this device -> + * unlink and terminate its monitoring thread. */ if (!thread->events) UNLINK_THREAD(thread); @@ -709,12 +882,12 @@ static int unregister_for_event(struct message_data *message_data) if (!thread->events) { /* turn codes negative */ - if ((ret = -terminate_thread(thread))) { + if ((ret = -terminate_thread(thread))) stack; - } else { + else { pthread_join(thread->thread, NULL); - lib_put(thread->dso_data); free_thread_status(thread); + lib_put(thread->dso_data); lock_mutex(); if (list_empty(&thread_registry)) @@ -725,20 +898,18 @@ static int unregister_for_event(struct message_data *message_data) out: - free_message(message_data); - return ret; } /* - * Get next registered device. + * Get registered device. * * Only one caller at a time here as with register_for_event(). */ static int registered_device(struct message_data *message_data, struct thread_status *thread) { - struct daemon_message *msg = message_data->msg; + 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, @@ -749,104 +920,139 @@ static int registered_device(struct message_data *message_data, return 0; } -static int get_registered_device(struct message_data *message_data, int next) +static int want_registered_device(char *dso_name, char *device_path, + struct thread_status *thread) { - int dev, dso, hit = 0; + /* If DSO names and device paths are equal. */ + if (dso_name && device_path) + return !strcmp(dso_name, thread->dso_data->dso_name) && + !strcmp(device_path, thread->device_path); + + /* 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); + + return 1; +} + +static int _get_registered_device(struct message_data *message_data, int next) +{ + int hit = 0; struct thread_status *thread; lock_mutex(); - thread = list_item(thread_registry.n, struct thread_status); - - if (!message_data->dso_name && - !message_data->device_path) - goto out; - - + /* Iterate list of threads checking if we want a particular one. */ list_iterate_items(thread, &thread_registry) { - dev = dso = 0; - -log_print("%s: working %s %s %u\n", __func__, thread->dso_data->dso_name, thread->device_path, thread->events); - /* If DSO name equals. */ - if (message_data->dso_name && - !strcmp(message_data->dso_name, - thread->dso_data->dso_name)) - dso = 1; - - /* If dev path equals. */ - if (message_data->device_path && - !strcmp(message_data->device_path, - thread->device_path)) - dev = 1; - - /* We've got both DSO name and device patch or either. */ - /* FIXME: wrong logic! */ - if (message_data->dso_name && message_data->device_path && - dso && dev) - hit = 1; - else if (message_data->dso_name && dso) - hit = 1; - else if (message_data->device_path && - dev) - hit = 1; - - if (hit) -{log_print("%s: HIT %s %s %u\n", __func__, thread->dso_data->dso_name, thread->device_path, thread->events); + if ((hit = want_registered_device(message_data->dso_name, + message_data->device_path, + thread))) break; -} } /* * If we got a registered device and want the next one -> - * fetch next element off the list. + * fetch next conforming element off the list. */ - if (hit && next) - thread = list_item(&thread->list.n, struct thread_status); + if (hit) { + if (next) { + do { + 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)); + } - out: - if (list_empty(&thread->list) || - &thread->list == &thread_registry) { - unlock_mutex(); - return -ENOENT; + return registered_device(message_data, thread); } + out: unlock_mutex(); - return registered_device(message_data, thread); + return -ENOENT; } -/* Initialize a fifos structure with path names. */ -static void init_fifos(struct fifos *fifos) +static int get_registered_device(struct message_data *message_data) { - memset(fifos, 0, sizeof(*fifos)); - fifos->client_path = FIFO_CLIENT; - fifos->server_path = FIFO_SERVER; + return _get_registered_device(message_data, 0); +} + +static int get_next_registered_device(struct message_data *message_data) +{ + return _get_registered_device(message_data, 1); +} + +static int set_timeout(struct message_data *message_data) +{ + struct thread_status *thread; + + lock_mutex(); + if ((thread = lookup_thread_status(message_data))) + thread->timeout = message_data->timeout.secs; + unlock_mutex(); + + return thread ? 0 : -ENODEV; +} + +static int get_timeout(struct message_data *message_data) +{ + struct thread_status *thread; + struct dm_event_daemon_message *msg = message_data->msg; + + lock_mutex(); + if ((thread = lookup_thread_status(message_data))) + snprintf(msg->msg, sizeof(msg->msg), + "%"PRIu32, thread->timeout); + unlock_mutex(); + + return thread ? 0 : -ENODEV; +} + + +/* Initialize a fifos structure with path names. */ +static int init_fifos(struct dm_event_fifos *fifos) +{ + if (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 fifos *fifos) +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)) == -1) { + if ((fifos->server = open(fifos->server_path, O_WRONLY)) < 0) { stack; - return 0; + return -EXIT_FIFO_FAILURE; } /* Need to open read+write for select() to work. */ - if ((fifos->client = open(fifos->client_path, O_RDWR)) == -1) { + if ((fifos->client = open(fifos->client_path, O_RDWR)) < 0) { stack; close(fifos->server); - return 0; + return -EXIT_FIFO_FAILURE; } - return 1; + return 0; } /* * Read message from client making sure that data is available * and a complete message is read. */ -static int client_read(struct fifos *fifos, struct daemon_message *msg) +static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg) { int bytes = 0, ret = 0; fd_set fds; @@ -869,7 +1075,7 @@ static int client_read(struct fifos *fifos, struct daemon_message *msg) /* * Write a message to the client making sure that it is ready to write. */ -static int client_write(struct fifos *fifos, struct daemon_message *msg) +static int client_write(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg) { int bytes = 0, ret = 0; fd_set fds; @@ -889,184 +1095,181 @@ static int client_write(struct fifos *fifos, struct daemon_message *msg) return bytes == sizeof(*msg); } +/* + * Handle a client request. + * + * We put the request handling functions into + * a list because of the growing number. + */ +static int handle_request(struct dm_event_daemon_message *msg, + struct message_data *message_data) +{ + static struct { + unsigned int cmd; + 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 }, + }, *req; + + for (req = requests; req < requests + sizeof(requests); req++) { + if (req->cmd == msg->opcode.cmd) + return req->f(message_data); + } + + return -EINVAL; +} + /* Process a request passed from the communication thread. */ -static int do_process_request(struct daemon_message *msg) +static int do_process_request(struct dm_event_daemon_message *msg) { int ret; static struct message_data message_data; -log_print("%s: \"%s\"\n", __func__, msg->msg); /* Parse the message. */ + memset(&message_data, 0, sizeof(message_data)); message_data.msg = msg; - if (msg->opcode.cmd != CMD_ACTIVE && + if (msg->opcode.cmd != DM_EVENT_CMD_ACTIVE && !parse_message(&message_data)) { stack; -fflush(stdout); - return -EINVAL; - } - -fflush(stdout); - - /* Check the request type. */ - switch (msg->opcode.cmd) { - case CMD_ACTIVE: - ret = 0; - break; - case CMD_REGISTER_FOR_EVENT: - ret = register_for_event(&message_data); - break; - case CMD_UNREGISTER_FOR_EVENT: - ret = unregister_for_event(&message_data); - break; - case CMD_GET_REGISTERED_DEVICE: - ret = get_registered_device(&message_data, 0); - break; - case CMD_GET_NEXT_REGISTERED_DEVICE: - ret = get_registered_device(&message_data, 1); - break; - default: ret = -EINVAL; - break; + } else { +log_print("%s: %u \"%s\"\n", __func__, msg->opcode.cmd, message_data.msg->msg); + ret = handle_request(msg, &message_data); } + free_message(&message_data); + return ret; } /* Only one caller at a time. */ -static void process_request(struct fifos *fifos, struct daemon_message *msg) +static void process_request(struct dm_event_fifos *fifos) { + struct dm_event_daemon_message msg; + + /* FIXME: better error handling */ + /* Read the request from the client. */ - memset(msg, 0, sizeof(*msg)); - if (!client_read(fifos, msg)) { + if (!memset(&msg, 0, sizeof(msg)) || + !client_read(fifos, &msg)) { stack; return; } - msg->opcode.status = do_process_request(msg); + msg.opcode.status = do_process_request(&msg); - memset(&msg->msg, 0, sizeof(msg->msg)); - if (!client_write(fifos, msg)) +log_print("%s: status: %s\n", __func__, strerror(-msg.opcode.status)); + if (!client_write(fifos, &msg)) stack; } -/* Communication thread. */ -static void comm_thread(struct fifos *fifos) +static void sig_alarm(int signum) { - struct daemon_message msg; - - /* Open fifos (must be created by client). */ - if (!open_fifos(fifos)) { - stack; - return; - } - - /* Exit after last unregister. */ - do { - process_request(fifos, &msg); - } while (!list_empty(&thread_registry)); -} - -/* Fork into the background and detach from our parent process. */ -static int daemonize(void) -{ - pid_t pid; - - if ((pid = fork()) == -1) { - log_err("%s: fork", __func__); - return 0; - } else if (pid > 0) /* Parent. */ - return 2; - -log_print("daemonizing 2nd...\n"); - - setsid(); - if (chdir("/")) { - log_err("%s: chdir /", __func__); - return 0; - } - -/* REMOVEME: */ - return 1; - - log_print("daemonizing 3rd...\n"); - - /* Detach ourself. */ - if (close(STDIN_FILENO) == -1 || - close(STDOUT_FILENO) == -1 || - close(STDERR_FILENO) == -1) - return 0; - -log_print("daemonized\n"); - - return 1; + pthread_testcancel(); } /* Init thread signal handling. */ -#define HANGUP SIGHUP -static void init_thread_signals(int hup) +static void init_thread_signals(void) { sigset_t sigset; + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = sig_alarm; + sigaction(SIGALRM, &act, NULL); sigfillset(&sigset); - - if (hup) - sigdelset(&sigset, HANGUP); - - pthread_sigmask(SIG_SETMASK, &sigset, NULL); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); } -int main(void) +static int daemonize(void) { - struct fifos fifos; - struct sys_log logdata = {DAEMON_NAME, LOG_DAEMON}; - /* Make sure, parent accepts HANGUP signal. */ - init_thread_signals(1); + setsid(); + if (chdir("/")) + return -EXIT_CHDIR_FAILURE; - switch (daemonize()) { - case 1: /* Child. */ - /* Try to lock pidfile. */ - if (!lock()) { - fprintf(stderr, "daemon already running\n"); - break; - } +/* 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; +*/ - init_thread_signals(0); - kill(getppid(), HANGUP); + return 0; +} - multilog_clear_logging(); - multilog_add_type(std_syslog, &logdata); - multilog_init_verbose(std_syslog, _LOG_DEBUG); - multilog_async(1); +static int lock_pidfile(void) +{ + int lf; + char pidfile[] = "/var/run/dmeventd.pid"; - init_fifos(&fifos); - pthread_mutex_init(&mutex, NULL); + if ((lf = open(pidfile, O_CREAT | O_RDWR, 0644)) < 0) + return -EXIT_OPEN_PID_FAILURE; - if (!storepid(lf)) { - stack; - exit(EXIT_FAILURE); - } + if (flock(lf, LOCK_EX | LOCK_NB) < 0) + return -EXIT_LOCKFILE_INUSE; - if (mlockall(MCL_FUTURE) == -1) { - stack; - exit(EXIT_FAILURE); - } + if (!storepid(lf)) + return -EXIT_FAILURE; - /* Communication thread runs forever... */ - comm_thread(&fifos); + return 0; +} - /* We should never get here. */ - munlockall(); - pthread_mutex_destroy(&mutex); +void dmeventd(void) +{ + int ret; + struct dm_event_fifos fifos; + // struct sys_log logdata = {DAEMON_NAME, LOG_DAEMON}; - case 0: /* Error (either on daemonize() or on comm_thread() return. */ - unlock(); + if ((ret = daemonize())) + exit(-ret); + + /* FIXME: set daemon name. */ + // set_name(); + + if ((ret = lock_pidfile())) + exit(-ret); + + init_thread_signals(); + + //multilog_clear_logging(); + //multilog_add_type(std_syslog, &logdata); + //multilog_init_verbose(std_syslog, _LOG_DEBUG); + //multilog_async(1); + + if ((ret = init_fifos(&fifos))) + exit(-ret); + + pthread_mutex_init(&mutex, NULL); + +#ifdef MCL_CURRENT + if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) exit(EXIT_FAILURE); - break; +#endif - case 2: /* Parent. */ - wait(NULL); - break; - } + if ((ret = open_fifos(&fifos))) + exit(-ret); + + /* Signal parent, letting them know we are ready to go. */ + kill(getppid(), SIGUSR1); + + /* + * We exit when there are no more devices to watch. + * That is, when the last unregister happens. + */ + do { + process_request(&fifos); + } while(!list_empty(&thread_registry)); + +#ifdef MCL_CURRENT + munlockall(); +#endif + pthread_mutex_destroy(&mutex); exit(EXIT_SUCCESS); } diff --git a/daemons/dmeventd/dmeventd.h b/daemons/dmeventd/dmeventd.h new file mode 100644 index 000000000..44c7d303d --- /dev/null +++ b/daemons/dmeventd/dmeventd.h @@ -0,0 +1,12 @@ +#ifndef __DMEVENTD_DOT_H__ +#define __DMEVENTD_DOT_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); + +#endif /* __DMEVENTD_DOT_H__ */ diff --git a/daemons/dmeventd/libdevmapper-event.c b/daemons/dmeventd/libdevmapper-event.c new file mode 100644 index 000000000..62b12b708 --- /dev/null +++ b/daemons/dmeventd/libdevmapper-event.c @@ -0,0 +1,465 @@ + /* + * Copyright (C) 2005 Red Hat, Inc. All rights reserved. + * + * This file is part of the device-mapper userspace tools. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "lib.h" +#include "libdevmapper-event.h" +//#include "libmultilog.h" +#include "dmeventd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Set by any of the external fxns the first time one of them is called */ +/* FIXME Unused */ +// static int _logging = 0; + +/* 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 = 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->msg; + + if ((*dso_name = fetch_string(&p)) && + (*device = fetch_string(&p))) { + *events = atoi(p); + + return 0; + } + + return -ENOMEM; +} + +/* Read message from daemon. */ +static int daemon_read(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg) +{ + int bytes = 0, ret = 0; + fd_set fds; + + memset(msg, 0, sizeof(*msg)); + errno = 0; + /* FIXME Fix error handling. Check 'ret' before errno. EINTR? EAGAIN? */ + /* FIXME errno != EOF? RTFM! */ + while (bytes < sizeof(*msg) && errno != EOF) { + do { + /* Watch daemon read FIFO for input. */ + FD_ZERO(&fds); + FD_SET(fifos->server, &fds); + /* FIXME Check for errors e.g. EBADF */ + } while (select(fifos->server+1, &fds, NULL, NULL, NULL) != 1); + + ret = read(fifos->server, msg, sizeof(*msg) - bytes); + bytes += ret > 0 ? ret : 0; + } + +// log_print("%s: \"%s\"\n", __func__, msg->msg); + return bytes == sizeof(*msg); +} + +/* Write message to daemon. */ +static int daemon_write(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg) +{ + int bytes = 0, ret = 0; + fd_set fds; + + +// log_print("%s: \"%s\"\n", __func__, msg->msg); + errno = 0; + /* FIXME Fix error handling. Check 'ret' before errno. EINTR? EAGAIN? */ + while (bytes < sizeof(*msg) && errno != EIO) { + do { + /* Watch daemon write FIFO to be ready for output. */ + FD_ZERO(&fds); + FD_SET(fifos->client, &fds); + /* FIXME Check for errors e.g. EBADF */ + } while (select(fifos->client +1, NULL, &fds, NULL, NULL) != 1); + + ret = write(fifos->client, msg, sizeof(*msg) - bytes); + bytes += ret > 0 ? ret : 0; + } + + return bytes == sizeof(*msg); +} + +static int daemon_talk(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg, + int cmd, char *dso_name, char *device, + enum dm_event_type events, uint32_t timeout) +{ + memset(msg, 0, sizeof(*msg)); + + /* + * Set command and pack the arguments + * into ASCII message string. + */ + msg->opcode.cmd = cmd; + + if (sizeof(msg->msg) <= snprintf(msg->msg, sizeof(msg->msg), + "%s %s %u %"PRIu32, + dso_name ? dso_name : "", + device ? device : "", + events, timeout)) { + stack; + return -ENAMETOOLONG; + } + + /* + * Write command and message to and + * read status return code from daemon. + */ + if (!daemon_write(fifos, msg)) { + stack; + return -EIO; + } + + if (!daemon_read(fifos, msg)) { + stack; + 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; +} + +/* + * 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. + * + * Returns: 1 on success, 0 otherwise + */ +static int start_daemon(void) +{ + int pid, ret=0; + int old_mask; + void *old_hand; + + /* 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."); + return 0; + } + +#ifdef linux + /* FIXME Deprecated. Try posix sigprocmask instead. */ + old_mask = siggetmask(); + old_mask &= ~sigmask(SIGUSR1); + old_mask = sigsetmask(old_mask); +#endif + + 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; + + /* FIXME Better way to do this? */ + while (!waitpid(pid, &status, WNOHANG) && !daemon_running) + sleep(1); + + if (daemon_running) { + log_print("dmeventd started.\n"); + 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; + } + } + } 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(); + exit(EXIT_FAILURE); + } + + /* FIXME What if old_hand is SIG_ERR? */ + if (signal(SIGUSR1, old_hand) == SIG_ERR) + log_error("Unable to reset signal handler."); + sigsetmask(old_mask); + + return ret; +} + +/* 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? */ + + /* init fifos */ + memset(fifos, 0, sizeof(*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__); + 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... + */ + if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) { + log_error("%s: open server fifo %s\n", + __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); + close(fifos->server); + return 0; + } + + /* Anyone listening? If not, errno will be ENXIO */ + if ((fifos->client = open(fifos->client_path, + O_WRONLY | O_NONBLOCK)) < 0) { + if (errno != ENXIO) { + log_error("%s: open client fifo %s\n", + __func__, fifos->client_path); + close(fifos->server); + stack; + return 0; + } + + /* FIXME Unnecessary if daemon was started before calling this */ + if (!start_daemon()) { + stack; + return 0; + } + + /* FIXME Unnecessary if daemon was started before calling this */ + /* Daemon is started, retry the open */ + fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK); + if (fifos->client < 0) { + log_error("%s: open client fifo %s\n", + __func__, fifos->client_path); + close(fifos->server); + 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); + + close(fifos->client); + close(fifos->server); +} + +/* Check, if a block device exists. */ +static int device_exists(char *device) +{ + struct stat st_buf; + char path2[PATH_MAX]; + + if (!device) + return 0; + + if (device[0] == '/') /* absolute path */ + return !stat(device, &st_buf) && S_ISBLK(st_buf.st_mode); + + if (PATH_MAX <= snprintf(path2, PATH_MAX, "%s/%s", dm_dir(), device)) + return 0; + + return !stat(path2, &st_buf) && S_ISBLK(st_buf.st_mode); +} + +/* 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, + 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 */ + + if (!init_client(&fifos)) { + stack; + return -ESRCH; + } + + ret = daemon_talk(&fifos, msg, cmd, dso_name, device, events, timeout); + + /* what is the opposite of init? */ + dtr_client(&fifos); + + return ret; +} + +/* External library interface. */ +int dm_event_register(char *dso_name, char *device_path, + enum dm_event_type events) +{ + struct dm_event_daemon_message msg; + + if (!device_exists(device_path)) + return -ENODEV; + + return do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, &msg, + dso_name, device_path, events, 0); +} + +int dm_event_unregister(char *dso_name, char *device_path, + enum dm_event_type events) +{ + struct dm_event_daemon_message msg; + + if (!device_exists(device_path)) + return -ENODEV; + + return do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, &msg, + dso_name, device_path, events, 0); +} + +int dm_event_get_registered_device(char **dso_name, char **device_path, + enum dm_event_type *events, int next) +{ + int ret; + char *dso_name_arg = NULL, *device_path_arg = NULL; + struct dm_event_daemon_message msg; + + 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, + events); + + if (next){ + if (*dso_name) + free(*dso_name); + if (*device_path) + free(*device_path); + *dso_name = dso_name_arg; + *device_path = device_path_arg; + } else { + if (!(*dso_name)) + *dso_name = dso_name_arg; + if (!(*device_path)) + *device_path = device_path_arg; + } + + return ret; +} + +int dm_event_set_timeout(char *device_path, uint32_t timeout) +{ + struct dm_event_daemon_message msg; + + if (!device_exists(device_path)) + return -ENODEV; + return do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg, + NULL, device_path, 0, timeout); +} + +int dm_event_get_timeout(char *device_path, uint32_t *timeout) +{ + int ret; + struct dm_event_daemon_message msg; + + 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); + 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: + */ diff --git a/daemons/dmeventd/libdevmapper-event.h b/daemons/dmeventd/libdevmapper-event.h new file mode 100644 index 000000000..9c9d637e5 --- /dev/null +++ b/daemons/dmeventd/libdevmapper-event.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2005 Red Hat, Inc. All rights reserved. + * + * This file is part of the device-mapper userspace tools. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Note that this file is released only as part of a technology preview + * and its contents may change in future updates in ways that do not + * preserve compatibility. + */ + +#ifndef LIB_DMEVENT_H +#define LIB_DMEVENT_H + +#include + +/* 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 { + DM_EVENT_SINGLE = 0x01, /* Report multiple errors just once. */ + DM_EVENT_MULTI = 0x02, /* 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_SYNC_STATUS = 0x40, /* Mirror synchronization completed/failed. */ + DM_EVENT_TIMEOUT = 0x80, /* Timeout has occured */ +}; + +/* 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) + +/* Prototypes for event lib interface. */ +/* 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, + 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); + +/* 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); + +#endif diff --git a/daemons/dmeventd/mktestdevices b/daemons/dmeventd/mktestdevices deleted file mode 100644 index 7a6afb099..000000000 --- a/daemons/dmeventd/mktestdevices +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# -# Create test devices for dmeventd -# - -trap "rm -f /tmp/tmp.$$" 0 1 2 3 15 - -echo "0 1024 zero" > /tmp/tmp.$$ -dmsetup create test /tmp/tmp.$$ -dmsetup create test1 /tmp/tmp.$$ - -kill -15 $$ diff --git a/daemons/dmeventd/noop.c b/daemons/dmeventd/noop.c deleted file mode 100644 index deb2ee489..000000000 --- a/daemons/dmeventd/noop.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2005 Red Hat, Inc. All rights reserved. - * - * This file is part of the device-mapper userspace tools. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU Lesser General Public License v.2.1. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "libdm-event.h" -#include "libmultilog.h" - - -void process_event(char *device, enum event_type event) -{ - log_err("[%s] %s(%d) - Device: %s, Event %d\n", - __FILE__, __func__, __LINE__, device, event); -} - -int register_device(char *device) -{ - log_err("[%s] %s(%d) - Device: %s\n", - __FILE__, __func__, __LINE__, device); - - return 1; -} - -int unregister_device(char *device) -{ - log_err("[%s] %s(%d) - Device: %s\n", - __FILE__, __func__, __LINE__, device); - - return 1; -} diff --git a/libdm/Makefile.in b/libdm/Makefile.in index d90a77bd2..f2c8df3c7 100644 --- a/libdm/Makefile.in +++ b/libdm/Makefile.in @@ -17,14 +17,6 @@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ interface = @interface@ -ifeq ("@DMEVENTD@", "yes") - SUBDIRS += event -endif - -ifeq ($(MAKECMDGOALS),distclean) - SUBDIRS += event -endif - SOURCES =\ datastruct/bitset.c \ datastruct/hash.c \ diff --git a/libdm/libdm-event.h b/libdm/libdm-event.h deleted file mode 100644 index d92eb71e2..000000000 --- a/libdm/libdm-event.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2005 Red Hat, Inc. All rights reserved. - * - * This file is part of the device-mapper userspace tools. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU Lesser General Public License v.2.1. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef LIB_DMEVENT_H -#define LIB_DMEVENT_H - -#include "list.h" - -#include - -#define DAEMON "/sbin/dmeventd" -#define LOCKFILE "/var/lock/dmeventd" -#define FIFO_CLIENT "/var/run/dmeventd-client" -#define FIFO_SERVER "/var/run/dmeventd-server" -#define PIDFILE "/var/run/dmeventd.pid" - -#define DEFAULT_TIMEOUT 10 -/* Commands for the daemon passed in the message below. */ -enum dmeventd_command { - CMD_ACTIVE = 1, - CMD_REGISTER_FOR_EVENT, - CMD_UNREGISTER_FOR_EVENT, - CMD_GET_REGISTERED_DEVICE, - CMD_GET_NEXT_REGISTERED_DEVICE, - CMD_SET_TIMEOUT, - CMD_GET_TIMEOUT, -}; - -/* Message passed between client and daemon. */ -struct daemon_message { - union { - unsigned int cmd; - int status; - } opcode; - char msg[252]; -} __attribute__((packed)); - -/* Fifos for client/daemon communication. */ -struct fifos { - int client; - int server; - const char *client_path; - const char *server_path; -}; - -/* Event type definitions. */ -enum event_type { - SINGLE = 0x01, /* Report multiple errors just once. */ - MULTI = 0x02, /* Report all of them. */ - SECTOR_ERROR = 0x04, /* Failure on a particular sector. */ - DEVICE_ERROR = 0x08, /* Device failure. */ - PATH_ERROR = 0x10, /* Failure on an io path. */ - ADAPTOR_ERROR = 0x20, /* Failure off a host adaptor. */ - SYNC_STATUS = 0x40, /* Mirror synchronization completed/failed. */ - TIMEOUT = 0x80, /* Timeout has occured */ -}; -#define ALL_ERRORS (SECTOR_ERROR | DEVICE_ERROR | PATH_ERROR | ADAPTOR_ERROR) - -/* Prototypes for event lib interface. */ -int dm_register_for_event(char *dso_name, char *device, enum event_type events); -int dm_unregister_for_event(char *dso_name, char *device, - enum event_type events); -int dm_get_registered_device(char **dso_name, char **device, - enum event_type *events, int next); -int dm_set_event_timeout(char *device, uint32_t timeout); -int dm_get_event_timeout(char *device, uint32_t *timeout); - -/* Prototypes for DSO interface. */ -void process_event(char *device, enum event_type event); -int register_device(char *device); -int unregister_device(char *device); - -#endif - -/* - * 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: - */