From cbdccf0a9cf5943e9624a5a5b9d8b574fe1230c6 Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Mon, 8 Jan 2007 15:18:52 +0000 Subject: [PATCH] Lots of dmevent changes. Export dm_basename(). Cope with a trailing space when comparing tables prior to possible reload. --- WHATS_NEW_DM | 4 +- daemons/dmeventd/Makefile.in | 18 +- daemons/dmeventd/dmeventd.c | 529 +++++++++++++++++--------- daemons/dmeventd/dmeventd.h | 50 ++- daemons/dmeventd/libdevmapper-event.c | 336 ++++++++-------- daemons/dmeventd/libdevmapper-event.h | 57 +-- libdm/.exported_symbols | 1 + libdm/ioctl/libdm-iface.c | 2 + libdm/libdevmapper.h | 5 + libdm/libdm-string.c | 8 + 10 files changed, 589 insertions(+), 421 deletions(-) diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM index adf50b08b..3a5018572 100644 --- a/WHATS_NEW_DM +++ b/WHATS_NEW_DM @@ -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 diff --git a/daemons/dmeventd/Makefile.in b/daemons/dmeventd/Makefile.in index 994076f0e..f5b2fea9a 100644 --- a/daemons/dmeventd/Makefile.in +++ b/daemons/dmeventd/Makefile.in @@ -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 diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c index 0d7fc850c..8b642b931 100644 --- a/daemons/dmeventd/dmeventd.c +++ b/daemons/dmeventd/dmeventd.c @@ -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 #include #include +#include /* for htonl, ntohl */ #ifdef linux #include #endif +/* We must use syslog for now, because multilog is not yet implemented */ +#include + +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 { @@ -122,15 +133,16 @@ struct message_data { * occurs and the event processing function of the DSO gets called. */ struct thread_status { - struct list list; + struct list list; - pthread_t thread; + pthread_t thread; struct dso_data *dso_data;/* DSO this thread accesses. */ 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), - "%"PRIu32, thread->timeout); + 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))) { - fifos->client_path = DM_EVENT_FIFO_CLIENT; - fifos->server_path = DM_EVENT_FIFO_SERVER; + memset(fifos, 0, sizeof(*fifos)); - return 0; - } - - return -ENOMEM; + fifos->client_path = DM_EVENT_FIFO_CLIENT; + fifos->server_path = DM_EVENT_FIFO_SERVER; } /* 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) { + 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; } /* @@ -1121,17 +1176,17 @@ static int handle_request(struct dm_event_daemon_message *msg, unsigned int cmd; int (*f)(struct message_data*); } requests[] = { - { DM_EVENT_CMD_REGISTER_FOR_EVENT, register_for_event }, + { DM_EVENT_CMD_REGISTER_FOR_EVENT, register_for_event }, { DM_EVENT_CMD_UNREGISTER_FOR_EVENT, unregister_for_event }, { DM_EVENT_CMD_GET_REGISTERED_DEVICE, get_registered_device }, { DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE, get_next_registered_device }, - { DM_EVENT_CMD_SET_TIMEOUT, set_timeout }, - { DM_EVENT_CMD_GET_TIMEOUT, get_timeout }, - { DM_EVENT_CMD_ACTIVE, active }, + { DM_EVENT_CMD_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) + 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->events) { - /* turn codes negative -- should we be returning this? */ - if ((ret = -terminate_thread(thread))) - stack; - else { - pthread_join(thread->thread, NULL); - lib_put(thread->dso_data); - free_thread_status(thread); + 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? */ + 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; + } + 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 { - log_error("thread can't be on unused list unless !thread->events"); - 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); } - } 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) +/* + * exit_handler + * @sig + * + * Set the global variable which the process should + * be watching to determine when to exit. + */ +static void exit_handler(int sig) { - setsid(); - if (chdir("/")) - return -EXIT_CHDIR_FAILURE; + /* + * 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; -/* 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; -*/ - - 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}; + //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); } diff --git a/daemons/dmeventd/dmeventd.h b/daemons/dmeventd/dmeventd.h index b60fdddde..084d13626 100644 --- a/daemons/dmeventd/dmeventd.h +++ b/daemons/dmeventd/dmeventd.h @@ -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__ */ diff --git a/daemons/dmeventd/libdevmapper-event.c b/daemons/dmeventd/libdevmapper-event.c index 35573d9be..ccbc36fb8 100644 --- a/daemons/dmeventd/libdevmapper-event.c +++ b/daemons/dmeventd/libdevmapper-event.c @@ -1,4 +1,4 @@ - /* +/* * Copyright (C) 2005 Red Hat, Inc. All rights reserved. * * This file is part of the device-mapper userspace tools. @@ -27,8 +27,8 @@ #include #include #include -#include #include +#include /* 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; } - } while (ret < 1); + } + if (ret < 1) { + log_error("Unable to read from event server."); + return 0; + } - 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 pid, ret = 0; + 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,63 +289,34 @@ 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", - __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; - } +/* 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; } return 1; @@ -354,14 +325,14 @@ static int init_client(struct dm_event_fifos *fifos) 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; - return 1; + if (msg.data) + dm_free(msg.data); + + return ret; } -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; - } + msg.data ? msg.data : strerror(-err)); + ret = 0; + } else + ret = 1; - return 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,11 +445,16 @@ 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, - events); + &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 (next){ + if (msg.data) + dm_free(msg.data); + + if (next) { if (*dso_name) dm_free(*dso_name); if (*device_path) @@ -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: - */ diff --git a/daemons/dmeventd/libdevmapper-event.h b/daemons/dmeventd/libdevmapper-event.h index c239060da..77460fcc6 100644 --- a/daemons/dmeventd/libdevmapper-event.h +++ b/daemons/dmeventd/libdevmapper-event.h @@ -23,45 +23,6 @@ #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 { @@ -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. -*/ + +/* 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, +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); diff --git a/libdm/.exported_symbols b/libdm/.exported_symbols index 718c567a3..cd2433563 100644 --- a/libdm/.exported_symbols +++ b/libdm/.exported_symbols @@ -113,3 +113,4 @@ dm_task_set_geometry dm_split_lvm_name dm_split_words dm_snprintf +dm_basename diff --git a/libdm/ioctl/libdm-iface.c b/libdm/ioctl/libdm-iface.c index 2b45bdcab..d343bc19c 100644 --- a/libdm/ioctl/libdm-iface.c +++ b/libdm/ioctl/libdm-iface.c @@ -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)) || diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h index 19d55c0d3..d58620c59 100644 --- a/libdm/libdevmapper.h +++ b/libdm/libdevmapper.h @@ -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 */ diff --git a/libdm/libdm-string.c b/libdm/libdm-string.c index f990d95bc..17defaca3 100644 --- a/libdm/libdm-string.c +++ b/libdm/libdm-string.c @@ -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; +} +