From b0c36cbd57647feaf530e8ac119a2dc43f8d7437 Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Wed, 27 Apr 2005 22:32:00 +0000 Subject: [PATCH] Prototype for a device-mapper event-handling daemon. --- daemons/dmeventd/Makefile.in | 43 ++ daemons/dmeventd/dmeventd.c | 955 +++++++++++++++++++++++++++++++++ daemons/dmeventd/mktestdevices | 12 + daemons/dmeventd/noop.c | 39 ++ daemons/dmeventd/test.c | 75 +++ libdm/Makefile.in | 8 + libdm/libdm-event.h | 80 +++ 7 files changed, 1212 insertions(+) create mode 100644 daemons/dmeventd/Makefile.in create mode 100644 daemons/dmeventd/dmeventd.c create mode 100644 daemons/dmeventd/mktestdevices create mode 100644 daemons/dmeventd/noop.c create mode 100644 daemons/dmeventd/test.c create mode 100644 libdm/libdm-event.h diff --git a/daemons/dmeventd/Makefile.in b/daemons/dmeventd/Makefile.in new file mode 100644 index 000000000..a26ba40fb --- /dev/null +++ b/daemons/dmeventd/Makefile.in @@ -0,0 +1,43 @@ +# +# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +# Copyright (C) 2004 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. +# +# You should have received a copy of the GNU 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@ + +TARGETS = test dmeventd +INSTALL_TYPE = install_dynamic + +SOURCES = test.c dmeventd.c +CLEAN_TARGETS = test + +LDFLAGS += -ldl -ldevmapper -lpthread + +include ../make.tmpl + +test: $(OBJECTS) $(interfacedir)/libdevmapper.$(LIB_SUFFIX) $(top_srcdir)/lib/event/libdmevent.$(LIB_SUFFIX) + $(CC) -o $@ test.o $(LDFLAGS) \ + -L$(interfacedir) -L$(DESTDIR)/lib -L$(top_srcdir)/lib/event -ldevmapper -ldmevent $(LIBS) + +dmeventd: $(OBJECTS) $(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 -ldevmapper -ldmevent $(LIBS) + +install: $(INSTALL_TYPE) + +.PHONY: install_dynamic + +install_dynamic: dmeventd + $(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) dmeventd $(sbindir)/dmeventd + diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c new file mode 100644 index 000000000..7671add5d --- /dev/null +++ b/daemons/dmeventd/dmeventd.c @@ -0,0 +1,955 @@ +/* + * 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 + */ + +/* + * dmeventd - dm event daemon to monitor active mapped devices + * + * Author - Heinz Mauelshagen, Red Hat GmbH. + */ + +#include "libdevmapper.h" +#include "log.h" +#include "libdm-event.h" +#include "list.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Macros to be moved external later... + */ +#undef log_print +#undef log_err +#undef stack +#define log_print(x...) fprintf(stdout, "[dmeventd] " x) +#define log_err(x...) fprintf(stderr, "ERROR: " x) +#define stack log_print("trace: %s:%s(%d)\n", \ + __FILE__, __func__, __LINE__); + +#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_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) +#define UNLINK_THREAD(x) UNLINK(x) + + +/* Global mutex for list accesses. */ +static pthread_mutex_t mutex; + +/* Data kept about a DSO. */ +struct dso_data { + struct list list; + + char *dso_name; /* DSO name (eg, "evms", "dmraid", "lvm2"). */ + + void *dso_handle; /* Opaque handle as returned from dlopen(). */ + unsigned int ref_count; /* Library reference count. */ + + /* + * Event processing. + * + * The DSO can do whatever appropriate steps if an event happens + * such as changing the mapping in case a mirror fails, update + * the application metadata etc. + */ + void (*process_event)(char *device, enum event_type event); + + /* + * Device registration. + * + * When an application registers a device for an event, the DSO + * can carry out appropriate steps so that a later call to + * the process_event() function is sane (eg, read metadata + * and activate a mapping). + */ + int (*register_device)(char *device); + + /* + * Device unregistration. + * + * In case all devices of a mapping (eg, RAID10) are unregistered + * for events, the DSO can recognize this and carry out appropriate + * steps (eg, deactivate mapping, metadata update). + */ + int (*unregister_device)(char *device); +}; +static LIST_INIT(dso_registry); + +/* Structure to keep parsed register variables from client message. */ +struct register_data { + char *dso_name; /* Name of DSO. */ + char *device_path; /* Mapped device path. */ + union { + char *str; /* Events string as fetched from message. */ + enum event_type field; /* Events bitfield. */ + } events; +}; + +/* + * Housekeeping of thread+device states. + * + * One thread per mapped device which can block on it until an event + * occurs and the event processing function of the DSO gets called. + */ +struct thread_status { + struct list list; + + 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. */ +}; +static LIST_INIT(thread_registry); + +/* Allocate/free the status structure for a monitoring thread. */ +static struct thread_status *alloc_thread_status(struct register_data *data, + struct dso_data *dso_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 { + dbg_free(ret); + ret = NULL; + } + } + + return ret; +} + +static void free_thread_status(struct thread_status *thread) +{ + dbg_free(thread->device_path); + dbg_free(thread); +} + +/* Allocate/free DSO data. */ +static struct dso_data *alloc_dso_data(struct register_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))) { + dbg_free(ret); + ret = NULL; + } + } + + return ret; +} + +static void free_dso_data(struct dso_data *data) +{ + dbg_free(data->dso_name); + dbg_free(data); +} + +/* Fetch a string off src and duplicate it into *dest. */ +static const char delimiter = ' '; +static char *fetch_string(char **src) +{ + char *p, *ret; + + if ((p = strchr(*src, delimiter))) + *p = 0; + + if ((ret = dbg_strdup(*src))) + *src += strlen(ret) + 1; + + if (p) + *p = delimiter; + + return ret; +} + +/* Free message memory. */ +static void free_message(struct register_data *register_data) +{ + if (register_data->dso_name) + dbg_free(register_data->dso_name); + + if (register_data->device_path) + dbg_free(register_data->device_path); +} + +/* Parse a register message from the client. */ +static int parse_message(struct register_data *register_data, char *msg) +{ + char *p = msg; + + memset(register_data, 0, sizeof(*register_data)); + + /* + * Retrieve application identifier, mapped device + * path and events # string from message. + */ + if ((register_data->dso_name = fetch_string(&p)) && + (register_data->device_path = fetch_string(&p)) && + (register_data->events.str = fetch_string(&p))) { + enum event_type i = atoi(register_data->events.str); + + /* Free string representaion of events. Not needed an more. */ + dbg_free(register_data->events.str); + register_data->events.field = i; + + return 1; + } + + free_message(register_data); + + return 0; +}; + +/* Global mutex to lock access to lists et al. */ +static int lock_mutex(void) +{ + return pthread_mutex_lock(&mutex); +} + +static int unlock_mutex(void) +{ + return pthread_mutex_unlock(&mutex); +} + +/* Store pid in pidfile. */ +static int storepid(int lf) +{ + int len; + char pid[8]; + + if ((len = snprintf(pid, sizeof(pid), "%u\n", getpid())) < 0) + return 0; + + if (len > sizeof(pid)) + len = sizeof(pid); + + if (write(lf, pid, len) != len) + return 0; + + fsync(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) +{ + int f; + + if ((f = open(device, O_RDONLY)) == -1) + return 0; + + close(f); + + return 1; +} + +/* + * Find an existing thread for a device. + * + * Mutex must be hold when calling this. + */ +static struct thread_status *lookup_thread_status(struct register_data *data) +{ + struct thread_status *thread; + + list_iterate_items(thread, &thread_registry) { + if (!strcmp(data->device_path, thread->device_path)) + return thread; + } + + return NULL; +} + + +/* Cleanup at exit. */ +static void exit_dm_lib(void) +{ + dm_lib_release(); + dm_lib_exit(); +} + +/* Derive error case from target parameter string. */ +static int error_detected(struct thread_status *thread, char *params) +{ + size_t len; + + if ((len = strlen(params)) && params[len - 1] == 'F') { + thread->current_events |= DEVICE_ERROR; + return 1; + } + + return 0; +} + +/* Wait on a device until an event occurs. */ +static int event_wait(struct thread_status *thread) +{ + int ret = 0; + void *next; + char *params, *target_type; + uint64_t start, length; + struct dm_task *dmt; + + 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))) { + 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); + if (error_detected(thread, params)) + break; + } while(next); + } + + dm_task_destroy(dmt); + + return ret; +} + +/* Register a device with the DSO. */ +static int register_device(struct thread_status *thread) +{ + return thread->dso_data->register_device(thread->device_path); +} + +/* Unregister a device with the DSO. */ +static int unregister_device(struct thread_status *thread) +{ + return thread->dso_data->unregister_device(thread->device_path); +} + +/* Process an event the DSO. */ +static void process_event(struct thread_status *thread) +{ + thread->dso_data->process_event(thread->device_path, + thread->current_events); +} + +/* Thread cleanup handler to unregister device. */ +static void monitor_unregister(void *arg) +{ + struct thread_status *thread = arg; + + if (!unregister_device(thread)) + log_err("%s: %s unregister failed\n", __func__, + thread->device_path); +} + +/* Device monitoring thread. */ +void *monitor_thread(void *arg) +{ + struct thread_status *thread = arg; + + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + pthread_cleanup_push(monitor_unregister, thread); + + /* Wait for comm_thread() to finish its task. */ + lock_mutex(); + unlock_mutex(); + + /* Loop forever awaiting/analyzing device events. */ + while (1) { + thread->current_events = 0; + + if (!event_wait(thread)) + continue; + +/* REMOVEME: */ +log_print("%s: cycle on %s\n", __func__, thread->device_path); + + /* Check against filter. */ + if (thread->events & + thread->current_events & + ~thread->processed_events) { + process_event(thread); + thread->processed_events |= thread->current_events; + } + } + + pthread_cleanup_pop(0); +} + +/* Create a device monitoring thread. */ +/* FIXME: call this with mutex hold ? */ +static int create_thread(struct thread_status *thread) +{ + return pthread_create(&thread->thread, NULL, monitor_thread, thread); +} + +static int terminate_thread(struct thread_status *thread) +{ + return pthread_cancel(thread->thread); +} + +/* DSO reference counting. */ +static void lib_get(struct dso_data *data) +{ + data->ref_count++; +} + +static void lib_put(struct dso_data *data) +{ + if (!--data->ref_count) { + dlclose(data->dso_handle); + UNLINK_DSO(data); + free_dso_data(data); + } +} + +/* Find DSO data. */ +static struct dso_data *lookup_dso(struct register_data *data) +{ + struct dso_data *dso_data, *ret = NULL; + + lock_mutex(); + + list_iterate_items(dso_data, &dso_registry) { + if (!strcmp(data->dso_name, dso_data->dso_name)) { + ret = dso_data; + lib_get(ret); + break; + } + } + + unlock_mutex(); + + return ret; +} + +/* Lookup DSO symbols we need. */ +static int lookup_symbol(void *dl, struct dso_data *data, + void **symbol, const char *name) +{ + if ((*symbol = dlsym(dl, name))) + return 1; + + log_err("looking up %s symbol in %s\n", name, data->dso_name); + + return 0; +} + +static int lookup_symbols(void *dl, struct dso_data *data) +{ + return lookup_symbol(dl, data, (void*) &data->process_event, + "process_event") && + lookup_symbol(dl, data, (void*) &data->register_device, + "register_device") && + lookup_symbol(dl, data, (void*) &data->unregister_device, + "unregister_device"); +} + +/* Create a DSO file name based on its name. */ +static char *create_dso_file(char *dso_name) +{ + char *ret; + static char *prefix = "libdmeventd_"; + static char *suffix = ".so"; + + if ((ret = dbg_malloc(strlen(prefix) + + strlen(dso_name) + + strlen(suffix) + 1))) + sprintf(ret, "%s%s%s", prefix, dso_name, suffix); + + return ret; +} + +/* Load an application specific DSO. */ +static struct dso_data *load_dso(struct register_data *data) +{ + void *dl; + struct dso_data *ret = NULL; + char *dso_file; + + if (!(dso_file = create_dso_file(data->dso_name))) + return NULL; + + if (!(dl = dlopen(dso_file, RTLD_NOW))) + goto free_dso_file; + + if (!(ret = alloc_dso_data(data))) + goto close; + + if (!(lookup_symbols(dl, ret))) + goto free_all; + + /* + * Keep handle to close the library once + * we've got no references to it any more. + */ + ret->dso_handle = dl; + lib_get(ret); + + lock_mutex(); + LINK_DSO(ret); + unlock_mutex(); + + goto free_dso_file; + + free_all: + free_dso_data(ret); + + close: + dlclose(dl); + + free_dso_file: + dbg_free(dso_file); + + return ret; +} + + +/* + * Register for an event. + * + * Only one caller at a time here, because we use a FIFO and lock + * it against multiple accesses. + */ +static int register_for_event(char *msg) +{ + int ret = 0; + struct register_data register_data; + struct thread_status *thread, *thread_new; + struct dso_data *dso_data; + + /* Parse the register message and find/load the appropriate DSO. */ + if (!parse_message(®ister_data, msg)) + return -EINVAL; + + if (!device_exists(register_data.device_path)) { + stack; + ret = -ENODEV; + goto out; + } + + if (!(dso_data = lookup_dso(®ister_data)) && + !(dso_data = load_dso(®ister_data))) { + stack; + ret = -ELIBACC; + goto out; + } + + /* Preallocate thread status struct to avoid deadlock. */ + if (!(thread_new = alloc_thread_status(®ister_data, dso_data))) { + stack; + ret = -ENOMEM; + goto out; + } + + if (!(ret = register_device(thread_new))) + goto out; + + lock_mutex(); + + if ((thread = lookup_thread_status(®ister_data))) + ret = -EPERM; + else { + thread = thread_new; + thread_new = NULL; + + /* Try to create the monitoring thread for this device. */ + if ((ret = -create_thread(thread))) { + unlock_mutex(); + free_thread_status(thread); + goto out; + } else + LINK_THREAD(thread); + } + + /* Or event # into events bitfield. */ + thread->events |= register_data.events.field; + + unlock_mutex(); + + /* + * Deallocate thread status after releasing + * the lock in case we haven't used it. + */ + if (thread_new) + free_thread_status(thread_new); + + out: + free_message(®ister_data); + + return ret; +} + +/* + * Unregister for an event. + * + * Only one caller at a time here as with register_for_event(). + */ +static int unregister_for_event(char *msg) +{ + int ret = 0; + struct register_data register_data; + struct thread_status *thread; + + if (!parse_message(®ister_data, msg)) + return -EINVAL; + + /* + * Clear event in bitfield and deactivate + * monitoring thread in case bitfield is 0. + */ + + lock_mutex(); + + if (!(thread = lookup_thread_status(®ister_data))) { + unlock_mutex(); + ret = -ESRCH; + goto out; + } + + thread->events &= ~register_data.events.field; + + /* + * In case there's no events to monitor on this + * device -> unlink and terminate its monitoring thread. + */ + if (!thread->events) + UNLINK_THREAD(thread); + + unlock_mutex(); + + if (!thread->events) { + /* turn codes negative */ + if ((ret = -terminate_thread(thread))) { + stack; + } else { + pthread_join(thread->thread, NULL); + lib_put(thread->dso_data); + free_thread_status(thread); + + lock_mutex(); + if (list_empty(&thread_registry)) + exit_dm_lib(); + unlock_mutex(); + } + } + + + out: + free_message(®ister_data); + + return ret; +} + +/* Initialize a fifos structure with path names. */ +static void init_fifos(struct fifos *fifos) +{ + memset(fifos, 0, sizeof(*fifos)); + fifos->client_path = FIFO_CLIENT; + fifos->server_path = FIFO_SERVER; +} + +/* Open fifos used for client communication. */ +static int open_fifos(struct fifos *fifos) +{ + /* Blocks until client is ready to write. */ + if ((fifos->server = open(fifos->server_path, O_WRONLY)) == -1) { + stack; + return 0; + } + + /* Need to open read+write for select() to work. */ + if ((fifos->client = open(fifos->client_path, O_RDWR)) == -1) { + stack; + close(fifos->server); + return 0; + } + + return 1; +} + +/* + * 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) +{ + int bytes = 0, ret = 0; + fd_set fds; + + errno = 0; + while (bytes < sizeof(*msg) && errno != EOF) { + do { + /* Watch client read FIFO for input. */ + FD_ZERO(&fds); + FD_SET(fifos->client, &fds); + } while (select(fifos->client+1, &fds, NULL, NULL, NULL) != 1); + + ret = read(fifos->client, msg, sizeof(*msg) - bytes); + bytes += ret > 0 ? ret : 0; + } + + return bytes == sizeof(*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) +{ + fd_set fds; + + do { + /* Watch client write FIFO if it's ready for output. */ + FD_ZERO(&fds); + FD_SET(fifos->server, &fds); + } while (select(fifos->server + 1, NULL, &fds, NULL, NULL) <= 0); + + return write(fifos->server, msg, sizeof(*msg)) == sizeof(*msg); +} + +/* Process a request passed from the communication thread. */ +static int _process_request(struct daemon_message *msg) +{ + int ret; + + /* Check the request type. */ + switch (msg->opcode.cmd) { + case CMD_ACTIVE: + ret = 0; + break; + + case CMD_REGISTER_FOR_EVENT: + ret = register_for_event(msg->msg); + break; + + case CMD_UNREGISTER_FOR_EVENT: + ret = unregister_for_event(msg->msg); + break; + } + + return ret; +} + +static void process_request(struct fifos *fifos, struct daemon_message *msg) +{ + /* Read the request from the client. */ + memset(msg, 0, sizeof(*msg)); + if (!client_read(fifos, msg)) { + stack; + return; + } + + msg->opcode.status = _process_request(msg); + + memset(&msg->msg, 0, sizeof(msg->msg)); + if (!client_write(fifos, msg)) + stack; +} + +/* Communication thread. */ +static void comm_thread(struct fifos *fifos) +{ + struct daemon_message msg; + + /* Open fifos (must be created by client). */ + if (!open_fifos(fifos)) { + stack; + return; + } + + /* Loop forever and handle client requests sequentially. */ + while (1) + process_request(fifos, &msg); +} + +/* 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; +} + +/* Init thread signal handling. */ +static void init_thread_signals(void) +{ + sigset_t sigset; + + sigfillset(&sigset); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); +} + +int main(void) +{ + int ret = 0; + struct fifos fifos; + + switch (daemonize()) { + case 1: /* Child. */ + /* Try to lock pidfile. */ + if (!lock()) { + fprintf(stderr, "daemon already running\n"); + break; + } + + init_thread_signals(); + kill(getppid(), SIGHUP); + init_fifos(&fifos); + pthread_mutex_init(&mutex, NULL); + + if (!storepid(lf)) { + stack; + return 0; + } + + if (mlockall(MCL_FUTURE) == -1) { + stack; + return 0; + } + + /* Communication thread runs forever... */ + comm_thread(&fifos); + + /* We should never get here. */ + munlockall(); + pthread_mutex_destroy(&mutex); + + case 0: /* Error (either on daemonize() or on comm_thread() return. */ + unlock(); + ret = 1; + break; + + case 2: /* Parent. */ + wait(NULL); + break; + } + + return 1; +} + +/* + * 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/mktestdevices b/daemons/dmeventd/mktestdevices new file mode 100644 index 000000000..7a6afb099 --- /dev/null +++ b/daemons/dmeventd/mktestdevices @@ -0,0 +1,12 @@ +#!/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 new file mode 100644 index 000000000..c9abf3f5a --- /dev/null +++ b/daemons/dmeventd/noop.c @@ -0,0 +1,39 @@ +/* + * 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 + +void process_event(char *device, enum event_type event) +{ + fprintf(stderr, "[%s] %s(%d) - Device: %s, Event %d\n", + __FILE__, __func__, __LINE__, device, event); +} + +int register_device(char *device) +{ + fprintf(stderr, "[%s] %s(%d) - Device: %s\n", + __FILE__, __func__, __LINE__, device); + + return 1; +} + +int unregister_device(char *device) +{ + fprintf(stderr, "[%s] %s(%d) - Device: %s\n", + __FILE__, __func__, __LINE__, device); + + return 1; +} diff --git a/daemons/dmeventd/test.c b/daemons/dmeventd/test.c new file mode 100644 index 000000000..43cb777cb --- /dev/null +++ b/daemons/dmeventd/test.c @@ -0,0 +1,75 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *dso_name = "kickme"; +static char *device = "/dev/mapper/test"; +static char *device1 = "/dev/mapper/test1"; +enum event_type events = ALL_ERRORS; + +static void log_return(int ret, char *what, char *device) +{ + printf("%s returned \"%s\"\n", what, strerror(-ret)); + + if (ret < 0) + printf("%s %s FAILED :-(((\n", what, device); + else + printf("%s %s succeded :-)\n", what, device); +} + +int main(int argc, char **argv) +{ + int reg = 0; + + if (argc > 1 && *argv[1] == 'r') + reg = 1; + + if (reg) { + log_return(dm_register_for_event(dso_name, device, events), + "register", device); + log_return(dm_register_for_event(dso_name, device1, events), + "register", device1); + } else { + log_return(dm_unregister_for_event(dso_name, device, events), + "unregister", device); + log_return(dm_unregister_for_event(dso_name, device1, events), + "unregister", device1); + } + + return 0; +} + +/* + * 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/libdm/Makefile.in b/libdm/Makefile.in index 7f9aa378d..4753fbfa3 100644 --- a/libdm/Makefile.in +++ b/libdm/Makefile.in @@ -17,6 +17,14 @@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ interface = @interface@ +ifeq ("@DMEVENTD@", "yes") + SUBDIRS += event +endif + +ifeq ($(MAKECMDGOALS),distclean) + SUBDIRS += event +endif + SOURCES = libdm-common.c libdm-file.c $(interface)/libdm-iface.c INCLUDES = -I$(interface) diff --git a/libdm/libdm-event.h b/libdm/libdm-event.h new file mode 100644 index 000000000..32168b77c --- /dev/null +++ b/libdm/libdm-event.h @@ -0,0 +1,80 @@ +/* + * 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" + +/* Commands for the daemon passed in the message below. */ +enum dmeventd_command { + CMD_ACTIVE = 1, + CMD_REGISTER_FOR_EVENT, + CMD_UNREGISTER_FOR_EVENT, +}; + +/* 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; + char *client_path; + 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. */ +}; +#define ALL_ERRORS (SECTOR_ERROR | DEVICE_ERROR | PATH_ERROR | ADAPTOR_ERROR) + +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); + +#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: + */