r408 - in devmapper/trunk: . dmeventd dmsetup include lib lib/datastruct lib/ioctl lib/mm man po

Bastian Blank waldi at alioth.debian.org
Sat Apr 7 09:09:51 UTC 2007


Author: waldi
Date: Sat Apr  7 09:09:48 2007
New Revision: 408

Added:
   devmapper/trunk/lib/libdm-common.h
   devmapper/trunk/lib/libdm-report.c
Removed:
   devmapper/trunk/lib/libdm-common.h.in
Modified:
   devmapper/trunk/   (props changed)
   devmapper/trunk/VERSION
   devmapper/trunk/WHATS_NEW
   devmapper/trunk/configure
   devmapper/trunk/configure.in
   devmapper/trunk/dmeventd/.exported_symbols
   devmapper/trunk/dmeventd/Makefile.in
   devmapper/trunk/dmeventd/dmeventd.c
   devmapper/trunk/dmeventd/dmeventd.h
   devmapper/trunk/dmeventd/libdevmapper-event.c
   devmapper/trunk/dmeventd/libdevmapper-event.h
   devmapper/trunk/dmsetup/Makefile.in
   devmapper/trunk/dmsetup/dmsetup.c
   devmapper/trunk/include/configure.h.in
   devmapper/trunk/lib/.exported_symbols
   devmapper/trunk/lib/Makefile.in
   devmapper/trunk/lib/datastruct/hash.c
   devmapper/trunk/lib/ioctl/libdm-iface.c
   devmapper/trunk/lib/libdevmapper.h
   devmapper/trunk/lib/libdm-common.c
   devmapper/trunk/lib/libdm-deptree.c
   devmapper/trunk/lib/libdm-string.c
   devmapper/trunk/lib/mm/dbg_malloc.c
   devmapper/trunk/make.tmpl.in
   devmapper/trunk/man/dmsetup.8
   devmapper/trunk/po/device-mapper.po
Log:
Merge /devmapper/upstream/current (1.02.18).


Modified: devmapper/trunk/VERSION
==============================================================================
--- devmapper/trunk/VERSION	(original)
+++ devmapper/trunk/VERSION	Sat Apr  7 09:09:48 2007
@@ -1 +1 @@
-1.02.12 (2006-10-13)
+1.02.18 (2007-02-13)

Modified: devmapper/trunk/WHATS_NEW
==============================================================================
--- devmapper/trunk/WHATS_NEW	(original)
+++ devmapper/trunk/WHATS_NEW	Sat Apr  7 09:09:48 2007
@@ -1,3 +1,53 @@
+Version 1.02.18 - 13th February 2007
+====================================
+  Improve dmeventd messaging protocol: drain pipe and tag messages.
+
+Version 1.02.17 - 29th January 2007
+===================================
+  Add recent reporting options to dmsetup man page.
+  Revise some report fields names.
+  Add dmsetup 'help' command and update usage text.
+  Use fixed-size fields in report interface and reorder.
+
+Version 1.02.16 - 25th January 2007
+===================================
+  Add some missing close() and fclose() return value checks.
+  Migrate dmsetup column-based output over to new libdevmapper report framework.
+  Add descriptions to reporting field definitions.
+  Add a dso-private variable to dmeventd dso interface.
+  Add dm_event_handler_[gs]et_timeout functions.
+  Streamline dm_report_field_* interface.
+  Add cmdline debug & version options to dmeventd.
+  Add DM_LIB_VERSION definition to configure.h.
+  Suppress 'Unrecognised field' error if report field is 'help'.
+  Add --separator and --sort to dmsetup (unused).
+  Make alignment flag optional when specifying report fields.
+
+Version 1.02.15 - 17th January 2007
+===================================
+  Add basic reporting functions to libdevmapper.
+  Fix a malloc error path in dmsetup message.
+  More libdevmapper-event interface changes and fixes.
+  Rename dm_saprintf() to dm_asprintf().
+  Report error if NULL pointer is supplied to dm_strdup_aux().
+  Reinstate dm_event_get_registered_device.
+
+Version 1.02.14 - 11th January 2007
+===================================
+  Add dm_saprintf().
+  Use CFLAGS when linking so mixed sparc builds can supply -m64.
+  Add dm_tree_use_no_flush_suspend().
+  Lots of dmevent changes including revised interface.
+  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
+=============================
+  Update dmsetup man page (setgeometry & message).
+  Fix dmsetup free after getline with debug.
+  Suppress encryption key in 'dmsetup table' output unless --showkeys supplied.
+
 Version 1.02.12 - 13 Oct 2006
 =============================
   Avoid deptree attempting to suspend a device that's already suspended.

Modified: devmapper/trunk/configure
==============================================================================
--- devmapper/trunk/configure	(original)
+++ devmapper/trunk/configure	Sat Apr  7 09:09:48 2007
@@ -865,12 +865,14 @@
   --with-device-uid=UID   Set the owner used for new device nodes [UID=0]
   --with-device-gid=UID   Set the group used for new device nodes [GID=0]
   --with-device-mode=MODE Set the mode used for new device nodes [MODE=0600]
-  --with-optimisation=OPT C optimisation flag OPT=-O2
-  --with-localedir=DIR    Translation files in DIR PREFIX/share/locale
+  --with-optimisation=OPT C optimisation flag [OPT=-O2]
+  --with-localedir=DIR    Translation files in DIR [PREFIX/share/locale]
   --with-kernel-dir=DIR   linux kernel source in DIR
   --with-kernel-version=VERSION   linux kernel version
-  --with-tmp-dir=DIR      temp directory to make kernel patches /tmp/kerndiff
-  --with-interface=IFACE  Choose kernel interface (ioctl or fs) ioctl
+  --with-tmp-dir=DIR      temp dir to make kernel patches [/tmp/kerndiff]
+  --with-interface=IFACE  Choose kernel interface (ioctl or fs) [ioctl]
+  --with-dmeventd-pidfile=PATH    dmeventd pidfile [/var/run/dmeventd.pid]
+  --with-dmeventd-path=PATH       dmeventd path [${exec_prefix}/sbin/dmeventd]
 
 Some influential environment variables:
   CC          C compiler command
@@ -6036,6 +6038,56 @@
 fi
 
 
+cat >>confdefs.h <<_ACEOF
+#define DM_LIB_VERSION $DM_LIB_VERSION
+_ACEOF
+
+
+################################################################################
+
+
+if test "$DMEVENTD" = yes; then
+
+# Check whether --with-dmeventd-pidfile or --without-dmeventd-pidfile was given.
+if test "${with_dmeventd_pidfile+set}" = set; then
+  withval="$with_dmeventd_pidfile"
+   cat >>confdefs.h <<_ACEOF
+#define DMEVENTD_PIDFILE "$withval"
+_ACEOF
+
+else
+   cat >>confdefs.h <<_ACEOF
+#define DMEVENTD_PIDFILE "/var/run/dmeventd.pid"
+_ACEOF
+
+fi;
+fi
+
+
+
+if test "$DMEVENTD" = yes; then
+        dmeventd_prefix="$exec_prefix"
+        if test "x$dmeventd_prefix" = "xNONE"; then
+                dmeventd_prefix="$prefix"
+        fi
+        if test "x$dmeventd_prefix" = "xNONE"; then
+                dmeventd_prefix=""
+        fi
+
+# Check whether --with-dmeventd-path or --without-dmeventd-path was given.
+if test "${with_dmeventd_path+set}" = set; then
+  withval="$with_dmeventd_path"
+   cat >>confdefs.h <<_ACEOF
+#define DMEVENTD_PATH "$withval"
+_ACEOF
+
+else
+   cat >>confdefs.h <<_ACEOF
+#define DMEVENTD_PATH "$dmeventd_pref/sbin/dmeventd"
+_ACEOF
+
+fi;
+fi
 ################################################################################
 
 
@@ -6068,7 +6120,7 @@
 
 
 ################################################################################
-                                                                                                                        ac_config_files="$ac_config_files Makefile make.tmpl include/Makefile dmsetup/Makefile lib/Makefile lib/libdevmapper.pc lib/libdm-common.h dmeventd/Makefile dmeventd/libdevmapper-event.pc kernel/Makefile man/Makefile po/Makefile"
+                                                                                                              ac_config_files="$ac_config_files Makefile make.tmpl include/Makefile dmsetup/Makefile lib/Makefile lib/libdevmapper.pc dmeventd/Makefile dmeventd/libdevmapper-event.pc kernel/Makefile man/Makefile po/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
@@ -6600,7 +6652,6 @@
   "dmsetup/Makefile" ) CONFIG_FILES="$CONFIG_FILES dmsetup/Makefile" ;;
   "lib/Makefile" ) CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;;
   "lib/libdevmapper.pc" ) CONFIG_FILES="$CONFIG_FILES lib/libdevmapper.pc" ;;
-  "lib/libdm-common.h" ) CONFIG_FILES="$CONFIG_FILES lib/libdm-common.h" ;;
   "dmeventd/Makefile" ) CONFIG_FILES="$CONFIG_FILES dmeventd/Makefile" ;;
   "dmeventd/libdevmapper-event.pc" ) CONFIG_FILES="$CONFIG_FILES dmeventd/libdevmapper-event.pc" ;;
   "kernel/Makefile" ) CONFIG_FILES="$CONFIG_FILES kernel/Makefile" ;;

Modified: devmapper/trunk/configure.in
==============================================================================
--- devmapper/trunk/configure.in	(original)
+++ devmapper/trunk/configure.in	Sat Apr  7 09:09:48 2007
@@ -168,7 +168,7 @@
 dnl -- Override optimisation
 AC_MSG_CHECKING(for C optimisation flag)
 AC_ARG_WITH(optimisation,
-  [  --with-optimisation=OPT C optimisation flag [OPT=-O2] ],
+  [  --with-optimisation=OPT C optimisation flag [[OPT=-O2]] ],
   [ COPTIMISE_FLAG="$withval" ])
 AC_MSG_RESULT($COPTIMISE_FLAG)
 
@@ -262,7 +262,7 @@
 	fi;
 
 	AC_ARG_WITH(localedir,
-  		    [  --with-localedir=DIR    Translation files in DIR [PREFIX/share/locale]],
+  		    [  --with-localedir=DIR    Translation files in DIR [[PREFIX/share/locale]] ],
   		    [ LOCALEDIR="$withval" ],
   		    [ LOCALEDIR='${prefix}/share/locale' ])
 fi
@@ -328,7 +328,7 @@
 ################################################################################
 dnl -- Temporary directory for kernel diffs
 AC_ARG_WITH(tmp-dir,
-  [  --with-tmp-dir=DIR      temp directory to make kernel patches [/tmp/kerndiff]],
+  [  --with-tmp-dir=DIR      temp dir to make kernel patches [[/tmp/kerndiff]] ],
   [ tmpdir="$withval" ],
   [ tmpdir=/tmp/kerndiff ])
 if test "${with_tmp_dir+set}" = set; then
@@ -342,7 +342,7 @@
 dnl -- which kernel interface to use (ioctl or fs)
 AC_MSG_CHECKING(for kernel interface choice)
 AC_ARG_WITH(interface,
-  [  --with-interface=IFACE  Choose kernel interface (ioctl or fs) [ioctl]],
+  [  --with-interface=IFACE  Choose kernel interface (ioctl or fs) [[ioctl]] ],
   [ interface="$withval" ],
   [ interface=ioctl ])
 if [[ "x$interface" != xfs -a "x$interface" != xioctl ]];
@@ -357,8 +357,33 @@
   DM_LIB_VERSION="Unknown version ($interface)"
 fi
 
+AC_DEFINE_UNQUOTED(DM_LIB_VERSION, $DM_LIB_VERSION, [Library version])
 
 ################################################################################
+dnl -- dmeventd pidfile and executable path
+AH_TEMPLATE(DMEVENTD_PIDFILE, [Path to dmeventd pidfile.])
+if test "$DMEVENTD" = yes; then
+	AC_ARG_WITH(dmeventd-pidfile,
+  		    [  --with-dmeventd-pidfile=PATH    dmeventd pidfile [[/var/run/dmeventd.pid]] ],
+  		    [ AC_DEFINE_UNQUOTED(DMEVENTD_PIDFILE,"$withval") ],
+  		    [ AC_DEFINE_UNQUOTED(DMEVENTD_PIDFILE,"/var/run/dmeventd.pid") ])
+fi
+
+AH_TEMPLATE(DMEVENTD_PATH, [Path to dmeventd binary.])
+if test "$DMEVENTD" = yes; then
+        dmeventd_prefix="$exec_prefix"
+        if test "x$dmeventd_prefix" = "xNONE"; then
+                dmeventd_prefix="$prefix"
+        fi
+        if test "x$dmeventd_prefix" = "xNONE"; then
+                dmeventd_prefix=""
+        fi
+	AC_ARG_WITH(dmeventd-path,
+  		    [  --with-dmeventd-path=PATH       dmeventd path [[${exec_prefix}/sbin/dmeventd]] ],
+  		    [ AC_DEFINE_UNQUOTED(DMEVENTD_PATH,"$withval") ],
+  		    [ AC_DEFINE_UNQUOTED(DMEVENTD_PATH,"$dmeventd_pref/sbin/dmeventd") ])
+fi
+################################################################################
 AC_SUBST(usrlibdir)
 AC_SUBST(JOBS)
 AC_SUBST(STATIC_LINK)
@@ -399,7 +424,6 @@
 dmsetup/Makefile							\
 lib/Makefile							 	\
 lib/libdevmapper.pc							\
-lib/libdm-common.h						 	\
 dmeventd/Makefile						 	\
 dmeventd/libdevmapper-event.pc						\
 kernel/Makefile								\

Modified: devmapper/trunk/dmeventd/.exported_symbols
==============================================================================
--- devmapper/trunk/dmeventd/.exported_symbols	(original)
+++ devmapper/trunk/dmeventd/.exported_symbols	Sat Apr  7 09:09:48 2007
@@ -1,5 +1,19 @@
-dm_event_register
-dm_event_unregister
+dm_event_handler_create
+dm_event_handler_destroy
+dm_event_handler_set_dso
+dm_event_handler_set_dev_name
+dm_event_handler_set_uuid
+dm_event_handler_set_major
+dm_event_handler_set_minor
+dm_event_handler_set_event_mask
+dm_event_handler_get_dso
+dm_event_handler_get_devname
+dm_event_handler_get_uuid
+dm_event_handler_get_major
+dm_event_handler_get_minor
+dm_event_handler_get_event_mask
+dm_event_register_handler
+dm_event_unregister_handler
 dm_event_get_registered_device
-dm_event_set_timeout
-dm_event_get_timeout
+dm_event_handler_set_timeout
+dm_event_handler_get_timeout

Modified: devmapper/trunk/dmeventd/Makefile.in
==============================================================================
--- devmapper/trunk/dmeventd/Makefile.in	(original)
+++ devmapper/trunk/dmeventd/Makefile.in	Sat Apr  7 09:09:48 2007
@@ -15,8 +15,7 @@
 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 @@
   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 $(CFLAGS) $(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 @@
   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 @@
 	$(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

Modified: devmapper/trunk/dmeventd/dmeventd.c
==============================================================================
--- devmapper/trunk/dmeventd/dmeventd.c	(original)
+++ devmapper/trunk/dmeventd/dmeventd.c	Sat Apr  7 09:09:48 2007
@@ -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.
  *
@@ -19,6 +19,7 @@
 #define _GNU_SOURCE
 #define _FILE_OFFSET_BITS 64
 
+#include "configure.h"
 #include "libdevmapper.h"
 #include "libdevmapper-event.h"
 #include "list.h"
@@ -28,29 +29,37 @@
 
 #include <dlfcn.h>
 #include <errno.h>
-#include <fcntl.h>
-#include <libgen.h>
 #include <pthread.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 #include <sys/file.h>
-#include <sys/mman.h>
-#include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
 #include <unistd.h>
-#include <stdarg.h>
+#include <arpa/inet.h>		/* for htonl, ntohl */
 
 #ifdef linux
-#include <malloc.h>
+#  include <malloc.h>
+
+#  define OOM_ADJ_FILE "/proc/self/oom_adj"
+
+/* From linux/oom.h */
+#  define OOM_DISABLE (-17)
+#  define OOM_ADJUST_MIN (-16)
+
 #endif
 
+/* FIXME We use syslog for now, because multilog is not yet implemented */
+#include <syslog.h>
+
+static volatile sig_atomic_t _exit_now = 0;	/* set to '1' when signal is given to exit */
+static volatile sig_atomic_t _thread_registries_empty = 1;	/* registries are empty initially */
+static int _debug = 0;
+
 /* 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)
@@ -58,26 +67,59 @@
 
 #define DAEMON_NAME "dmeventd"
 
-/* Global mutex for list accesses. */
-static pthread_mutex_t mutex;
+/*
+  Global mutex for thread list access. Has to be held when:
+  - iterating thread list
+  - adding or removing elements from thread list
+  - changing or reading thread_status's fields:
+    processing, status, events
+  Use _lock_mutex() and _unlock_mutex() to hold/release it
+*/
+static pthread_mutex_t _global_mutex;
+
+/*
+  There are three states a thread can attain (see struct
+  thread_status, field int status):
+
+  - DM_THREAD_RUNNING: thread has started up and is either working or
+  waiting for events... transitions to either SHUTDOWN or DONE
+  - DM_THREAD_SHUTDOWN: thread is still doing something, but it is
+  supposed to terminate (and transition to DONE) as soon as it
+  finishes whatever it was doing at the point of flipping state to
+  SHUTDOWN... the thread is still on the thread list
+  - DM_THREAD_DONE: thread has terminated and has been moved over to
+  unused thread list, cleanup pending
+ */
+#define DM_THREAD_RUNNING  0
+#define DM_THREAD_SHUTDOWN 1
+#define DM_THREAD_DONE     2
+
+#define THREAD_STACK_SIZE (300*1024)
+
+#define DEBUGLOG(fmt, args...) _debuglog(fmt, ## args)
 
 /* Data kept about a DSO. */
 struct dso_data {
 	struct list list;
 
-	char *dso_name; /* DSO name (eg, "evms", "dmraid", "lvm2"). */
+	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. */
+	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.
+	 * 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.
+	 *
+	 * This function gets a dm_task that is a result of
+	 * DM_DEVICE_WAITEVENT ioctl (results equivalent to
+	 * DM_DEVICE_STATUS). It should not destroy it.
+	 * The caller must dispose of the task.
 	 */
-	void (*process_event)(const char *device, enum dm_event_type event);
+	void (*process_event)(struct dm_task *dmt, enum dm_event_mask event, void **user);
 
 	/*
 	 * Device registration.
@@ -87,7 +129,8 @@
 	 * the process_event() function is sane (eg, read metadata
 	 * and activate a mapping).
 	 */
-	int (*register_device)(const char *device);
+	int (*register_device)(const char *device, const char *uuid, int major,
+			       int minor, void **user);
 
 	/*
 	 * Device unregistration.
@@ -96,17 +139,19 @@
 	 * for events, the DSO can recognize this and carry out appropriate
 	 * steps (eg, deactivate mapping, metadata update).
 	 */
-	int (*unregister_device)(const char *device);
+	int (*unregister_device)(const char *device, const char *uuid,
+				 int major, int minor, void **user);
 };
-static LIST_INIT(dso_registry);
+static LIST_INIT(_dso_registry);
 
 /* Structure to keep parsed register variables from client message. */
 struct message_data {
+	char *id;
 	char *dso_name;		/* Name of DSO. */
-	char *device_path;	/* Mapped device path. */
+	char *device_uuid;	/* Mapped device path. */
 	union {
 		char *str;	/* Events string as fetched from message. */
-		enum dm_event_type field;	/* Events bitfield. */
+		enum dm_event_mask field;	/* Events bitfield. */
 	} events;
 	union {
 		char *str;
@@ -122,95 +167,131 @@
  * 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 */
-	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. */
+	struct dso_data *dso_data;	/* DSO this thread accesses. */
+
+	struct {
+		char *uuid;
+		char *name;
+		int major, minor;
+	} device;
+	uint32_t event_nr;	/* event number */
+	int processing;		/* Set when event is being processed */
+
+	int status;		/* see DM_THREAD_{RUNNING,SHUTDOWN,DONE}
+				   constants above */
+	enum dm_event_mask events;	/* bitfield for event filter. */
+	enum dm_event_mask current_events;	/* bitfield for occured events. */
+	struct dm_task *current_task;
 	time_t next_time;
 	uint32_t timeout;
 	struct list timeout_list;
+	void *dso_private; /* dso per-thread status variable */
 };
-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 LIST_INIT(_timeout_registry);
+static pthread_mutex_t _timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t _timeout_cond = PTHREAD_COND_INITIALIZER;
+
+static void _debuglog(const char *fmt, ...)
+{
+        time_t P;
+        va_list ap;
+ 
+        if (!_debug)
+                return;
+ 
+        va_start(ap,fmt);
+
+        time(&P);
+        fprintf(stderr, "dmeventd[%p]: %.15s ", (void *) pthread_self(), ctime(&P)+4 );
+        vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
 
-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;
+        va_end(ap);
+}
 
 /* Allocate/free the status structure for a monitoring thread. */
-static struct thread_status *alloc_thread_status(struct message_data *data,
-						 struct dso_data *dso_data)
+static struct thread_status *_alloc_thread_status(struct message_data *data,
+						  struct dso_data *dso_data)
 {
 	struct thread_status *ret = (typeof(ret)) dm_malloc(sizeof(*ret));
 
-	if (ret) {
-		if (!memset(ret, 0, sizeof(*ret)) ||
-		    !(ret->device_path = dm_strdup(data->device_path))) {
-			dm_free(ret);
-			ret = NULL;
-		} else {
-			ret->dso_data = dso_data;
-			ret->events   = data->events.field;
-			ret->timeout  = data->timeout.secs;
-			list_init(&ret->timeout_list);
-		}
+	if (!ret)
+		return NULL;
+
+	if (!memset(ret, 0, sizeof(*ret)) ||
+	    !(ret->device.uuid = dm_strdup(data->device_uuid))) {
+		dm_free(ret);
+		return NULL;
 	}
 
+	ret->current_task = NULL;
+	ret->device.name = NULL;
+	ret->device.major = ret->device.minor = 0;
+	ret->dso_data = dso_data;
+	ret->events = data->events.field;
+	ret->timeout = data->timeout.secs;
+	list_init(&ret->timeout_list);
+
 	return ret;
 }
 
-static void free_thread_status(struct thread_status *thread)
+static void _free_thread_status(struct thread_status *thread)
 {
-	dm_free(thread->device_path);
+	dm_free(thread->device.uuid);
+	dm_free(thread->device.name);
 	dm_free(thread);
 }
 
 /* Allocate/free DSO data. */
-static struct dso_data *alloc_dso_data(struct message_data *data)
+static struct dso_data *_alloc_dso_data(struct message_data *data)
 {
 	struct dso_data *ret = (typeof(ret)) dm_malloc(sizeof(*ret));
 
-	if (ret) {
-		if (!memset(ret, 0, sizeof(*ret)) ||
-		    !(ret->dso_name = dm_strdup(data->dso_name))) {
-			dm_free(ret);
-			ret = NULL;
-		}
+	if (!ret)
+		return NULL;
+
+	if (!memset(ret, 0, sizeof(*ret)) ||
+	    !(ret->dso_name = dm_strdup(data->dso_name))) {
+		dm_free(ret);
+		return NULL;
 	}
 
 	return ret;
 }
 
-static void free_dso_data(struct dso_data *data)
+/* Create a device monitoring thread. */
+static int _pthread_create_smallstack(pthread_t *t, void *(*fun)(void *), void *arg)
 {
-	dm_free(data->dso_name);
-	dm_free(data);
+	pthread_attr_t attr;
+	pthread_attr_init(&attr);
+	/*
+	 * We use a smaller stack since it gets preallocated in its entirety
+	 */
+	pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE);
+	return pthread_create(t, &attr, fun, arg);
 }
 
-/* FIXME: Factor out. */
-static char *dm_basename(char *str)
+static void _free_dso_data(struct dso_data *data)
 {
-	char *p = strrchr(str, '/');
-
-	return p ? p + 1 : str;
+	dm_free(data->dso_name);
+	dm_free(data);
 }
 
 /*
  * Fetch a string off src and duplicate it into *ptr.
- * Pay attention to 0 lenght strings.
+ * Pay attention to zero-length strings.
  */
-/* FIXME: move to separate module to share with the client lib. */
-static const char delimiter = ' ';
-static int fetch_string(char **ptr, char **src)
+/* FIXME? move to libdevmapper to share with the client lib (need to
+   make delimiter a parameter then) */
+static int _fetch_string(char **ptr, char **src, const int delimiter)
 {
 	int ret = 0;
 	char *p;
@@ -238,30 +319,39 @@
 }
 
 /* Free message memory. */
-static void free_message(struct message_data *message_data)
+static void _free_message(struct message_data *message_data)
 {
+	if (message_data->id)
+		dm_free(message_data->id);
 	if (message_data->dso_name)
 		dm_free(message_data->dso_name);
 
-	if (message_data->device_path)
-		dm_free(message_data->device_path);
+	if (message_data->device_uuid)
+		dm_free(message_data->device_uuid);
+
 }
 
 /* Parse a register message from the client. */
-static int parse_message(struct message_data *message_data)
+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
 	 * path and events # string from message.
 	 */
-	if (fetch_string(&message_data->dso_name, &p) &&
-	    fetch_string(&message_data->device_path, &p) &&
-	    fetch_string(&message_data->events.str, &p) &&
-	    fetch_string(&message_data->timeout.str, &p)) {
+	if (_fetch_string(&message_data->id, &p, ' ') &&
+	    _fetch_string(&message_data->dso_name, &p, ' ') &&
+	    _fetch_string(&message_data->device_uuid, &p, ' ') &&
+	    _fetch_string(&message_data->events.str, &p, ' ') &&
+	    _fetch_string(&message_data->timeout.str, &p, ' ')) {
 		if (message_data->events.str) {
-			enum dm_event_type i = atoi(message_data->events.str);
+			enum dm_event_mask i = atoi(message_data->events.str);
 
 			/*
 			 * Free string representaion of events.
@@ -274,28 +364,32 @@
 			uint32_t secs = atoi(message_data->timeout.str);
 			dm_free(message_data->timeout.str);
 			message_data->timeout.secs = secs ? secs :
-							    DM_EVENT_DEFAULT_TIMEOUT;
+			    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)
+/* Global mutex to lock access to lists et al. See _global_mutex
+   above. */
+static int _lock_mutex(void)
 {
-	return pthread_mutex_lock(&mutex);
+	return pthread_mutex_lock(&_global_mutex);
 }
 
-static int unlock_mutex(void)
+static int _unlock_mutex(void)
 {
-	return pthread_mutex_unlock(&mutex);
+	return pthread_mutex_unlock(&_global_mutex);
 }
 
 /* Store pid in pidfile. */
-static int storepid(int lf)
+static int _storepid(int lf)
 {
 	int len;
 	char pid[8];
@@ -314,23 +408,43 @@
 	return 1;
 }
 
-/* FIXME This is unreliable: should use DM_DEVICE_INFO ioctl instead. */
 /* Check, if a device exists. */
-static int device_exists(char *device)
+static int _fill_device_data(struct thread_status *ts)
 {
-	struct stat st_buf;
-	char path2[PATH_MAX];
+	struct dm_task *dmt;
+	struct dm_info dmi;
 
-	if (!device || !*device)
+	if (!ts->device.uuid)
 		return 0;
 
-	if (device[0] == '/') /* absolute path */
-		return !stat(device, &st_buf) && S_ISBLK(st_buf.st_mode);
+	ts->device.name = NULL;
+	ts->device.major = ts->device.minor = 0;
 
-	if (PATH_MAX <= snprintf(path2, PATH_MAX, "%s/%s", dm_dir(), device))
+	dmt = dm_task_create(DM_DEVICE_INFO);
+	if (!dmt)
 		return 0;
 
-	return !stat(path2, &st_buf) && S_ISBLK(st_buf.st_mode);
+	dm_task_set_uuid(dmt, ts->device.uuid);
+	if (!dm_task_run(dmt))
+		goto fail;
+
+	ts->device.name = dm_strdup(dm_task_get_name(dmt));
+	if (!ts->device.name)
+		goto fail;
+
+	if (!dm_task_get_info(dmt, &dmi))
+		goto fail;
+
+	ts->device.major = dmi.major;
+	ts->device.minor = dmi.minor;
+
+	dm_task_destroy(dmt);
+	return 1;
+
+      fail:
+	dm_task_destroy(dmt);
+	dm_free(ts->device.name);
+	return 0;
 }
 
 /*
@@ -338,79 +452,58 @@
  *
  * Mutex must be held when calling this.
  */
-static struct thread_status *lookup_thread_status(struct message_data *data)
+static struct thread_status *_lookup_thread_status(struct message_data *data)
 {
 	struct thread_status *thread;
 
-	list_iterate_items(thread, &thread_registry) {
-		if (!strcmp(data->device_path, thread->device_path))
-			return thread;
-	}
+	list_iterate_items(thread, &_thread_registry)
+	    if (!strcmp(data->device_uuid, thread->device.uuid))
+		return thread;
 
 	return NULL;
 }
 
-
 /* Cleanup at exit. */
-static void exit_dm_lib(void)
+static void _exit_dm_lib(void)
 {
 	dm_lib_release();
 	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)
+static void _exit_timeout(void *unused __attribute((unused)))
 {
-	timeout_running = 0;
-	pthread_mutex_unlock(&timeout_mutex);
+	_timeout_running = 0;
+	pthread_mutex_unlock(&_timeout_mutex);
 }
 
 /* Wake up monitor threads every so often. */
-static void *timeout_thread(void *unused)
+static void *_timeout_thread(void *unused __attribute((unused)))
 {
 	struct timespec timeout;
 	time_t curr_time;
 
 	timeout.tv_nsec = 0;
-	pthread_cleanup_push(exit_timeout, NULL);
-	pthread_mutex_lock(&timeout_mutex);
+	pthread_cleanup_push(_exit_timeout, NULL);
+	pthread_mutex_lock(&_timeout_mutex);
 
-	while (!list_empty(&timeout_registry)) {
+	while (!list_empty(&_timeout_registry)) {
 		struct thread_status *thread;
 
-		timeout.tv_sec = (time_t)-1;
+		timeout.tv_sec = 0;
 		curr_time = time(NULL);
 
-		list_iterate_items_gen(thread, &timeout_registry,
-				       timeout_list) {
-			if (thread->next_time < curr_time) {
+		list_iterate_items_gen(thread, &_timeout_registry, timeout_list) {
+			if (thread->next_time <= curr_time) {
 				thread->next_time = curr_time + thread->timeout;
 				pthread_kill(thread->thread, SIGALRM);
 			}
 
-			if (thread->next_time < timeout.tv_sec)
+			if (thread->next_time < timeout.tv_sec || !timeout.tv_sec)
 				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);
@@ -418,45 +511,44 @@
 	return NULL;
 }
 
-static int register_for_timeout(struct thread_status *thread)
+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);
+		list_add(&_timeout_registry, &thread->timeout_list);
+		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;
+		if (!(ret = -_pthread_create_smallstack(&timeout_id, _timeout_thread, NULL)))
+			_timeout_running = 1;
 	}
 
-	pthread_mutex_unlock(&timeout_mutex);
+	pthread_mutex_unlock(&_timeout_mutex);
 
 	return ret;
 }
 
-static void unregister_for_timeout(struct thread_status *thread)
+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,
-		       const char *f, ...)
+static void _no_intr_log(int level, const char *file, int line,
+			const char *f, ...)
 {
 	va_list ap;
 
@@ -480,7 +572,7 @@
 		fprintf(stdout, "\n");
 }
 
-static sigset_t unblock_sigalrm(void)
+static sigset_t _unblock_sigalrm(void)
 {
 	sigset_t set, old;
 
@@ -490,165 +582,234 @@
 	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)
+static int _event_wait(struct thread_status *thread, struct dm_task **task)
 {
 	sigset_t set;
-	int ret = 0;
-/*
-	void *next = NULL;
-	char *params, *target_type;
-	uint64_t start, length;
-*/
+	int ret = DM_WAIT_RETRY;
 	struct dm_task *dmt;
 	struct dm_info info;
 
+	*task = 0;
+
 	if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
-		return 0;
+		return DM_WAIT_RETRY;
+
+	thread->current_task = dmt;
 
-	if (!(ret = dm_task_set_name(dmt, dm_basename(thread->device_path))) ||
-	    !(ret = dm_task_set_event_nr(dmt, thread->event_nr)))
+	if (!dm_task_set_uuid(dmt, thread->device.uuid) ||
+	    !dm_task_set_event_nr(dmt, thread->event_nr))
 		goto out;
 
 	/*
 	 * This is so that you can break out of waiting on an event,
 	 * either for a timeout event, or to cancel the thread.
 	 */
-	set = unblock_sigalrm();
-	dm_log_init(no_intr_log);
+	set = _unblock_sigalrm();
+	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, &params);
-
-			log_error("%s: %s\n", __func__, params);
-			if ((ret = error_detected(thread, params)))
-				break;
-		} while(next);
-*/
+	if (dm_task_run(dmt)) {
 		thread->current_events |= DM_EVENT_DEVICE_ERROR;
-		ret = 1;
-
-		/*
-		 * FIXME:  I am setting processed_events to zero here
-		 * because it is causing problems.  for example, the
-		 * mirror target emits a signal for INSYNC, then
-		 * subsequent events (device failures) are not handled
-		 */
-		thread->processed_events = 0;
+		ret = DM_WAIT_INTR;
 
 		if ((ret = dm_task_get_info(dmt, &info)))
 			thread->event_nr = info.event_nr;
 	} else if (thread->events & DM_EVENT_TIMEOUT && errno == EINTR) {
 		thread->current_events |= DM_EVENT_TIMEOUT;
-		ret = 1;
-		thread->processed_events = 0;
+		ret = DM_WAIT_INTR;
+	} else {
+		syslog(LOG_NOTICE, "dm_task_run failed, errno = %d, %s",
+		       errno, strerror(errno));
+		if (errno == ENXIO) {
+			syslog(LOG_ERR, "%s disappeared, detaching",
+			       thread->device.name);
+			ret = DM_WAIT_FATAL;
+		}
 	}
 
 	pthread_sigmask(SIG_SETMASK, &set, NULL);
 	dm_log_init(NULL);
 
-   out:
-	dm_task_destroy(dmt);
+      out:
+	if (ret == DM_WAIT_FATAL || ret == DM_WAIT_RETRY) {
+		dm_task_destroy(dmt);
+		thread->current_task = NULL;
+	} else
+		*task = dmt;
 
 	return ret;
 }
 
 /* Register a device with the DSO. */
-static int do_register_device(struct thread_status *thread)
+static int _do_register_device(struct thread_status *thread)
 {
-	return thread->dso_data->register_device(thread->device_path);
+	return thread->dso_data->register_device(thread->device.name,
+						 thread->device.uuid,
+						 thread->device.major,
+						 thread->device.minor,
+						 &(thread->dso_private));
 }
 
 /* Unregister a device with the DSO. */
-static int do_unregister_device(struct thread_status *thread)
+static int _do_unregister_device(struct thread_status *thread)
 {
-	return thread->dso_data->unregister_device(thread->device_path);
+	return thread->dso_data->unregister_device(thread->device.name,
+						   thread->device.uuid,
+						   thread->device.major,
+						   thread->device.minor,
+						   &(thread->dso_private));
 }
 
 /* Process an event in the DSO. */
-static void do_process_event(struct thread_status *thread)
+static void _do_process_event(struct thread_status *thread, struct dm_task *task)
 {
-	thread->dso_data->process_event(thread->device_path,
-					thread->current_events);
+	thread->dso_data->process_event(task, thread->current_events, &(thread->dso_private));
 }
 
 /* Thread cleanup handler to unregister device. */
-static void monitor_unregister(void *arg)
+static void _monitor_unregister(void *arg)
 {
-	struct thread_status *thread = arg;
+	struct thread_status *thread = arg, *thread_iter;
+
+	if (!_do_unregister_device(thread))
+		syslog(LOG_ERR, "%s: %s unregister failed\n", __func__,
+		       thread->device.name);
+	if (thread->current_task)
+		dm_task_destroy(thread->current_task);
+	thread->current_task = NULL;
+
+	_lock_mutex();
+	if (thread->events & DM_EVENT_TIMEOUT) {
+		/* _unregister_for_timeout locks another mutex, we
+		   don't want to deadlock so we release our mutex for
+		   a bit */
+		_unlock_mutex();
+		_unregister_for_timeout(thread);
+		_lock_mutex();
+	}
+	/* we may have been relinked to unused registry since we were
+	   called, so check that */
+	list_iterate_items(thread_iter, &_thread_registry_unused)
+		if (thread_iter == thread) {
+			thread->status = DM_THREAD_DONE;
+			_unlock_mutex();
+			return;
+		}
+	thread->status = DM_THREAD_DONE;
+	UNLINK_THREAD(thread);
+	LINK(thread, &_thread_registry_unused);
+	_unlock_mutex();
+}
+
+static struct dm_task *_get_device_status(struct thread_status *ts)
+{
+	struct dm_task *dmt = dm_task_create(DM_DEVICE_STATUS);
+
+	if (!dmt)
+		return NULL;
+
+	dm_task_set_uuid(dmt, ts->device.uuid);
+
+	if (!dm_task_run(dmt)) {
+		dm_task_destroy(dmt);
+		return NULL;
+	}
 
-	if (!do_unregister_device(thread))
-		log_error("%s: %s unregister failed\n", __func__,
-			thread->device_path);
+	return dmt;
 }
 
 /* Device monitoring thread. */
-static void *monitor_thread(void *arg)
-    __attribute((noreturn));
-static void *monitor_thread(void *arg)
+static void *_monitor_thread(void *arg)
 {
 	struct thread_status *thread = arg;
+	int wait_error = 0;
+	struct dm_task *task;
 
 	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
-	pthread_cleanup_push(monitor_unregister, thread);
+	pthread_cleanup_push(_monitor_unregister, thread);
 
 	/* Wait for do_process_request() to finish its task. */
-	lock_mutex();
-	unlock_mutex();
+	_lock_mutex();
+	thread->status = DM_THREAD_RUNNING;
+	_unlock_mutex();
 
 	/* Loop forever awaiting/analyzing device events. */
 	while (1) {
 		thread->current_events = 0;
 
+		wait_error = _event_wait(thread, &task);
+		if (wait_error == DM_WAIT_RETRY)
+			continue;
+
+		if (wait_error == DM_WAIT_FATAL)
+			break;
+
+		/* Timeout occurred, task is not filled properly.
+		 * We get device status here for processing it in DSO.
+		 */
+		if (wait_error == DM_WAIT_INTR &&
+		    thread->current_events & DM_EVENT_TIMEOUT) {
+			dm_task_destroy(task);
+			task = _get_device_status(thread);
+			/* FIXME: syslog fail here ? */
+			if (!(thread->current_task = task))
+				continue;
+		}
+
 		/*
-		 * FIXME: if unrecoverable error (ENODEV) happens,
-		 * we loop indefinitely.  event_wait should return
-		 * more than 0/1.
+		 * We know that wait succeeded and stored a
+		 * pointer to dm_task with device status into task.
 		 */
-		if (!event_wait(thread))
-			continue;
 
 		/*
 		 * Check against filter.
 		 *
-		 * If there's current events delivered from event_wait() AND
+		 * If there's current events delivered from _event_wait() AND
 		 * the device got registered for those events AND
 		 * those events haven't been processed yet, call
 		 * the DSO's process_event() handler.
-		 *
-		 * FIXME: when does processed_events get cleared?  What if
-		 * the same type of event happens later... after the first
-		 * was handled properly?
 		 */
-		if (thread->events &
-		    thread->current_events &
-		    ~thread->processed_events) {
-			lock_mutex();
+		_lock_mutex();
+		if (thread->status == DM_THREAD_SHUTDOWN) {
+			_unlock_mutex();
+			break;
+		}
+		_unlock_mutex();
+
+		if (thread->events & thread->current_events) {
+			_lock_mutex();
 			thread->processing = 1;
-			unlock_mutex();
-			do_process_event(thread);
-			thread->processed_events |= thread->current_events;
-			lock_mutex();
+			_unlock_mutex();
+
+			_do_process_event(thread, task);
+			dm_task_destroy(task);
+			thread->current_task = NULL;
+
+			_lock_mutex();
 			thread->processing = 0;
-			unlock_mutex();
+			_unlock_mutex();
+		} else {
+			dm_task_destroy(task);
+			thread->current_task = NULL;
 		}
 	}
 
-	pthread_cleanup_pop(0);
+	pthread_cleanup_pop(1);
+
+	return NULL;
 }
 
 /* Create a device monitoring thread. */
-/* FIXME: call this with mutex hold ? */
-static int create_thread(struct thread_status *thread)
+static int _create_thread(struct thread_status *thread)
 {
-	return pthread_create(&thread->thread, NULL, monitor_thread, thread);
+	return _pthread_create_smallstack(&thread->thread, _monitor_thread, thread);
 }
 
-static int terminate_thread(struct thread_status *thread)
+static int _terminate_thread(struct thread_status *thread)
 {
 	int ret;
 
@@ -658,84 +819,78 @@
 	return pthread_kill(thread->thread, SIGALRM);
 }
 
-/* DSO reference counting. */
-static void lib_get(struct dso_data *data)
+/* DSO reference counting. Call with _global_mutex locked! */
+static void _lib_get(struct dso_data *data)
 {
 	data->ref_count++;
 }
 
-static void lib_put(struct dso_data *data)
+static void _lib_put(struct dso_data *data)
 {
 	if (!--data->ref_count) {
 		dlclose(data->dso_handle);
 		UNLINK_DSO(data);
-		free_dso_data(data);
+		_free_dso_data(data);
 	}
 }
 
 /* Find DSO data. */
-static struct dso_data *lookup_dso(struct message_data *data)
+static struct dso_data *_lookup_dso(struct message_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)) {
-			lib_get(dso_data);
-			ret = dso_data;
-			break;
-		}
+	list_iterate_items(dso_data, &_dso_registry)
+	    if (!strcmp(data->dso_name, dso_data->dso_name)) {
+		_lib_get(dso_data);
+		ret = dso_data;
+		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)
+static int _lookup_symbol(void *dl, void **symbol, const char *name)
 {
 	if ((*symbol = dlsym(dl, name)))
 		return 1;
 
-	log_error("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,
+	return _lookup_symbol(dl, (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");
+	    _lookup_symbol(dl, (void *) &data->register_device,
+			  "register_device") &&
+	    _lookup_symbol(dl, (void *) &data->unregister_device,
+			  "unregister_device");
 }
 
 /* Load an application specific DSO. */
-static struct dso_data *load_dso(struct message_data *data)
+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());
+	if (!(dl = dlopen(data->dso_name, RTLD_NOW))) {
+		const char *dlerr = dlerror();
+		syslog(LOG_ERR, "dmeventd %s dlopen failed: %s", data->dso_name,
+		       dlerr);
+		data->msg->size =
+		    dm_asprintf(&(data->msg->data), "%s %s dlopen failed: %s",
+				data->id, data->dso_name, dlerr);
 		return NULL;
 	}
 
-	if (!(ret = alloc_dso_data(data))) {
+	if (!(ret = _alloc_dso_data(data))) {
 		dlclose(dl);
 		return NULL;
 	}
 
 	if (!(lookup_symbols(dl, ret))) {
-		free_dso_data(ret);
+		_free_dso_data(ret);
 		dlclose(dl);
 		return NULL;
 	}
@@ -745,18 +900,17 @@
 	 * we've got no references to it any more.
 	 */
 	ret->dso_handle = dl;
-	lib_get(ret);
+	_lib_get(ret);
 
-	lock_mutex();
+	_lock_mutex();
 	LINK_DSO(ret);
-	unlock_mutex();
+	_unlock_mutex();
 
 	return ret;
 }
 
-
 /* Return success on daemon active check. */
-static int active(struct message_data *message_data)
+static int _active(struct message_data *message_data)
 {
 	return 0;
 }
@@ -767,22 +921,15 @@
  * Only one caller at a time here, because we use
  * a FIFO and lock it against multiple accesses.
  */
-static int register_for_event(struct message_data *message_data)
+static int _register_for_event(struct message_data *message_data)
 {
 	int ret = 0;
 	struct thread_status *thread, *thread_new = NULL;
 	struct dso_data *dso_data;
 
-	if (!device_exists(message_data->device_path)) {
+	if (!(dso_data = _lookup_dso(message_data)) &&
+	    !(dso_data = _load_dso(message_data))) {
 		stack;
-		ret = -ENODEV;
-		goto out;
-	}
-
-	if (!(dso_data = lookup_dso(message_data)) &&
-	    !(dso_data = load_dso(message_data))) {
-		stack;
-/* FIXME */
 #ifdef ELIBACC
 		ret = -ELIBACC;
 #else
@@ -790,35 +937,51 @@
 #endif
 		goto out;
 	}
-		
+
 	/* Preallocate thread status struct to avoid deadlock. */
-	if (!(thread_new = alloc_thread_status(message_data, dso_data))) {
+	if (!(thread_new = _alloc_thread_status(message_data, dso_data))) {
 		stack;
 		ret = -ENOMEM;
 		goto out;
 	}
 
-	lock_mutex();
+	if (!_fill_device_data(thread_new)) {
+		stack;
+		ret = -ENODEV;
+		goto out;
+	}
 
-	if (!(thread = lookup_thread_status(message_data))) {
-		unlock_mutex();
+	_lock_mutex();
 
-		/*
-		 * FIXME: better do this asynchronously in the
-		 *        monitoring thread ?
-		 */
-		if (!(ret = do_register_device(thread_new)))
+	/* If creation of timeout thread fails (as it may), we fail
+	   here completely. The client is responsible for either
+	   retrying later or trying to register without timeout
+	   events. However, if timeout thread cannot be started, it
+	   usually means we are so starved on resources that we are
+	   almost as good as dead already... */
+	if (thread_new->events & DM_EVENT_TIMEOUT) {
+		ret = -_register_for_timeout(thread_new);
+		if (ret) {
+		    _unlock_mutex();
+		    goto out;
+		}
+	}
+
+	if (!(thread = _lookup_thread_status(message_data))) {
+		_unlock_mutex();
+
+		if (!(ret = _do_register_device(thread_new)))
 			goto out;
 
 		thread = thread_new;
 		thread_new = NULL;
 
 		/* Try to create the monitoring thread for this device. */
-		lock_mutex();
-		if ((ret = -create_thread(thread))) {
-			unlock_mutex();
-			do_unregister_device(thread);
-			free_thread_status(thread);
+		_lock_mutex();
+		if ((ret = -_create_thread(thread))) {
+			_unlock_mutex();
+			_do_unregister_device(thread);
+			_free_thread_status(thread);
 			goto out;
 		} else
 			LINK_THREAD(thread);
@@ -827,24 +990,15 @@
 	/* Or event # into events bitfield. */
 	thread->events |= message_data->events.field;
 
-	unlock_mutex(); 
+	_unlock_mutex();
 
-	/* FIXME - If you fail to register for timeout events, you
-	   still monitor all the other events. Is this the right
-	   action for newly created devices?  Also, you are still
-	   on the timeout registry, so if a timeout thread is
-	   successfully started up later, you will start receiving
-	   DM_EVENT_TIMEOUT events */
-	if (thread->events & DM_EVENT_TIMEOUT)
-		ret = -register_for_timeout(thread);
-
-   out:
+      out:
 	/*
 	 * Deallocate thread status after releasing
 	 * the lock in case we haven't used it.
 	 */
 	if (thread_new)
-		free_thread_status(thread_new);
+		_free_thread_status(thread_new);
 
 	return ret;
 }
@@ -854,7 +1008,7 @@
  *
  * Only one caller at a time here as with register_for_event().
  */
-static int unregister_for_event(struct message_data *message_data)
+static int _unregister_for_event(struct message_data *message_data)
 {
 	int ret = 0;
 	struct thread_status *thread;
@@ -863,29 +1017,36 @@
 	 * Clear event in bitfield and deactivate
 	 * monitoring thread in case bitfield is 0.
 	 */
-	lock_mutex();
+	_lock_mutex();
 
-	if (!(thread = lookup_thread_status(message_data))) {
-		unlock_mutex();
+	if (!(thread = _lookup_thread_status(message_data))) {
+		_unlock_mutex();
 		ret = -ENODEV;
 		goto out;
 	}
 
+	if (thread->status == DM_THREAD_DONE) {
+		/* the thread has terminated while we were not
+		   watching */
+		_unlock_mutex();
+		return 0;
+	}
+
 	thread->events &= ~message_data->events.field;
 
 	if (!(thread->events & DM_EVENT_TIMEOUT))
-		unregister_for_timeout(thread);
+		_unregister_for_timeout(thread);
 	/*
 	 * In case there's no events to monitor on this device ->
 	 * unlink and terminate its monitoring thread.
 	 */
 	if (!thread->events) {
 		UNLINK_THREAD(thread);
-		LINK(thread, &thread_registry_unused);
+		LINK(thread, &_thread_registry_unused);
 	}
-	unlock_mutex();
+	_unlock_mutex();
 
-   out:
+      out:
 	return ret;
 }
 
@@ -894,143 +1055,202 @@
  *
  * Only one caller at a time here as with register_for_event().
  */
-static int registered_device(struct message_data *message_data,
+static int _registered_device(struct message_data *message_data,
 			     struct thread_status *thread)
 {
 	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 %s %u";
+	const char *id = message_data->id;
+	const char *dso = thread->dso_data->dso_name;
+	const char *dev = thread->device.uuid;
+	unsigned events = ((thread->status == DM_THREAD_RUNNING)
+			   && (thread->events)) ? thread->events : thread->
+	    events | DM_EVENT_REGISTRATION_PENDING;
+
+	if (msg->data)
+		dm_free(msg->data);
 
-	unlock_mutex();
+	msg->size = dm_asprintf(&(msg->data), fmt, id, dso, dev, events);
+
+	_unlock_mutex();
 
 	return 0;
 }
 
-static int want_registered_device(char *dso_name, char *device_path,
+static int _want_registered_device(char *dso_name, char *device_uuid,
 				  struct thread_status *thread)
 {
 	/* If DSO names and device paths are equal. */
-	if (dso_name && device_path)
+	if (dso_name && device_uuid)
 		return !strcmp(dso_name, thread->dso_data->dso_name) &&
-		       !strcmp(device_path, thread->device_path);
+		    !strcmp(device_uuid, thread->device.uuid) &&
+			(thread->status == DM_THREAD_RUNNING ||
+			 (thread->events & DM_EVENT_REGISTRATION_PENDING));
 
 	/* If DSO names are equal. */
 	if (dso_name)
-		return !strcmp(dso_name, thread->dso_data->dso_name);
-		
+		return !strcmp(dso_name, thread->dso_data->dso_name) &&
+			(thread->status == DM_THREAD_RUNNING ||
+			 (thread->events & DM_EVENT_REGISTRATION_PENDING));
+
 	/* If device paths are equal. */
-	if (device_path)
-		return !strcmp(device_path, thread->device_path);
+	if (device_uuid)
+		return !strcmp(device_uuid, thread->device.uuid) &&
+			(thread->status == DM_THREAD_RUNNING ||
+			 (thread->events & DM_EVENT_REGISTRATION_PENDING));
 
 	return 1;
 }
 
-static int _get_registered_device(struct message_data *message_data, int next)
+static int _get_registered_dev(struct message_data *message_data, int next)
 {
-	int hit = 0;
-	struct thread_status *thread;
+	struct thread_status *thread, *hit = NULL;
 
-	lock_mutex();
+	_lock_mutex();
 
 	/* Iterate list of threads checking if we want a particular one. */
-	list_iterate_items(thread, &thread_registry) {
-		if ((hit = want_registered_device(message_data->dso_name,
-						  message_data->device_path,
-						  thread)))
+	list_iterate_items(thread, &_thread_registry)
+		if (_want_registered_device(message_data->dso_name,
+					    message_data->device_uuid,
+					    thread)) {
+			hit = thread;
 			break;
-	}
+		}
 
 	/*
 	 * If we got a registered device and want the next one ->
 	 * fetch next conforming element off the list.
 	 */
-	if (hit) {
-		if (next) {
-			do {
-				if (list_end(&thread_registry, &thread->list))
-					goto out;
-				
-				thread = list_item(thread->list.n,
-						   struct thread_status);
-			} while (!want_registered_device(message_data->dso_name,
-							 NULL, thread));
-		}
+	if (hit && !next) {
+		_unlock_mutex();
+		return _registered_device(message_data, hit);
+	}
+
+	if (!hit)
+		goto out;
+
+	thread = hit;
 
-		return registered_device(message_data, thread);
+	while (1) {
+		if (list_end(&_thread_registry, &thread->list))
+			goto out;
+
+		thread = list_item(thread->list.n, struct thread_status);
+		if (_want_registered_device(message_data->dso_name, NULL, thread)) {
+			hit = thread;
+			break;
+		}
 	}
 
-   out:
-	unlock_mutex();
+	_unlock_mutex();
+	return _registered_device(message_data, hit);
 
+      out:
+	_unlock_mutex();
+	
 	return -ENOENT;
 }
 
-static int get_registered_device(struct message_data *message_data)
+static int _get_registered_device(struct message_data *message_data)
 {
-	return _get_registered_device(message_data, 0);
+	return _get_registered_dev(message_data, 0);
 }
 
-static int get_next_registered_device(struct message_data *message_data)
+static int _get_next_registered_device(struct message_data *message_data)
 {
-	return _get_registered_device(message_data, 1);
+	return _get_registered_dev(message_data, 1);
 }
 
-static int set_timeout(struct message_data *message_data)
+static int _set_timeout(struct message_data *message_data)
 {
 	struct thread_status *thread;
 
-	lock_mutex();
-	if ((thread = lookup_thread_status(message_data)))
-		thread->timeout = message_data->timeout.secs; 
-	unlock_mutex();
+	_lock_mutex();
+	if ((thread = _lookup_thread_status(message_data)))
+		thread->timeout = message_data->timeout.secs;
+	_unlock_mutex();
 
 	return thread ? 0 : -ENODEV;
 }
 
-static int get_timeout(struct message_data *message_data)
+static int _get_timeout(struct message_data *message_data)
 {
 	struct thread_status *thread;
 	struct dm_event_daemon_message *msg = message_data->msg;
 
-	lock_mutex();
-	if ((thread = lookup_thread_status(message_data)))
-		snprintf(msg->msg, sizeof(msg->msg),
-			 "%"PRIu32, thread->timeout);
-	unlock_mutex();
+	if (msg->data)
+		dm_free(msg->data);
+
+	_lock_mutex();
+	if ((thread = _lookup_thread_status(message_data))) {
+		msg->size =
+		    dm_asprintf(&(msg->data), "%s %" PRIu32, message_data->id,
+				thread->timeout);
+	} else {
+		msg->data = NULL;
+		msg->size = 0;
+	}
+	_unlock_mutex();
 
 	return thread ? 0 : -ENODEV;
 }
-	
 
 /* 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;
-
-		return 0;
-	}
+	memset(fifos, 0, sizeof(*fifos));
 
-	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)
+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;
+	}
+
+	struct stat st;
+
+	/* Warn about wrong permissions if applicable */
+	if ((!stat(fifos->client_path, &st)) && (st.st_mode & 0777) != 0600)
+		syslog(LOG_WARNING, "Fixing wrong permissions on %s",
+		       fifos->client_path);
+
+	if ((!stat(fifos->server_path, &st)) && (st.st_mode & 0777) != 0600)
+		syslog(LOG_WARNING, "Fixing wrong permissions on %s",
+		       fifos->server_path);
+
+	/* 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;
@@ -1040,60 +1260,91 @@
  * Read message from client making sure that data is available
  * and a complete message is read.  Must not block indefinitely.
  */
-static int client_read(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg)
+static int _client_read(struct dm_event_fifos *fifos,
+		       struct dm_event_daemon_message *msg)
 {
 	struct timeval t;
 	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);
 		t.tv_sec = 1;
 		t.tv_usec = 0;
-		ret = select(fifos->client+1, &fds, NULL, NULL, &t);
+		ret = select(fifos->client + 1, &fds, NULL, NULL, &t);
 
-		if (!ret && !bytes) /* nothing to read */
+		if (!ret && !bytes)	/* nothing to read */
 			return 0;
 
-		if (!ret)     /* trying to finish read */
+		if (!ret)	/* trying to finish read */
 			continue;
 
-		if (ret < 0)  /* error */
+		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;
+		}
+	}
+
+	if (bytes != size) {
+		if (msg->data)
+			dm_free(msg->data);
+		msg->data = NULL;
+		msg->size = 0;
 	}
 
-	return bytes == sizeof(*msg);
+	return bytes == size;
 }
 
 /*
  * Write a message to the client making sure that it is ready to write.
  */
-static int client_write(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg)
+static int _client_write(struct dm_event_fifos *fifos,
+			struct dm_event_daemon_message *msg)
 {
 	unsigned bytes = 0;
 	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);
+	if (msg->data)
+		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);
+		} 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;
 }
 
 /*
@@ -1102,238 +1353,410 @@
  * We put the request handling functions into
  * a list because of the growing number.
  */
-static int handle_request(struct dm_event_daemon_message *msg,
+static int _handle_request(struct dm_event_daemon_message *msg,
 			  struct message_data *message_data)
 {
 	static struct {
 		unsigned int cmd;
-		int (*f)(struct message_data*);
+		int (*f)(struct message_data *);
 	} requests[] = {
-		{ DM_EVENT_CMD_REGISTER_FOR_EVENT,         register_for_event },
-		{ DM_EVENT_CMD_UNREGISTER_FOR_EVENT,       unregister_for_event },
-		{ DM_EVENT_CMD_GET_REGISTERED_DEVICE,      get_registered_device },
-		{ DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE, get_next_registered_device },
-		{ DM_EVENT_CMD_SET_TIMEOUT,                set_timeout },
-		{ DM_EVENT_CMD_GET_TIMEOUT,                get_timeout },
-		{ DM_EVENT_CMD_ACTIVE,                     active },
+		{ DM_EVENT_CMD_REGISTER_FOR_EVENT, _register_for_event},
+		{ DM_EVENT_CMD_UNREGISTER_FOR_EVENT, _unregister_for_event},
+		{ DM_EVENT_CMD_GET_REGISTERED_DEVICE, _get_registered_device},
+		{ DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
+			_get_next_registered_device},
+		{ DM_EVENT_CMD_SET_TIMEOUT, _set_timeout},
+		{ DM_EVENT_CMD_GET_TIMEOUT, _get_timeout},
+		{ DM_EVENT_CMD_ACTIVE, _active},
 	}, *req;
 
-	for (req = requests; req < requests + sizeof(requests); req++) {
-		if (req->cmd == msg->opcode.cmd)
+	for (req = requests; req < requests + sizeof(requests); req++)
+		if (req->cmd == msg->cmd)
 			return req->f(message_data);
-	}
 
 	return -EINVAL;
 }
 
 /* Process a request passed from the communication thread. */
-static int do_process_request(struct dm_event_daemon_message *msg)
+static int _do_process_request(struct dm_event_daemon_message *msg)
 {
 	int ret;
+	char *answer;
 	static struct message_data message_data;
 
 	/* Parse the message. */
 	memset(&message_data, 0, sizeof(message_data));
 	message_data.msg = msg;
-	if (msg->opcode.cmd != DM_EVENT_CMD_ACTIVE &&
-	    !parse_message(&message_data)) {
+	if (msg->cmd == DM_EVENT_CMD_HELLO)  {
+		ret = 0;
+		answer = dm_strdup(msg->data);
+		if (answer) {
+			msg->size = dm_asprintf(&(msg->data), "%s HELLO", answer);
+			dm_free(answer);
+		} else {
+			msg->size = 0;
+			msg->data = NULL;
+		}
+	} else if (msg->cmd != DM_EVENT_CMD_ACTIVE && !_parse_message(&message_data)) {
 		stack;
 		ret = -EINVAL;
-	} else {
-		ret = handle_request(msg, &message_data);
-	}
+	} else
+		ret = _handle_request(msg, &message_data);
 
-	free_message(&message_data);
+	msg->cmd = ret;
+	if (!msg->data)
+		msg->size = dm_asprintf(&(msg->data), "%s %s", message_data.id, strerror(-ret));
+
+	_free_message(&message_data);
 
 	return ret;
 }
 
 /* Only one caller at a time. */
-static void process_request(struct dm_event_fifos *fifos)
+static void _process_request(struct dm_event_fifos *fifos)
 {
 	struct dm_event_daemon_message msg;
 
-	/* FIXME: better error handling */
-
 	memset(&msg, 0, sizeof(msg));
 
 	/*
-	 * Read the request from the client.
-	 * Of course, it's tough to tell what to do when
-	 * we use fucking retarded return codes like
-	 * 0 for error.
+	 * Read the request from the client (client_read, client_write
+	 * give true on success and false on failure).
 	 */
-	if (!client_read(fifos, &msg))
+	if (!_client_read(fifos, &msg))
 		return;
 
-	msg.opcode.status = do_process_request(&msg);
+	/* _do_process_request fills in msg (if memory allows for
+	   data, otherwise just cmd and size = 0) */
+	_do_process_request(&msg);
 
-	if (!client_write(fifos, &msg))
+	if (!_client_write(fifos, &msg))
 		stack;
+
+	if (msg.data)
+		dm_free(msg.data);
 }
 
-static void cleanup_unused_threads(void)
+static void _cleanup_unused_threads(void)
 {
 	int ret;
 	struct list *l;
 	struct thread_status *thread;
 
-	lock_mutex();
-	while ((l = list_first(&thread_registry_unused))) {
+	_lock_mutex();
+	while ((l = list_first(&_thread_registry_unused))) {
 		thread = list_item(l, struct thread_status);
-		if (thread->processing) {
-			goto out;  /* cleanup on the next round */
+		if (thread->processing)
+			break;	/* cleanup on the next round */
+
+		if (thread->status == DM_THREAD_RUNNING) {
+			thread->status = DM_THREAD_SHUTDOWN;
+			break;
 		}
-		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_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;
+				}
+				break;
 			}
-		} else {
-			log_error("thread can't be on unused list unless !thread->events");
+
+			list_del(l);
+			syslog(LOG_ERR,
+			       "thread can't be on unused list unless !thread->events");
+			thread->status = DM_THREAD_RUNNING;
 			LINK_THREAD(thread);
+
+			continue;
 		}
 
+		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();
+
+	_unlock_mutex();
 }
 
-static void sig_alarm(int signum)
+static void _sig_alarm(int signum __attribute((unused)))
 {
 	pthread_testcancel();
 }
 
 /* Init thread signal handling. */
-static void init_thread_signals(void)
+static void _init_thread_signals(void)
 {
 	sigset_t my_sigset;
 	struct sigaction act;
-	
+
 	memset(&act, 0, sizeof(act));
-	act.sa_handler = sig_alarm;
+	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 __attribute((unused)))
 {
-	setsid();
-	if (chdir("/"))
-		return -EXIT_CHDIR_FAILURE;
-
-/* FIXME: activate again after we're done with tracing.
-	if ((close(STDIN_FILENO) < 0) ||
-	    (close(STDOUT_FILENO) < 0) ||
-	    (close(STDERR_FILENO) < 0))
-		return -EXIT_DESC_CLOSE_FAILURE;
-*/
+	/*
+	 * We exit when '_exit_now' is set.
+	 * That is, when a signal has been received.
+	 *
+	 * We can not simply set '_exit_now' unless all
+	 * threads are done processing.
+	 */
+	if (!_thread_registries_empty) {
+		syslog(LOG_ERR, "There are still devices being monitored.");
+		syslog(LOG_ERR, "Refusing to exit.");
+	} else
+		_exit_now = 1;
 
-	return 0;
 }
 
-static int lock_pidfile(void)
+static int _lock_pidfile(void)
 {
 	int lf;
-	char pidfile[] = "/var/run/dmeventd.pid";
+	char pidfile[] = DMEVENTD_PIDFILE;
 
 	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;
+	if (!_storepid(lf))
+		exit(EXIT_FAILURE);
 
 	return 0;
 }
 
-void dmeventd(void)
+#ifdef linux
+/*
+ * Protection against OOM killer if kernel supports it
+ */
+static int _set_oom_adj(int val)
+{
+	FILE *fp;
+
+	struct stat st;
+
+	if (stat(OOM_ADJ_FILE, &st) == -1) {
+		if (errno == ENOENT)
+			DEBUGLOG(OOM_ADJ_FILE " not found");
+		else
+			perror(OOM_ADJ_FILE ": stat failed");
+		return 1;
+	}
+
+	if (!(fp = fopen(OOM_ADJ_FILE, "w"))) {
+		perror(OOM_ADJ_FILE ": fopen failed");
+		return 0;
+	}
+
+	fprintf(fp, "%i", val);
+	if (fclose(fp))
+		perror(OOM_ADJ_FILE ": fclose failed");
+
+	return 1;
+}
+#endif
+
+static void _daemonize(void)
+{
+	int child_status;
+	int fd;
+	pid_t pid;
+	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.\n");
+		exit(EXIT_FAILURE);
+	}
+	signal(SIGTERM, &_exit_handler);
+
+	switch (pid = fork()) {
+	case -1:
+		perror("fork failed:");
+		exit(EXIT_FAILURE);
+
+	case 0:		/* Child */
+		break;
+
+	default:
+		/* Wait for response from child */
+		while (!waitpid(pid, &child_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(child_status)) {
+		case EXIT_LOCKFILE_INUSE:
+			fprintf(stderr, "Another dmeventd daemon is already running\n");
+			break;
+		case EXIT_DESC_CLOSE_FAILURE:
+		case EXIT_DESC_OPEN_FAILURE:
+		case EXIT_OPEN_PID_FAILURE:
+		case EXIT_FIFO_FAILURE:
+		case EXIT_CHDIR_FAILURE:
+		default:
+			fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(child_status));
+			break;
+		}
+
+		exit(WEXITSTATUS(child_status));
+	}
+
+	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);
+
+	setsid();
+}
+
+static void usage(char *prog, FILE *file)
+{
+	fprintf(file, "Usage:\n");
+	fprintf(file, "%s [Vhd]\n", prog);
+	fprintf(file, "\n");
+	fprintf(file, "   -V       Show version of dmeventd\n");
+	fprintf(file, "   -h       Show this help information\n");
+	fprintf(file, "   -d       Don't fork, run in the foreground\n");
+	fprintf(file, "\n");
+}
+
+int main(int argc, char *argv[])
 {
 	int ret;
+	signed char opt;
 	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);
+	opterr = 0;
+	optind = 0;
 
-	/* FIXME: set daemon name. */
-	// set_name();
+	while ((opt = getopt(argc, argv, "?hVd")) != EOF) {
+		switch (opt) {
+		case 'h':
+			usage(argv[0], stdout);
+			exit(0);
+		case '?':
+			usage(argv[0], stderr);
+			exit(0);
+		case 'd':
+			_debug++;
+			break;
+		case 'V':
+			printf("dmeventd version: %s\n", DM_LIB_VERSION);
+			exit(1);
+			break;
+		}
+	}
+
+	if (!_debug)
+		_daemonize();
 
-	if ((ret = lock_pidfile()))
-		exit(-ret);
+	openlog("dmeventd", LOG_PID, LOG_DAEMON);
 
-	init_thread_signals();
+	_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);
+
+#ifdef linux
+	if (!_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN))
+		syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer");
+#endif
+
+	_init_thread_signals();
 
 	//multilog_clear_logging();
 	//multilog_add_type(std_syslog, &logdata);
 	//multilog_init_verbose(std_syslog, _LOG_DEBUG);
 	//multilog_async(1);
 
-	if ((ret = init_fifos(&fifos)))
-		exit(-ret);
+	_init_fifos(&fifos);
 
-	pthread_mutex_init(&mutex, NULL);
+	pthread_mutex_init(&_global_mutex, NULL);
 
 #ifdef MCL_CURRENT
 	if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1)
 		exit(EXIT_FAILURE);
 #endif
 
-	if ((ret = open_fifos(&fifos)))
-		exit(-ret);
+	if ((ret = _open_fifos(&fifos)))
+		exit(EXIT_FIFO_FAILURE);
 
 	/* Signal parent, letting them know we are ready to go. */
-	kill(getppid(), SIGUSR1);
+	kill(getppid(), SIGTERM);
+	syslog(LOG_NOTICE, "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 {
-		process_request(&fifos);
-		cleanup_unused_threads();
-	} while(!list_empty(&thread_registry) || !list_empty(&thread_registry_unused));
+	while (!_exit_now) {
+		_process_request(&fifos);
+		_cleanup_unused_threads();
+		if (!list_empty(&_thread_registry)
+		    || !list_empty(&_thread_registry_unused))
+			_thread_registries_empty = 0;
+		else
+			_thread_registries_empty = 1;
+	}
 
-	exit_dm_lib();
+	_exit_dm_lib();
 
 #ifdef MCL_CURRENT
 	munlockall();
 #endif
-	pthread_mutex_destroy(&mutex);
+	pthread_mutex_destroy(&_global_mutex);
+
+	syslog(LOG_NOTICE, "dmeventd shutting down.");
+	closelog();
 
 	exit(EXIT_SUCCESS);
 }
-
-/*
- * 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:
- */

Modified: devmapper/trunk/dmeventd/dmeventd.h
==============================================================================
--- devmapper/trunk/dmeventd/dmeventd.h	(original)
+++ devmapper/trunk/dmeventd/dmeventd.h	Sat Apr  7 09:09:48 2007
@@ -1,13 +1,52 @@
 #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,
+	DM_EVENT_CMD_HELLO,
+};
+
+/* 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__ */

Modified: devmapper/trunk/dmeventd/libdevmapper-event.c
==============================================================================
--- devmapper/trunk/dmeventd/libdevmapper-event.c	(original)
+++ devmapper/trunk/dmeventd/libdevmapper-event.c	Sat Apr  7 09:09:48 2007
@@ -1,4 +1,4 @@
- /*
+/*
  * Copyright (C) 2005 Red Hat, Inc. All rights reserved.
  *
  * This file is part of the device-mapper userspace tools.
@@ -27,46 +27,176 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <signal.h>
 #include <sys/wait.h>
+#include <arpa/inet.h>		/* for htonl, ntohl */
 
-/* Set by any of the external fxns the first time one of them is called */
-/* FIXME Unused */
-// static int _logging = 0;
+static int _sequence_nr = 0;
 
-/* Fetch a string off src and duplicate it into *dest. */
-/* FIXME: move to seperate module to share with the daemon. */
-static const char delimiter = ' ';
-static char *fetch_string(char **src)
+struct dm_event_handler {
+	char *dso;
+
+	char *dev_name;
+
+	char *uuid;
+	int major;
+	int minor;
+	uint32_t timeout;
+
+	enum dm_event_mask mask;
+};
+
+static void _dm_event_handler_clear_dev_info(struct dm_event_handler *dmevh)
 {
-	char *p, *ret;
+	if (dmevh->dev_name)
+		dm_free(dmevh->dev_name);
+	if (dmevh->uuid)
+		dm_free(dmevh->uuid);
+	dmevh->dev_name = dmevh->uuid = NULL;
+	dmevh->major = dmevh->minor = 0;
+}
 
-	if ((p = strchr(*src, delimiter)))
-		*p = 0;
+struct dm_event_handler *dm_event_handler_create(void)
+{
+	struct dm_event_handler *dmevh = NULL;
 
-	if ((ret = dm_strdup(*src)))
-		*src += strlen(ret) + 1;
+	if (!(dmevh = dm_malloc(sizeof(*dmevh))))
+		return NULL;
 
-	if (p)
-		*p = delimiter;
+	dmevh->dso = dmevh->dev_name = dmevh->uuid = NULL;
+	dmevh->major = dmevh->minor = 0;
+	dmevh->mask = 0;
+	dmevh->timeout = 0;
 
-	return ret;
+	return dmevh;
 }
 
-/* Parse a device message from the daemon. */
-static int parse_message(struct dm_event_daemon_message *msg, char **dso_name,
-			 char **device, enum dm_event_type *events)
+void dm_event_handler_destroy(struct dm_event_handler *dmevh)
+{
+	_dm_event_handler_clear_dev_info(dmevh);
+	if (dmevh->dso)
+		dm_free(dmevh->dso);
+	dm_free(dmevh);
+}
+
+int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path)
+{
+	if (!path) /* noop */
+		return 0;
+	if (dmevh->dso)
+		dm_free(dmevh->dso);
+
+	dmevh->dso = dm_strdup(path);
+	if (!dmevh->dso)
+		return -ENOMEM;
+
+	return 0;
+}
+
+int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *dev_name)
+{
+	if (!dev_name)
+		return 0;
+
+	_dm_event_handler_clear_dev_info(dmevh);
+
+	dmevh->dev_name = dm_strdup(dev_name);
+	if (!dmevh->dev_name)
+		return -ENOMEM;
+	return 0;
+}
+
+int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid)
+{
+	if (!uuid)
+		return 0;
+
+	_dm_event_handler_clear_dev_info(dmevh);
+
+	dmevh->uuid = dm_strdup(uuid);
+	if (!dmevh->dev_name)
+		return -ENOMEM;
+	return 0;
+}
+
+void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major)
+{
+	int minor = dmevh->minor;
+
+	_dm_event_handler_clear_dev_info(dmevh);
+
+	dmevh->major = major;
+	dmevh->minor = minor;
+}
+
+void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor)
+{
+	int major = dmevh->major;
+
+	_dm_event_handler_clear_dev_info(dmevh);
+
+	dmevh->major = major;
+	dmevh->minor = minor;
+}
+
+void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
+				     enum dm_event_mask evmask)
+{
+	dmevh->mask = evmask;
+}
+
+void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout)
+{
+	dmevh->timeout = timeout;
+}
+
+const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh)
+{
+	return dmevh->dso;
+}
+
+const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh)
+{
+	return dmevh->dev_name;
+}
+
+const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh)
+{
+	return dmevh->uuid;
+}
+
+int dm_event_handler_get_major(const struct dm_event_handler *dmevh)
+{
+	return dmevh->major;
+}
+
+int dm_event_handler_get_minor(const struct dm_event_handler *dmevh)
+{
+	return dmevh->minor;
+}
+
+int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh)
 {
-	char *p = msg->msg;
+	return dmevh->timeout;
+}
 
-	if ((*dso_name = fetch_string(&p)) &&
-	    (*device   = fetch_string(&p))) {
-		*events = atoi(p);
+enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh)
+{
+	return dmevh->mask;
+}
 
+static int _check_message_id(struct dm_event_daemon_message *msg)
+{
+	int pid, seq_nr;
+
+	if ((sscanf(msg->data, "%d:%d", &pid, &seq_nr) != 2) ||
+	    (pid != getpid()) || (seq_nr != _sequence_nr)) {
+		log_error("Ignoring out-of-sequence reply from dmeventd. "
+			  "Expected %d:%d but received %s", getpid(),
+			  _sequence_nr, msg->data);
 		return 0;
 	}
 
-	return -ENOMEM;
+	return 1;
 }
 
 /*
@@ -78,66 +208,116 @@
  *
  * Returns: 0 on failure, 1 on success
  */
-static int daemon_read(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg)
+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;
+		}
+	}
+
+	if (bytes != size) {
+		if (msg->data)
+			dm_free(msg->data);
+		msg->data = NULL;
 	}
 
-	return bytes == sizeof(*msg);
+	return bytes == size;
 }
 
 /* Write message to daemon. */
-static int daemon_write(struct dm_event_fifos *fifos, struct dm_event_daemon_message *msg)
+static int _daemon_write(struct dm_event_fifos *fifos,
+			 struct dm_event_daemon_message *msg)
 {
 	unsigned bytes = 0;
 	int ret = 0;
 	fd_set fds;
 
-	while (bytes < sizeof(*msg)) {
+	size_t size = 2 * sizeof(uint32_t) + msg->size;
+	char *buf = alloca(size);
+	char drainbuf[128];
+	struct timeval tval = { 0, 0 };
+
+	*((uint32_t *)buf) = htonl(msg->cmd);
+	*((uint32_t *)buf + 1) = htonl(msg->size);
+	memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
+
+	/* drain the answer fifo */
+	while (1) {
+		FD_ZERO(&fds);
+		FD_SET(fifos->server, &fds);
+		tval.tv_usec = 100;
+		ret = select(fifos->server + 1, &fds, NULL, NULL, &tval);
+		if ((ret < 0) && (errno != EINTR)) {
+			log_error("Unable to talk to event daemon");
+			return 0;
+		}
+		if (ret == 0)
+			break;
+		read(fifos->server, drainbuf, 127);
+	}
+
+	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);
+			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,366 +325,462 @@
 		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,
-		       enum dm_event_type events, uint32_t timeout)
-{
+static int _daemon_talk(struct dm_event_fifos *fifos,
+			struct dm_event_daemon_message *msg, int cmd,
+			const char *dso_name, const char *dev_name,
+			enum dm_event_mask evmask, uint32_t timeout)
+{
+	const char *dso = dso_name ? dso_name : "";
+	const char *dev = dev_name ? dev_name : "";
+	const char *fmt = "%d:%d %s %s %u %" PRIu32;
+	int msg_size;
 	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;
+	if (cmd == DM_EVENT_CMD_HELLO)
+		fmt = "%d:%d HELLO";
+	if ((msg_size = dm_asprintf(&(msg->data), fmt, getpid(), _sequence_nr,
+				    dso, dev, evmask, timeout)) < 0) {
+		log_error("_daemon_talk: message allocation failed");
+		return -ENOMEM;
 	}
+	msg->size = msg_size;
 
 	/*
 	 * Write command and message to and
 	 * read status return code from daemon.
 	 */
-	if (!daemon_write(fifos, msg)) {
-		stack;
-		return -EIO;
-	}
-
-	if (!daemon_read(fifos, msg)) {
+	if (!_daemon_write(fifos, msg)) {
 		stack;
 		return -EIO;
 	}
 
-	return msg->opcode.status;
-}
+	do {
+		if (!_daemon_read(fifos, msg)) {
+			stack;
+			return -EIO;
+		}
+	} while (!_check_message_id(msg));
 
-static volatile sig_atomic_t daemon_running = 0;
+	_sequence_nr++;
 
-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;
-
-		/* 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 */
+		log_error("Unable to fork.");
 
-		/* 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_PATH, NULL);
 		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;
 }
 
 /* Initialize client. */
-static int init_client(struct dm_event_fifos *fifos)
+static int _init_client(struct dm_event_fifos *fifos)
 {
-	/* FIXME Is fifo the most suitable method? */
-	/* FIXME Why not share comms/daemon code with something else e.g. multipath? */
+	/* FIXME? Is fifo the most suitable method? Why not share
+	   comms/daemon code with something else e.g. multipath? */
 
 	/* init fifos */
 	memset(fifos, 0, sizeof(*fifos));
 	fifos->client_path = DM_EVENT_FIFO_CLIENT;
 	fifos->server_path = DM_EVENT_FIFO_SERVER;
 
-	/* FIXME The server should be responsible for these, not the client. */
-	/* Create fifos */
-	if (((mkfifo(fifos->client_path, 0600) == -1) && errno != EEXIST) ||
-	    ((mkfifo(fifos->server_path, 0600) == -1) && errno != EEXIST)) {
-		log_error("%s: Failed to create a fifo.\n", __func__);
-		return 0;
-	}
-
-	/* FIXME Warn/abort if perms are wrong - not something to fix silently. */
-	/* If they were already there, make sure permissions are ok. */
-	if (chmod(fifos->client_path, 0600)) {
-		log_error("Unable to set correct file permissions on %s",
-			fifos->client_path);
-		return 0;
-	}
-
-	if (chmod(fifos->server_path, 0600)) {
-		log_error("Unable to set correct file permissions on %s",
-			fifos->server_path);
+	if (!_start_daemon(fifos)) {
+		stack;
 		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",
-			__func__, fifos->server_path);
+		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);
+	if (flock(fifos->server, LOCK_EX) < 0) {
+		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;
 }
 
-static void dtr_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)
+/* Get uuid of a device */
+static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
 {
-	struct stat st_buf;
-	char path2[PATH_MAX];
+	struct dm_task *dmt;
+	struct dm_info info;
 
-	if (!device)
-		return 0;
+	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
+		log_error("_get_device_info: dm_task creation for info failed");
+		return NULL;
+	}
 
-	if (device[0] == '/') /* absolute path */
-		return !stat(device, &st_buf) && S_ISBLK(st_buf.st_mode);
+	if (dmevh->uuid)
+		dm_task_set_uuid(dmt, dmevh->uuid);
+	else if (dmevh->dev_name)
+		dm_task_set_name(dmt, dmevh->dev_name);
+	else if (dmevh->major && dmevh->minor) {
+		dm_task_set_major(dmt, dmevh->major);
+		dm_task_set_minor(dmt, dmevh->minor);
+        }
 
-	if (PATH_MAX <= snprintf(path2, PATH_MAX, "%s/%s", dm_dir(), device))
-		return 0;
+	/* FIXME Add name or uuid or devno to messages */
+	if (!dm_task_run(dmt)) {
+		log_error("_get_device_info: dm_task_run() failed");
+		goto failed;
+	}
+
+	if (!dm_task_get_info(dmt, &info)) {
+		log_error("_get_device_info: failed to get info for device");
+		goto failed;
+	}
+
+	if (!info.exists) {
+		log_error("_get_device_info: device not found");
+		goto failed;
+	}
+
+	return dmt;
 
-	return !stat(path2, &st_buf) && S_ISBLK(st_buf.st_mode);
+failed:
+	dm_task_destroy(dmt);
+	return NULL;
 }
 
 /* Handle the event (de)registration call and return negative error codes. */
-static int do_event(int cmd, struct dm_event_daemon_message *msg,
-		    char *dso_name, char *device, enum dm_event_type events,
-		    uint32_t timeout)
+static int _do_event(int cmd, struct dm_event_daemon_message *msg,
+		     const char *dso_name, const char *dev_name,
+		     enum dm_event_mask evmask, uint32_t timeout)
 {
 	int ret;
 	struct dm_event_fifos fifos;
 
-	/* FIXME Start the daemon here if it's not running e.g. exclusive lock file */
-	/* FIXME Move this to separate 'dm_event_register_handler' - if no daemon here, fail */
-	if (!init_client(&fifos)) {
+	if (!_init_client(&fifos)) {
 		stack;
 		return -ESRCH;
 	}
 
-	/* FIXME Use separate 'dm_event_register_handler' function to pass in dso? */
-	ret = daemon_talk(&fifos, msg, cmd, dso_name, device, events, timeout);
+	ret = _daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, 0, 0, 0, 0);
+	if (!ret)
+		ret = _daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
 
 	/* what is the opposite of init? */
-	dtr_client(&fifos);
-	
+	_dtr_client(&fifos);
+
 	return ret;
 }
 
-/* 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,
-		      enum dm_event_type events)
+int dm_event_register_handler(const struct dm_event_handler *dmevh)
 {
-	int ret;
-	struct dm_event_daemon_message msg;
+	int ret = 1, err;
+	const char *uuid;
+	struct dm_task *dmt;
+	struct dm_event_daemon_message msg = { 0, 0, NULL };
 
-	if (!device_exists(device_path)) {
-		log_error("%s: device not found", device_path);
+	if (!(dmt = _get_device_info(dmevh))) {
+		stack;
 		return 0;
 	}
 
-	if ((ret = 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;
+	uuid = dm_task_get_uuid(dmt);
+
+	if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, &msg,
+			     dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
+		log_error("%s: event registration failed: %s",
+			  dm_task_get_name(dmt),
+			  msg.data ? msg.data : strerror(-err));
+		ret = 0;
 	}
 
-	return 1;
+	if (msg.data)
+		dm_free(msg.data);
+
+	dm_task_destroy(dmt);
+
+	return ret;
 }
 
-int dm_event_unregister(char *dso_name, char *device_path,
-			enum dm_event_type events)
+int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
 {
-	int ret;
-	struct dm_event_daemon_message msg;
+	int ret = 1, err;
+	const char *uuid;
+	struct dm_task *dmt;
+	struct dm_event_daemon_message msg = { 0, 0, NULL };
 
-	if (!device_exists(device_path)) {
-		log_error("%s: device not found", device_path);
+	if (!(dmt = _get_device_info(dmevh))) {
+		stack;
 		return 0;
 	}
 
-	if ((ret = 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));
+	uuid = dm_task_get_uuid(dmt);
+
+	if ((err = _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, &msg,
+			    dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
+		log_error("%s: event deregistration failed: %s",
+			  dm_task_get_name(dmt),
+			  msg.data ? msg.data : strerror(-err));
+		ret = 0;
+	}
+
+	if (msg.data)
+		dm_free(msg.data);
+
+	dm_task_destroy(dmt);
+
+	return ret;
+}
+
+/* Fetch a string off src and duplicate it into *dest. */
+/* FIXME: move to separate module to share with the daemon. */
+static char *_fetch_string(char **src, const int delimiter)
+{
+	char *p, *ret;
+
+	if ((p = strchr(*src, delimiter)))
+		*p = 0;
+
+	if ((ret = dm_strdup(*src)))
+		*src += strlen(ret) + 1;
+
+	if (p)
+		*p = delimiter;
+
+	return ret;
+}
+
+/* Parse a device message from the daemon. */
+static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
+			 char **uuid, enum dm_event_mask *evmask)
+{
+	char *id = NULL;
+	char *p = msg->data;
+
+	if ((id = _fetch_string(&p, ' ')) &&
+	    (*dso_name = _fetch_string(&p, ' ')) &&
+	    (*uuid = _fetch_string(&p, ' '))) {
+		*evmask = atoi(p);
+
+		dm_free(id);
 		return 0;
 	}
 
-	return 1;
+	if (id)
+		dm_free(id);
+	return -ENOMEM;
 }
 
-int dm_event_get_registered_device(char **dso_name, char **device_path,
-			     enum dm_event_type *events, int next)
+/*
+ * dm_event_get_registered_device
+ * @dso_name
+ * @device_path
+ * @mask
+ * @next
+ *
+ * Returns: 0 if handler found, error (-ENOMEM, -ENOENT) otherwise
+ */
+int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
 {
-	int ret;
-	char *dso_name_arg = NULL, *device_path_arg = NULL;
-	struct dm_event_daemon_message msg;
+	int ret = 0;
+	const char *uuid = NULL;
+	char *reply_dso = NULL, *reply_uuid = NULL;
+	enum dm_event_mask reply_mask = 0;
+	struct dm_task *dmt = NULL;
+	struct dm_event_daemon_message msg = { 0, 0, NULL };
 
-	if (!(ret = do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
-				    DM_EVENT_CMD_GET_REGISTERED_DEVICE,
-			     &msg, *dso_name, *device_path, *events, 0)))
-		ret = parse_message(&msg, &dso_name_arg, &device_path_arg,
-				    events);
-
-	if (next){
-		if (*dso_name)
-			dm_free(*dso_name);
-		if (*device_path)
-			dm_free(*device_path);
-		*dso_name = dso_name_arg;
-		*device_path = device_path_arg;
+	if (!(dmt = _get_device_info(dmevh))) {
+		stack;
+		return 0;
+	}
+
+	uuid = dm_task_get_uuid(dmt);
+
+	if (!(ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
+			     DM_EVENT_CMD_GET_REGISTERED_DEVICE,
+			      &msg, dmevh->dso, uuid, dmevh->mask, 0))) {
+		/* FIXME this will probably horribly break if we get
+		   ill-formatted reply */
+		ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask);
 	} else {
-		if (!(*dso_name))
-			*dso_name = dso_name_arg;
-		if (!(*device_path))
-			*device_path = device_path_arg;
+		ret = -ENOENT;
+		goto fail;
+	}
+
+	dm_task_destroy(dmt);
+
+	if (msg.data) {
+		dm_free(msg.data);
+		msg.data = NULL;
+	}
+
+	_dm_event_handler_clear_dev_info(dmevh);
+	dmevh->uuid = dm_strdup(reply_uuid);
+	if (!dmevh->uuid) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	if (!(dmt = _get_device_info(dmevh))) {
+		ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
+		goto fail;
+	}
+
+	dm_event_handler_set_dso(dmevh, reply_dso);
+	dm_event_handler_set_event_mask(dmevh, reply_mask);
+	dmevh->dev_name = dm_strdup(dm_task_get_name(dmt));
+	if (!dmevh->dev_name) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	struct dm_info info;
+	if (!dm_task_get_info(dmt, &info)) {
+		ret = -1;
+		goto fail;
 	}
 
+	dmevh->major = info.major;
+	dmevh->minor = info.minor;
+
+	dm_task_destroy(dmt);
+
+	return ret;
+
+ fail:
+	if (msg.data)
+		dm_free(msg.data);
+	_dm_event_handler_clear_dev_info(dmevh);
+	dm_task_destroy(dmt);
 	return ret;
 }
 
-int dm_event_set_timeout(char *device_path, uint32_t timeout)
+#if 0				/* left out for now */
+
+static char *_skip_string(char *src, const int delimiter)
 {
-	struct dm_event_daemon_message msg;
+	src = srtchr(src, delimiter);
+	if (src && *(src + 1))
+		return src + 1;
+	return NULL;
+}
+
+int dm_event_set_timeout(const char *device_path, uint32_t timeout)
+{
+	struct dm_event_daemon_message msg = { 0, 0, NULL };
 
 	if (!device_exists(device_path))
 		return -ENODEV;
-	return do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg,
-			NULL, device_path, 0, timeout);
+
+	return _do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg,
+			 NULL, device_path, 0, timeout);
 }
 
-int dm_event_get_timeout(char *device_path, uint32_t *timeout)
+int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
 {
 	int ret;
-	struct dm_event_daemon_message msg;
+	struct dm_event_daemon_message msg = { 0, 0, NULL };
 
 	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);
+	if (!(ret = _do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
+			     0, 0))) {
+		char *p = _skip_string(msg.data, ' ');
+		if (!p) {
+			log_error("malformed reply from dmeventd '%s'\n",
+				  msg.data);
+			return -EIO;
+		}
+		*timeout = atoi(p);
+	}
+	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:
- */
+#endif

Modified: devmapper/trunk/dmeventd/libdevmapper-event.h
==============================================================================
--- devmapper/trunk/dmeventd/libdevmapper-event.h	(original)
+++ devmapper/trunk/dmeventd/libdevmapper-event.h	Sat Apr  7 09:09:48 2007
@@ -23,86 +23,84 @@
 
 #include <stdint.h>
 
-/* FIXME This stuff must be configurable. */
+/*
+ * Event library interface.
+ */
+
+enum dm_event_mask {
+	DM_EVENT_SETTINGS_MASK  = 0x0000FF,
+	DM_EVENT_SINGLE		= 0x000001, /* Report multiple errors just once. */
+	DM_EVENT_MULTI		= 0x000002, /* Report all of them. */
+
+	DM_EVENT_ERROR_MASK     = 0x00FF00,
+	DM_EVENT_SECTOR_ERROR	= 0x000100, /* Failure on a particular sector. */
+	DM_EVENT_DEVICE_ERROR	= 0x000200, /* Device failure. */
+	DM_EVENT_PATH_ERROR	= 0x000400, /* Failure on an io path. */
+	DM_EVENT_ADAPTOR_ERROR	= 0x000800, /* Failure of a host adaptor. */
+
+	DM_EVENT_STATUS_MASK    = 0xFF0000,
+	DM_EVENT_SYNC_STATUS	= 0x010000, /* Mirror synchronization completed/failed. */
+	DM_EVENT_TIMEOUT	= 0x020000, /* Timeout has occured */
 
-#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,
+	DM_EVENT_REGISTRATION_PENDING = 0x1000000, /* Monitor thread is setting-up/shutting-down */
 };
 
-/* 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;
-};
+#define DM_EVENT_ALL_ERRORS DM_EVENT_ERROR_MASK
 
-/* Event type definitions. */
-/* FIXME Use masks to separate the types and provide for extension. */
-enum dm_event_type {
-	DM_EVENT_SINGLE		= 0x01, /* Report multiple errors just once. */
-	DM_EVENT_MULTI		= 0x02, /* Report all of them. */
-
-	DM_EVENT_SECTOR_ERROR	= 0x04, /* Failure on a particular sector. */
-	DM_EVENT_DEVICE_ERROR	= 0x08, /* Device failure. */
-	DM_EVENT_PATH_ERROR	= 0x10, /* Failure on an io path. */
-	DM_EVENT_ADAPTOR_ERROR	= 0x20, /* Failure off a host adaptor. */
+struct dm_event_handler;
 
-	DM_EVENT_SYNC_STATUS	= 0x40, /* Mirror synchronization completed/failed. */
-	DM_EVENT_TIMEOUT	= 0x80, /* Timeout has occured */
-};
+struct dm_event_handler *dm_event_handler_create(void);
+void dm_event_handler_destroy(struct dm_event_handler *dmevh);
 
-/* FIXME Use a mask. */
-#define	DM_EVENT_ALL_ERRORS (DM_EVENT_SECTOR_ERROR | DM_EVENT_DEVICE_ERROR | \
-			     DM_EVENT_PATH_ERROR | DM_EVENT_ADAPTOR_ERROR)
-
-/* Prototypes for event lib interface. */
-
-/* FIXME Replace device with standard name/uuid/devno choice */
-/* Interface changes: 
-   First register a handler, passing in a unique ref for the device. */
-//  int dm_event_register_handler(const char *dso_name, const char *device);
-//  int dm_event_register(const char *dso_name, const char *name, const char *uuid, uint32_t major, uint32_t minor, enum dm_event_type events);
-/* Or (better?) add to task structure and use existing functions - run a task to register/unregister events - we may need to run task withe that with the new event mechanism anyway, then the dso calls just hook in.
-*/
- 
-/* FIXME Missing consts? */
-int dm_event_register(char *dso_name, char *device, enum dm_event_type events);
-int dm_event_unregister(char *dso_name, char *device,
-			enum dm_event_type events);
-int dm_event_get_registered_device(char **dso_name, char **device,
-				   enum dm_event_type *events, int next);
-int dm_event_set_timeout(char *device, uint32_t timeout);
-int dm_event_get_timeout(char *device, uint32_t *timeout);
-
-/* Prototypes for DSO interface. */
-void process_event(const char *device, enum dm_event_type event);
-int register_device(const char *device);
-int unregister_device(const char *device);
+/*
+ * Path of shared library to handle events.
+ *
+ * All of dso, device_name and uuid strings are duplicated, you do not
+ * need to keep the pointers valid after the call succeeds. Thes may
+ * return -ENOMEM though.
+ */
+int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path);
+
+/*
+ * Identify the device to monitor by exactly one of device_name, uuid or
+ * device number. String arguments are duplicated, see above.
+ */
+int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *device_name);
+
+int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid);
+
+void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major);
+void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor);
+void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout);
+
+/*
+ * Specify mask for events to monitor.
+ */
+void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
+				     enum dm_event_mask evmask);
+
+const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh);
+const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh);
+const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh);
+int dm_event_handler_get_major(const struct dm_event_handler *dmevh);
+int dm_event_handler_get_minor(const struct dm_event_handler *dmevh);
+int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh);
+enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh);
+
+/* FIXME Review interface (what about this next thing?) */
+int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next);
+
+/*
+ * Initiate monitoring using dmeventd.
+ */
+int dm_event_register_handler(const struct dm_event_handler *dmevh);
+int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
+
+/* Prototypes for DSO interface, see dmeventd.c, struct dso_data for
+   detailed descriptions. */
+void process_event(struct dm_task *dmt, enum dm_event_mask evmask, void **user);
+int register_device(const char *device_name, const char *uuid, int major, int minor, void **user);
+int unregister_device(const char *device_name, const char *uuid, int major,
+		      int minor, void **user);
 
 #endif

Modified: devmapper/trunk/dmsetup/Makefile.in
==============================================================================
--- devmapper/trunk/dmsetup/Makefile.in	(original)
+++ devmapper/trunk/dmsetup/Makefile.in	Sat Apr  7 09:09:48 2007
@@ -30,11 +30,11 @@
 include ../make.tmpl
 
 dmsetup: $(OBJECTS) $(interfacedir)/libdevmapper.$(LIB_SUFFIX)
-	$(CC) -o $@ $(OBJECTS) $(LDFLAGS) \
+	$(CC) -o $@ $(OBJECTS) $(CFLAGS) $(LDFLAGS) \
 	      -L$(interfacedir) -L$(DESTDIR)/lib -ldevmapper $(LIBS)
 
 dmsetup.static: $(OBJECTS) $(interfacedir)/libdevmapper.a
-	$(CC) -o $@ $(OBJECTS) $(LDFLAGS) -static \
+	$(CC) -o $@ $(OBJECTS) $(CFLAGS) $(LDFLAGS) -static \
 	      -L$(interfacedir) -L$(DESTDIR)/lib -ldevmapper $(LIBS)
 
 install: $(INSTALL_TYPE)

Modified: devmapper/trunk/dmsetup/dmsetup.c
==============================================================================
--- devmapper/trunk/dmsetup/dmsetup.c	(original)
+++ devmapper/trunk/dmsetup/dmsetup.c	Sat Apr  7 09:09:48 2007
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2005 NEC Corperation
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2005-2007 NEC Corperation
  *
  * This file is part of the device-mapper userspace tools.
  *
@@ -114,6 +114,9 @@
 	NOOPENCOUNT_ARG,
 	NOTABLE_ARG,
 	OPTIONS_ARG,
+	SEPARATOR_ARG,
+	SHOWKEYS_ARG,
+	SORT_ARG,
 	TABLE_ARG,
 	TARGET_ARG,
 	TREE_ARG,
@@ -125,18 +128,30 @@
 };
 
 static int _switches[NUM_SWITCHES];
-static int _values[NUM_SWITCHES];
+static int _int_args[NUM_SWITCHES];
+static char *_string_args[NUM_SWITCHES];
 static int _num_devices;
 static char *_uuid;
-static char *_fields;
 static char *_table;
 static char *_target;
 static char *_command;
 static struct dm_tree *_dtree;
+static struct dm_report *_report;
 
 /*
  * Commands
  */
+
+typedef int (*command_fn) (int argc, char **argv, void *data);
+
+struct command {
+	const char *name;
+	const char *help;
+	int min_args;
+	int max_args;
+	command_fn fn;
+};
+
 static int _parse_line(struct dm_task *dmt, char *buffer, const char *file,
 		       int line)
 {
@@ -211,72 +226,38 @@
 	r = 1;
 
       out:
+#ifndef HAVE_GETLINE
 	dm_free(buffer);
-	if (file)
-		fclose(fp);
+#else
+	free(buffer);
+#endif
+	if (file && fclose(fp))
+		fprintf(stderr, "%s: fclose failed: %s", file, strerror(errno));
+
 	return r;
 }
 
-static void _display_info_cols_noheadings(struct dm_task *dmt,
-					  struct dm_info *info)
-{
-	const char *uuid;
-
-	if (!info->exists)
-		return;
-
-	uuid = dm_task_get_uuid(dmt);
-
-	if (_switches[OPTIONS_ARG])
-		printf("%s\n", dm_task_get_name(dmt));
-	else
-		printf("%s:%d:%d:%s%s%s%s:%d:%d:%" PRIu32 ":%s\n",
-		       dm_task_get_name(dmt),
-		       info->major, info->minor,
-		       info->live_table ? "L" : "-",
-		       info->inactive_table ? "I" : "-",
-		       info->suspended ? "s" : "-",
-		       info->read_only ? "r" : "w",
-		       info->open_count, info->target_count, info->event_nr,
-		       uuid && *uuid ? uuid : "");
-}
+struct dmsetup_report_obj {
+	struct dm_task *task;
+	struct dm_info *info;
+};
 
-static void _display_info_cols(struct dm_task *dmt, struct dm_info *info)
+static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
 {
-	static int _headings = 0;
-	const char *uuid;
+	struct dmsetup_report_obj obj;
 
 	if (!info->exists) {
-		printf("Device does not exist.\n");
-		return;
-	}
-
-	if (!_headings) {
-		if (_switches[OPTIONS_ARG])
-			printf("Name\n");
-		else
-			printf("Name             Maj Min Stat Open Targ "
-			       "Event  UUID\n");
-		_headings = 1;
+		fprintf(stderr, "Device does not exist.\n");
+		return 0;
 	}
 
-	if (_switches[OPTIONS_ARG])
-		printf("%s\n", dm_task_get_name(dmt));
-	else {
-		printf("%-16s %3d %3d %s%s%s%s %4d %4d %6" PRIu32 " ",
-		       dm_task_get_name(dmt),
-		       info->major, info->minor,
-		       info->live_table ? "L" : "-",
-		       info->inactive_table ? "I" : "-",
-		       info->suspended ? "s" : "-",
-		       info->read_only ? "r" : "w",
-		       info->open_count, info->target_count, info->event_nr);
+	obj.task = dmt;
+	obj.info = info;
 
-		if ((uuid = dm_task_get_uuid(dmt)) && *uuid)
-			printf("%s", uuid);
+	if (!dm_report_object(_report, &obj))
+		return 0;
 
-		printf("\n");
-	}
+	return 1;
 }
 
 static void _display_info_long(struct dm_task *dmt, struct dm_info *info)
@@ -326,9 +307,8 @@
 
 	if (!_switches[COLS_ARG])
 		_display_info_long(dmt, &info);
-	else if (_switches[NOHEADINGS_ARG])
-		_display_info_cols_noheadings(dmt, &info);
 	else
+		/* FIXME return code */
 		_display_info_cols(dmt, &info);
 
 	return info.exists ? 1 : 0;
@@ -343,8 +323,8 @@
 		if (!dm_task_set_uuid(dmt, _uuid))
 			return 0;
 	} else if (_switches[MAJOR_ARG] && _switches[MINOR_ARG]) {
-		if (!dm_task_set_major(dmt, _values[MAJOR_ARG]) ||
-		    !dm_task_set_minor(dmt, _values[MINOR_ARG]))
+		if (!dm_task_set_major(dmt, _int_args[MAJOR_ARG]) ||
+		    !dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
 			return 0;
 	} else if (!optional) {
 		fprintf(stderr, "No device specified.\n");
@@ -435,19 +415,19 @@
 	if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
 		goto out;
 
-	if (_switches[MAJOR_ARG] && !dm_task_set_major(dmt, _values[MAJOR_ARG]))
+	if (_switches[MAJOR_ARG] && !dm_task_set_major(dmt, _int_args[MAJOR_ARG]))
 		goto out;
 
-	if (_switches[MINOR_ARG] && !dm_task_set_minor(dmt, _values[MINOR_ARG]))
+	if (_switches[MINOR_ARG] && !dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
 		goto out;
 
-	if (_switches[UID_ARG] && !dm_task_set_uid(dmt, _values[UID_ARG]))
+	if (_switches[UID_ARG] && !dm_task_set_uid(dmt, _int_args[UID_ARG]))
 		goto out;
 
-	if (_switches[GID_ARG] && !dm_task_set_gid(dmt, _values[GID_ARG]))
+	if (_switches[GID_ARG] && !dm_task_set_gid(dmt, _int_args[GID_ARG]))
 		goto out;
 
-	if (_switches[MODE_ARG] && !dm_task_set_mode(dmt, _values[MODE_ARG]))
+	if (_switches[MODE_ARG] && !dm_task_set_mode(dmt, _int_args[MODE_ARG]))
 		goto out;
 
 	if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
@@ -528,7 +508,11 @@
 	for (i = 0; i < argc; i++)
 		sz += strlen(argv[i]) + 1;
 
-	str = dm_malloc(sz);
+	if (!(str = dm_malloc(sz))) {
+		err("message string allocation failed");
+		goto out;
+	}
+
 	memset(str, 0, sz);
 
 	for (i = 0; i < argc; i++) {
@@ -915,7 +899,7 @@
 	void *next = NULL;
 	uint64_t start, length;
 	char *target_type = NULL;
-	char *params;
+	char *params, *c;
 	int cmd;
 	struct dm_names *names = (struct dm_names *) data;
 	const char *name = NULL;
@@ -978,6 +962,17 @@
 			if (data && !_switches[VERBOSE_ARG])
 				printf("%s: ", name);
 			if (target_type) {
+
+			/* Suppress encryption key */
+			if (!_switches[SHOWKEYS_ARG] &&
+			    !strcmp(target_type, "crypt")) {
+				c = params;
+				while (*c && *c != ' ')
+					c++;
+				c++;
+				while (*c && *c != ' ')
+					*c++ = '0';
+			}
 				printf("%" PRIu64 " %" PRIu64 " %s %s",
 				       start, length, target_type, params);
 			}
@@ -1477,6 +1472,194 @@
 }
 
 /*
+ * Report device information
+ */
+
+/* dm specific display functions */
+
+static int _int32_disp(struct dm_report *rh,
+		       struct dm_pool *mem __attribute((unused)),
+		       struct dm_report_field *field, const void *data,
+		       void *private __attribute((unused)))
+{
+	const int32_t value = *(const int32_t *)data;
+
+	return dm_report_field_int32(rh, field, &value);
+}
+
+static int _uint32_disp(struct dm_report *rh,
+			struct dm_pool *mem __attribute((unused)),
+		        struct dm_report_field *field, const void *data,
+		        void *private __attribute((unused)))
+{
+	const uint32_t value = *(const int32_t *)data;
+
+	return dm_report_field_uint32(rh, field, &value);
+}
+
+static int _dm_name_disp(struct dm_report *rh,
+			 struct dm_pool *mem __attribute((unused)),
+			 struct dm_report_field *field, const void *data,
+			 void *private __attribute((unused)))
+{
+	const char *name = dm_task_get_name((struct dm_task *) data);
+
+	return dm_report_field_string(rh, field, &name);
+}
+
+static int _dm_uuid_disp(struct dm_report *rh,
+			 struct dm_pool *mem __attribute((unused)),
+			 struct dm_report_field *field,
+			 const void *data, void *private __attribute((unused)))
+{
+	const char *uuid = dm_task_get_uuid((struct dm_task *) data);
+
+	if (!uuid || !*uuid)
+		uuid = "";
+
+	return dm_report_field_string(rh, field, &uuid);
+}
+
+static int _dm_info_status_disp(struct dm_report *rh,
+				struct dm_pool *mem __attribute((unused)),
+				struct dm_report_field *field, const void *data,
+				void *private __attribute((unused)))
+{
+	char buf[5];
+	const char *s = buf;
+	struct dm_info *info = (struct dm_info *) data;
+
+	buf[0] = info->live_table ? 'L' : '-';
+	buf[1] = info->inactive_table ? 'I' : '-';
+	buf[2] = info->suspended ? 's' : '-';
+	buf[3] = info->read_only ? 'r' : 'w';
+	buf[4] = '\0';
+
+	return dm_report_field_string(rh, field, &s);
+}
+
+/* Report types */
+enum { DR_TASK = 1, DR_INFO = 2 };
+
+static void *_task_get_obj(void *obj)
+{
+	return ((struct dmsetup_report_obj *)obj)->task;
+}
+
+static void *_info_get_obj(void *obj)
+{
+	return ((struct dmsetup_report_obj *)obj)->info;
+}
+
+static const struct dm_report_object_type _report_types[] = {
+	{ DR_TASK, "Mapped Device Name", "", _task_get_obj },
+	{ DR_INFO, "Mapped Device Information", "", _info_get_obj },
+	{ 0, "", "", NULL },
+};
+
+/* Column definitions */
+#define OFFSET_OF(strct, field) ((unsigned int) &((struct strct *)NULL)->field)
+#define STR (DM_REPORT_FIELD_TYPE_STRING)
+#define NUM (DM_REPORT_FIELD_TYPE_NUMBER)
+#define FIELD_O(type, strct, sorttype, head, field, width, func, id, desc) {DR_ ## type, sorttype, OFFSET_OF(strct, field), width, id, head, &_ ## func ## _disp, desc},
+#define FIELD_F(type, sorttype, head, width, func, id, desc) {DR_ ## type, sorttype, 0, width, id, head, &_ ## func ## _disp, desc},
+
+static const struct dm_report_field_type _report_fields[] = {
+/* *INDENT-OFF* */
+FIELD_F(TASK, STR, "Name", 16, dm_name, "name", "Name of mapped device.")
+FIELD_F(TASK, STR, "UUID", 32, dm_uuid, "uuid", "Unique (optional) identifier for mapped device.")
+FIELD_F(INFO, STR, "Stat", 4, dm_info_status, "attr", "(L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.")
+FIELD_O(INFO, dm_info, NUM, "Maj", major, 3, int32, "major", "Block device major number.")
+FIELD_O(INFO, dm_info, NUM, "Min", minor, 3, int32, "minor", "Block device minor number.")
+FIELD_O(INFO, dm_info, NUM, "Open", open_count, 4, int32, "open", "Number of references to open device, if requested.")
+FIELD_O(INFO, dm_info, NUM, "Targ", target_count, 4, int32, "segments", "Number of segments in live table, if present.")
+FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "events", "Number of most recent event.")
+{0, 0, 0, 0, "", "", NULL, NULL},
+/* *INDENT-ON* */
+};
+
+#undef STR
+#undef NUM
+#undef FIELD_O
+#undef FIELD_F
+
+static const char *default_report_options = "name,major,minor,attr,open,segments,events,uuid";
+
+static int _report_init(struct command *c)
+{
+	char *options = (char *) default_report_options;
+	const char *keys = "";
+	const char *separator = " ";
+	int aligned = 1, headings = 1, buffered = 0;
+	uint32_t report_type = 0;
+	uint32_t flags = 0;
+	size_t len = 0;
+	int r = 0;
+
+	/* emulate old dmsetup behaviour */
+	if (_switches[NOHEADINGS_ARG]) {
+		separator = ":";
+		aligned = 0;
+		headings = 0;
+	}
+
+	if (_switches[OPTIONS_ARG] && _string_args[OPTIONS_ARG]) {
+		if (*_string_args[OPTIONS_ARG] != '+')
+			options = _string_args[OPTIONS_ARG];
+		else {
+			len = strlen(default_report_options) +
+			      strlen(_string_args[OPTIONS_ARG]) + 1;
+			if (!(options = dm_malloc(len))) {
+				err("Failed to allocate option string.");
+				return 0;
+			}
+			if (dm_snprintf(options, len, "%s,%s",
+					default_report_options,
+					&_string_args[OPTIONS_ARG][1]) < 0) {
+				err("snprintf failed");
+				goto out;
+			}
+		}
+	}
+
+	if (_switches[SORT_ARG] && _string_args[SORT_ARG]) {
+		keys = _string_args[SORT_ARG];
+		buffered = 1;
+		if (c && (!strcmp(c->name, "status") || !strcmp(c->name, "table"))) {
+			err("--sort is not yet supported with status and table");
+			goto out;
+		}
+	}
+
+	if (_switches[SEPARATOR_ARG] && _string_args[SEPARATOR_ARG]) {
+		separator = _string_args[SEPARATOR_ARG];
+		aligned = 0;
+	}
+
+	if (aligned)
+		flags |= DM_REPORT_OUTPUT_ALIGNED;
+
+	if (buffered)
+		flags |= DM_REPORT_OUTPUT_BUFFERED;
+
+	if (headings)
+		flags |= DM_REPORT_OUTPUT_HEADINGS;
+
+	if (!(_report = dm_report_init(&report_type,
+					_report_types, _report_fields,
+					options, separator, flags, keys, NULL)))
+		goto out;
+
+	r = 1;
+
+out:
+	if (len)
+		dm_free(options);
+
+	return r;
+}
+
+/*
  * List devices
  */
 static int _ls(int argc, char **argv, void *data)
@@ -1490,20 +1673,13 @@
 		return _process_all(argc, argv, 0, _display_name);
 }
 
+static int _help(int argc, char **argv, void *data);
+
 /*
- * dispatch table
+ * Dispatch table
  */
-typedef int (*command_fn) (int argc, char **argv, void *data);
-
-struct command {
-	const char *name;
-	const char *help;
-	int min_args;
-	int max_args;
-	command_fn fn;
-};
-
 static struct command _commands[] = {
+	{"help", "[-c|-C|--columns]", 0, 0, _help},
 	{"create", "<dev_name> [-j|--major <major> -m|--minor <minor>]\n"
 	  "\t                  [-U|--uid <uid>] [-G|--gid <gid>] [-M|--mode <octal_mode>]\n"
 	  "\t                  [-u|uuid <uuid>]\n"
@@ -1522,7 +1698,7 @@
 	{"info", "[<device>]", 0, 1, _info},
 	{"deps", "[<device>]", 0, 1, _deps},
 	{"status", "[<device>] [--target <target_type>]", 0, 1, _status},
-	{"table", "[<device>] [--target <target_type>]", 0, 1, _status},
+	{"table", "[<device>] [--target <target_type>] [--showkeys]", 0, 1, _status},
 	{"wait", "<device> [<event_nr>]", 0, 2, _wait},
 	{"mknodes", "[<device>]", 0, 1, _mknodes},
 	{"targets", "", 0, 0, _targets},
@@ -1537,14 +1713,18 @@
 
 	fprintf(out, "Usage:\n\n");
 	fprintf(out, "dmsetup [--version] [-v|--verbose [-v|--verbose ...]]\n"
-		"        [-r|--readonly] [--noopencount] [--nolockfs]\n\n");
+		"        [-r|--readonly] [--noopencount] [--nolockfs]\n"
+		"        [-c|-C|--columns] [-o <fields>] [-O|--sort <sort_fields>]\n"
+		"        [--noheadings] [--separator <separator>]\n\n");
 	for (i = 0; _commands[i].name; i++)
 		fprintf(out, "\t%s %s\n", _commands[i].name, _commands[i].help);
 	fprintf(out, "\n<device> may be device name or -u <uuid> or "
 		     "-j <major> -m <minor>\n");
+	fprintf(out, "<fields> are comma-separated.  Use 'help -c' for list.\n");
 	fprintf(out, "Table_file contents may be supplied on stdin.\n");
 	fprintf(out, "Tree options are: ascii, utf, vt100; compact, inverted, notrunc;\n"
-		     "                  [no]device, active, open, rw and uuid.\n\n");
+		     "                  [no]device, active, open, rw and uuid.\n");
+	fprintf(out, "\n");
 	return;
 }
 
@@ -1555,6 +1735,23 @@
 		     "[-o offset] [-f|loop_device] [file]\n\n");
 }
 
+static int _help(int argc __attribute((unused)),
+		 char **argv __attribute((unused)),
+		 void *data __attribute((unused)))
+{
+	_usage(stderr);
+
+	if (_switches[COLS_ARG]) {
+		_switches[OPTIONS_ARG] = 1;
+		_string_args[OPTIONS_ARG] = (char *) "help";
+		_switches[SORT_ARG] = 0;
+	
+		(void) _report_init(NULL);
+	}
+
+	return 1;
+}
+
 static struct command *_find_command(const char *name)
 {
 	int i;
@@ -1653,7 +1850,7 @@
 	char *buf;
 	char *device;
 
-	if (!(buf = dm_malloc(PATH_MAX)));
+	if (!(buf = dm_malloc(PATH_MAX)))
 		return NULL;
 
 	if (dev[0] == '/') {
@@ -1683,7 +1880,8 @@
 /*
  *  create a table for a mapped device using the loop target.
  */
-static int _loop_table(char *table, size_t tlen, char *file, char *dev, off_t off)
+static int _loop_table(char *table, size_t tlen, char *file,
+		       char *dev __attribute((unused)), off_t off)
 {
 	struct stat fbuf;
 	off_t size, sectors;
@@ -1716,7 +1914,7 @@
 
 #ifdef HAVE_SYS_STATVFS_H
 	if (fstatvfs(fd, &fsbuf))
-		goto error;       
+		goto error;
 
 	/* FIXME Fragment size currently unused */
 	blksize = fsbuf.f_frsize;
@@ -1724,7 +1922,7 @@
 
 	close(fd);
 
-	if (dm_snprintf(table, tlen, "%llu %llu loop %s %llu\n", 0ULL, 
+	if (dm_snprintf(table, tlen, "%llu %llu loop %s %llu\n", 0ULL,
 			(long long unsigned)sectors, file, off) < 0)
 		return 0;
 
@@ -1868,6 +2066,9 @@
 		{"noopencount", 0, &ind, NOOPENCOUNT_ARG},
 		{"notable", 0, &ind, NOTABLE_ARG},
 		{"options", 1, &ind, OPTIONS_ARG},
+		{"separator", 1, &ind, SEPARATOR_ARG},
+		{"showkeys", 0, &ind, SHOWKEYS_ARG},
+		{"sort", 1, &ind, SORT_ARG},
 		{"table", 1, &ind, TABLE_ARG},
 		{"target", 1, &ind, TARGET_ARG},
 		{"tree", 0, &ind, TREE_ARG},
@@ -1885,7 +2086,7 @@
 	 * Zero all the index counts.
 	 */
 	memset(&_switches, 0, sizeof(_switches));
-	memset(&_values, 0, sizeof(_values));
+	memset(&_int_args, 0, sizeof(_int_args));
 
 	namebase = strdup((*argv)[0]);
 	base = basename(namebase);
@@ -1897,17 +2098,17 @@
 		_switches[OPTIONS_ARG]++;
 		_switches[MAJOR_ARG]++;
 		_switches[MINOR_ARG]++;
-		_fields = (char *) "name";
+		_string_args[OPTIONS_ARG] = (char *) "name";
 
 		if (*argc == 3) {
-			_values[MAJOR_ARG] = atoi((*argv)[1]);
-			_values[MINOR_ARG] = atoi((*argv)[2]);
+			_int_args[MAJOR_ARG] = atoi((*argv)[1]);
+			_int_args[MINOR_ARG] = atoi((*argv)[2]);
 			*argc -= 2;
 			*argv += 2;
 		} else if ((*argc == 2) &&
 			   (2 == sscanf((*argv)[1], "%i:%i",
-					&_values[MAJOR_ARG],
-					&_values[MINOR_ARG]))) {
+					&_int_args[MAJOR_ARG],
+					&_int_args[MINOR_ARG]))) {
 			*argc -= 1;
 			*argv += 1;
 		} else {
@@ -1919,7 +2120,7 @@
 		return 1;
 	}
 
-	if(!strcmp(base, "losetup") || !strcmp(base, "dmlosetup")){
+	if (!strcmp(base, "losetup") || !strcmp(base, "dmlosetup")){
 		r = _process_losetup_switches(base, argc, argv);
 		free(namebase);
 		return r;
@@ -1929,7 +2130,7 @@
 
 	optarg = 0;
 	optind = OPTIND_INIT;
-	while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "cCfG:j:m:M:no:ru:U:v",
+	while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "cCfG:j:m:M:no:O:ru:U:v",
 					    long_options, NULL)) != -1) {
 		if (c == ':' || c == '?')
 			return 0;
@@ -1941,17 +2142,25 @@
 			_switches[READ_ONLY]++;
 		if (c == 'j' || ind == MAJOR_ARG) {
 			_switches[MAJOR_ARG]++;
-			_values[MAJOR_ARG] = atoi(optarg);
+			_int_args[MAJOR_ARG] = atoi(optarg);
 		}
 		if (c == 'm' || ind == MINOR_ARG) {
 			_switches[MINOR_ARG]++;
-			_values[MINOR_ARG] = atoi(optarg);
+			_int_args[MINOR_ARG] = atoi(optarg);
 		}
 		if (c == 'n' || ind == NOTABLE_ARG)
 			_switches[NOTABLE_ARG]++;
 		if (c == 'o' || ind == OPTIONS_ARG) {
 			_switches[OPTIONS_ARG]++;
-			_fields = optarg;
+			_string_args[OPTIONS_ARG] = optarg;
+		}
+		if (ind == SEPARATOR_ARG) {
+			_switches[SEPARATOR_ARG]++;
+			_string_args[SEPARATOR_ARG] = optarg;
+		}
+		if (c == 'O' || ind == SORT_ARG) {
+			_switches[SORT_ARG]++;
+			_string_args[SORT_ARG] = optarg;
 		}
 		if (c == 'v' || ind == VERBOSE_ARG)
 			_switches[VERBOSE_ARG]++;
@@ -1961,16 +2170,16 @@
 		}
 		if (c == 'G' || ind == GID_ARG) {
 			_switches[GID_ARG]++;
-			_values[GID_ARG] = atoi(optarg);
+			_int_args[GID_ARG] = atoi(optarg);
 		}
 		if (c == 'U' || ind == UID_ARG) {
 			_switches[UID_ARG]++;
-			_values[UID_ARG] = atoi(optarg);
+			_int_args[UID_ARG] = atoi(optarg);
 		}
 		if (c == 'M' || ind == MODE_ARG) {
 			_switches[MODE_ARG]++;
 			/* FIXME Accept modes as per chmod */
-			_values[MODE_ARG] = (int) strtol(optarg, NULL, 8);
+			_int_args[MODE_ARG] = (int) strtol(optarg, NULL, 8);
 		}
 		if ((ind == EXEC_ARG)) {
 			_switches[EXEC_ARG]++;
@@ -1988,6 +2197,8 @@
 			_switches[NOLOCKFS_ARG]++;
 		if ((ind == NOOPENCOUNT_ARG))
 			_switches[NOOPENCOUNT_ARG]++;
+		if ((ind == SHOWKEYS_ARG))
+			_switches[SHOWKEYS_ARG]++;
 		if ((ind == TABLE_ARG)) {
 			_switches[TABLE_ARG]++;
 			_table = optarg;
@@ -2008,13 +2219,7 @@
 		return 0;
 	}
 
-	if (_switches[COLS_ARG] && _switches[OPTIONS_ARG] &&
-	    strcmp(_fields, "name")) {
-		fprintf(stderr, "Only -o name is supported so far.\n");
-		return 0;
-	}
-
-	if (_switches[TREE_ARG] && !_process_tree_options(_fields))
+	if (_switches[TREE_ARG] && !_process_tree_options(_string_args[OPTIONS_ARG]))
 		return 0;
 
 	if (_switches[TABLE_ARG] && _switches[NOTABLE_ARG]) {
@@ -2062,6 +2267,9 @@
 		goto out;
 	}
 
+	if (_switches[COLS_ARG] && !_report_init(c))
+		goto out;
+
       doit:
 	if (!c->fn(argc, argv, NULL)) {
 		fprintf(stderr, "Command failed\n");
@@ -2071,5 +2279,10 @@
 	r = 0;
 
 out:
+	if (_report) {
+		dm_report_output(_report);
+		dm_report_free(_report);
+	}
+
 	return r;
 }

Modified: devmapper/trunk/include/configure.h.in
==============================================================================
--- devmapper/trunk/include/configure.h.in	(original)
+++ devmapper/trunk/include/configure.h.in	Sat Apr  7 09:09:48 2007
@@ -3,6 +3,15 @@
 /* Define to 1 if the `closedir' function returns void instead of `int'. */
 #undef CLOSEDIR_VOID
 
+/* Path to dmeventd binary. */
+#undef DMEVENTD_PATH
+
+/* Path to dmeventd pidfile. */
+#undef DMEVENTD_PIDFILE
+
+/* Library version */
+#undef DM_LIB_VERSION
+
 /* Define to 1 if canonicalize_file_name is available. */
 #undef HAVE_CANONICALIZE_FILE_NAME
 
@@ -168,11 +177,9 @@
 /* Define to empty if `const' does not conform to ANSI C. */
 #undef const
 
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+   if it is not supported. */
 #undef inline
-#endif
 
 /* Define to rpl_malloc if the replacement function should be used. */
 #undef malloc

Modified: devmapper/trunk/lib/.exported_symbols
==============================================================================
--- devmapper/trunk/lib/.exported_symbols	(original)
+++ devmapper/trunk/lib/.exported_symbols	Sat Apr  7 09:09:48 2007
@@ -67,6 +67,7 @@
 dm_tree_node_add_mirror_target_log
 dm_tree_node_add_target_area
 dm_tree_skip_lockfs
+dm_tree_use_no_flush_suspend
 dm_is_dm_major
 dm_mknodes
 dm_malloc_aux
@@ -115,3 +116,16 @@
 dm_split_lvm_name
 dm_split_words
 dm_snprintf
+dm_basename
+dm_asprintf
+dm_report_init
+dm_report_object
+dm_report_output
+dm_report_free
+dm_report_get_private
+dm_report_field_string
+dm_report_field_int
+dm_report_field_int32
+dm_report_field_uint32
+dm_report_field_uint64
+dm_report_field_set_value

Modified: devmapper/trunk/lib/Makefile.in
==============================================================================
--- devmapper/trunk/lib/Makefile.in	(original)
+++ devmapper/trunk/lib/Makefile.in	Sat Apr  7 09:09:48 2007
@@ -24,6 +24,7 @@
 	libdm-file.c \
 	libdm-deptree.c \
 	libdm-string.c \
+	libdm-report.c \
 	mm/abi.c \
 	mm/dbg_malloc.c \
 	mm/pool.c \
@@ -39,8 +40,8 @@
   LIB_SHARED = $(interface)/libdevmapper.so
 endif
 
-CFLAGS += -DDEVICE_UID=@DEVICE_UID@ -DDEVICE_GID=@DEVICE_GID@ \
-	  -DDEVICE_MODE=@DEVICE_MODE@
+DEFS += -DDEVICE_UID=@DEVICE_UID@ -DDEVICE_GID=@DEVICE_GID@ \
+	-DDEVICE_MODE=@DEVICE_MODE@
 
 include ../make.tmpl
 
@@ -85,7 +86,7 @@
 .PHONY: distclean_lib distclean
 
 distclean_lib: 
-	$(RM) libdm-common.h libdevmapper.pc
+	$(RM) libdevmapper.pc
 
 distclean: distclean_lib
 

Modified: devmapper/trunk/lib/datastruct/hash.c
==============================================================================
--- devmapper/trunk/lib/datastruct/hash.c	(original)
+++ devmapper/trunk/lib/datastruct/hash.c	Sat Apr  7 09:09:48 2007
@@ -230,12 +230,14 @@
 	t->num_nodes = 0u;
 }
 
-char *dm_hash_get_key(struct dm_hash_table *t, struct dm_hash_node *n)
+char *dm_hash_get_key(struct dm_hash_table *t __attribute((unused)),
+		      struct dm_hash_node *n)
 {
 	return n->key;
 }
 
-void *dm_hash_get_data(struct dm_hash_table *t, struct dm_hash_node *n)
+void *dm_hash_get_data(struct dm_hash_table *t __attribute((unused)),
+		       struct dm_hash_node *n)
 {
 	return n->data;
 }

Modified: devmapper/trunk/lib/ioctl/libdm-iface.c
==============================================================================
--- devmapper/trunk/lib/ioctl/libdm-iface.c	(original)
+++ devmapper/trunk/lib/ioctl/libdm-iface.c	Sat Apr  7 09:09:48 2007
@@ -149,7 +149,8 @@
 			if (!strcmp(name, nm)) {
 				if (number) {
 					*number = num;
-					fclose(fl);
+					if (fclose(fl))
+						log_error("%s: fclose failed: %s", file, strerror(errno));
 					return 1;
 				}
 				dm_bit_set(_dm_bitset, num);
@@ -158,7 +159,8 @@
 			c = fgetc(fl);
 		} while (c != EOF && c != '\n');
 	}
-	fclose(fl);
+	if (fclose(fl))
+		log_error("%s: fclose failed: %s", file, strerror(errno));
 
 	if (number) {
 		log_error("%s: No entry for %s found", file, name);
@@ -1322,7 +1324,7 @@
 
 	dir = dm_dir();
 	if (!(d = opendir(dir))) {
-		fprintf(stderr, "opendir %s: %s", dir, strerror(errno));
+		log_error("opendir %s: %s", dir, strerror(errno));
 		return 0;
 	}
 
@@ -1335,9 +1337,8 @@
 		dm_task_run(dmt);
 	}
 
-	if (closedir(d)) {
-		fprintf(stderr, "closedir %s: %s", dir, strerror(errno));
-	}
+	if (closedir(d))
+		log_error("closedir %s: %s", dir, strerror(errno));
 
 	return r;
 }
@@ -1506,6 +1507,8 @@
 	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)) ||

Modified: devmapper/trunk/lib/libdevmapper.h
==============================================================================
--- devmapper/trunk/lib/libdevmapper.h	(original)
+++ devmapper/trunk/lib/libdevmapper.h	Sat Apr  7 09:09:48 2007
@@ -124,10 +124,10 @@
 };
 
 struct dm_versions {
-        uint32_t next;		/* Offset to next struct from start of this struct */
-        uint32_t version[3];
+	uint32_t next;		/* Offset to next struct from start of this struct */
+	uint32_t version[3];
 
-        char name[0];
+	char name[0];
 };
 
 int dm_get_library_version(char *version, size_t size);
@@ -236,12 +236,12 @@
  * Add a new node to the tree if it doesn't already exist.
  */
 struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *tree,
-                                            const char *name,
-                                            const char *uuid,
-                                            uint32_t major, uint32_t minor,
-                                            int read_only,
-                                            int clear_inactive,
-                                            void *context);
+					 const char *name,
+					 const char *uuid,
+					 uint32_t major, uint32_t minor,
+					 int read_only,
+					 int clear_inactive,
+					 void *context);
 
 struct dm_tree_node *_dm_tree_add_new_dev_mode(struct dm_tree *tree,
                                             const char *name,
@@ -298,16 +298,16 @@
  * Ignores devices that don't have a uuid starting with uuid_prefix.
  */
 int dm_tree_preload_children(struct dm_tree_node *dnode,
-                                 const char *uuid_prefix,
-                                 size_t uuid_prefix_len);
+			     const char *uuid_prefix,
+			     size_t uuid_prefix_len);
 
 /*
  * Resume a device plus all dependencies.
  * Ignores devices that don't have a uuid starting with uuid_prefix.
  */
 int dm_tree_activate_children(struct dm_tree_node *dnode,
-                                 const char *uuid_prefix,
-                                 size_t uuid_prefix_len);
+			      const char *uuid_prefix,
+			      size_t uuid_prefix_len);
 
 /*
  * Suspend a device plus all dependencies.
@@ -325,6 +325,16 @@
 void dm_tree_skip_lockfs(struct dm_tree_node *dnode);
 
 /*
+ * Set the 'noflush' flag when suspending devices.
+ * If the kernel supports it, instead of erroring outstanding I/O that
+ * cannot be completed, the I/O is queued and resubmitted when the
+ * device is resumed.  This affects multipath devices when all paths
+ * have failed and queue_if_no_path is set, and mirror devices when
+ * block_on_error is set and the mirror log has failed.
+ */
+void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode);
+
+/*
  * Is the uuid prefix present in the tree?
  * Only returns 0 if every node was checked successfully.
  * Returns 1 if the tree walk has to be aborted.
@@ -617,4 +627,97 @@
  */
 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);
+
+/*
+ * Returns size of a buffer which is allocated with dm_malloc.
+ * Pointer to the buffer is stored in *buf.
+ * Returns -1 on failure leaving buf undefined.
+ */
+int dm_asprintf(char **buf, const char *format, ...);
+
+/*********************
+ * reporting functions
+ *********************/
+
+struct dm_report_object_type {
+	uint32_t id;			/* Powers of 2 */
+	const char *desc;
+	const char *prefix;		/* field id string prefix (optional) */
+	void *(*data_fn)(void *object);	/* callback from report_object() */
+};
+
+struct dm_report_field;
+
+/*
+ * dm_report_field_type flags
+ */
+#define DM_REPORT_FIELD_MASK		0x000000FF
+#define DM_REPORT_FIELD_ALIGN_MASK	0x0000000F
+#define DM_REPORT_FIELD_ALIGN_LEFT	0x00000001
+#define DM_REPORT_FIELD_ALIGN_RIGHT	0x00000002
+#define DM_REPORT_FIELD_TYPE_MASK	0x000000F0
+#define DM_REPORT_FIELD_TYPE_STRING	0x00000010
+#define DM_REPORT_FIELD_TYPE_NUMBER	0x00000020
+
+struct dm_report;
+struct dm_report_field_type {
+	uint32_t type;		/* object type id */
+	uint32_t flags;		/* DM_REPORT_FIELD_* */
+	uint32_t offset;	/* byte offset in the object */
+	int32_t width;		/* default width */
+	const char id[32];	/* string used to specify the field */
+	const char heading[32];	/* string printed in header */
+	int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
+			 struct dm_report_field *field, const void *data,
+			 void *private);
+	const char *desc;	/* description of the field */
+};
+
+/*
+ * dm_report_init output_flags
+ */
+#define DM_REPORT_OUTPUT_MASK		0x000000FF
+#define DM_REPORT_OUTPUT_ALIGNED	0x00000001
+#define DM_REPORT_OUTPUT_BUFFERED	0x00000002
+#define DM_REPORT_OUTPUT_HEADINGS	0x00000004
+
+struct dm_report *dm_report_init(uint32_t *report_types,
+				 const struct dm_report_object_type *types,
+				 const struct dm_report_field_type *fields,
+				 const char *output_fields,
+				 const char *output_separator,
+				 uint32_t output_flags,
+				 const char *sort_keys,
+				 void *private);
+int dm_report_object(struct dm_report *rh, void *object);
+int dm_report_output(struct dm_report *rh);
+void dm_report_free(struct dm_report *rh);
+
+/*
+ * Report functions are provided for simple data types.
+ * They take care of allocating copies of the data.
+ */
+int dm_report_field_string(struct dm_report *rh, struct dm_report_field *field,
+			   const char **data);
+int dm_report_field_int32(struct dm_report *rh, struct dm_report_field *field,
+			  const int32_t *data);
+int dm_report_field_uint32(struct dm_report *rh, struct dm_report_field *field,
+			   const uint32_t *data);
+int dm_report_field_int(struct dm_report *rh, struct dm_report_field *field,
+			const int *data);
+int dm_report_field_uint64(struct dm_report *rh, struct dm_report_field *field,
+			   const uint64_t *data);
+
+/*
+ * For custom fields, allocate the data in 'mem' and use
+ * dm_report_field_set_value().
+ * 'sortvalue' may be NULL if it matches 'value'
+ */
+void dm_report_field_set_value(struct dm_report_field *field, const void *value,
+			       const void *sortvalue);
+
 #endif				/* LIB_DEVICE_MAPPER_H */

Modified: devmapper/trunk/lib/libdm-common.c
==============================================================================
--- devmapper/trunk/lib/libdm-common.c	(original)
+++ devmapper/trunk/lib/libdm-common.c	Sat Apr  7 09:09:48 2007
@@ -38,8 +38,8 @@
  * Library users can provide their own logging
  * function.
  */
-static void _default_log(int level, const char *file, int line,
-			 const char *f, ...)
+static void _default_log(int level, const char *file __attribute((unused)),
+			 int line __attribute((unused)), const char *f, ...)
 {
 	va_list ap;
 

Added: devmapper/trunk/lib/libdm-common.h
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/libdm-common.h	Sat Apr  7 09:09:48 2007
@@ -0,0 +1,31 @@
+/*
+ * 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 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_DMCOMMON_H
+#define LIB_DMCOMMON_H
+
+#include "libdevmapper.h"
+
+struct target *create_target(uint64_t start,
+			     uint64_t len,
+			     const char *type, const char *params);
+
+int add_dev_node(const char *dev_name, uint32_t minor, uint32_t major,
+		 uid_t uid, gid_t gid, mode_t mode);
+int rm_dev_node(const char *dev_name);
+int rename_dev_node(const char *old_name, const char *new_name);
+void update_devs(void);
+
+#endif

Modified: devmapper/trunk/lib/libdm-deptree.c
==============================================================================
--- devmapper/trunk/lib/libdm-deptree.c	(original)
+++ devmapper/trunk/lib/libdm-deptree.c	Sat Apr  7 09:09:48 2007
@@ -133,6 +133,7 @@
 	struct dm_hash_table *uuids;
 	struct dm_tree_node root;
 	int skip_lockfs;		/* 1 skips lockfs (for non-snapshots) */
+	int no_flush;		/* 1 sets noflush (mirrors/multipath) */
 };
 
 /* FIXME Consider exporting this */
@@ -165,6 +166,7 @@
 	list_init(&dtree->root.uses);
 	list_init(&dtree->root.used_by);
 	dtree->skip_lockfs = 0;
+	dtree->no_flush = 0;
 
 	if (!(dtree->mem = dm_pool_create("dtree", 1024))) {
 		log_error("dtree pool creation failed");
@@ -922,13 +924,15 @@
 }
 
 static int _suspend_node(const char *name, uint32_t major, uint32_t minor,
-			 int skip_lockfs, struct dm_info *newinfo)
+			 int skip_lockfs, int no_flush, struct dm_info *newinfo)
 {
 	struct dm_task *dmt;
 	int r;
 
-	log_verbose("Suspending %s (%" PRIu32 ":%" PRIu32 ")%s", name, major,
-		    minor, skip_lockfs ? "" : " with filesystem sync.");
+	log_verbose("Suspending %s (%" PRIu32 ":%" PRIu32 ")%s%s",
+		    name, major, minor,
+		    skip_lockfs ? "" : " with filesystem sync",
+		    no_flush ? "" : " without device flush");
 
 	if (!(dmt = dm_task_create(DM_DEVICE_SUSPEND))) {
 		log_error("Suspend dm_task creation failed for %s", name);
@@ -947,6 +951,9 @@
 	if (skip_lockfs && !dm_task_skip_lockfs(dmt))
 		log_error("Failed to set skip_lockfs flag.");
 
+	if (no_flush && !dm_task_no_flush(dmt))
+		log_error("Failed to set no_flush flag.");
+
 	if ((r = dm_task_run(dmt)))
 		r = dm_task_get_info(dmt, newinfo);
 
@@ -1010,6 +1017,11 @@
 	dnode->dtree->skip_lockfs = 1;
 }
 
+void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode)
+{
+	dnode->dtree->no_flush = 1;
+}
+
 int dm_tree_suspend_children(struct dm_tree_node *dnode,
 				   const char *uuid_prefix,
 				   size_t uuid_prefix_len)
@@ -1051,7 +1063,8 @@
 			continue;
 
 		if (!_suspend_node(name, info.major, info.minor,
-				   child->dtree->skip_lockfs, &newinfo)) {
+				   child->dtree->skip_lockfs,
+				   child->dtree->no_flush, &newinfo)) {
 			log_error("Unable to suspend %s (%" PRIu32
 				  ":%" PRIu32 ")", name, info.major,
 				  info.minor);
@@ -1224,7 +1237,9 @@
 	return 1;
 }
 
-static int _emit_areas_line(struct dm_task *dmt, struct load_segment *seg, char *params, size_t paramsize, int *pos)
+static int _emit_areas_line(struct dm_task *dmt __attribute((unused)),
+			    struct load_segment *seg, char *params,
+			    size_t paramsize, int *pos)
 {
 	struct seg_area *area;
 	char devbuf[10];

Added: devmapper/trunk/lib/libdm-report.c
==============================================================================
--- (empty file)
+++ devmapper/trunk/lib/libdm-report.c	Sat Apr  7 09:09:48 2007
@@ -0,0 +1,838 @@
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of device-mapper userspace tools.
+ * The code is based on LVM2 report function.
+ *
+ * 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
+ */
+
+#include "libdevmapper.h"
+#include "list.h"
+#include "log.h"
+
+/*
+ * Internal flags
+ */
+#define RH_SORT_REQUIRED	0x00000100
+#define RH_HEADINGS_PRINTED	0x00000200
+
+struct dm_report {
+	struct dm_pool *mem;
+
+	uint32_t report_types;
+	const char *field_prefix;
+	uint32_t flags;
+	const char *separator;
+
+	uint32_t keys_count;
+
+	/* Ordered list of fields needed for this report */
+	struct list field_props;
+
+	/* Rows of report data */
+	struct list rows;
+
+	/* Array of field definitions */
+	const struct dm_report_field_type *fields;
+	const struct dm_report_object_type *types;
+
+	/* To store caller private data */
+	void *private;
+};
+
+/*
+ * Internal per-field flags
+ */
+#define FLD_HIDDEN	0x00000100
+#define FLD_SORT_KEY	0x00000200
+#define FLD_ASCENDING	0x00000400
+#define FLD_DESCENDING	0x00000800
+
+struct field_properties {
+	struct list list;
+	uint32_t field_num;
+	uint32_t sort_posn;
+	int32_t width;
+	const struct dm_report_object_type *type;
+	uint32_t flags;
+};
+
+/*
+ * Report data field
+ */
+struct dm_report_field {
+	struct list list;
+	struct field_properties *props;
+
+	const char *report_string;	/* Formatted ready for display */
+	const void *sort_value;		/* Raw value for sorting */
+};
+
+struct row {
+	struct list list;
+	struct dm_report *rh;
+	struct list fields;			  /* Fields in display order */
+	struct dm_report_field *(*sort_fields)[]; /* Fields in sort order */
+};
+
+static const struct dm_report_object_type *_find_type(struct dm_report *rh,
+						      uint32_t report_type)
+{
+	const struct dm_report_object_type *t;
+
+	for (t = rh->types; t->data_fn; t++)
+		if (t->id == report_type)
+			return t;
+
+	return NULL;
+}
+
+/*
+ * Data-munging functions to prepare each data type for display and sorting
+ */
+
+int dm_report_field_string(struct dm_report *rh,
+			   struct dm_report_field *field, const char **data)
+{
+	char *repstr;
+
+	if (!(repstr = dm_pool_strdup(rh->mem, *data))) {
+		log_error("dm_report_field_string: dm_pool_strdup failed");
+		return 0;
+	}
+
+	field->report_string = repstr;
+	field->sort_value = (const void *) field->report_string;
+
+	return 1;
+}
+
+int dm_report_field_int(struct dm_report *rh,
+			struct dm_report_field *field, const int *data)
+{
+	const int value = *data;
+	uint64_t *sortval;
+	char *repstr;
+
+	if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
+		log_error("dm_report_field_int: dm_pool_alloc failed");
+		return 0;
+	}
+
+	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
+		log_error("dm_report_field_int: dm_pool_alloc failed");
+		return 0;
+	}
+
+	if (dm_snprintf(repstr, 12, "%d", value) < 0) {
+		log_error("dm_report_field_int: int too big: %d", value);
+		return 0;
+	}
+
+	*sortval = (const uint64_t) value;
+	field->sort_value = sortval;
+	field->report_string = repstr;
+
+	return 1;
+}
+
+int dm_report_field_uint32(struct dm_report *rh,
+			   struct dm_report_field *field, const uint32_t *data)
+{
+	const uint32_t value = *data;
+	uint64_t *sortval;
+	char *repstr;
+
+	if (!(repstr = dm_pool_zalloc(rh->mem, 12))) {
+		log_error("dm_report_field_uint32: dm_pool_alloc failed");
+		return 0;
+	}
+
+	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
+		log_error("dm_report_field_uint32: dm_pool_alloc failed");
+		return 0;
+	}
+
+	if (dm_snprintf(repstr, 11, "%u", value) < 0) {
+		log_error("dm_report_field_uint32: uint32 too big: %u", value);
+		return 0;
+	}
+
+	*sortval = (const uint64_t) value;
+	field->sort_value = sortval;
+	field->report_string = repstr;
+
+	return 1;
+}
+
+int dm_report_field_int32(struct dm_report *rh,
+			  struct dm_report_field *field, const int32_t *data)
+{
+	const int32_t value = *data;
+	uint64_t *sortval;
+	char *repstr;
+
+	if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
+		log_error("dm_report_field_int32: dm_pool_alloc failed");
+		return 0;
+	}
+
+	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
+		log_error("dm_report_field_int32: dm_pool_alloc failed");
+		return 0;
+	}
+
+	if (dm_snprintf(repstr, 12, "%d", value) < 0) {
+		log_error("dm_report_field_int32: int32 too big: %d", value);
+		return 0;
+	}
+
+	*sortval = (const uint64_t) value;
+	field->sort_value = sortval;
+	field->report_string = repstr;
+
+	return 1;
+}
+
+int dm_report_field_uint64(struct dm_report *rh,
+			   struct dm_report_field *field, const uint64_t *data)
+{
+	const int value = *data;
+	uint64_t *sortval;
+	char *repstr;
+
+	if (!(repstr = dm_pool_zalloc(rh->mem, 22))) {
+		log_error("dm_report_field_uint64: dm_pool_alloc failed");
+		return 0;
+	}
+
+	if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
+		log_error("dm_report_field_uint64: dm_pool_alloc failed");
+		return 0;
+	}
+
+	if (dm_snprintf(repstr, 21, "%d", value) < 0) {
+		log_error("dm_report_field_uint64: uint64 too big: %d", value);
+		return 0;
+	}
+
+	*sortval = (const uint64_t) value;
+	field->sort_value = sortval;
+	field->report_string = repstr;
+
+	return 1;
+}
+
+/*
+ * Helper functions for custom report functions
+ */
+void dm_report_field_set_value(struct dm_report_field *field, const void *value, const void *sortvalue)
+{
+	field->report_string = (const char *) value;
+	field->sort_value = sortvalue ? : value;
+}
+
+/*
+ * show help message
+ */
+static void _display_fields(struct dm_report *rh)
+{
+	uint32_t f;
+	const struct dm_report_object_type *type;
+	const char *desc, *last_desc = "";
+	size_t id_len = 0;
+
+	for (f = 0; rh->fields[f].report_fn; f++)
+		if (strlen(rh->fields[f].id) > id_len)
+			id_len = strlen(rh->fields[f].id);
+
+	for (f = 0; rh->fields[f].report_fn; f++) {
+		if ((type = _find_type(rh, rh->fields[f].type)) && type->desc)
+			desc = type->desc;
+		else
+			desc = " ";
+		if (desc != last_desc) {
+			if (*last_desc)
+				log_print(" ");
+			log_print("%s Fields", desc);
+			log_print("%*.*s", (int) strlen(desc) + 7,
+				  (int) strlen(desc) + 7,
+				  "------------------------------------------");
+				  
+		}
+
+		/* FIXME Add line-wrapping at terminal width (or 80 cols) */
+		log_print("  %-*s - %s", (int) id_len, rh->fields[f].id, rh->fields[f].desc);
+		last_desc = desc;
+	}
+}
+
+/*
+ * Initialise report handle
+ */
+static int _copy_field(struct dm_report *rh, struct field_properties *dest,
+		       uint32_t field_num)
+{
+	dest->field_num = field_num;
+	dest->width = rh->fields[field_num].width;
+	dest->flags = rh->fields[field_num].flags & DM_REPORT_FIELD_MASK;
+
+	/* set object type method */
+	dest->type = _find_type(rh, rh->fields[field_num].type);
+	if (!dest->type) {
+		log_error("dm_report: field not match: %s",
+			  rh->fields[field_num].id);
+		return 0;
+	}
+
+	return 1;
+}
+
+static int _field_match(struct dm_report *rh, const char *field, size_t flen)
+{
+	uint32_t f, l;
+	struct field_properties *fp;
+
+	if (!flen)
+		return 0;
+
+	for (f = 0; rh->fields[f].report_fn; f++) {
+		if ((!strncasecmp(rh->fields[f].id, field, flen) &&
+		     strlen(rh->fields[f].id) == flen) ||
+		    (l = strlen(rh->field_prefix),
+		     !strncasecmp(rh->field_prefix, rh->fields[f].id, l) &&
+		     !strncasecmp(rh->fields[f].id + l, field, flen) &&
+		     strlen(rh->fields[f].id) == l + flen)) {
+			rh->report_types |= rh->fields[f].type;
+			if (!(fp = dm_pool_zalloc(rh->mem, sizeof(*fp)))) {
+				log_error("dm_report: "
+					  "struct field_properties allocation "
+					  "failed");
+				return 0;
+			}
+			if (!_copy_field(rh, fp, f))
+				return 0;
+
+			list_add(&rh->field_props, &fp->list);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static int _add_sort_key(struct dm_report *rh, uint32_t field_num,
+			 uint32_t flags)
+{
+	struct field_properties *fp, *found = NULL;
+
+	list_iterate_items(fp, &rh->field_props) {
+		if (fp->field_num == field_num) {
+			found = fp;
+			break;
+		}
+	}
+
+	if (!found) {
+		rh->report_types |= rh->fields[field_num].type;
+		if (!(found = dm_pool_zalloc(rh->mem, sizeof(*found)))) {
+			log_error("dm_report: "
+				  "struct field_properties allocation failed");
+			return 0;
+		}
+		if (!_copy_field(rh, found, field_num))
+			return 0;
+
+		/* Add as a non-display field */
+		found->flags |= FLD_HIDDEN;
+
+		list_add(&rh->field_props, &found->list);
+	}
+
+	if (found->flags & FLD_SORT_KEY) {
+		log_error("dm_report: Ignoring duplicate sort field: %s",
+			  rh->fields[field_num].id);
+		return 1;
+	}
+
+	found->flags |= FLD_SORT_KEY;
+	found->sort_posn = rh->keys_count++;
+	found->flags |= flags;
+
+	return 1;
+}
+
+static int _key_match(struct dm_report *rh, const char *key, size_t len)
+{
+	uint32_t f, l;
+	uint32_t flags;
+
+	if (!len)
+		return 0;
+
+	if (*key == '+') {
+		key++;
+		len--;
+		flags = FLD_ASCENDING;
+	} else if (*key == '-') {
+		key++;
+		len--;
+		flags = FLD_DESCENDING;
+	} else
+		flags = FLD_ASCENDING;
+
+	if (!len) {
+		log_error("dm_report: Missing sort field name");
+		return 0;
+	}
+
+	for (f = 0; rh->fields[f].report_fn; f++) {
+		if ((!strncasecmp(rh->fields[f].id, key, len) &&
+		     strlen(rh->fields[f].id) == len) ||
+		    (l = strlen(rh->field_prefix),
+		     !strncasecmp(rh->field_prefix, rh->fields[f].id, l) &&
+		     !strncasecmp(rh->fields[f].id + l, key, len) &&
+		     strlen(rh->fields[f].id) == l + len)) {
+			return _add_sort_key(rh, f, flags);
+		}
+	}
+
+	return 0;
+}
+
+static int _parse_options(struct dm_report *rh, const char *format)
+{
+	const char *ws;		/* Word start */
+	const char *we = format;	/* Word end */
+
+	while (*we) {
+		/* Allow consecutive commas */
+		while (*we && *we == ',')
+			we++;
+
+		/* start of the field name */
+		ws = we;
+		while (*we && *we != ',')
+			we++;
+
+		if (!_field_match(rh, ws, (size_t) (we - ws))) {
+			_display_fields(rh);
+			log_print(" ");
+			if (strcasecmp(ws, "help") && strcmp(ws, "?"))
+				log_error("Unrecognised field: %.*s",
+					  (int) (we - ws), ws);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static int _parse_keys(struct dm_report *rh, const char *keys)
+{
+	const char *ws;		/* Word start */
+	const char *we = keys;	/* Word end */
+
+	while (*we) {
+		/* Allow consecutive commas */
+		while (*we && *we == ',')
+			we++;
+		ws = we;
+		while (*we && *we != ',')
+			we++;
+		if (!_key_match(rh, ws, (size_t) (we - ws))) {
+			log_error("dm_report: Unrecognised field: %.*s",
+				  (int) (we - ws), ws);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+struct dm_report *dm_report_init(uint32_t *report_types,
+				 const struct dm_report_object_type *types,
+				 const struct dm_report_field_type *fields,
+				 const char *output_fields,
+				 const char *output_separator,
+				 uint32_t output_flags,
+				 const char *sort_keys,
+				 void *private)
+{
+	struct dm_report *rh;
+	const struct dm_report_object_type *type;
+
+	if (!(rh = dm_malloc(sizeof(*rh)))) {
+		log_error("dm_report_init: dm_malloc failed");
+		return 0;
+	}
+	memset(rh, 0, sizeof(*rh));
+
+	/*
+	 * rh->report_types is updated in _parse_options() and _parse_keys()
+	 * to contain all types corresponding to the fields specified by
+	 * options or keys.
+	 */
+	if (report_types)
+		rh->report_types = *report_types;
+
+	rh->separator = output_separator;
+	rh->fields = fields;
+	rh->types = types;
+	rh->private = private;
+
+	rh->flags |= output_flags & DM_REPORT_OUTPUT_MASK;
+
+	if (output_flags & DM_REPORT_OUTPUT_BUFFERED)
+		rh->flags |= RH_SORT_REQUIRED;
+
+	list_init(&rh->field_props);
+	list_init(&rh->rows);
+
+	if ((type = _find_type(rh, rh->report_types)) && type->prefix)
+		rh->field_prefix = type->prefix;
+	else
+		rh->field_prefix = "";
+
+	if (!(rh->mem = dm_pool_create("report", 10 * 1024))) {
+		log_error("dm_report_init: allocation of memory pool failed");
+		return NULL;
+	}
+
+	/* Generate list of fields for output based on format string & flags */
+	if (!_parse_options(rh, output_fields))
+		return NULL;
+
+	if (!_parse_keys(rh, sort_keys))
+		return NULL;
+
+	/* Return updated types value for further compatility check by caller */
+	if (report_types)
+		*report_types = rh->report_types;
+
+	return rh;
+}
+
+void dm_report_free(struct dm_report *rh)
+{
+	dm_pool_destroy(rh->mem);
+	dm_free(rh);
+}
+
+/*
+ * Create a row of data for an object
+ */
+static void * _report_get_field_data(struct dm_report *rh,
+			      struct field_properties *fp, void *object)
+{
+	void *ret = fp->type->data_fn(object);
+
+	if (!ret)
+		return NULL;
+
+	return ret + rh->fields[fp->field_num].offset;
+}
+
+int dm_report_object(struct dm_report *rh, void *object)
+{
+	struct field_properties *fp;
+	struct row *row;
+	struct dm_report_field *field;
+	void *data = NULL;
+
+	if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
+		log_error("dm_report_object: struct row allocation failed");
+		return 0;
+	}
+
+	row->rh = rh;
+
+	if ((rh->flags & RH_SORT_REQUIRED) &&
+	    !(row->sort_fields =
+		dm_pool_zalloc(rh->mem, sizeof(struct dm_report_field *) *
+			       rh->keys_count))) {
+		log_error("dm_report_object: "
+			  "row sort value structure allocation failed");
+		return 0;
+	}
+
+	list_init(&row->fields);
+	list_add(&rh->rows, &row->list);
+
+	/* For each field to be displayed, call its report_fn */
+	list_iterate_items(fp, &rh->field_props) {
+		if (!(field = dm_pool_zalloc(rh->mem, sizeof(*field)))) {
+			log_error("dm_report_object: "
+				  "struct dm_report_field allocation failed");
+			return 0;
+		}
+		field->props = fp;
+
+		data = _report_get_field_data(rh, fp, object);
+		if (!data)
+			return 0;
+
+		if (!rh->fields[fp->field_num].report_fn(rh, rh->mem,
+							 field, data,
+							 rh->private)) {
+			log_error("dm_report_object: "
+				  "report function failed for field %s",
+				  rh->fields[fp->field_num].id);
+			return 0;
+		}
+
+		if ((strlen(field->report_string) > field->props->width))
+			field->props->width = strlen(field->report_string);
+
+		if ((rh->flags & RH_SORT_REQUIRED) &&
+		    (field->props->flags & FLD_SORT_KEY)) {
+			(*row->sort_fields)[field->props->sort_posn] = field;
+		}
+		list_add(&row->fields, &field->list);
+	}
+
+	if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
+		return dm_report_output(rh);
+
+	return 1;
+}
+
+/*
+ * Print row of headings
+ */
+static int _report_headings(struct dm_report *rh)
+{
+	struct field_properties *fp;
+	const char *heading;
+	char buf[1024];
+
+	if (rh->flags & RH_HEADINGS_PRINTED)
+		return 1;
+
+	rh->flags |= RH_HEADINGS_PRINTED;
+
+	if (!(rh->flags & DM_REPORT_OUTPUT_HEADINGS))
+		return 1;
+
+	if (!dm_pool_begin_object(rh->mem, 128)) {
+		log_error("dm_report: "
+			  "dm_pool_begin_object failed for headings");
+		return 0;
+	}
+
+	/* First heading line */
+	list_iterate_items(fp, &rh->field_props) {
+		if (fp->flags & FLD_HIDDEN)
+			continue;
+
+		heading = rh->fields[fp->field_num].heading;
+		if (rh->flags & DM_REPORT_OUTPUT_ALIGNED) {
+			if (dm_snprintf(buf, sizeof(buf), "%-*.*s",
+					 fp->width, fp->width, heading) < 0) {
+				log_error("dm_report: snprintf heading failed");
+				goto bad;
+			}
+			if (!dm_pool_grow_object(rh->mem, buf, fp->width)) {
+				log_error("dm_report: Failed to generate report headings for printing");
+				goto bad;
+			}
+		} else if (!dm_pool_grow_object(rh->mem, heading,
+						strlen(heading))) {
+			log_error("dm_report: Failed to generate report headings for printing");
+			goto bad;
+		}
+
+		if (!list_end(&rh->field_props, &fp->list))
+			if (!dm_pool_grow_object(rh->mem, rh->separator,
+					      strlen(rh->separator))) {
+				log_error("dm_report: Failed to generate report headings for printing");
+				goto bad;
+			}
+	}
+	if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
+		log_error("dm_report: Failed to generate report headings for printing");
+		goto bad;
+	}
+	log_print("%s", (char *) dm_pool_end_object(rh->mem));
+
+	return 1;
+
+      bad:
+	dm_pool_abandon_object(rh->mem);
+	return 0;
+}
+
+/*
+ * Sort rows of data
+ */
+static int _row_compare(const void *a, const void *b)
+{
+	const struct row *rowa = *(const struct row **) a;
+	const struct row *rowb = *(const struct row **) b;
+	const struct dm_report_field *sfa, *sfb;
+	uint32_t cnt;
+
+	for (cnt = 0; cnt < rowa->rh->keys_count; cnt++) {
+		sfa = (*rowa->sort_fields)[cnt];
+		sfb = (*rowb->sort_fields)[cnt];
+		if (sfa->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) {
+			const uint64_t numa =
+			    *(const uint64_t *) sfa->sort_value;
+			const uint64_t numb =
+			    *(const uint64_t *) sfb->sort_value;
+
+			if (numa == numb)
+				continue;
+
+			if (sfa->props->flags & FLD_ASCENDING) {
+				return (numa > numb) ? 1 : -1;
+			} else {	/* FLD_DESCENDING */
+				return (numa < numb) ? 1 : -1;
+			}
+		} else {	/* DM_REPORT_FIELD_TYPE_STRING */
+			const char *stra = (const char *) sfa->sort_value;
+			const char *strb = (const char *) sfb->sort_value;
+			int cmp = strcmp(stra, strb);
+
+			if (!cmp)
+				continue;
+
+			if (sfa->props->flags & FLD_ASCENDING) {
+				return (cmp > 0) ? 1 : -1;
+			} else {	/* FLD_DESCENDING */
+				return (cmp < 0) ? 1 : -1;
+			}
+		}
+	}
+
+	return 0;		/* Identical */
+}
+
+static int _sort_rows(struct dm_report *rh)
+{
+	struct row *(*rows)[];
+	uint32_t count = 0;
+	struct row *row;
+
+	if (!(rows = dm_pool_alloc(rh->mem, sizeof(**rows) *
+				list_size(&rh->rows)))) {
+		log_error("dm_report: sort array allocation failed");
+		return 0;
+	}
+
+	list_iterate_items(row, &rh->rows)
+		(*rows)[count++] = row;
+
+	qsort(rows, count, sizeof(**rows), _row_compare);
+
+	list_init(&rh->rows);
+	while (count--)
+		list_add_h(&rh->rows, &(*rows)[count]->list);
+
+	return 1;
+}
+
+/*
+ * Produce report output
+ */
+int dm_report_output(struct dm_report *rh)
+{
+	struct list *fh, *rowh, *ftmp, *rtmp;
+	struct row *row = NULL;
+	struct dm_report_field *field;
+	const char *repstr;
+	char buf[4096];
+	int32_t width;
+	uint32_t align;
+
+	if (list_empty(&rh->rows))
+		return 1;
+
+	/* Sort rows */
+	if ((rh->flags & RH_SORT_REQUIRED))
+		_sort_rows(rh);
+
+	/* If headings not printed yet, calculate field widths and print them */
+	if (!(rh->flags & RH_HEADINGS_PRINTED))
+		_report_headings(rh);
+
+	/* Print and clear buffer */
+	list_iterate_safe(rowh, rtmp, &rh->rows) {
+		if (!dm_pool_begin_object(rh->mem, 512)) {
+			log_error("dm_report: Unable to allocate output line");
+			return 0;
+		}
+		row = list_item(rowh, struct row);
+		list_iterate_safe(fh, ftmp, &row->fields) {
+			field = list_item(fh, struct dm_report_field);
+			if (field->props->flags & FLD_HIDDEN)
+				continue;
+
+			repstr = field->report_string;
+			width = field->props->width;
+			if (!(rh->flags & DM_REPORT_OUTPUT_ALIGNED)) {
+				if (!dm_pool_grow_object(rh->mem, repstr,
+						      strlen(repstr))) {
+					log_error("dm_report: Unable to extend output line");
+					goto bad;
+				}
+			} else {
+				if (!(align = field->props->flags & DM_REPORT_FIELD_ALIGN_MASK))
+					align = (field->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) ? 
+						DM_REPORT_FIELD_ALIGN_RIGHT : DM_REPORT_FIELD_ALIGN_LEFT;
+				if (align & DM_REPORT_FIELD_ALIGN_LEFT) {
+					if (dm_snprintf(buf, sizeof(buf), "%-*.*s",
+							 width, width, repstr) < 0) {
+						log_error("dm_report: left-aligned snprintf() failed");
+						goto bad;
+					}
+					if (!dm_pool_grow_object(rh->mem, buf, width)) {
+						log_error("dm_report: Unable to extend output line");
+						goto bad;
+					}
+				} else if (align & DM_REPORT_FIELD_ALIGN_RIGHT) {
+					if (dm_snprintf(buf, sizeof(buf), "%*.*s",
+							 width, width, repstr) < 0) {
+						log_error("dm_report: right-aligned snprintf() failed");
+						goto bad;
+					}
+					if (!dm_pool_grow_object(rh->mem, buf, width)) {
+						log_error("dm_report: Unable to extend output line");
+						goto bad;
+					}
+				}
+			}
+
+			if (!list_end(&row->fields, fh))
+				if (!dm_pool_grow_object(rh->mem, rh->separator,
+						      strlen(rh->separator))) {
+					log_error("dm_report: Unable to extend output line");
+					goto bad;
+				}
+			list_del(&field->list);
+		}
+		if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
+			log_error("dm_report: Unable to terminate output line");
+			goto bad;
+		}
+		log_print("%s", (char *) dm_pool_end_object(rh->mem));
+		list_del(&row->list);
+	}
+
+	if (row)
+		dm_pool_free(rh->mem, row);
+
+	return 1;
+
+      bad:
+	dm_pool_abandon_object(rh->mem);
+	return 0;
+}

Modified: devmapper/trunk/lib/libdm-string.c
==============================================================================
--- devmapper/trunk/lib/libdm-string.c	(original)
+++ devmapper/trunk/lib/libdm-string.c	Sat Apr  7 09:09:48 2007
@@ -37,7 +37,8 @@
  * Split buffer into NULL-separated words in argv.
  * Returns number of words.
  */
-int dm_split_words(char *buffer, unsigned max, unsigned ignore_comments,
+int dm_split_words(char *buffer, unsigned max,
+		   unsigned ignore_comments __attribute((unused)),
 		   char **argv)
 {
 	unsigned arg;
@@ -121,3 +122,41 @@
 
 	return n;
 }
+
+char *dm_basename(const char *path)
+{
+	char *p = strrchr(path, '/');
+
+	return p ? p + 1 : (char *) path;
+}
+
+int dm_asprintf(char **result, const char *format, ...)
+{
+	int n, ok = 0, size = 32;
+	va_list ap;
+	char *buf = dm_malloc(size);
+
+	*result = 0;
+
+	if (!buf)
+		return -1;
+
+	while (!ok) {
+		va_start(ap, format);
+		n = vsnprintf(buf, size, format, ap);
+		if (0 <= n && n < size)
+			ok = 1;
+		else {
+			dm_free(buf);
+			size *= 2;
+			buf = dm_malloc(size);
+			if (!buf)
+				return -1;
+		};
+		va_end(ap);
+	}
+
+	*result = dm_strdup(buf);
+	dm_free(buf);
+	return n + 1;
+}

Modified: devmapper/trunk/lib/mm/dbg_malloc.c
==============================================================================
--- devmapper/trunk/lib/mm/dbg_malloc.c	(original)
+++ devmapper/trunk/lib/mm/dbg_malloc.c	Sat Apr  7 09:09:48 2007
@@ -26,9 +26,14 @@
 
 char *dm_strdup_aux(const char *str, const char *file, int line)
 {
-	char *ret = dm_malloc_aux_debug(strlen(str) + 1, file, line);
+	char *ret;
 
-	if (ret)
+	if (!str) {
+		log_error("Internal error: dm_strdup called with NULL pointer");
+		return NULL;
+	}
+
+	if ((ret = dm_malloc_aux_debug(strlen(str) + 1, file, line)))
 		strcpy(ret, str);
 
 	return ret;
@@ -232,7 +237,8 @@
 	}
 }
 
-void *dm_malloc_aux(size_t s, const char *file, int line)
+void *dm_malloc_aux(size_t s, const char *file __attribute((unused)),
+		    int line __attribute((unused)))
 {
 	if (s > 50000000) {
 		log_error("Huge memory allocation (size %" PRIsize_t

Modified: devmapper/trunk/make.tmpl.in
==============================================================================
--- devmapper/trunk/make.tmpl.in	(original)
+++ devmapper/trunk/make.tmpl.in	Sat Apr  7 09:09:48 2007
@@ -163,13 +163,13 @@
 ifeq ("@LIB_SUFFIX@","so")
 $(LIB_SHARED): $(OBJECTS) $(LDDEPS)
 	$(CC) -shared -Wl,-soname,$(notdir $@).$(LIB_VERSION) \
-	$(CLDFLAGS) $(OBJECTS) $(LIBS) -o $@
+	$(CFLAGS) $(CLDFLAGS) $(OBJECTS) $(LIBS) -o $@
 endif
 
 ifeq ("@LIB_SUFFIX@","dylib")
 $(LIB_SHARED): $(OBJECTS) $(LDDEPS)
 	$(CC) -dynamiclib -dylib_current_version,$(LIB_VERSION) \
-	$(CLDFLAGS) $(OBJECTS) $(LIBS) -o $@
+	$(CFLAGS) $(CLDFLAGS) $(OBJECTS) $(LIBS) -o $@
 endif
 
 $(LIB_STATIC): $(OBJECTS)

Modified: devmapper/trunk/man/dmsetup.8
==============================================================================
--- devmapper/trunk/man/dmsetup.8	(original)
+++ devmapper/trunk/man/dmsetup.8	Sat Apr  7 09:09:48 2007
@@ -3,6 +3,9 @@
 dmsetup \- low level logical volume management
 .SH SYNOPSIS
 .ad l
+.B dmsetup help
+.I [-c|-C|--columns]
+.br
 .B dmsetup create 
 .I device_name [-u uuid] [--notable | --table <table> | table_file]
 .br
@@ -30,21 +33,28 @@
 .B dmsetup rename
 .I device_name new_name
 .br
-.B dmsetup ls [--target target_type] [--exec command] [--tree [-o options]]
+.B dmsetup message
+.I device_name sector message
+.br
+.B dmsetup ls
+.I [--target target_type] [--exec command] [--tree [-o options]]
 .br
 .B dmsetup info 
 .I [device_name]
 .br
-.B dmsetup info -c|-C|--columns [--noheadings] [-o name]
+.B dmsetup info -c|-C|--columns
+.I [--noheadings] [--separator separator] [-o fields] [-O|--sort sort_fields]
 .I [device_name]
 .br
 .B dmsetup deps
 .I [device_name]
 .br
-.B dmsetup status [--target target_type]
+.B dmsetup status
+.I [--target target_type]
 .I [device_name]
 .br
-.B dmsetup table [--target target_type]
+.B dmsetup table
+.I [--target target_type]
 .I [device_name]
 .br
 .B dmsetup wait
@@ -58,6 +68,9 @@
 .br
 .B dmsetup version
 .br
+.B dmsetup setgeometry
+.I device_name cyl head sect start
+.br
 
 .B devmap_name
 .I major minor
@@ -97,7 +110,7 @@
 When creating a device, don't load any table.
 .IP \fB-o|--options
 .br
-Specify which fields to display.  Only \fB-o\ name\fP is supported.
+Specify which fields to display.
 .IP \fB-r|--readonly
 .br
 Set the table being loaded read-only.
@@ -130,6 +143,11 @@
 .br
 Outputs a list of (major, minor) pairs for devices referenced by the
 live table for the specified device.
+.IP \fBhelp
+.I [-c|-C|--columns]
+.br
+Outputs a summary of the commands available, optionally including
+the list of report fields.
 .IP \fBinfo
 .I [device_name]
 .br
@@ -148,6 +166,17 @@
     Number of targets in the live table
 .br
     UUID
+.IP \fBinfo -c|-C|--columns
+.I [--noheadings] [--separator separator] [-o fields] [-O|--sort sort_fields]
+.I [device_name]
+.br
+Output you can customise.
+Fields are comma-separated and chosen from the following list:
+name, major, minor, attr, open, segments, events, uuid.
+Attributes are: (L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.
+Precede the list with '+' to append
+to the default selection of columns instead of replacing it.
+Precede any sort_field with - for a reverse sort on that column.
 .IP \fBls
 .I [--target target_type]
 .I [--exec command]
@@ -167,6 +196,10 @@
 .br
 Loads <table> or table_file into the inactive table slot for device_name.
 If neither is supplied, reads a table from standard input.
+.IP \fBmessage
+.I device_name sector message
+.br
+Send message to target. If sector not needed use 0.
 .IP \fBmknodes
 .I [device_name]
 .br
@@ -205,6 +238,10 @@
 Un-suspends a device.  
 If an inactive table has been loaded, it becomes live.
 Postponed I/O then gets re-queued for processing.
+.IP \fBsetgeometry
+.I device_name cyl head sect start
+.br
+Sets the device geometry to C/H/S.
 .IP \fBstatus
 .I [--target target_type]
 .I [device_name]

Modified: devmapper/trunk/po/device-mapper.po
==============================================================================
--- devmapper/trunk/po/device-mapper.po	(original)
+++ devmapper/trunk/po/device-mapper.po	Sat Apr  7 09:09:48 2007
@@ -1,12 +1,14 @@
 # SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR Free Software Foundation, Inc.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
 # FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
 #
 #, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2004-02-24 15:16+0000\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-01-29 19:56+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -14,138 +16,829 @@
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: ioctl/libdevmapper.c:113
+#: datastruct/hash.c:96 datastruct/hash.c:109 ioctl/libdm-iface.c:247
+#: libdm-deptree.c:471 libdm-deptree.c:475 libdm-deptree.c:481
+#: libdm-deptree.c:491 libdm-deptree.c:502 libdm-deptree.c:594
+#: libdm-deptree.c:598 libdm-deptree.c:614 libdm-deptree.c:712
+#: libdm-deptree.c:721 libdm-deptree.c:863 libdm-deptree.c:959
+#: libdm-deptree.c:964 libdm-deptree.c:969 libdm-deptree.c:1020
+#: libdm-deptree.c:1025 libdm-deptree.c:1030 libdm-deptree.c:1064
+#: libdm-deptree.c:1093 libdm-deptree.c:1109 libdm-deptree.c:1120
+#: libdm-deptree.c:1226 libdm-deptree.c:1230 libdm-deptree.c:1267
+#: libdm-deptree.c:1279 libdm-deptree.c:1283 libdm-deptree.c:1290
+#: libdm-deptree.c:1297 libdm-deptree.c:1304 libdm-deptree.c:1312
+#: libdm-deptree.c:1318 libdm-deptree.c:1326 libdm-deptree.c:1333
+#: libdm-deptree.c:1341 libdm-deptree.c:1343 libdm-deptree.c:1348
+#: libdm-deptree.c:1354 libdm-deptree.c:1357 libdm-deptree.c:1365
+#: libdm-deptree.c:1381 libdm-deptree.c:1391 libdm-deptree.c:1415
+#: libdm-deptree.c:1460 libdm-deptree.c:1504 libdm-deptree.c:1511
+#: libdm-deptree.c:1518 libdm-deptree.c:1609 libdm-deptree.c:1618
+#: libdm-deptree.c:1637 libdm-deptree.c:1646 libdm-deptree.c:1655
+#: libdm-deptree.c:1667 libdm-deptree.c:1676 libdm-deptree.c:1685
+#: libdm-deptree.c:1697 libdm-deptree.c:1733 libdm-deptree.c:1752
+#: libdm-deptree.c:1795 libdm-deptree.c:1809 libdm-deptree.c:1820
+msgid "<backtrace>"
+msgstr ""
+
+#: ioctl/libdm-iface.c:143
 #, c-format
-msgid "%s: open failed: %s"
+msgid "%s: fopen failed: %s"
+msgstr ""
+
+#: ioctl/libdm-iface.c:153 ioctl/libdm-iface.c:163
+#, c-format
+msgid "%s: fclose failed: %s"
+msgstr ""
+
+#: ioctl/libdm-iface.c:166
+#, c-format
+msgid "%s: No entry for %s found"
+msgstr ""
+
+#: ioctl/libdm-iface.c:193
+#, c-format
+msgid "%s: stat failed: %s"
+msgstr ""
+
+#: ioctl/libdm-iface.c:199
+#, c-format
+msgid "%s: Wrong inode type"
+msgstr ""
+
+#: ioctl/libdm-iface.c:202 ioctl/libdm-iface.c:214
+#, c-format
+msgid "%s: unlink failed: %s"
+msgstr ""
+
+#: ioctl/libdm-iface.c:208
+#, c-format
+msgid "%s: Wrong device number: (%u, %u) instead of (%u, %u)"
+msgstr ""
+
+#: ioctl/libdm-iface.c:237
+#, c-format
+msgid "Creating device %s (%u, %u)"
 msgstr ""
 
-#: ioctl/libdevmapper.c:114
+#: ioctl/libdm-iface.c:241
+#, c-format
+msgid "%s: mknod failed: %s"
+msgstr ""
+
+#: ioctl/libdm-iface.c:297
 msgid "Is device-mapper driver missing from kernel?"
 msgstr ""
 
-#: ioctl/libdevmapper.c:555
+#: ioctl/libdm-iface.c:304
+#, c-format
+msgid "%s: open failed: %s"
+msgstr ""
+
+#: ioctl/libdm-iface.c:309
+msgid "Failed to set up list of device-mapper major numbers"
+msgstr ""
+
+#: ioctl/libdm-iface.c:316
+msgid "Failure to communicate with kernel device-mapper driver."
+msgstr ""
+
+#: ioctl/libdm-iface.c:778
 msgid "Failed to get device-mapper version"
 msgstr ""
 
-#: ioctl/libdevmapper.c:591
+#: ioctl/libdm-iface.c:814
 #, c-format
 msgid ""
-"device-mapper ioctl protocol version %d failed. Trying protocol version 1."
+"device-mapper ioctl protocol version %u failed. Trying protocol version 1."
 msgstr ""
 
-#: ioctl/libdevmapper.c:595
+#: ioctl/libdm-iface.c:818
 msgid "Using device-mapper ioctl protocol version 1"
 msgstr ""
 
-#: ioctl/libdevmapper.c:603
+#: ioctl/libdm-iface.c:826
 #, c-format
 msgid "Incompatible libdevmapper %s%s and kernel driver %s"
 msgstr ""
 
-#: ioctl/libdevmapper.c:760
+#: ioctl/libdm-iface.c:990
 #, c-format
 msgid "dm_task_set_newname: strdup(%s) failed"
 msgstr ""
 
-#: ioctl/libdevmapper.c:780
+#: ioctl/libdm-iface.c:1000
+#, c-format
+msgid "dm_task_set_message: strdup(%s) failed"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1019
+msgid "dm_task_set_geometry: dm_malloc failed"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1024
+msgid "dm_task_set_geometry: sprintf failed"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1065
 #, c-format
-msgid "create_target: malloc(%d) failed"
+msgid "create_target: malloc(%zu) failed"
 msgstr ""
 
-#: ioctl/libdevmapper.c:787
+#: ioctl/libdm-iface.c:1073
 msgid "create_target: strdup(params) failed"
 msgstr ""
 
-#: ioctl/libdevmapper.c:792
+#: ioctl/libdm-iface.c:1078
 msgid "create_target: strdup(type) failed"
 msgstr ""
 
-#: ioctl/libdevmapper.c:831
+#: ioctl/libdm-iface.c:1117
 #, c-format
 msgid "t->params= '%s'"
 msgstr ""
 
-#: ioctl/libdevmapper.c:864
+#: ioctl/libdm-iface.c:1185
+msgid "targets and message are incompatible"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1190
 msgid "targets and newname are incompatible"
 msgstr ""
 
-#: ioctl/libdevmapper.c:902
-msgid "Missing major number for persistent device"
+#: ioctl/libdm-iface.c:1195
+msgid "targets and geometry are incompatible"
 msgstr ""
 
-#: ioctl/libdevmapper.c:939 ioctl/libdevmapper.c:964
-msgid "Failed to create device-mapper task struct"
+#: ioctl/libdm-iface.c:1200
+msgid "message and newname are incompatible"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1205
+msgid "geometry and newname are incompatible"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1210
+msgid "geometry and message are incompatible"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1215
+msgid "message is required with sector"
 msgstr ""
 
-#: ioctl/libdevmapper.c:1008
+#: ioctl/libdm-iface.c:1255
+msgid "Missing major number for persistent device."
+msgstr ""
+
+#: ioctl/libdm-iface.c:1265
 #, c-format
-msgid "Internal error: unknown device-mapper task %d"
+msgid "Unable to find name for device (%u:%u)"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1269
+#, c-format
+msgid "device (%u:%u) is %s for compatibility with old kernel"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1327
+#, c-format
+msgid "opendir %s: %s"
 msgstr ""
 
-#: ioctl/libdevmapper.c:1024
-msgid "Couldn't create ioctl argument"
+#: ioctl/libdm-iface.c:1341
+#, c-format
+msgid "closedir %s: %s"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1399 ioctl/libdm-iface.c:1427 ioctl/libdm-iface.c:1478
+msgid "Failed to create device-mapper task struct"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1465
+msgid "Failed to revert device creation."
 msgstr ""
 
-#: ioctl/libdevmapper.c:1032
+#: ioctl/libdm-iface.c:1545
+msgid "Couldn't create ioctl argument."
+msgstr ""
+
+#: ioctl/libdm-iface.c:1557
+#, c-format
+msgid "dm %s %s %s%s%s %s%.0d%s%.0d%s%s%c%c%s %.0llu %s [%u]"
+msgstr ""
+
+#: ioctl/libdm-iface.c:1581 ioctl/libdm-iface.c:1586
 #, c-format
-msgid "dm %s %s %s %s"
+msgid "device-mapper: %s ioctl failed: %s"
 msgstr ""
 
-#: ioctl/libdevmapper.c:1041 ioctl/libdevmapper.c:1044
+#: ioctl/libdm-iface.c:1616
 #, c-format
-msgid "device-mapper ioctl cmd %d failed: %s"
+msgid "Internal error: unknown device-mapper task %d"
 msgstr ""
 
-#: libdm-common.c:94
+#: ioctl/libdm-iface.c:1653
+msgid "Warning: libdevmapper buffer too small for data"
+msgstr ""
+
+#: libdm-common.c:99
 #, c-format
-msgid "dm_task_create: malloc(%d) failed"
+msgid "dm_task_create: malloc(%zu) failed"
 msgstr ""
 
-#: libdm-common.c:126
+#: libdm-common.c:141
 #, c-format
 msgid "dm_task_set_name: Device %s not found"
 msgstr ""
 
-#: libdm-common.c:135
+#: libdm-common.c:150
 #, c-format
 msgid "dm_task_set_name: strdup(%s) failed"
 msgstr ""
 
-#: libdm-common.c:150
+#: libdm-common.c:165
 #, c-format
 msgid "dm_task_set_uuid: strdup(%s) failed"
 msgstr ""
 
-#: libdm-common.c:160
+#: libdm-common.c:234
+#, c-format
+msgid "%s: matchpathcon %07o failed: %s"
+msgstr ""
+
+#: libdm-common.c:239
 #, c-format
-msgid "Setting major: %d"
+msgid "Setting SELinux context for %s to %s."
 msgstr ""
 
-#: libdm-common.c:168
+#: libdm-common.c:242
 #, c-format
-msgid "Setting minor: %d"
+msgid "%s: lsetfilecon failed: %s"
 msgstr ""
 
-#: libdm-common.c:201 libdm-common.c:235
+#: libdm-common.c:264 libdm-common.c:311
 #, c-format
 msgid "A non-block device file at '%s' is already present"
 msgstr ""
 
-#: libdm-common.c:210 libdm-common.c:245 libdm-common.c:271
+#: libdm-common.c:274 libdm-common.c:321 libdm-common.c:347
 #, c-format
 msgid "Unable to unlink device node for '%s'"
 msgstr ""
 
-#: libdm-common.c:217
+#: libdm-common.c:282
 #, c-format
 msgid "Unable to make device node for '%s'"
 msgstr ""
 
-#: libdm-common.c:252
+#: libdm-common.c:288
+#, c-format
+msgid "%s: chown failed: %s"
+msgstr ""
+
+#: libdm-common.c:328
 #, c-format
 msgid "Unable to rename device node from '%s' to '%s'"
 msgstr ""
 
-#: libdm-common.c:326
+#: libdm-common.c:407
 msgid "Insufficient memory to stack mknod operation"
 msgstr ""
+
+#: libdm-common.c:504
+msgid "Failed to get driver version"
+msgstr ""
+
+#: libdm-deptree.c:157
+msgid "dm_tree_create malloc failed"
+msgstr ""
+
+#: libdm-deptree.c:169
+msgid "dtree pool creation failed"
+msgstr ""
+
+#: libdm-deptree.c:175
+msgid "dtree hash creation failed"
+msgstr ""
+
+#: libdm-deptree.c:182
+msgid "dtree uuid hash creation failed"
+msgstr ""
+
+#: libdm-deptree.c:220
+msgid "dtree link allocation failed"
+msgstr ""
+
+#: libdm-deptree.c:314
+msgid "_create_dm_tree_node alloc failed"
+msgstr ""
+
+#: libdm-deptree.c:334
+msgid "dtree node hash insertion failed"
+msgstr ""
+
+#: libdm-deptree.c:341
+msgid "dtree uuid hash insertion failed"
+msgstr ""
+
+#: libdm-deptree.c:394
+msgid "deps dm_task creation failed"
+msgstr ""
+
+#: libdm-deptree.c:399
+#, c-format
+msgid "_deps: failed to set major for (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:405
+#, c-format
+msgid "_deps: failed to set minor for (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:411
+#, c-format
+msgid "_deps: task run failed for (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:417
+#, c-format
+msgid "_deps: failed to get info for (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:428
+#, c-format
+msgid "Inconsistent dtree major number: %u != %u"
+msgstr ""
+
+#: libdm-deptree.c:433
+#, c-format
+msgid "Inconsistent dtree minor number: %u != %u"
+msgstr ""
+
+#: libdm-deptree.c:438 libdm-deptree.c:577 libdm-deptree.c:606
+msgid "name pool_strdup failed"
+msgstr ""
+
+#: libdm-deptree.c:442 libdm-deptree.c:581
+msgid "uuid pool_strdup failed"
+msgstr ""
+
+#: libdm-deptree.c:520
+msgid "_node_clear_table failed: missing info"
+msgstr ""
+
+#: libdm-deptree.c:525
+msgid "_node_clear_table failed: missing name"
+msgstr ""
+
+#: libdm-deptree.c:533
+#, c-format
+msgid "Clearing inactive table %s (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:538
+#, c-format
+msgid "Table clear dm_task creation failed for %s"
+msgstr ""
+
+#: libdm-deptree.c:544
+#, c-format
+msgid "Failed to set device number for %s table clear"
+msgstr ""
+
+#: libdm-deptree.c:552
+#, c-format
+msgid "_node_clear_table failed: info missing after running task for %s"
+msgstr ""
+
+#: libdm-deptree.c:791
+msgid "_info_by_dev: dm_task creation failed"
+msgstr ""
+
+#: libdm-deptree.c:796
+msgid "_info_by_dev: Failed to set device number"
+msgstr ""
+
+#: libdm-deptree.c:802 libdm-deptree.c:831 libdm-deptree.c:866
+#: libdm-deptree.c:897 libdm-deptree.c:930 libdm-deptree.c:1191
+#: libdm-deptree.c:1456
+msgid "Failed to disable open_count"
+msgstr ""
+
+#: libdm-deptree.c:817
+#, c-format
+msgid "Removing %s (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:820
+#, c-format
+msgid "Deactivation dm_task creation failed for %s"
+msgstr ""
+
+#: libdm-deptree.c:825
+#, c-format
+msgid "Failed to set device number for %s deactivation"
+msgstr ""
+
+#: libdm-deptree.c:850
+#, c-format
+msgid "Renaming %s (%u:%u) to %s"
+msgstr ""
+
+#: libdm-deptree.c:853
+#, c-format
+msgid "Rename dm_task creation failed for %s"
+msgstr ""
+
+#: libdm-deptree.c:858
+#, c-format
+msgid "Failed to set name for %s rename."
+msgstr ""
+
+#: libdm-deptree.c:883
+#, c-format
+msgid "Resuming %s (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:886 libdm-deptree.c:919
+#, c-format
+msgid "Suspend dm_task creation failed for %s"
+msgstr ""
+
+#: libdm-deptree.c:891
+#, c-format
+msgid "Failed to set device number for %s resumption."
+msgstr ""
+
+#: libdm-deptree.c:913
+#, c-format
+msgid "Suspending %s (%u:%u)%s%s"
+msgstr ""
+
+#: libdm-deptree.c:924
+#, c-format
+msgid "Failed to set device number for %s suspension."
+msgstr ""
+
+#: libdm-deptree.c:933
+msgid "Failed to set skip_lockfs flag."
+msgstr ""
+
+#: libdm-deptree.c:936
+msgid "Failed to set no_flush flag."
+msgstr ""
+
+#: libdm-deptree.c:983
+#, c-format
+msgid "Unable to deactivate %s (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:1049
+#, c-format
+msgid "Unable to suspend %s (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:1127
+#, c-format
+msgid "Failed to rename %s (%u:%u) to %s"
+msgstr ""
+
+#: libdm-deptree.c:1140 libdm-deptree.c:1531
+#, c-format
+msgid "Unable to resume %s (%u:%u)"
+msgstr ""
+
+#: libdm-deptree.c:1161
+#, c-format
+msgid "Creating %s"
+msgstr ""
+
+#: libdm-deptree.c:1164
+#, c-format
+msgid "Create dm_task creation failed for %s"
+msgstr ""
+
+#: libdm-deptree.c:1169
+#, c-format
+msgid "Failed to set device name for %s"
+msgstr ""
+
+#: libdm-deptree.c:1174
+#, c-format
+msgid "Failed to set uuid for %s"
+msgstr ""
+
+#: libdm-deptree.c:1181
+#, c-format
+msgid "Failed to set device number for %s creation."
+msgstr ""
+
+#: libdm-deptree.c:1186 libdm-deptree.c:1451
+#, c-format
+msgid "Failed to set read only flag for %s"
+msgstr ""
+
+#: libdm-deptree.c:1206
+#, c-format
+msgid "Failed to format %s device number for %s as dm target (%u,%u)"
+msgstr ""
+
+#: libdm-deptree.c:1387
+#, c-format
+msgid "Adding target: %lu %lu %s %s"
+msgstr ""
+
+#: libdm-deptree.c:1407
+msgid "Insufficient space for target parameters."
+msgstr ""
+
+#: libdm-deptree.c:1420
+#, c-format
+msgid "Insufficient space in params[%zu] for target parameters."
+msgstr ""
+
+#: libdm-deptree.c:1426
+msgid "Target parameter size too big. Aborting."
+msgstr ""
+
+#: libdm-deptree.c:1437
+#, c-format
+msgid "Loading %s table"
+msgstr ""
+
+#: libdm-deptree.c:1440
+#, c-format
+msgid "Reload dm_task creation failed for %s"
+msgstr ""
+
+#: libdm-deptree.c:1446
+#, c-format
+msgid "Failed to set device number for %s reload."
+msgstr ""
+
+#: libdm-deptree.c:1463
+msgid "Failed to suppress reload of identical tables."
+msgstr ""
+
+#: libdm-deptree.c:1468
+#, c-format
+msgid "Suppressed %s identical table reload."
+msgstr ""
+
+#: libdm-deptree.c:1559
+msgid "Failed to get uuid for dtree node."
+msgstr ""
+
+#: libdm-deptree.c:1581
+msgid "dtree node segment allocation failed"
+msgstr ""
+
+#: libdm-deptree.c:1612 libdm-deptree.c:1640 libdm-deptree.c:1649
+#, c-format
+msgid "Couldn't find snapshot origin uuid %s."
+msgstr ""
+
+#: libdm-deptree.c:1715 libdm-deptree.c:1813
+msgid "Internal error: Attempt to add target area to missing segment."
+msgstr ""
+
+#: libdm-deptree.c:1723
+msgid "log uuid pool_strdup failed"
+msgstr ""
+
+#: libdm-deptree.c:1728
+#, c-format
+msgid "Couldn't find mirror log uuid %s."
+msgstr ""
+
+#: libdm-deptree.c:1762
+msgid "Failed to allocate target segment area."
+msgstr ""
+
+#: libdm-deptree.c:1785
+msgid "dm_tree_node_add_target_area called without device"
+msgstr ""
+
+#: libdm-deptree.c:1791
+#, c-format
+msgid "Couldn't find area uuid %s."
+msgstr ""
+
+#: libdm-deptree.c:1798
+#, c-format
+msgid "Device %s not found."
+msgstr ""
+
+#: libdm-deptree.c:1803
+#, c-format
+msgid "Device %s is not a block device."
+msgstr ""
+
+#: libdm-file.c:28
+#, c-format
+msgid "Creating directory \"%s\""
+msgstr ""
+
+#: libdm-file.c:36 libdm-file.c:47
+#, c-format
+msgid "%s: mkdir failed: %s"
+msgstr ""
+
+#: libdm-file.c:71
+#, c-format
+msgid "Directory \"%s\" not found"
+msgstr ""
+
+#: libdm-report.c:108
+msgid "dm_report_field_string: dm_pool_strdup failed"
+msgstr ""
+
+#: libdm-report.c:126 libdm-report.c:131
+msgid "dm_report_field_int: dm_pool_alloc failed"
+msgstr ""
+
+#: libdm-report.c:136
+#, c-format
+msgid "dm_report_field_int: int too big: %d"
+msgstr ""
+
+#: libdm-report.c:155 libdm-report.c:160
+msgid "dm_report_field_uint32: dm_pool_alloc failed"
+msgstr ""
+
+#: libdm-report.c:165
+#, c-format
+msgid "dm_report_field_uint32: uint32 too big: %u"
+msgstr ""
+
+#: libdm-report.c:184 libdm-report.c:189
+msgid "dm_report_field_int32: dm_pool_alloc failed"
+msgstr ""
+
+#: libdm-report.c:194
+#, c-format
+msgid "dm_report_field_int32: int32 too big: %d"
+msgstr ""
+
+#: libdm-report.c:213 libdm-report.c:218
+msgid "dm_report_field_uint64: dm_pool_alloc failed"
+msgstr ""
+
+#: libdm-report.c:223
+#, c-format
+msgid "dm_report_field_uint64: uint64 too big: %d"
+msgstr ""
+
+#: libdm-report.c:264 libdm-report.c:428
+msgid " "
+msgstr ""
+
+#: libdm-report.c:265
+#, c-format
+msgid "%s Fields"
+msgstr ""
+
+#: libdm-report.c:266
+#, c-format
+msgid "%*.*s"
+msgstr ""
+
+#: libdm-report.c:273
+#, c-format
+msgid "  %-*s - %s"
+msgstr ""
+
+#: libdm-report.c:291
+#, c-format
+msgid "dm_report: field not match: %s"
+msgstr ""
+
+#: libdm-report.c:316 libdm-report.c:347
+msgid "dm_report: struct field_properties allocation failed"
+msgstr ""
+
+#: libdm-report.c:361
+#, c-format
+msgid "dm_report: Ignoring duplicate sort field: %s"
+msgstr ""
+
+#: libdm-report.c:393
+msgid "dm_report: Missing sort field name"
+msgstr ""
+
+#: libdm-report.c:430
+#, c-format
+msgid "Unrecognised field: %.*s"
+msgstr ""
+
+#: libdm-report.c:452
+#, c-format
+msgid "dm_report: Unrecognised field: %.*s"
+msgstr ""
+
+#: libdm-report.c:474
+msgid "dm_report_init: dm_malloc failed"
+msgstr ""
+
+#: libdm-report.c:506
+msgid "dm_report_init: allocation of memory pool failed"
+msgstr ""
+
+#: libdm-report.c:552
+msgid "dm_report_object: struct row allocation failed"
+msgstr ""
+
+#: libdm-report.c:562
+msgid "dm_report_object: row sort value structure allocation failed"
+msgstr ""
+
+#: libdm-report.c:573
+msgid "dm_report_object: struct dm_report_field allocation failed"
+msgstr ""
+
+#: libdm-report.c:586
+#, c-format
+msgid "dm_report_object: report function failed for field %s"
+msgstr ""
+
+#: libdm-report.c:626
+msgid "dm_report: dm_pool_begin_object failed for headings"
+msgstr ""
+
+#: libdm-report.c:640
+msgid "dm_report: snprintf heading failed"
+msgstr ""
+
+#: libdm-report.c:644 libdm-report.c:649 libdm-report.c:656 libdm-report.c:661
+msgid "dm_report: Failed to generate report headings for printing"
+msgstr ""
+
+#: libdm-report.c:664 libdm-report.c:826
+#, c-format
+msgid "%s"
+msgstr ""
+
+#: libdm-report.c:727
+msgid "dm_report: sort array allocation failed"
+msgstr ""
+
+#: libdm-report.c:770
+msgid "dm_report: Unable to allocate output line"
+msgstr ""
+
+#: libdm-report.c:784 libdm-report.c:798 libdm-report.c:808 libdm-report.c:817
+msgid "dm_report: Unable to extend output line"
+msgstr ""
+
+#: libdm-report.c:794
+msgid "dm_report: left-aligned snprintf() failed"
+msgstr ""
+
+#: libdm-report.c:804
+msgid "dm_report: right-aligned snprintf() failed"
+msgstr ""
+
+#: libdm-report.c:823
+msgid "dm_report: Unable to terminate output line"
+msgstr ""
+
+#: mm/dbg_malloc.c:26
+msgid "Internal error: dm_strdup called with NULL pointer"
+msgstr ""
+
+#: mm/dbg_malloc.c:63 mm/dbg_malloc.c:238
+#, c-format
+msgid "Huge memory allocation (size %zu) rejected - metadata corruption?"
+msgstr ""
+
+#: mm/dbg_malloc.c:69
+#, c-format
+msgid "couldn't allocate any memory, size = %zu"
+msgstr ""
+
+#: mm/dbg_malloc.c:193
+msgid "You have a memory leak:"
+msgstr ""
+
+#: mm/dbg_malloc.c:208
+#, c-format
+msgid "block %d at %p, size %zu\t [%s]"
+msgstr ""
+
+#: mm/dbg_malloc.c:215
+#, c-format
+msgid "%ld bytes leaked in total"
+msgstr ""
+
+#: mm/pool-fast.c:43
+#, c-format
+msgid "Couldn't create memory pool %s (size %zu)"
+msgstr ""
+
+#: mm/pool-fast.c:132
+msgid "Internal error: pool_free asked to free pointer not in pool"
+msgstr ""
+
+#: mm/pool-fast.c:221
+#, c-format
+msgid "Out of memory.  Requested %zu bytes."
+msgstr ""



More information about the pkg-lvm-commits mailing list