From b8b029b7d3aab65665dc7be92da9d57f50b1a42b Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Fri, 2 Dec 2005 19:52:06 +0000 Subject: [PATCH] Add mirror dmeventd library --- Makefile.in | 11 +- WHATS_NEW | 3 +- configure | 33 +- configure.in | 21 ++ daemons/dmeventd/plugins/Makefile.in | 22 ++ .../dmeventd/plugins/mirror/.exported_symbols | 3 + daemons/dmeventd/plugins/mirror/Makefile.in | 32 ++ .../dmeventd/plugins/mirror/dmeventd_mirror.c | 311 ++++++++++++++++++ dmeventd/Makefile.in | 22 ++ dmeventd/mirror/.exported_symbols | 3 + dmeventd/mirror/Makefile.in | 32 ++ dmeventd/mirror/dmeventd_mirror.c | 311 ++++++++++++++++++ 12 files changed, 799 insertions(+), 5 deletions(-) create mode 100644 daemons/dmeventd/plugins/Makefile.in create mode 100644 daemons/dmeventd/plugins/mirror/.exported_symbols create mode 100644 daemons/dmeventd/plugins/mirror/Makefile.in create mode 100644 daemons/dmeventd/plugins/mirror/dmeventd_mirror.c create mode 100644 dmeventd/Makefile.in create mode 100644 dmeventd/mirror/.exported_symbols create mode 100644 dmeventd/mirror/Makefile.in create mode 100644 dmeventd/mirror/dmeventd_mirror.c diff --git a/Makefile.in b/Makefile.in index af63fc5d9..bfd26eac3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,8 +24,13 @@ endif SUBDIRS += lib tools daemons +ifeq ("@DMEVENTD@", "yes") + SUBDIRS += dmeventd +endif + ifeq ($(MAKECMDGOALS),distclean) SUBDIRS += daemons/clvmd \ + dmeventd \ lib/format1 \ lib/format_pool \ lib/locking \ @@ -40,13 +45,15 @@ include make.tmpl daemons: lib lib: include tools: lib -po: tools daemons +dmeventd: tools +po: tools daemons dmeventd ifeq ("@INTL@", "yes") lib.pofile: include.pofile tools.pofile: lib.pofile daemons.pofile: lib.pofile -po.pofile: tools.pofile daemons.pofile +dmeventd.pofile: tools.pofile +po.pofile: tools.pofile daemons.pofile dmeventd.pofile pofile: po.pofile endif diff --git a/WHATS_NEW b/WHATS_NEW index dcf9c80bf..3603996ff 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,5 +1,6 @@ -Version 2.02.02 +Version 2.02.02 - ==================================== + Add mirror dmeventd library. Add some activation logic to remove_mirror_images(). lvconvert can remove specified PVs from a mirror. lvconvert turns an existing LV into a mirror. diff --git a/configure b/configure index 53eb5cc2b..e2e88a889 100755 --- a/configure +++ b/configure @@ -310,7 +310,7 @@ ac_includes_default="\ #endif" ac_default_prefix=/usr -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os AWK CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S SET_MAKE RANLIB ac_ct_RANLIB CPP EGREP ALLOCA LIBOBJS POW_LIB MSGFMT MODPROBE_CMD JOBS STATIC_LINK LVM1 POOL SNAPSHOTS MIRRORS OWNER GROUP COPTIMISE_FLAG CLDFLAGS CLDWHOLEARCHIVE CLDNOWHOLEARCHIVE LDDEPS SOFLAG LVM_VERSION LVM1_FALLBACK DEBUG DEVMAPPER HAVE_LIBDL HAVE_SELINUX CMDLIB LOCALEDIR CONFDIR STATICDIR INTL_PACKAGE INTL CLVMD CLUSTER FSADM LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os AWK CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S SET_MAKE RANLIB ac_ct_RANLIB CPP EGREP ALLOCA LIBOBJS POW_LIB MSGFMT MODPROBE_CMD JOBS STATIC_LINK LVM1 POOL SNAPSHOTS MIRRORS OWNER GROUP COPTIMISE_FLAG CLDFLAGS CLDWHOLEARCHIVE CLDNOWHOLEARCHIVE LDDEPS SOFLAG LVM_VERSION LVM1_FALLBACK DEBUG DEVMAPPER HAVE_LIBDL HAVE_SELINUX CMDLIB LOCALEDIR CONFDIR STATICDIR INTL_PACKAGE INTL CLVMD CLUSTER FSADM DMEVENTD LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -858,6 +858,7 @@ Optional Features: --disable-o_direct Disable O_DIRECT --enable-cmdlib Build shared command library --enable-fsadm Enable fsadm + --enable-dmeventd Enable dmeventd --enable-nls Enable Native Language Support Optional Packages: @@ -7428,6 +7429,25 @@ fi; echo "$as_me:$LINENO: result: $FSADM" >&5 echo "${ECHO_T}$FSADM" >&6 +################################################################################ +echo "$as_me:$LINENO: checking whether to use dmeventd" >&5 +echo $ECHO_N "checking whether to use dmeventd... $ECHO_C" >&6 +# Check whether --enable-dmeventd or --disable-dmeventd was given. +if test "${enable_dmeventd+set}" = set; then + enableval="$enable_dmeventd" + DMEVENTD=$enableval +fi; +echo "$as_me:$LINENO: result: $DMEVENTD" >&5 +echo "${ECHO_T}$DMEVENTD" >&6 + +if test x$DMEVENTD = xyes && test x$MIRRORS != xinternal; then +{ { echo "$as_me:$LINENO: error: --enable-dmeventd currently requires --with-mirrors=internal +" >&5 +echo "$as_me: error: --enable-dmeventd currently requires --with-mirrors=internal +" >&2;} + { (exit 1); exit 1; }; } +fi + ################################################################################ if [ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]; then exec_prefix=""; @@ -10853,10 +10873,11 @@ fi + ################################################################################ - ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile doc/Makefile include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/snapshot/Makefile man/Makefile po/Makefile tools/Makefile tools/version.h tools/fsadm/Makefile test/mm/Makefile test/device/Makefile test/format1/Makefile test/regex/Makefile test/filters/Makefile" + ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile dmeventd/Makefile dmeventd/mirror/Makefile doc/Makefile include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/snapshot/Makefile man/Makefile po/Makefile tools/Makefile tools/version.h tools/fsadm/Makefile test/mm/Makefile test/device/Makefile test/format1/Makefile test/regex/Makefile test/filters/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -11412,6 +11433,8 @@ do "make.tmpl" ) CONFIG_FILES="$CONFIG_FILES make.tmpl" ;; "daemons/Makefile" ) CONFIG_FILES="$CONFIG_FILES daemons/Makefile" ;; "daemons/clvmd/Makefile" ) CONFIG_FILES="$CONFIG_FILES daemons/clvmd/Makefile" ;; + "dmeventd/Makefile" ) CONFIG_FILES="$CONFIG_FILES dmeventd/Makefile" ;; + "dmeventd/mirror/Makefile" ) CONFIG_FILES="$CONFIG_FILES dmeventd/mirror/Makefile" ;; "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "include/Makefile" ) CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; "lib/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; @@ -11576,6 +11599,7 @@ s,@INTL@,$INTL,;t t s,@CLVMD@,$CLVMD,;t t s,@CLUSTER@,$CLUSTER,;t t s,@FSADM@,$FSADM,;t t +s,@DMEVENTD@,$DMEVENTD,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF @@ -11855,3 +11879,8 @@ if test x$FSADM == xyes; then { echo "$as_me:$LINENO: WARNING: fsadm support is untested" >&5 echo "$as_me: WARNING: fsadm support is untested" >&2;} fi + +if test x$DMEVENTD == xyes; then + { echo "$as_me:$LINENO: WARNING: dmeventd support is untested" >&5 +echo "$as_me: WARNING: dmeventd support is untested" >&2;} +fi diff --git a/configure.in b/configure.in index 684d03c76..b406e8158 100644 --- a/configure.in +++ b/configure.in @@ -355,6 +355,20 @@ AC_ARG_ENABLE(fsadm, [ --enable-fsadm Enable fsadm], FSADM=$enableval) AC_MSG_RESULT($FSADM) +################################################################################ +dnl -- enable dmeventd handling +AC_MSG_CHECKING(whether to use dmeventd) +AC_ARG_ENABLE(dmeventd, [ --enable-dmeventd Enable the device-mapper event daemon], +DMEVENTD=$enableval) +AC_MSG_RESULT($DMEVENTD) + +dnl -- dmeventd currently requires internal mirror support +if test x$DMEVENTD = xyes && test x$MIRRORS != xinternal; then +AC_MSG_ERROR( +--enable-dmeventd currently requires --with-mirrors=internal +) +fi + ################################################################################ dnl -- Mess with default exec_prefix if [[ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]]; @@ -563,6 +577,7 @@ AC_SUBST(INTL) AC_SUBST(CLVMD) AC_SUBST(CLUSTER) AC_SUBST(FSADM) +AC_SUBST(DMEVENTD) ################################################################################ dnl -- First and last lines should not contain files to generate in order to @@ -572,6 +587,8 @@ Makefile \ make.tmpl \ daemons/Makefile \ daemons/clvmd/Makefile \ +dmeventd/Makefile \ +dmeventd/mirror/Makefile \ doc/Makefile \ include/Makefile \ lib/Makefile \ @@ -599,3 +616,7 @@ fi if test x$FSADM == xyes; then AC_MSG_WARN(fsadm support is untested) fi + +if test x$DMEVENTD == xyes; then + AC_MSG_WARN(dmeventd support is untested) +fi diff --git a/daemons/dmeventd/plugins/Makefile.in b/daemons/dmeventd/plugins/Makefile.in new file mode 100644 index 000000000..9b7062d3f --- /dev/null +++ b/daemons/dmeventd/plugins/Makefile.in @@ -0,0 +1,22 @@ +# +# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +# Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +# +# This file is part of the LVM2. +# +# 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@ + +SUBDIRS += mirror + +include $(top_srcdir)/make.tmpl + diff --git a/daemons/dmeventd/plugins/mirror/.exported_symbols b/daemons/dmeventd/plugins/mirror/.exported_symbols new file mode 100644 index 000000000..b88c70501 --- /dev/null +++ b/daemons/dmeventd/plugins/mirror/.exported_symbols @@ -0,0 +1,3 @@ +process_event +register_device +unregister_device diff --git a/daemons/dmeventd/plugins/mirror/Makefile.in b/daemons/dmeventd/plugins/mirror/Makefile.in new file mode 100644 index 000000000..04386bc98 --- /dev/null +++ b/daemons/dmeventd/plugins/mirror/Makefile.in @@ -0,0 +1,32 @@ +# +# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +# Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +# +# This file is part of the LVM2. +# +# 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@ + +CFLAGS += -I${top_srcdir}/tools -I${top_srcdir}/include +CLDFLAGS += -ldevmapper -llvm2cmd + +SOURCES = dmeventd_mirror.c + +LIB_SHARED = libdevmapper-event-lvm2mirror.so + +include $(top_srcdir)/make.tmpl + +install: libdevmapper-event-lvm2mirror.so + $(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) $< \ + $(libdir)/$<.$(LIB_VERSION) + $(LN_S) -f $(libdir)/$<.$(LIB_VERSION) $(libdir)/$< + diff --git a/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c new file mode 100644 index 000000000..27b8826c9 --- /dev/null +++ b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2005 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * 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 "libdevmapper-event.h" +#include "lvm2cmd.h" +#include "lvm-string.h" + +#include +#include +#include +#include +#include +#include +#include + +/* FIXME Replace syslog with multilog */ +#include + +#define ME_IGNORE 0 +#define ME_INSYNC 1 +#define ME_FAILURE 2 + +static int _get_mirror_event(char *params) +{ + int i, rtn = ME_INSYNC; + int max_args = 30; /* should support at least 8-way mirrors */ + char *args[max_args]; + char *dev_status_str; + char *log_status_str; + char *sync_str; + char *p; + int log_argc, num_devs, num_failures=0; + + if (max_args <= split_words(params, max_args, args)) { + syslog(LOG_ERR, "Unable to split mirror parameters: Arg list too long"); + return -E2BIG; + } + + /* + * Unused: 0 409600 mirror + * Used : 2 253:4 253:5 400/400 1 AA 3 cluster 253:3 A + */ + num_devs = atoi(args[0]); + dev_status_str = args[3 + num_devs]; + log_argc = atoi(args[4 + num_devs]); + log_status_str = args[4 + num_devs + log_argc]; + sync_str = args[1 + num_devs]; + + syslog(LOG_DEBUG, " num_devs = %d", num_devs); + syslog(LOG_DEBUG, " dev_status_str: %s", dev_status_str); + syslog(LOG_DEBUG, " log_argc = %d", log_argc); + syslog(LOG_DEBUG, " log_status_str: %s", log_status_str); + syslog(LOG_DEBUG, " sync_str: %s", sync_str); + + /* Check for bad mirror devices */ + for (i = 0; i < num_devs; i++) { + if (dev_status_str[i] == 'D') { + syslog(LOG_ERR, "Mirror device, %s, has failed.\n", args[i+1]); + num_failures++; + } + } + + /* Check for bad log device */ + if (log_status_str[0] == 'D') { + syslog(LOG_ERR, "Log device, %s, has failed.\n", + args[3 + num_devs + log_argc]); + num_failures++; + } + + if (num_failures) { + rtn = ME_FAILURE; + goto out; + } + + for(p = strstr(sync_str, "/"), i = (p - sync_str) - 1; + p && (i >= 0); + i--) { + syslog(LOG_DEBUG, "p[%d] (%c) ?= sync_str[%d] (%c)", + i+1, p[i+1], i, sync_str[i]); + if(p[i+1] != sync_str[i]){ + rtn = ME_IGNORE; + break; + } + } + + out: + return rtn; +} + +/* dmname_to_lvmname + * @dmname: the device mapper name + * @vg: the returned vg name + * @lv: the returned lv name + * + * Caller must free strings when finished + * + * Returns: 0, -ENOMEM, -EINVAL + */ + +/* FIXME Replace with split_dm_name */ + +static int _dmname_to_lvmname(char *dmname, char **vg, char **lv) +{ + int rtn = 0; + char *ptr1, *ptr2; + // syslog(LOG_DEBUG, "Entering dmname_to_lvmname\n"); + + for((ptr1 = dmname); (ptr2 = strstr(ptr1, "/")); ptr2++, (ptr1 = ptr2)); + for(; (ptr2 = strstr(ptr1, "-")) && (ptr2[1] == '-'); ptr2+=2, ptr1 = ptr2); + + if (!ptr2) + return -EINVAL; + + ptr2[0] = '\0'; + if(!(*vg = strdup(ptr1))) { + rtn = -ENOMEM; + goto fail; + } + + ptr1 = ptr2+1; + if(!(*lv = strdup(ptr1))) { + rtn = -ENOMEM; + free(*vg); + } + +fail: + ptr2[0] = '-'; + + // syslog(LOG_DEBUG, "Exiting dmname_to_lvmname\n"); + return rtn; +} + +static void _temporary_log_fn(int level, const char *file, int line, const char *format) +{ + syslog(LOG_DEBUG, "%s", format); +} + +static int _remove_failed_devices(const char *device) +{ + int r; + void *handle; + int cmd_size = 256; /* FIXME Use system restriction */ + char cmd_str[cmd_size]; + char *vg = NULL, *lv = NULL; + + // syslog(LOG_DEBUG, "Entering remove_failed_device\n"); + if (strlen(device) > 200) + return -ENAMETOOLONG; + + if ((r = _dmname_to_lvmname(device, &vg, &lv))) { + syslog(LOG_ERR, + "Unable to determine LVM name for %s", + device); + return r; + } + + /* FIXME Is any sanity-checking required on %s? */ + syslog(LOG_INFO, "vgreduce --removemissing %s\n", vg); + if (cmd_size <= snprintf(cmd_str, cmd_size, "vgreduce --removemissing %s", vg)) { + /* this error should be caught above, but doesn't hurt to check again */ + syslog(LOG_ERR, "Unable to form LVM command: Device name too long"); + return -ENAMETOOLONG; + } + + lvm2_log_fn(_temporary_log_fn); + handle = lvm2_init(); + lvm2_log_level(handle, 1); + r = lvm2_run(handle, cmd_str); + + syslog(LOG_INFO, "lvconvert -m0 %s/%s\n", vg, lv); + if (cmd_size <= snprintf(cmd_str, cmd_size, "lvconvert -m0 %s/%s\n", vg, lv)) { + /* this error should be caught above, but doesn't hurt to check again */ + syslog(LOG_ERR, "Unable to form LVM command: Device name too long"); + return -ENAMETOOLONG; + } + r = lvm2_run(handle, cmd_str); + + // syslog(LOG_DEBUG, "Exiting remove_failed_device\n"); + + free(vg); + free(lv); + return (r == 1)? 0: -1; +} + +void process_event(const char *device, enum dm_event_type event) +{ + int pid; + struct dm_task *dmt; + void *next = NULL; + uint64_t start, length; + char *target_type = NULL; + char *params; + + /* FIXME Too late to fork here under low memory! */ + /* FIXME This should run within a dmeventd thread instead. */ + pid = fork(); + if (pid > 0) + return; + + /* + * Fork twice to prevent the process from + * becoming a zombie after exiting. + */ + pid = fork(); + if(pid > 0) { + exit(EXIT_SUCCESS); + } else if (pid < 0) { + openlog("dmeventd (mirror_dso)", LOG_PID, LOG_DAEMON); + syslog(LOG_ERR, "Fork new process to handle device failure"); + syslog(LOG_ERR, "Handling failure anyway (a harmless zombie process will result)"); + } else { + setsid(); + openlog("dmeventd (mirror_dso)", LOG_PID, LOG_DAEMON); + } + + syslog(LOG_NOTICE, "An event occurred on %s\n", device); + + /* FIXME Move inside libdevmapper */ + if (!(dmt = dm_task_create(DM_DEVICE_STATUS))) { + syslog(LOG_ERR, "Unable to create dm_task.\n"); + goto fail; + } + + if (!dm_task_set_name(dmt, device)) { + syslog(LOG_ERR, "Unable to set device name.\n"); + goto fail; + } + + if (!dm_task_run(dmt)) { + syslog(LOG_ERR, "Unable to run task.\n"); + goto fail; + } + + do { + next = dm_get_next_target(dmt, next, &start, &length, + &target_type, ¶ms); + + if (strcmp(target_type, "mirror")) { + syslog(LOG_INFO, "%s has unmirrored portion.\n", device); + continue; + } + + switch(_get_mirror_event(params)) { + case ME_INSYNC: + /* FIXME: all we really know is that this + _part_ of the device is in sync + Also, this is not an error + */ + syslog(LOG_NOTICE, "%s is now in-sync\n", device); + break; + case ME_FAILURE: + syslog(LOG_ERR, "Device failure in %s\n", device); + if (_remove_failed_devices(device)) + syslog(LOG_ERR, "Failed to remove faulty devices in %s\n", + device); + else + syslog(LOG_NOTICE, "%s is now a linear device.\n", + device); + break; + case ME_IGNORE: + break; + default: + syslog(LOG_INFO, "Unknown event received.\n"); + } + } while (next); + + fail: + if (dmt) + dm_task_destroy(dmt); + + exit(0); +} + +int register_device(const char *device) +{ + syslog(LOG_INFO, "[%s] %s(%d) - Device: %s\n", + __FILE__, __func__, __LINE__, device); + + return 1; +} + +int unregister_device(const char *device) +{ + syslog(LOG_INFO, "[%s] %s(%d) - Device: %s\n", + __FILE__, __func__, __LINE__, device); + + 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/dmeventd/Makefile.in b/dmeventd/Makefile.in new file mode 100644 index 000000000..9b7062d3f --- /dev/null +++ b/dmeventd/Makefile.in @@ -0,0 +1,22 @@ +# +# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +# Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +# +# This file is part of the LVM2. +# +# 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@ + +SUBDIRS += mirror + +include $(top_srcdir)/make.tmpl + diff --git a/dmeventd/mirror/.exported_symbols b/dmeventd/mirror/.exported_symbols new file mode 100644 index 000000000..b88c70501 --- /dev/null +++ b/dmeventd/mirror/.exported_symbols @@ -0,0 +1,3 @@ +process_event +register_device +unregister_device diff --git a/dmeventd/mirror/Makefile.in b/dmeventd/mirror/Makefile.in new file mode 100644 index 000000000..04386bc98 --- /dev/null +++ b/dmeventd/mirror/Makefile.in @@ -0,0 +1,32 @@ +# +# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +# Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. +# +# This file is part of the LVM2. +# +# 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@ + +CFLAGS += -I${top_srcdir}/tools -I${top_srcdir}/include +CLDFLAGS += -ldevmapper -llvm2cmd + +SOURCES = dmeventd_mirror.c + +LIB_SHARED = libdevmapper-event-lvm2mirror.so + +include $(top_srcdir)/make.tmpl + +install: libdevmapper-event-lvm2mirror.so + $(INSTALL) -D $(OWNER) $(GROUP) -m 555 $(STRIP) $< \ + $(libdir)/$<.$(LIB_VERSION) + $(LN_S) -f $(libdir)/$<.$(LIB_VERSION) $(libdir)/$< + diff --git a/dmeventd/mirror/dmeventd_mirror.c b/dmeventd/mirror/dmeventd_mirror.c new file mode 100644 index 000000000..27b8826c9 --- /dev/null +++ b/dmeventd/mirror/dmeventd_mirror.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2005 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * 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 "libdevmapper-event.h" +#include "lvm2cmd.h" +#include "lvm-string.h" + +#include +#include +#include +#include +#include +#include +#include + +/* FIXME Replace syslog with multilog */ +#include + +#define ME_IGNORE 0 +#define ME_INSYNC 1 +#define ME_FAILURE 2 + +static int _get_mirror_event(char *params) +{ + int i, rtn = ME_INSYNC; + int max_args = 30; /* should support at least 8-way mirrors */ + char *args[max_args]; + char *dev_status_str; + char *log_status_str; + char *sync_str; + char *p; + int log_argc, num_devs, num_failures=0; + + if (max_args <= split_words(params, max_args, args)) { + syslog(LOG_ERR, "Unable to split mirror parameters: Arg list too long"); + return -E2BIG; + } + + /* + * Unused: 0 409600 mirror + * Used : 2 253:4 253:5 400/400 1 AA 3 cluster 253:3 A + */ + num_devs = atoi(args[0]); + dev_status_str = args[3 + num_devs]; + log_argc = atoi(args[4 + num_devs]); + log_status_str = args[4 + num_devs + log_argc]; + sync_str = args[1 + num_devs]; + + syslog(LOG_DEBUG, " num_devs = %d", num_devs); + syslog(LOG_DEBUG, " dev_status_str: %s", dev_status_str); + syslog(LOG_DEBUG, " log_argc = %d", log_argc); + syslog(LOG_DEBUG, " log_status_str: %s", log_status_str); + syslog(LOG_DEBUG, " sync_str: %s", sync_str); + + /* Check for bad mirror devices */ + for (i = 0; i < num_devs; i++) { + if (dev_status_str[i] == 'D') { + syslog(LOG_ERR, "Mirror device, %s, has failed.\n", args[i+1]); + num_failures++; + } + } + + /* Check for bad log device */ + if (log_status_str[0] == 'D') { + syslog(LOG_ERR, "Log device, %s, has failed.\n", + args[3 + num_devs + log_argc]); + num_failures++; + } + + if (num_failures) { + rtn = ME_FAILURE; + goto out; + } + + for(p = strstr(sync_str, "/"), i = (p - sync_str) - 1; + p && (i >= 0); + i--) { + syslog(LOG_DEBUG, "p[%d] (%c) ?= sync_str[%d] (%c)", + i+1, p[i+1], i, sync_str[i]); + if(p[i+1] != sync_str[i]){ + rtn = ME_IGNORE; + break; + } + } + + out: + return rtn; +} + +/* dmname_to_lvmname + * @dmname: the device mapper name + * @vg: the returned vg name + * @lv: the returned lv name + * + * Caller must free strings when finished + * + * Returns: 0, -ENOMEM, -EINVAL + */ + +/* FIXME Replace with split_dm_name */ + +static int _dmname_to_lvmname(char *dmname, char **vg, char **lv) +{ + int rtn = 0; + char *ptr1, *ptr2; + // syslog(LOG_DEBUG, "Entering dmname_to_lvmname\n"); + + for((ptr1 = dmname); (ptr2 = strstr(ptr1, "/")); ptr2++, (ptr1 = ptr2)); + for(; (ptr2 = strstr(ptr1, "-")) && (ptr2[1] == '-'); ptr2+=2, ptr1 = ptr2); + + if (!ptr2) + return -EINVAL; + + ptr2[0] = '\0'; + if(!(*vg = strdup(ptr1))) { + rtn = -ENOMEM; + goto fail; + } + + ptr1 = ptr2+1; + if(!(*lv = strdup(ptr1))) { + rtn = -ENOMEM; + free(*vg); + } + +fail: + ptr2[0] = '-'; + + // syslog(LOG_DEBUG, "Exiting dmname_to_lvmname\n"); + return rtn; +} + +static void _temporary_log_fn(int level, const char *file, int line, const char *format) +{ + syslog(LOG_DEBUG, "%s", format); +} + +static int _remove_failed_devices(const char *device) +{ + int r; + void *handle; + int cmd_size = 256; /* FIXME Use system restriction */ + char cmd_str[cmd_size]; + char *vg = NULL, *lv = NULL; + + // syslog(LOG_DEBUG, "Entering remove_failed_device\n"); + if (strlen(device) > 200) + return -ENAMETOOLONG; + + if ((r = _dmname_to_lvmname(device, &vg, &lv))) { + syslog(LOG_ERR, + "Unable to determine LVM name for %s", + device); + return r; + } + + /* FIXME Is any sanity-checking required on %s? */ + syslog(LOG_INFO, "vgreduce --removemissing %s\n", vg); + if (cmd_size <= snprintf(cmd_str, cmd_size, "vgreduce --removemissing %s", vg)) { + /* this error should be caught above, but doesn't hurt to check again */ + syslog(LOG_ERR, "Unable to form LVM command: Device name too long"); + return -ENAMETOOLONG; + } + + lvm2_log_fn(_temporary_log_fn); + handle = lvm2_init(); + lvm2_log_level(handle, 1); + r = lvm2_run(handle, cmd_str); + + syslog(LOG_INFO, "lvconvert -m0 %s/%s\n", vg, lv); + if (cmd_size <= snprintf(cmd_str, cmd_size, "lvconvert -m0 %s/%s\n", vg, lv)) { + /* this error should be caught above, but doesn't hurt to check again */ + syslog(LOG_ERR, "Unable to form LVM command: Device name too long"); + return -ENAMETOOLONG; + } + r = lvm2_run(handle, cmd_str); + + // syslog(LOG_DEBUG, "Exiting remove_failed_device\n"); + + free(vg); + free(lv); + return (r == 1)? 0: -1; +} + +void process_event(const char *device, enum dm_event_type event) +{ + int pid; + struct dm_task *dmt; + void *next = NULL; + uint64_t start, length; + char *target_type = NULL; + char *params; + + /* FIXME Too late to fork here under low memory! */ + /* FIXME This should run within a dmeventd thread instead. */ + pid = fork(); + if (pid > 0) + return; + + /* + * Fork twice to prevent the process from + * becoming a zombie after exiting. + */ + pid = fork(); + if(pid > 0) { + exit(EXIT_SUCCESS); + } else if (pid < 0) { + openlog("dmeventd (mirror_dso)", LOG_PID, LOG_DAEMON); + syslog(LOG_ERR, "Fork new process to handle device failure"); + syslog(LOG_ERR, "Handling failure anyway (a harmless zombie process will result)"); + } else { + setsid(); + openlog("dmeventd (mirror_dso)", LOG_PID, LOG_DAEMON); + } + + syslog(LOG_NOTICE, "An event occurred on %s\n", device); + + /* FIXME Move inside libdevmapper */ + if (!(dmt = dm_task_create(DM_DEVICE_STATUS))) { + syslog(LOG_ERR, "Unable to create dm_task.\n"); + goto fail; + } + + if (!dm_task_set_name(dmt, device)) { + syslog(LOG_ERR, "Unable to set device name.\n"); + goto fail; + } + + if (!dm_task_run(dmt)) { + syslog(LOG_ERR, "Unable to run task.\n"); + goto fail; + } + + do { + next = dm_get_next_target(dmt, next, &start, &length, + &target_type, ¶ms); + + if (strcmp(target_type, "mirror")) { + syslog(LOG_INFO, "%s has unmirrored portion.\n", device); + continue; + } + + switch(_get_mirror_event(params)) { + case ME_INSYNC: + /* FIXME: all we really know is that this + _part_ of the device is in sync + Also, this is not an error + */ + syslog(LOG_NOTICE, "%s is now in-sync\n", device); + break; + case ME_FAILURE: + syslog(LOG_ERR, "Device failure in %s\n", device); + if (_remove_failed_devices(device)) + syslog(LOG_ERR, "Failed to remove faulty devices in %s\n", + device); + else + syslog(LOG_NOTICE, "%s is now a linear device.\n", + device); + break; + case ME_IGNORE: + break; + default: + syslog(LOG_INFO, "Unknown event received.\n"); + } + } while (next); + + fail: + if (dmt) + dm_task_destroy(dmt); + + exit(0); +} + +int register_device(const char *device) +{ + syslog(LOG_INFO, "[%s] %s(%d) - Device: %s\n", + __FILE__, __func__, __LINE__, device); + + return 1; +} + +int unregister_device(const char *device) +{ + syslog(LOG_INFO, "[%s] %s(%d) - Device: %s\n", + __FILE__, __func__, __LINE__, device); + + 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: + */