summaryrefslogtreecommitdiff
path: root/daemons/dmeventd
diff options
context:
space:
mode:
Diffstat (limited to 'daemons/dmeventd')
-rw-r--r--daemons/dmeventd/.exported_symbols1
-rw-r--r--daemons/dmeventd/Makefile.in8
-rw-r--r--daemons/dmeventd/dmeventd.c373
-rw-r--r--daemons/dmeventd/dmeventd.h9
-rw-r--r--daemons/dmeventd/libdevmapper-event.c142
-rw-r--r--daemons/dmeventd/libdevmapper-event.h4
-rw-r--r--daemons/dmeventd/plugins/Makefile.in29
-rw-r--r--daemons/dmeventd/plugins/lvm2/.exported_symbols1
-rw-r--r--daemons/dmeventd/plugins/lvm2/Makefile.in6
-rw-r--r--daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c36
-rw-r--r--daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h3
-rw-r--r--daemons/dmeventd/plugins/mirror/Makefile.in6
-rw-r--r--daemons/dmeventd/plugins/mirror/dmeventd_mirror.c42
-rw-r--r--daemons/dmeventd/plugins/raid/.exported_symbols3
-rw-r--r--daemons/dmeventd/plugins/raid/Makefile.in36
-rw-r--r--daemons/dmeventd/plugins/raid/dmeventd_raid.c172
-rw-r--r--daemons/dmeventd/plugins/snapshot/Makefile.in6
-rw-r--r--daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c99
-rw-r--r--daemons/dmeventd/plugins/thin/.exported_symbols3
-rw-r--r--daemons/dmeventd/plugins/thin/Makefile.in36
-rw-r--r--daemons/dmeventd/plugins/thin/dmeventd_thin.c447
21 files changed, 1201 insertions, 261 deletions
diff --git a/daemons/dmeventd/.exported_symbols b/daemons/dmeventd/.exported_symbols
index 25690c8..fab74dc 100644
--- a/daemons/dmeventd/.exported_symbols
+++ b/daemons/dmeventd/.exported_symbols
@@ -1,3 +1,4 @@
init_fifos
fini_fifos
daemon_talk
+dm_event_get_version
diff --git a/daemons/dmeventd/Makefile.in b/daemons/dmeventd/Makefile.in
index e99e089..1302a44 100644
--- a/daemons/dmeventd/Makefile.in
+++ b/daemons/dmeventd/Makefile.in
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2005-2011 Red Hat, Inc. All rights reserved.
#
# This file is part of the device-mapper userspace tools.
#
@@ -60,11 +60,11 @@ LIBS += -ldevmapper
LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS)
dmeventd: $(LIB_SHARED) dmeventd.o
- $(CC) $(CFLAGS) $(LDFLAGS) -L. -o $@ dmeventd.o \
+ $(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -L. -o $@ dmeventd.o \
$(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic
dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a
- $(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \
+ $(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \
dmeventd.o $(DL_LIBS) $(LVMLIBS) $(LIBS) $(STATIC_LIBS)
ifeq ("@PKGCONFIG@", "yes")
@@ -105,4 +105,4 @@ install: install_include install_lib install_dmeventd
install_device-mapper: install_include install_lib install_dmeventd
-DISTCLEAN_TARGETS += libdevmapper-event.pc .exported_symbols_generated
+DISTCLEAN_TARGETS += libdevmapper-event.pc
diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c
index 2b454f9..13148c3 100644
--- a/daemons/dmeventd/dmeventd.c
+++ b/daemons/dmeventd/dmeventd.c
@@ -39,13 +39,27 @@
#include <arpa/inet.h> /* for htonl, ntohl */
#ifdef linux
-# include <malloc.h>
-
-# define OOM_ADJ_FILE "/proc/self/oom_adj"
+/*
+ * Kernel version 2.6.36 and higher has
+ * new OOM killer adjustment interface.
+ */
+# define OOM_ADJ_FILE_OLD "/proc/self/oom_adj"
+# define OOM_ADJ_FILE "/proc/self/oom_score_adj"
/* From linux/oom.h */
+/* Old interface */
# define OOM_DISABLE (-17)
# define OOM_ADJUST_MIN (-16)
+/* New interface */
+# define OOM_SCORE_ADJ_MIN (-1000)
+
+/* Systemd on-demand activation support */
+# define SD_ACTIVATION_ENV_VAR_NAME "SD_ACTIVATION"
+# define SD_LISTEN_PID_ENV_VAR_NAME "LISTEN_PID"
+# define SD_LISTEN_FDS_ENV_VAR_NAME "LISTEN_FDS"
+# define SD_LISTEN_FDS_START 3
+# define SD_FD_FIFO_SERVER SD_LISTEN_FDS_START
+# define SD_FD_FIFO_CLIENT (SD_LISTEN_FDS_START + 1)
#endif
@@ -95,9 +109,8 @@ static pthread_mutex_t _global_mutex;
#define THREAD_STACK_SIZE (300*1024)
-#define DEBUGLOG(fmt, args...) _debuglog(fmt, ## args)
-
int dmeventd_debug = 0;
+static int _systemd_activation = 0;
static int _foreground = 0;
static int _restart = 0;
static char **_initial_registrations = 0;
@@ -203,24 +216,6 @@ static DM_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 (!_foreground)
- 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");
-
- 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)
@@ -407,7 +402,9 @@ static int _fill_device_data(struct thread_status *ts)
if (!dmt)
return 0;
- dm_task_set_uuid(dmt, ts->device.uuid);
+ if (!dm_task_set_uuid(dmt, ts->device.uuid))
+ goto fail;
+
if (!dm_task_run(dmt))
goto fail;
@@ -450,7 +447,7 @@ static int _get_status(struct message_data *message_data)
{
struct dm_event_daemon_message *msg = message_data->msg;
struct thread_status *thread;
- int i = 0, j = 0;
+ int i, j;
int ret = -1;
int count = dm_list_size(&_thread_registry);
int size = 0, current = 0;
@@ -585,6 +582,7 @@ static void _unregister_for_timeout(struct thread_status *thread)
pthread_mutex_unlock(&_timeout_mutex);
}
+__attribute__((format(printf, 4, 5)))
static void _no_intr_log(int level, const char *file, int line,
const char *f, ...)
{
@@ -740,8 +738,10 @@ static void _monitor_unregister(void *arg)
return;
}
thread->status = DM_THREAD_DONE;
+ pthread_mutex_lock(&_timeout_mutex);
UNLINK_THREAD(thread);
LINK(thread, &_thread_registry_unused);
+ pthread_mutex_unlock(&_timeout_mutex);
_unlock_mutex();
}
@@ -752,7 +752,10 @@ static struct dm_task *_get_device_status(struct thread_status *ts)
if (!dmt)
return NULL;
- dm_task_set_uuid(dmt, ts->device.uuid);
+ if (!dm_task_set_uuid(dmt, ts->device.uuid)) {
+ dm_task_destroy(dmt);
+ return NULL;
+ }
if (!dm_task_run(dmt)) {
dm_task_destroy(dmt);
@@ -996,10 +999,8 @@ static int _register_for_event(struct message_data *message_data)
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 (ret)
+ goto outth;
}
if (!(thread = _lookup_thread_status(message_data))) {
@@ -1025,6 +1026,7 @@ static int _register_for_event(struct message_data *message_data)
/* Or event # into events bitfield. */
thread->events |= message_data->events.field;
+ outth:
_unlock_mutex();
out:
@@ -1076,8 +1078,10 @@ static int _unregister_for_event(struct message_data *message_data)
* unlink and terminate its monitoring thread.
*/
if (!thread->events) {
+ pthread_mutex_lock(&_timeout_mutex);
UNLINK_THREAD(thread);
LINK(thread, &_thread_registry_unused);
+ pthread_mutex_unlock(&_timeout_mutex);
}
_unlock_mutex();
@@ -1099,15 +1103,19 @@ static int _registered_device(struct message_data *message_data,
const char *id = message_data->id;
const char *dso = thread->dso_data->dso_name;
const char *dev = thread->device.uuid;
+ int r;
unsigned events = ((thread->status == DM_THREAD_RUNNING)
&& (thread->events)) ? thread->events : thread->
events | DM_EVENT_REGISTRATION_PENDING;
dm_free(msg->data);
- msg->size = dm_asprintf(&(msg->data), fmt, id, dso, dev, events);
+ if ((r = dm_asprintf(&(msg->data), fmt, id, dso, dev, events)) < 0) {
+ msg->size = 0;
+ return -ENOMEM;
+ }
- _unlock_mutex();
+ msg->size = (uint32_t) r;
return 0;
}
@@ -1140,6 +1148,7 @@ static int _want_registered_device(char *dso_name, char *device_uuid,
static int _get_registered_dev(struct message_data *message_data, int next)
{
struct thread_status *thread, *hit = NULL;
+ int ret = -ENOENT;
_lock_mutex();
@@ -1156,16 +1165,12 @@ static int _get_registered_dev(struct message_data *message_data, int next)
* If we got a registered device and want the next one ->
* fetch next conforming element off the list.
*/
- if (hit && !next) {
- _unlock_mutex();
- return _registered_device(message_data, hit);
- }
+ if (hit && !next)
+ goto reg;
if (!hit)
goto out;
- thread = hit;
-
while (1) {
if (dm_list_end(&_thread_registry, &thread->list))
goto out;
@@ -1177,13 +1182,13 @@ static int _get_registered_dev(struct message_data *message_data, int next)
}
}
- _unlock_mutex();
- return _registered_device(message_data, hit);
+ reg:
+ ret = _registered_device(message_data, hit);
out:
_unlock_mutex();
-
- return -ENOENT;
+
+ return ret;
}
static int _get_registered_device(struct message_data *message_data)
@@ -1241,68 +1246,66 @@ static void _init_fifos(struct dm_event_fifos *fifos)
/* Open fifos used for client communication. */
static int _open_fifos(struct dm_event_fifos *fifos)
{
- int orig_errno;
+ struct stat st;
/* Create client fifo. */
(void) dm_prepare_selinux_context(fifos->client_path, S_IFIFO);
if ((mkfifo(fifos->client_path, 0600) == -1) && errno != EEXIST) {
- syslog(LOG_ERR, "%s: Failed to create client fifo.\n", __func__);
- orig_errno = errno;
+ syslog(LOG_ERR, "%s: Failed to create client fifo %s: %m.\n",
+ __func__, fifos->client_path);
(void) dm_prepare_selinux_context(NULL, 0);
- stack;
- return -orig_errno;
+ return 0;
}
/* Create server fifo. */
(void) dm_prepare_selinux_context(fifos->server_path, S_IFIFO);
if ((mkfifo(fifos->server_path, 0600) == -1) && errno != EEXIST) {
- syslog(LOG_ERR, "%s: Failed to create server fifo.\n", __func__);
- orig_errno = errno;
+ syslog(LOG_ERR, "%s: Failed to create server fifo %s: %m.\n",
+ __func__, fifos->server_path);
(void) dm_prepare_selinux_context(NULL, 0);
- stack;
- return -orig_errno;
+ return 0;
}
(void) dm_prepare_selinux_context(NULL, 0);
- 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",
+ syslog(LOG_WARNING, "Fixing wrong permissions on %s: %m.\n",
fifos->client_path);
if ((!stat(fifos->server_path, &st)) && (st.st_mode & 0777) != 0600)
- syslog(LOG_WARNING, "Fixing wrong permissions on %s",
+ syslog(LOG_WARNING, "Fixing wrong permissions on %s: %m.\n",
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",
+ syslog(LOG_ERR, "Unable to set correct file permissions on %s: %m.\n",
fifos->client_path);
- return -errno;
+ return 0;
}
if (chmod(fifos->server_path, 0600)) {
- syslog(LOG_ERR, "Unable to set correct file permissions on %s",
+ syslog(LOG_ERR, "Unable to set correct file permissions on %s: %m.\n",
fifos->server_path);
- return -errno;
+ return 0;
}
/* Need to open read+write or we will block or fail */
if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
- stack;
- return -errno;
+ syslog(LOG_ERR, "Failed to open fifo server %s: %m.\n",
+ fifos->server_path);
+ return 0;
}
/* Need to open read+write for select() to work. */
if ((fifos->client = open(fifos->client_path, O_RDWR)) < 0) {
- stack;
- close(fifos->server);
- return -errno;
+ syslog(LOG_ERR, "Failed to open fifo client %s: %m", fifos->client_path);
+ if (close(fifos->server))
+ syslog(LOG_ERR, "Failed to close fifo server %s: %m", fifos->server_path);
+ return 0;
}
- return 0;
+ return 1;
}
/*
@@ -1405,7 +1408,7 @@ static int _client_write(struct dm_event_fifos *fifos,
static int _handle_request(struct dm_event_daemon_message *msg,
struct message_data *message_data)
{
- static struct {
+ static struct request {
unsigned int cmd;
int (*f)(struct message_data *);
} requests[] = {
@@ -1420,7 +1423,7 @@ static int _handle_request(struct dm_event_daemon_message *msg,
{ DM_EVENT_CMD_GET_STATUS, _get_status},
}, *req;
- for (req = requests; req < requests + sizeof(requests); req++)
+ for (req = requests; req < requests + sizeof(requests) / sizeof(struct request); req++)
if (req->cmd == msg->cmd)
return req->f(message_data);
@@ -1432,17 +1435,16 @@ static int _do_process_request(struct dm_event_daemon_message *msg)
{
int ret;
char *answer;
- static struct message_data message_data;
+ struct message_data message_data = { .msg = msg };
/* Parse the message. */
- memset(&message_data, 0, sizeof(message_data));
- message_data.msg = msg;
if (msg->cmd == DM_EVENT_CMD_HELLO || msg->cmd == DM_EVENT_CMD_DIE) {
ret = 0;
answer = msg->data;
if (answer) {
- msg->size = dm_asprintf(&(msg->data), "%s %s", answer,
- msg->cmd == DM_EVENT_CMD_DIE ? "DYING" : "HELLO");
+ msg->size = dm_asprintf(&(msg->data), "%s %s %d", answer,
+ msg->cmd == DM_EVENT_CMD_DIE ? "DYING" : "HELLO",
+ DM_EVENT_PROTOCOL_VERSION);
dm_free(answer);
} else {
msg->size = 0;
@@ -1467,9 +1469,7 @@ static int _do_process_request(struct dm_event_daemon_message *msg)
static void _process_request(struct dm_event_fifos *fifos)
{
int die = 0;
- struct dm_event_daemon_message msg;
-
- memset(&msg, 0, sizeof(msg));
+ struct dm_event_daemon_message msg = { 0 };
/*
* Read the request from the client (client_read, client_write
@@ -1488,9 +1488,9 @@ static void _process_request(struct dm_event_fifos *fifos)
if (!_client_write(fifos, &msg))
stack;
- if (die) raise(9);
-
dm_free(msg.data);
+
+ if (die) raise(9);
}
static void _process_initial_registrations(void)
@@ -1501,9 +1501,10 @@ static void _process_initial_registrations(void)
while ((reg = _initial_registrations[i])) {
msg.cmd = DM_EVENT_CMD_REGISTER_FOR_EVENT;
- msg.size = strlen(reg);
- msg.data = reg;
- _do_process_request(&msg);
+ if ((msg.size = strlen(reg))) {
+ msg.data = reg;
+ _do_process_request(&msg);
+ }
++ i;
}
}
@@ -1513,6 +1514,7 @@ static void _cleanup_unused_threads(void)
int ret;
struct dm_list *l;
struct thread_status *thread;
+ int join_ret = 0;
_lock_mutex();
while ((l = dm_list_first(&_thread_registry_unused))) {
@@ -1552,12 +1554,15 @@ static void _cleanup_unused_threads(void)
if (thread->status == DM_THREAD_DONE) {
dm_list_del(l);
- pthread_join(thread->thread, NULL);
+ join_ret = pthread_join(thread->thread, NULL);
_free_thread_status(thread);
}
}
_unlock_mutex();
+
+ if (join_ret)
+ syslog(LOG_ERR, "Failed pthread_join: %s\n", strerror(join_ret));
}
static void _sig_alarm(int signum __attribute__((unused)))
@@ -1569,10 +1574,8 @@ static void _sig_alarm(int signum __attribute__((unused)))
static void _init_thread_signals(void)
{
sigset_t my_sigset;
- struct sigaction act;
+ struct sigaction act = { .sa_handler = _sig_alarm };
- memset(&act, 0, sizeof(act));
- act.sa_handler = _sig_alarm;
sigaction(SIGALRM, &act, NULL);
sigfillset(&my_sigset);
@@ -1610,40 +1613,138 @@ static void _exit_handler(int sig __attribute__((unused)))
}
#ifdef linux
+static int _set_oom_adj(const char *oom_adj_path, int val)
+{
+ FILE *fp;
+
+ if (!(fp = fopen(oom_adj_path, "w"))) {
+ perror("oom_adj: fopen failed");
+ return 0;
+ }
+
+ fprintf(fp, "%i", val);
+
+ if (dm_fclose(fp))
+ perror("oom_adj: fclose failed");
+
+ return 1;
+}
+
/*
* Protection against OOM killer if kernel supports it
*/
-static int _set_oom_adj(int val)
+static int _protect_against_oom_killer(void)
{
- FILE *fp;
-
struct stat st;
if (stat(OOM_ADJ_FILE, &st) == -1) {
- if (errno == ENOENT)
- DEBUGLOG(OOM_ADJ_FILE " not found");
- else
+ if (errno != ENOENT)
perror(OOM_ADJ_FILE ": stat failed");
- return 1;
+
+ /* Try old oom_adj interface as a fallback */
+ if (stat(OOM_ADJ_FILE_OLD, &st) == -1) {
+ if (errno == ENOENT)
+ perror(OOM_ADJ_FILE_OLD " not found");
+ else
+ perror(OOM_ADJ_FILE_OLD ": stat failed");
+ return 1;
+ }
+
+ return _set_oom_adj(OOM_ADJ_FILE_OLD, OOM_DISABLE) ||
+ _set_oom_adj(OOM_ADJ_FILE_OLD, OOM_ADJUST_MIN);
}
- if (!(fp = fopen(OOM_ADJ_FILE, "w"))) {
- perror(OOM_ADJ_FILE ": fopen failed");
+ return _set_oom_adj(OOM_ADJ_FILE, OOM_SCORE_ADJ_MIN);
+}
+
+static int _handle_preloaded_fifo(int fd, const char *path)
+{
+ struct stat st_fd, st_path;
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFD)) < 0)
return 0;
- }
- fprintf(fp, "%i", val);
- if (dm_fclose(fp))
- perror(OOM_ADJ_FILE ": fclose failed");
+ if (flags & FD_CLOEXEC)
+ return 0;
+
+ if (fstat(fd, &st_fd) < 0 || !S_ISFIFO(st_fd.st_mode))
+ return 0;
+
+ if (stat(path, &st_path) < 0 ||
+ st_path.st_dev != st_fd.st_dev ||
+ st_path.st_ino != st_fd.st_ino)
+ return 0;
+
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
+ return 0;
return 1;
}
+
+static int _systemd_handover(struct dm_event_fifos *fifos)
+{
+ const char *e;
+ char *p;
+ unsigned long env_pid, env_listen_fds;
+ int r = 0;
+
+ memset(fifos, 0, sizeof(*fifos));
+
+ /* SD_ACTIVATION must be set! */
+ if (!(e = getenv(SD_ACTIVATION_ENV_VAR_NAME)) || strcmp(e, "1"))
+ goto out;
+
+ /* LISTEN_PID must be equal to our PID! */
+ if (!(e = getenv(SD_LISTEN_PID_ENV_VAR_NAME)))
+ goto out;
+
+ errno = 0;
+ env_pid = strtoul(e, &p, 10);
+ if (errno || !p || *p || env_pid <= 0 ||
+ getpid() != (pid_t) env_pid)
+ goto out;
+
+ /* LISTEN_FDS must be 2 and the fds must be FIFOSs! */
+ if (!(e = getenv(SD_LISTEN_FDS_ENV_VAR_NAME)))
+ goto out;
+
+ errno = 0;
+ env_listen_fds = strtoul(e, &p, 10);
+ if (errno || !p || *p || env_listen_fds != 2)
+ goto out;
+
+ /* Check and handle the FIFOs passed in */
+ r = (_handle_preloaded_fifo(SD_FD_FIFO_SERVER, DM_EVENT_FIFO_SERVER) &&
+ _handle_preloaded_fifo(SD_FD_FIFO_CLIENT, DM_EVENT_FIFO_CLIENT));
+
+ if (r) {
+ fifos->server = SD_FD_FIFO_SERVER;
+ fifos->server_path = DM_EVENT_FIFO_SERVER;
+ fifos->client = SD_FD_FIFO_CLIENT;
+ fifos->client_path = DM_EVENT_FIFO_CLIENT;
+ }
+
+out:
+ unsetenv(SD_ACTIVATION_ENV_VAR_NAME);
+ unsetenv(SD_LISTEN_PID_ENV_VAR_NAME);
+ unsetenv(SD_LISTEN_FDS_ENV_VAR_NAME);
+ return r;
+}
#endif
-static void remove_lockfile(void)
+static void _remove_files_on_exit(void)
{
if (unlink(DMEVENTD_PIDFILE))
perror(DMEVENTD_PIDFILE ": unlink failed");
+
+ if (!_systemd_activation) {
+ if (unlink(DM_EVENT_FIFO_CLIENT))
+ perror(DM_EVENT_FIFO_CLIENT " : unlink failed");
+
+ if (unlink(DM_EVENT_FIFO_SERVER))
+ perror(DM_EVENT_FIFO_SERVER " : unlink failed");
+ }
}
static void _daemonize(void)
@@ -1703,8 +1804,15 @@ static void _daemonize(void)
else
fd = rlim.rlim_cur;
- for (--fd; fd >= 0; fd--)
- close(fd);
+ for (--fd; fd >= 0; fd--) {
+#ifdef linux
+ /* Do not close fds preloaded by systemd! */
+ if (_systemd_activation &&
+ (fd == SD_FD_FIFO_SERVER || fd == SD_FD_FIFO_CLIENT))
+ continue;
+#endif
+ (void) close(fd);
+ }
if ((open("/dev/null", O_RDONLY) < 0) ||
(open("/dev/null", O_WRONLY) < 0) ||
@@ -1721,16 +1829,25 @@ static void restart(void)
int i, count = 0;
char *message;
int length;
+ int version;
/* Get the list of registrations from the running daemon. */
if (!init_fifos(&fifos)) {
- fprintf(stderr, "Could not initiate communication with existing dmeventd.\n");
+ fprintf(stderr, "WARNING: Could not initiate communication with existing dmeventd.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!dm_event_get_version(&fifos, &version)) {
+ fprintf(stderr, "WARNING: Could not communicate with existing dmeventd.\n");
+ fini_fifos(&fifos);
exit(EXIT_FAILURE);
}
- if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0)) {
- fprintf(stderr, "Could not communicate with existing dmeventd.\n");
+ if (version < 1) {
+ fprintf(stderr, "WARNING: The running dmeventd instance is too old.\n"
+ "Protocol version %d (required: 1). Action cancelled.\n",
+ version);
exit(EXIT_FAILURE);
}
@@ -1749,9 +1866,16 @@ static void restart(void)
}
}
- _initial_registrations = dm_malloc(sizeof(char*) * (count + 1));
+ if (!(_initial_registrations = dm_malloc(sizeof(char*) * (count + 1)))) {
+ fprintf(stderr, "Memory allocation registration failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
for (i = 0; i < count; ++i) {
- _initial_registrations[i] = dm_strdup(message);
+ if (!(_initial_registrations[i] = dm_strdup(message))) {
+ fprintf(stderr, "Memory allocation for message failed.\n");
+ exit(EXIT_FAILURE);
+ }
message += strlen(message) + 1;
}
_initial_registrations[count] = 0;
@@ -1761,17 +1885,28 @@ static void restart(void)
exit(EXIT_FAILURE);
}
+ /*
+ * Wait for daemon to die, detected by sending further DIE messages
+ * until one fails.
+ */
+ for (i = 0; i < 10; ++i) {
+ if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_DIE, "-", "-", 0, 0))
+ break; /* yep, it's dead probably */
+ usleep(10);
+ }
+
fini_fifos(&fifos);
}
static void usage(char *prog, FILE *file)
{
fprintf(file, "Usage:\n"
- "%s [-V] [-h] [-d] [-d] [-d] [-f]\n\n"
- " -V Show version of dmeventd\n"
- " -h Show this help information\n"
+ "%s [-d [-d [-d]]] [-f] [-h] [-R] [-V] [-?]\n\n"
" -d Log debug messages to syslog (-d, -dd, -ddd)\n"
- " -f Don't fork, run in the foreground\n\n", prog);
+ " -f Don't fork, run in the foreground\n"
+ " -h -? Show this help information\n"
+ " -R Restart dmeventd\n"
+ " -V Show version of dmeventd\n\n", prog);
}
int main(int argc, char *argv[])
@@ -1803,7 +1938,6 @@ int main(int argc, char *argv[])
case 'V':
printf("dmeventd version: %s\n", DM_LIB_VERSION);
exit(1);
- break;
}
}
@@ -1818,6 +1952,10 @@ int main(int argc, char *argv[])
if (_restart)
restart();
+#ifdef linux
+ _systemd_activation = _systemd_handover(&fifos);
+#endif
+
if (!_foreground)
_daemonize();
@@ -1827,17 +1965,19 @@ int main(int argc, char *argv[])
if (dm_create_lockfile(DMEVENTD_PIDFILE) == 0)
exit(EXIT_FAILURE);
- atexit(remove_lockfile);
+ atexit(_remove_files_on_exit);
(void) dm_prepare_selinux_context(NULL, 0);
/* Set the rest of the signals to cause '_exit_now' to be set */
+ signal(SIGTERM, &_exit_handler);
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");
+ /* Systemd has adjusted oom killer for us already */
+ if (!_systemd_activation && !_protect_against_oom_killer())
+ syslog(LOG_ERR, "Failed to protect against OOM killer");
#endif
_init_thread_signals();
@@ -1847,11 +1987,12 @@ int main(int argc, char *argv[])
//multilog_init_verbose(std_syslog, _LOG_DEBUG);
//multilog_async(1);
- _init_fifos(&fifos);
+ if (!_systemd_activation)
+ _init_fifos(&fifos);
pthread_mutex_init(&_global_mutex, NULL);
- if (_open_fifos(&fifos))
+ if (!_systemd_activation && !_open_fifos(&fifos))
exit(EXIT_FIFO_FAILURE);
/* Signal parent, letting them know we are ready to go. */
@@ -1865,11 +2006,13 @@ int main(int argc, char *argv[])
while (!_exit_now) {
_process_request(&fifos);
_cleanup_unused_threads();
+ _lock_mutex();
if (!dm_list_empty(&_thread_registry)
|| !dm_list_empty(&_thread_registry_unused))
_thread_registries_empty = 0;
else
_thread_registries_empty = 1;
+ _unlock_mutex();
}
_exit_dm_lib();
diff --git a/daemons/dmeventd/dmeventd.h b/daemons/dmeventd/dmeventd.h
index 254758e..e21cf45 100644
--- a/daemons/dmeventd/dmeventd.h
+++ b/daemons/dmeventd/dmeventd.h
@@ -17,11 +17,8 @@
/* 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_FIFO_CLIENT DEFAULT_DM_RUN_DIR "/dmeventd-client"
+#define DM_EVENT_FIFO_SERVER DEFAULT_DM_RUN_DIR "/dmeventd-server"
#define DM_EVENT_DEFAULT_TIMEOUT 10
@@ -66,11 +63,13 @@ struct dm_event_fifos {
#define EXIT_CHDIR_FAILURE 7
/* Implemented in libdevmapper-event.c, but not part of public API. */
+// FIXME misuse of bitmask as enum
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);
int init_fifos(struct dm_event_fifos *fifos);
void fini_fifos(struct dm_event_fifos *fifos);
+int dm_event_get_version(struct dm_event_fifos *fifos, int *version);
#endif /* __DMEVENTD_DOT_H__ */
diff --git a/daemons/dmeventd/libdevmapper-event.c b/daemons/dmeventd/libdevmapper-event.c
index bc8ad99..1f8fbef 100644
--- a/daemons/dmeventd/libdevmapper-event.c
+++ b/daemons/dmeventd/libdevmapper-event.c
@@ -59,14 +59,10 @@ struct dm_event_handler *dm_event_handler_create(void)
{
struct dm_event_handler *dmevh = NULL;
- if (!(dmevh = dm_malloc(sizeof(*dmevh))))
+ if (!(dmevh = dm_zalloc(sizeof(*dmevh)))) {
+ log_error("Failed to allocate event handler.");
return NULL;
-
- dmevh->dmeventd_path = NULL;
- dmevh->dso = dmevh->dev_name = dmevh->uuid = NULL;
- dmevh->major = dmevh->minor = 0;
- dmevh->mask = 0;
- dmevh->timeout = 0;
+ }
return dmevh;
}
@@ -245,6 +241,10 @@ static int _daemon_read(struct dm_event_fifos *fifos,
log_error("Unable to read from event server");
return 0;
}
+ if ((ret == 0) && (i > 4) && !bytes) {
+ log_error("No input from event server.");
+ return 0;
+ }
}
if (ret < 1) {
log_error("Unable to read from event server.");
@@ -309,7 +309,7 @@ static int _daemon_write(struct dm_event_fifos *fifos,
}
if (ret == 0)
break;
- read(fifos->server, drainbuf, 127);
+ ret = read(fifos->server, drainbuf, 127);
}
while (bytes < size) {
@@ -424,30 +424,27 @@ static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos)
fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK);
if (fifos->client >= 0) {
/* server is running and listening */
-
- close(fifos->client);
+ if (close(fifos->client))
+ log_sys_error("close", fifos->client_path);
return 1;
} else if (errno != ENXIO) {
/* problem */
-
- log_error("%s: Can't open client fifo %s: %s",
- __func__, fifos->client_path, strerror(errno));
- stack;
+ log_sys_error("open", fifos->client_path);
return 0;
}
start_server:
/* server is not running */
- if (!strncmp(DMEVENTD_PATH, "/", 1) && stat(DMEVENTD_PATH, &statbuf)) {
- log_error("Unable to find dmeventd.");
- return_0;
+ if ((args[0][0] == '/') && stat(args[0], &statbuf)) {
+ log_sys_error("stat", args[0]);
+ return 0;
}
pid = fork();
if (pid < 0)
- log_error("Unable to fork.");
+ log_sys_error("fork", "");
else if (!pid) {
execvp(args[0], args);
@@ -477,25 +474,23 @@ int init_fifos(struct dm_event_fifos *fifos)
/* 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",
- __func__, fifos->server_path);
- stack;
+ log_sys_error("open", fifos->server_path);
return 0;
}
/* Lock out anyone else trying to do communication with the daemon. */
if (flock(fifos->server, LOCK_EX) < 0) {
- log_error("%s: flock %s", __func__, fifos->server_path);
- close(fifos->server);
+ log_sys_error("flock", fifos->server_path);
+ if (close(fifos->server))
+ log_sys_error("close", fifos->server_path);
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;
+ log_sys_error("open", fifos->client_path);
+ if (close(fifos->server))
+ log_sys_error("close", fifos->server_path);
return 0;
}
@@ -523,8 +518,10 @@ void fini_fifos(struct dm_event_fifos *fifos)
if (flock(fifos->server, LOCK_UN))
log_error("flock unlock %s", fifos->server_path);
- close(fifos->client);
- close(fifos->server);
+ if (close(fifos->client))
+ log_sys_error("close", fifos->client_path);
+ if (close(fifos->server))
+ log_sys_error("close", fifos->server_path);
}
/* Get uuid of a device */
@@ -538,34 +535,46 @@ static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
return NULL;
}
- 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 (dmevh->uuid) {
+ if (!dm_task_set_uuid(dmt, dmevh->uuid))
+ goto_bad;
+ } else if (dmevh->dev_name) {
+ if (!dm_task_set_name(dmt, dmevh->dev_name))
+ goto_bad;
+ } else if (dmevh->major && dmevh->minor) {
+ if (!dm_task_set_major(dmt, dmevh->major) ||
+ !dm_task_set_minor(dmt, dmevh->minor))
+ goto_bad;
+ }
/* 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;
+ goto bad;
}
if (!dm_task_get_info(dmt, &info)) {
log_error("_get_device_info: failed to get info for device");
- goto failed;
+ goto bad;
}
if (!info.exists) {
- log_error("_get_device_info: device not found");
- goto failed;
+ log_error("_get_device_info: %s%s%s%.0d%s%.0d%s%s: device not found",
+ dmevh->uuid ? : "",
+ (!dmevh->uuid && dmevh->dev_name) ? dmevh->dev_name : "",
+ (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? "(" : "",
+ (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? dmevh->major : 0,
+ (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? ":" : "",
+ (!dmevh->uuid && !dmevh->dev_name && dmevh->minor > 0) ? dmevh->minor : 0,
+ (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) && dmevh->minor == 0 ? "0" : "",
+ (!dmevh->uuid && !dmevh->dev_name && dmevh->major > 0) ? ") " : "");
+ goto bad;
}
+
return dmt;
-failed:
+ bad:
dm_task_destroy(dmt);
return NULL;
}
@@ -715,17 +724,18 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
uuid = dm_task_get_uuid(dmt);
- if (!(ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
- DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
- &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 (_do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
+ DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
+ &msg, dmevh->dso, uuid, dmevh->mask, 0)) {
+ log_debug("%s: device not registered.", dm_task_get_name(dmt));
ret = -ENOENT;
goto fail;
}
+ /* FIXME this will probably horribly break if we get
+ ill-formatted reply */
+ ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask);
+
dm_task_destroy(dmt);
dmt = NULL;
@@ -733,6 +743,10 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
msg.data = NULL;
_dm_event_handler_clear_dev_info(dmevh);
+ if (!reply_uuid) {
+ ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
+ goto fail;
+ }
dmevh->uuid = dm_strdup(reply_uuid);
if (!dmevh->uuid) {
ret = -ENOMEM;
@@ -781,6 +795,36 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
return ret;
}
+/*
+ * You can (and have to) call this at the stage of the protocol where
+ * daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0)
+ *
+ * would be normally sent. This call will parse the version reply from
+ * dmeventd, in addition to above call. It is not safe to call this at any
+ * other place in the protocol.
+ *
+ * This is an internal function, not exposed in the public API.
+ */
+
+int dm_event_get_version(struct dm_event_fifos *fifos, int *version) {
+ char *p;
+ struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+ if (daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0))
+ return 0;
+ p = msg.data;
+ *version = 0;
+
+ p = strchr(p, ' '); /* Message ID */
+ if (!p) return 0;
+ p = strchr(p + 1, ' '); /* HELLO */
+ if (!p) return 0;
+ p = strchr(p + 1, ' '); /* HELLO, once more */
+ if (p)
+ *version = atoi(p);
+ return 1;
+}
+
#if 0 /* left out for now */
static char *_skip_string(char *src, const int delimiter)
diff --git a/daemons/dmeventd/libdevmapper-event.h b/daemons/dmeventd/libdevmapper-event.h
index 0de20c1..7ce3f39 100644
--- a/daemons/dmeventd/libdevmapper-event.h
+++ b/daemons/dmeventd/libdevmapper-event.h
@@ -46,6 +46,7 @@ enum dm_event_mask {
};
#define DM_EVENT_ALL_ERRORS DM_EVENT_ERROR_MASK
+#define DM_EVENT_PROTOCOL_VERSION 1
struct dm_event_handler;
@@ -81,6 +82,7 @@ void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout);
/*
* Specify mask for events to monitor.
*/
+// FIXME misuse of bitmask as enum
void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
enum dm_event_mask evmask);
@@ -90,6 +92,7 @@ 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);
+// FIXME misuse of bitmask as enum
enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh);
/* FIXME Review interface (what about this next thing?) */
@@ -103,6 +106,7 @@ int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
/* Prototypes for DSO interface, see dmeventd.c, struct dso_data for
detailed descriptions. */
+// FIXME misuse of bitmask as enum
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,
diff --git a/daemons/dmeventd/plugins/Makefile.in b/daemons/dmeventd/plugins/Makefile.in
index 45176ad..b26e6d8 100644
--- a/daemons/dmeventd/plugins/Makefile.in
+++ b/daemons/dmeventd/plugins/Makefile.in
@@ -1,6 +1,6 @@
#
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
-# Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2004-2005, 2011 Red Hat, Inc. All rights reserved.
#
# This file is part of LVM2.
#
@@ -16,10 +16,31 @@ srcdir = @srcdir@
top_srcdir = @top_srcdir@
top_builddir = @top_builddir@
-SUBDIRS += lvm2 mirror snapshot
+SUBDIRS += lvm2
+
+ifneq ("@MIRRORS@", "none")
+ SUBDIRS += mirror
+endif
+
+ifneq ("@SNAPSHOTS@", "none")
+ SUBDIRS += snapshot
+endif
+
+ifneq ("@RAID@", "none")
+ SUBDIRS += raid
+endif
+
+ifneq ("@THIN@", "none")
+ SUBDIRS += thin
+endif
+
+ifeq ($(MAKECMDGOALS),distclean)
+ SUBDIRS = lvm2 mirror snapshot raid thin
+endif
include $(top_builddir)/make.tmpl
-mirror: lvm2
snapshot: lvm2
-
+mirror: lvm2
+raid: lvm2
+thin: lvm2
diff --git a/daemons/dmeventd/plugins/lvm2/.exported_symbols b/daemons/dmeventd/plugins/lvm2/.exported_symbols
index ebe3d05..646e4cf 100644
--- a/daemons/dmeventd/plugins/lvm2/.exported_symbols
+++ b/daemons/dmeventd/plugins/lvm2/.exported_symbols
@@ -4,3 +4,4 @@ dmeventd_lvm2_lock
dmeventd_lvm2_unlock
dmeventd_lvm2_pool
dmeventd_lvm2_run
+dmeventd_lvm2_command
diff --git a/daemons/dmeventd/plugins/lvm2/Makefile.in b/daemons/dmeventd/plugins/lvm2/Makefile.in
index 46247aa..fcb2a0a 100644
--- a/daemons/dmeventd/plugins/lvm2/Makefile.in
+++ b/daemons/dmeventd/plugins/lvm2/Makefile.in
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2010-2011 Red Hat, Inc. All rights reserved.
#
# This file is part of LVM2.
#
@@ -24,10 +24,8 @@ LIB_VERSION = $(LIB_VERSION_LVM)
include $(top_builddir)/make.tmpl
-LIBS += @LVM2CMD_LIB@ -ldevmapper $(PTHREAD_LIBS)
+LIBS += @LVM2CMD_LIB@ -ldevmapper $(PTHREAD_LIBS) $(DAEMON_LIBS)
install_lvm2: install_lib_shared
install: install_lvm2
-
-DISTCLEAN_TARGETS += .exported_symbols_generated
diff --git a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c
index 937d81d..5d5a46b 100644
--- a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c
+++ b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.c
@@ -16,8 +16,6 @@
#include "log.h"
#include "lvm2cmd.h"
-#include "errors.h"
-#include "libdevmapper-event.h"
#include "dmeventd_lvm.h"
#include <pthread.h>
@@ -82,10 +80,7 @@ static void _temporary_log_fn(int level,
void dmeventd_lvm2_lock(void)
{
- if (pthread_mutex_trylock(&_event_mutex)) {
- syslog(LOG_NOTICE, "Another thread is handling an event. Waiting...");
- pthread_mutex_lock(&_event_mutex);
- }
+ pthread_mutex_lock(&_event_mutex);
}
void dmeventd_lvm2_unlock(void)
@@ -113,6 +108,7 @@ int dmeventd_lvm2_init(void)
_mem_pool = NULL;
goto out;
}
+ lvm2_disable_dmeventd_monitoring(_lvm_handle);
/* FIXME Temporary: move to dmeventd core */
lvm2_run(_lvm_handle, "_memlock_inc");
}
@@ -150,3 +146,31 @@ int dmeventd_lvm2_run(const char *cmdline)
return lvm2_run(_lvm_handle, cmdline);
}
+int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
+ const char *cmd, const char *device)
+{
+ char *vg = NULL, *lv = NULL, *layer;
+ int r;
+
+ if (!dm_split_lvm_name(mem, device, &vg, &lv, &layer)) {
+ syslog(LOG_ERR, "Unable to determine VG name from %s.\n",
+ device);
+ return 0;
+ }
+
+ /* strip off the mirror component designations */
+ layer = strstr(lv, "_mlog");
+ if (layer)
+ *layer = '\0';
+
+ r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv);
+
+ dm_pool_free(mem, vg);
+
+ if (r < 0) {
+ syslog(LOG_ERR, "Unable to form LVM command. (too long).\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h
index 8efcb9b..1960c71 100644
--- a/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h
+++ b/daemons/dmeventd/plugins/lvm2/dmeventd_lvm.h
@@ -36,4 +36,7 @@ void dmeventd_lvm2_unlock(void);
struct dm_pool *dmeventd_lvm2_pool(void);
+int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size,
+ const char *cmd, const char *device);
+
#endif /* _DMEVENTD_LVMWRAP_H */
diff --git a/daemons/dmeventd/plugins/mirror/Makefile.in b/daemons/dmeventd/plugins/mirror/Makefile.in
index 7f80629..85b33c9 100644
--- a/daemons/dmeventd/plugins/mirror/Makefile.in
+++ b/daemons/dmeventd/plugins/mirror/Makefile.in
@@ -1,6 +1,6 @@
#
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
-# Copyright (C) 2004-2005, 2008-2010 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2004-2005, 2008-2011 Red Hat, Inc. All rights reserved.
#
# This file is part of LVM2.
#
@@ -30,10 +30,8 @@ CFLOW_LIST_TARGET = $(LIB_NAME).cflow
include $(top_builddir)/make.tmpl
-LIBS += -ldevmapper-event-lvm2 -ldevmapper
+LIBS += -ldevmapper-event-lvm2 -ldevmapper $(DAEMON_LIBS)
install_lvm2: install_dm_plugin
install: install_lvm2
-
-DISTCLEAN_TARGETS += .exported_symbols_generated
diff --git a/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c
index 3e97d87..e59feb4 100644
--- a/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c
+++ b/daemons/dmeventd/plugins/mirror/dmeventd_mirror.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2005-2012 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -18,6 +18,7 @@
#include "errors.h"
#include "libdevmapper-event.h"
#include "dmeventd_lvm.h"
+#include "defaults.h"
#include <syslog.h> /* FIXME Replace syslog with multilog */
/* FIXME Missing openlog? */
@@ -81,7 +82,8 @@ static int _get_mirror_event(char *params)
if (!dm_split_words(params, 1, 0, &p))
goto out_parse;
- if (!(num_devs = atoi(p)))
+ if (!(num_devs = atoi(p)) ||
+ (num_devs > DEFAULT_MIRROR_MAX_IMAGES) || (num_devs < 0))
goto out_parse;
p += strlen(p) + 1;
@@ -90,6 +92,7 @@ static int _get_mirror_event(char *params)
if (!args || dm_split_words(p, num_devs + 7, 0, args) < num_devs + 5)
goto out_parse;
+ /* FIXME: Code differs from lib/mirror/mirrored.c */
dev_status_str = args[2 + num_devs];
log_argc = atoi(args[3 + num_devs]);
log_status_str = args[3 + num_devs + log_argc];
@@ -121,7 +124,7 @@ static int _get_mirror_event(char *params)
out:
dm_free(args);
return r;
-
+
out_parse:
dm_free(args);
syslog(LOG_ERR, "Unable to parse mirror status string.");
@@ -133,32 +136,15 @@ static int _remove_failed_devices(const char *device)
int r;
#define CMD_SIZE 256 /* FIXME Use system restriction */
char cmd_str[CMD_SIZE];
- char *vg = NULL, *lv = NULL, *layer = NULL;
-
- if (strlen(device) > 200) /* FIXME Use real restriction */
- return -ENAMETOOLONG; /* FIXME These return code distinctions are not used so remove them! */
-
- if (!dm_split_lvm_name(dmeventd_lvm2_pool(), device, &vg, &lv, &layer)) {
- syslog(LOG_ERR, "Unable to determine VG name from %s.",
- device);
- return -ENOMEM; /* FIXME Replace with generic error return - reason for failure has already got logged */
- }
-
- /* strip off the mirror component designations */
- layer = strstr(lv, "_mlog");
- if (layer)
- *layer = '\0';
- /* FIXME Is any sanity-checking required on %s? */
- if (CMD_SIZE <= snprintf(cmd_str, CMD_SIZE, "lvconvert --config devices{ignore_suspended_devices=1} --repair --use-policies %s/%s", vg, lv)) {
- /* this error should be caught above, but doesn't hurt to check again */
- syslog(LOG_ERR, "Unable to form LVM command: Device name too long.");
+ if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str),
+ "lvconvert --config devices{ignore_suspended_devices=1} "
+ "--repair --use-policies", device))
return -ENAMETOOLONG; /* FIXME Replace with generic error return - reason for failure has already got logged */
- }
r = dmeventd_lvm2_run(cmd_str);
- syslog(LOG_INFO, "Repair of mirrored LV %s/%s %s.", vg, lv,
+ syslog(LOG_INFO, "Repair of mirrored device %s %s.", device,
(r == ECMD_PROCESSED) ? "finished successfully" : "failed");
return (r == ECMD_PROCESSED) ? 0 : -1;
@@ -227,9 +213,12 @@ int register_device(const char *device,
int minor __attribute__((unused)),
void **unused __attribute__((unused)))
{
- int r = dmeventd_lvm2_init();
+ if (!dmeventd_lvm2_init())
+ return 0;
+
syslog(LOG_INFO, "Monitoring mirror device %s for events.", device);
- return r;
+
+ return 1;
}
int unregister_device(const char *device,
@@ -241,5 +230,6 @@ int unregister_device(const char *device,
syslog(LOG_INFO, "No longer monitoring mirror device %s for events.",
device);
dmeventd_lvm2_exit();
+
return 1;
}
diff --git a/daemons/dmeventd/plugins/raid/.exported_symbols b/daemons/dmeventd/plugins/raid/.exported_symbols
new file mode 100644
index 0000000..b88c705
--- /dev/null
+++ b/daemons/dmeventd/plugins/raid/.exported_symbols
@@ -0,0 +1,3 @@
+process_event
+register_device
+unregister_device
diff --git a/daemons/dmeventd/plugins/raid/Makefile.in b/daemons/dmeventd/plugins/raid/Makefile.in
new file mode 100644
index 0000000..a6b7788
--- /dev/null
+++ b/daemons/dmeventd/plugins/raid/Makefile.in
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2011 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+INCLUDES += -I$(top_srcdir)/tools -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
+CLDFLAGS += -L$(top_builddir)/tools -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
+
+SOURCES = dmeventd_raid.c
+
+LIB_NAME = libdevmapper-event-lvm2raid
+LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = $(LIB_NAME).cflow
+
+include $(top_builddir)/make.tmpl
+
+LIBS += -ldevmapper-event-lvm2 -ldevmapper
+
+install_lvm2: install_dm_plugin
+
+install: install_lvm2
diff --git a/daemons/dmeventd/plugins/raid/dmeventd_raid.c b/daemons/dmeventd/plugins/raid/dmeventd_raid.c
new file mode 100644
index 0000000..a3ecdc1
--- /dev/null
+++ b/daemons/dmeventd/plugins/raid/dmeventd_raid.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2005-2011 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+
+#include "lvm2cmd.h"
+#include "errors.h"
+#include "libdevmapper-event.h"
+#include "dmeventd_lvm.h"
+
+#include <syslog.h> /* FIXME Replace syslog with multilog */
+/* FIXME Missing openlog? */
+/* FIXME Replace most syslogs with log_error() style messages and add complete context. */
+/* FIXME Reformat to 80 char lines. */
+
+/*
+ * run_repair is a close copy to
+ * plugins/mirror/dmeventd_mirror.c:_remove_failed_devices()
+ */
+static int run_repair(const char *device)
+{
+ int r;
+#define CMD_SIZE 256 /* FIXME Use system restriction */
+ char cmd_str[CMD_SIZE];
+
+ if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(), cmd_str, sizeof(cmd_str),
+ "lvconvert --config devices{ignore_suspended_devices=1} "
+ "--repair --use-policies", device))
+ return -1;
+
+ r = dmeventd_lvm2_run(cmd_str);
+
+ if (r != ECMD_PROCESSED)
+ syslog(LOG_INFO, "Repair of RAID device %s failed.", device);
+
+ return (r == ECMD_PROCESSED) ? 0 : -1;
+}
+
+static int _process_raid_event(char *params, const char *device)
+{
+ int i, n, failure = 0;
+ char *p, *a[4];
+ char *raid_type;
+ char *num_devices;
+ char *health_chars;
+ char *resync_ratio;
+
+ /*
+ * RAID parms: <raid_type> <#raid_disks> \
+ * <health chars> <resync ratio>
+ */
+ if (!dm_split_words(params, 4, 0, a)) {
+ syslog(LOG_ERR, "Failed to process status line for %s\n",
+ device);
+ return -EINVAL;
+ }
+ raid_type = a[0];
+ num_devices = a[1];
+ health_chars = a[2];
+ resync_ratio = a[3];
+
+ if (!(n = atoi(num_devices))) {
+ syslog(LOG_ERR, "Failed to parse number of devices for %s: %s",
+ device, num_devices);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < n; i++) {
+ switch (health_chars[i]) {
+ case 'A':
+ /* Device is 'A'live and well */
+ case 'a':
+ /* Device is 'a'live, but not yet in-sync */
+ break;
+ case 'D':
+ syslog(LOG_ERR,
+ "Device #%d of %s array, %s, has failed.",
+ i, raid_type, device);
+ failure++;
+ break;
+ default:
+ /* Unhandled character returned from kernel */
+ break;
+ }
+ if (failure)
+ return run_repair(device);
+ }
+
+ p = strstr(resync_ratio, "/");
+ if (!p) {
+ syslog(LOG_ERR, "Failed to parse resync_ratio for %s: %s",
+ device, resync_ratio);
+ return -EINVAL;
+ }
+ p[0] = '\0';
+ syslog(LOG_INFO, "%s array, %s, is %s in-sync.",
+ raid_type, device, strcmp(resync_ratio, p+1) ? "not" : "now");
+
+ return 0;
+}
+
+void process_event(struct dm_task *dmt,
+ enum dm_event_mask event __attribute__((unused)),
+ void **unused __attribute__((unused)))
+{
+ void *next = NULL;
+ uint64_t start, length;
+ char *target_type = NULL;
+ char *params;
+ const char *device = dm_task_get_name(dmt);
+
+ dmeventd_lvm2_lock();
+
+ do {
+ next = dm_get_next_target(dmt, next, &start, &length,
+ &target_type, &params);
+
+ if (!target_type) {
+ syslog(LOG_INFO, "%s mapping lost.", device);
+ continue;
+ }
+
+ if (strcmp(target_type, "raid")) {
+ syslog(LOG_INFO, "%s has non-raid portion.", device);
+ continue;
+ }
+
+ if (_process_raid_event(params, device))
+ syslog(LOG_ERR, "Failed to process event for %s",
+ device);
+ } while (next);
+
+ dmeventd_lvm2_unlock();
+}
+
+int register_device(const char *device,
+ const char *uuid __attribute__((unused)),
+ int major __attribute__((unused)),
+ int minor __attribute__((unused)),
+ void **unused __attribute__((unused)))
+{
+ if (!dmeventd_lvm2_init())
+ return 0;
+
+ syslog(LOG_INFO, "Monitoring RAID device %s for events.", device);
+
+ return 1;
+}
+
+int unregister_device(const char *device,
+ const char *uuid __attribute__((unused)),
+ int major __attribute__((unused)),
+ int minor __attribute__((unused)),
+ void **unused __attribute__((unused)))
+{
+ syslog(LOG_INFO, "No longer monitoring RAID device %s for events.",
+ device);
+ dmeventd_lvm2_exit();
+
+ return 1;
+}
diff --git a/daemons/dmeventd/plugins/snapshot/Makefile.in b/daemons/dmeventd/plugins/snapshot/Makefile.in
index b8414b2..a4cff15 100644
--- a/daemons/dmeventd/plugins/snapshot/Makefile.in
+++ b/daemons/dmeventd/plugins/snapshot/Makefile.in
@@ -1,6 +1,6 @@
#
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
-# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
#
# This file is part of the LVM2.
#
@@ -26,10 +26,8 @@ LIB_VERSION = $(LIB_VERSION_LVM)
include $(top_builddir)/make.tmpl
-LIBS += -ldevmapper-event-lvm2 -ldevmapper
+LIBS += -ldevmapper-event-lvm2 -ldevmapper $(DAEMON_LIBS)
install_lvm2: install_dm_plugin
install: install_lvm2
-
-DISTCLEAN_TARGETS += .exported_symbols_generated
diff --git a/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c b/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
index 6fc9f56..205218a 100644
--- a/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
+++ b/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2007-2011 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -19,8 +19,6 @@
#include "libdevmapper-event.h"
#include "dmeventd_lvm.h"
-#include "lvm-string.h"
-
#include <sys/wait.h>
#include <syslog.h> /* FIXME Replace syslog with multilog */
/* FIXME Missing openlog? */
@@ -40,6 +38,12 @@ struct snap_status {
int max;
};
+struct dso_state {
+ int percent_check;
+ int known_size;
+ char cmd_str[1024];
+};
+
/* FIXME possibly reconcile this with target_percent when we gain
access to regular LVM library here. */
static void _parse_snapshot_params(char *params, struct snap_status *status)
@@ -115,26 +119,9 @@ static int _run(const char *cmd, ...)
return 1; /* all good */
}
-static int _extend(const char *device)
+static int _extend(const char *cmd)
{
- char *vg = NULL, *lv = NULL, *layer = NULL;
- char cmd_str[1024];
- int r = 0;
-
- if (!dm_split_lvm_name(dmeventd_lvm2_pool(), device, &vg, &lv, &layer)) {
- syslog(LOG_ERR, "Unable to determine VG name from %s.", device);
- return 0;
- }
- if (sizeof(cmd_str) <= snprintf(cmd_str, sizeof(cmd_str),
- "lvextend --use-policies %s/%s", vg, lv)) {
- syslog(LOG_ERR, "Unable to form LVM command: Device name too long.");
- return 0;
- }
-
- r = dmeventd_lvm2_run(cmd_str);
- syslog(LOG_INFO, "Extension of snapshot %s/%s %s.", vg, lv,
- (r == ECMD_PROCESSED) ? "finished successfully" : "failed");
- return r == ECMD_PROCESSED;
+ return dmeventd_lvm2_run(cmd) == ECMD_PROCESSED;
}
static void _umount(const char *device, int major, int minor)
@@ -155,7 +142,8 @@ static void _umount(const char *device, int major, int minor)
break; /* eof, likely */
/* words[0] is the mount point and words[1] is the device path */
- dm_split_words(buffer, 3, 0, words);
+ if (dm_split_words(buffer, 3, 0, words) < 2)
+ continue;
/* find the major/minor of the device */
if (stat(words[0], &st))
@@ -164,9 +152,9 @@ static void _umount(const char *device, int major, int minor)
if (S_ISBLK(st.st_mode) &&
major(st.st_rdev) == major &&
minor(st.st_rdev) == minor) {
- syslog(LOG_ERR, "Unmounting invalid snapshot %s from %s.", device, words[1]);
+ syslog(LOG_ERR, "Unmounting invalid snapshot %s from %s.\n", device, words[1]);
if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
- syslog(LOG_ERR, "Failed to umount snapshot %s from %s: %s.",
+ syslog(LOG_ERR, "Failed to umount snapshot %s from %s: %s.\n",
device, words[1], strerror(errno));
}
}
@@ -185,10 +173,11 @@ void process_event(struct dm_task *dmt,
char *params;
struct snap_status status = { 0 };
const char *device = dm_task_get_name(dmt);
- int percent, *percent_check = (int*)private;
+ int percent;
+ struct dso_state *state = *private;
/* No longer monitoring, waiting for remove */
- if (!*percent_check)
+ if (!state->percent_check)
return;
dmeventd_lvm2_lock();
@@ -200,7 +189,6 @@ void process_event(struct dm_task *dmt,
_parse_snapshot_params(params, &status);
if (status.invalid) {
- syslog(LOG_ERR, "Trying to umount invalid snapshot %s...\n", device);
struct dm_info info;
if (dm_task_get_info(dmt, &info)) {
dmeventd_lvm2_unlock();
@@ -209,27 +197,35 @@ void process_event(struct dm_task *dmt,
} /* else; too bad, but this is best-effort thing... */
}
+ /* Snapshot size had changed. Clear the threshold. */
+ if (state->known_size != status.max) {
+ state->percent_check = CHECK_MINIMUM;
+ state->known_size = status.max;
+ }
+
/*
* If the snapshot has been invalidated or we failed to parse
* the status string. Report the full status string to syslog.
*/
if (status.invalid || !status.max) {
syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params);
- *percent_check = 0;
+ state->percent_check = 0;
goto out;
}
percent = 100 * status.used / status.max;
- if (percent >= *percent_check) {
+ if (percent >= state->percent_check) {
/* Usage has raised more than CHECK_STEP since the last
time. Run actions. */
- *percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
+ state->percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
+
if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent);
/* Try to extend the snapshot, in accord with user-set policies */
- if (!_extend(device))
- syslog(LOG_ERR, "Failed to extend snapshot %s.", device);
+ if (!_extend(state->cmd_str))
+ syslog(LOG_ERR, "Failed to extend snapshot %s.\n", device);
}
+
out:
dmeventd_lvm2_unlock();
}
@@ -240,23 +236,46 @@ int register_device(const char *device,
int minor __attribute__((unused)),
void **private)
{
- int *percent_check = (int*)private;
- int r = dmeventd_lvm2_init();
+ struct dso_state *state;
+
+ if (!dmeventd_lvm2_init())
+ goto out;
- *percent_check = CHECK_MINIMUM;
+ if (!(state = dm_zalloc(sizeof(*state))))
+ goto bad;
+
+ if (!dmeventd_lvm2_command(dmeventd_lvm2_pool(),
+ state->cmd_str, sizeof(state->cmd_str),
+ "lvextend --use-policies", device))
+ goto bad;
+
+ state->percent_check = CHECK_MINIMUM;
+ state->known_size = 0;
+ *private = state;
syslog(LOG_INFO, "Monitoring snapshot %s\n", device);
- return r;
+
+ return 1;
+bad:
+ dm_free(state);
+ dmeventd_lvm2_exit();
+out:
+ syslog(LOG_ERR, "Failed to monitor snapshot %s.\n", device);
+
+ return 0;
}
int unregister_device(const char *device,
const char *uuid __attribute__((unused)),
int major __attribute__((unused)),
int minor __attribute__((unused)),
- void **unused __attribute__((unused)))
+ void **private)
{
- syslog(LOG_INFO, "No longer monitoring snapshot %s\n",
- device);
+ struct dso_state *state = *private;
+
+ syslog(LOG_INFO, "No longer monitoring snapshot %s\n", device);
+ dm_free(state);
dmeventd_lvm2_exit();
+
return 1;
}
diff --git a/daemons/dmeventd/plugins/thin/.exported_symbols b/daemons/dmeventd/plugins/thin/.exported_symbols
new file mode 100644
index 0000000..b88c705
--- /dev/null
+++ b/daemons/dmeventd/plugins/thin/.exported_symbols
@@ -0,0 +1,3 @@
+process_event
+register_device
+unregister_device
diff --git a/daemons/dmeventd/plugins/thin/Makefile.in b/daemons/dmeventd/plugins/thin/Makefile.in
new file mode 100644
index 0000000..e964ab5
--- /dev/null
+++ b/daemons/dmeventd/plugins/thin/Makefile.in
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2011 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+INCLUDES += -I$(top_srcdir)/tools -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
+CLDFLAGS += -L$(top_builddir)/tools -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
+
+SOURCES = dmeventd_thin.c
+
+LIB_NAME = libdevmapper-event-lvm2thin
+LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = $(LIB_NAME).cflow
+
+include $(top_builddir)/make.tmpl
+
+LIBS += -ldevmapper-event-lvm2 -ldevmapper
+
+install_lvm2: install_dm_plugin
+
+install: install_lvm2
diff --git a/daemons/dmeventd/plugins/thin/dmeventd_thin.c b/daemons/dmeventd/plugins/thin/dmeventd_thin.c
new file mode 100644
index 0000000..a1af4c0
--- /dev/null
+++ b/daemons/dmeventd/plugins/thin/dmeventd_thin.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2011-2012 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+
+#include "lvm2cmd.h"
+#include "errors.h"
+#include "libdevmapper-event.h"
+#include "dmeventd_lvm.h"
+
+#include <sys/wait.h>
+#include <syslog.h> /* FIXME Replace syslog with multilog */
+/* FIXME Missing openlog? */
+
+/* First warning when thin is 80% full. */
+#define WARNING_THRESH 80
+/* Run a check every 5%. */
+#define CHECK_STEP 5
+/* Do not bother checking thins less than 50% full. */
+#define CHECK_MINIMUM 50
+
+#define UMOUNT_COMMAND "/bin/umount"
+
+#define THIN_DEBUG 0
+
+struct dso_state {
+ struct dm_pool *mem;
+ int metadata_percent_check;
+ int data_percent_check;
+ uint64_t known_metadata_size;
+ uint64_t known_data_size;
+ char cmd_str[1024];
+};
+
+
+/* TODO - move this mountinfo code into library to be reusable */
+#ifdef linux
+# include "kdev_t.h"
+#else
+# define MAJOR(x) major((x))
+# define MINOR(x) minor((x))
+# define MKDEV(x,y) makedev((x),(y))
+#endif
+
+/* Macros to make string defines */
+#define TO_STRING_EXP(A) #A
+#define TO_STRING(A) TO_STRING_EXP(A)
+
+static int _is_octal(int a)
+{
+ return (((a) & ~7) == '0');
+}
+
+/* Convert mangled mountinfo into normal ASCII string */
+static void _unmangle_mountinfo_string(const char *src, char *buf)
+{
+ if (!src)
+ return;
+
+ while (*src) {
+ if ((*src == '\\') &&
+ _is_octal(src[1]) && _is_octal(src[2]) && _is_octal(src[3])) {
+ *buf++ = 64 * (src[1] & 7) + 8 * (src[2] & 7) + (src[3] & 7);
+ src += 4;
+ } else
+ *buf++ = *src++;
+ }
+ *buf = '\0';
+}
+
+/* Parse one line of mountinfo */
+static int _parse_mountinfo_line(const char *line, unsigned *maj, unsigned *min, char *buf)
+{
+ char root[PATH_MAX + 1];
+ char target[PATH_MAX + 1];
+
+ /* TODO: maybe detect availability of %ms glib support ? */
+ if (sscanf(line, "%*u %*u %u:%u %" TO_STRING(PATH_MAX)
+ "s %" TO_STRING(PATH_MAX) "s",
+ maj, min, root, target) < 4)
+ return 0;
+
+ _unmangle_mountinfo_string(target, buf);
+
+#if THIN_DEBUG
+ syslog(LOG_DEBUG, "Mounted %u:%u %s", *maj, *min, buf);
+#endif
+
+ return 1;
+}
+
+/* Get dependencies for device, and try to find matching device */
+static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_minor)
+{
+ struct dm_task *dmt;
+ const struct dm_deps *deps;
+ struct dm_info info;
+ int major, minor;
+ int r = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+
+ if (!dm_task_no_open_count(dmt))
+ goto out;
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ if (!dm_task_get_info(dmt, &info))
+ goto out;
+
+ if (!(deps = dm_task_get_deps(dmt)))
+ goto out;
+
+ if (!info.exists || deps->count != 1)
+ goto out;
+
+ major = (int) MAJOR(deps->device[0]);
+ minor = (int) MINOR(deps->device[0]);
+ if ((major != tp_major) || (minor != tp_minor))
+ goto out;
+
+ *dev_minor = info.minor;
+
+#if THIN_DEBUG
+ {
+ char dev_name[PATH_MAX];
+ if (dm_device_get_name(major, minor, 0, dev_name, sizeof(dev_name)))
+ syslog(LOG_DEBUG, "Found %s (%u:%u) depends on %s",
+ name, major, *dev_minor, dev_name);
+ }
+#endif
+ r = 1;
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+/* Get all active devices */
+static int _find_all_devs(dm_bitset_t bs, int tp_major, int tp_minor)
+{
+ struct dm_task *dmt;
+ struct dm_names *names;
+ unsigned next = 0;
+ int minor, r = 1;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+ return 0;
+
+ if (!dm_task_run(dmt)) {
+ r = 0;
+ goto out;
+ }
+
+ if (!(names = dm_task_get_names(dmt))) {
+ r = 0;
+ goto out;
+ }
+
+ if (!names->dev)
+ goto out;
+
+ do {
+ names = (struct dm_names *)((char *) names + next);
+ if (_has_deps(names->name, tp_major, tp_minor, &minor))
+ dm_bit_set(bs, minor);
+ next = names->next;
+ } while (next);
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _extend(struct dso_state *state)
+{
+#if THIN_DEBUG
+ syslog(LOG_INFO, "dmeventd executes: %s.\n", state->cmd_str);
+#endif
+ return (dmeventd_lvm2_run(state->cmd_str) == ECMD_PROCESSED);
+}
+
+static int _run(const char *cmd, ...)
+{
+ va_list ap;
+ int argc = 1; /* for argv[0], i.e. cmd */
+ int i = 0;
+ const char **argv;
+ pid_t pid = fork();
+ int status;
+
+ if (pid == 0) { /* child */
+ va_start(ap, cmd);
+ while (va_arg(ap, const char *))
+ ++argc;
+ va_end(ap);
+
+ /* + 1 for the terminating NULL */
+ argv = alloca(sizeof(const char *) * (argc + 1));
+
+ argv[0] = cmd;
+ va_start(ap, cmd);
+ while ((argv[++i] = va_arg(ap, const char *)));
+ va_end(ap);
+
+ execvp(cmd, (char **)argv);
+ syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno));
+ exit(127);
+ }
+
+ if (pid > 0) { /* parent */
+ if (waitpid(pid, &status, 0) != pid)
+ return 0; /* waitpid failed */
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ return 0; /* the child failed */
+ }
+
+ if (pid < 0)
+ return 0; /* fork failed */
+
+ return 1; /* all good */
+}
+
+/*
+ * Find all thin pool users and try to umount them.
+ * TODO: work with read-only thin pool support
+ */
+static void _umount(struct dm_task *dmt, const char *device)
+{
+ static const char mountinfo[] = "/proc/self/mountinfo";
+ static const size_t MINORS = 4096;
+ FILE *minfo;
+ char buffer[4096];
+ char target[PATH_MAX];
+ struct dm_info info;
+ unsigned maj, min;
+ dm_bitset_t minors; /* Bitset for active thin pool minors */
+
+ if (!dm_task_get_info(dmt, &info))
+ return;
+
+ dmeventd_lvm2_unlock();
+
+ if (!(minors = dm_bitset_create(NULL, MINORS))) {
+ syslog(LOG_ERR, "Failed to allocate bitset. Not unmounting %s.\n", device);
+ goto out;
+ }
+
+ if (!(minfo = fopen(mountinfo, "r"))) {
+ syslog(LOG_ERR, "Could not read %s. Not umounting %s.\n", mountinfo, device);
+ goto out;
+ }
+
+ if (!_find_all_devs(minors, info.major, info.minor)) {
+ syslog(LOG_ERR, "Failed to detect mounted volumes for %s.\n", device);
+ goto out;
+ }
+
+ while (!feof(minfo)) {
+ /* read mountinfo line */
+ if (!fgets(buffer, sizeof(buffer), minfo))
+ break; /* eof, likely */
+
+ if (_parse_mountinfo_line(buffer, &maj, &min, target) &&
+ (maj == info.major) && dm_bit(minors, min)) {
+ syslog(LOG_INFO, "Unmounting thin volume %s from %s.\n",
+ device, target);
+ if (!_run(UMOUNT_COMMAND, "-fl", target, NULL))
+ syslog(LOG_ERR, "Failed to umount thin %s from %s: %s.\n",
+ device, target, strerror(errno));
+ }
+ }
+
+ if (fclose(minfo))
+ syslog(LOG_ERR, "Failed to close %s\n", mountinfo);
+
+ dm_bitset_destroy(minors);
+out:
+ dmeventd_lvm2_lock();
+}
+
+void process_event(struct dm_task *dmt,
+ enum dm_event_mask event __attribute__((unused)),
+ void **private)
+{
+ const char *device = dm_task_get_name(dmt);
+ int percent;
+ struct dso_state *state = *private;
+ struct dm_status_thin_pool *tps = NULL;
+ void *next = NULL;
+ uint64_t start, length;
+ char *target_type = NULL;
+ char *params;
+
+#if 0
+ /* No longer monitoring, waiting for remove */
+ if (!state->meta_percent_check && !state->data_percent_check)
+ return;
+#endif
+ dmeventd_lvm2_lock();
+
+ dm_get_next_target(dmt, next, &start, &length, &target_type, &params);
+
+ if (!target_type || (strcmp(target_type, "thin-pool") != 0)) {
+ syslog(LOG_ERR, "Invalid target type.\n");
+ goto out;
+ }
+
+ if (!dm_get_status_thin_pool(state->mem, params, &tps)) {
+ syslog(LOG_ERR, "Failed to parse status.\n");
+ _umount(dmt, device);
+ goto out;
+ }
+
+#if THIN_DEBUG
+ syslog(LOG_INFO, "%p: Got status %" PRIu64 " / %" PRIu64
+ " %" PRIu64 " / %" PRIu64 ".\n", state,
+ tps->used_metadata_blocks, tps->total_metadata_blocks,
+ tps->used_data_blocks, tps->total_data_blocks);
+#endif
+
+ /* Thin pool size had changed. Clear the threshold. */
+ if (state->known_metadata_size != tps->total_metadata_blocks) {
+ state->metadata_percent_check = CHECK_MINIMUM;
+ state->known_metadata_size = tps->total_metadata_blocks;
+ }
+
+ if (state->known_data_size != tps->total_data_blocks) {
+ state->data_percent_check = CHECK_MINIMUM;
+ state->known_data_size = tps->total_data_blocks;
+ }
+
+ percent = 100 * tps->used_metadata_blocks / tps->total_metadata_blocks;
+ if (percent >= state->metadata_percent_check) {
+ /*
+ * Usage has raised more than CHECK_STEP since the last
+ * time. Run actions.
+ */
+ state->metadata_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
+
+ /* FIXME: extension of metadata needs to be written! */
+ if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
+ syslog(LOG_WARNING, "Thin metadata %s is now %i%% full.\n",
+ device, percent);
+ /* Try to extend the metadata, in accord with user-set policies */
+ if (!_extend(state)) {
+ syslog(LOG_ERR, "Failed to extend thin metadata %s.\n",
+ device);
+ _umount(dmt, device);
+ }
+ /* FIXME: hmm READ-ONLY switch should happen in error path */
+ }
+
+ percent = 100 * tps->used_data_blocks / tps->total_data_blocks;
+ if (percent >= state->data_percent_check) {
+ /*
+ * Usage has raised more than CHECK_STEP since
+ * the last time. Run actions.
+ */
+ state->data_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
+
+ if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
+ syslog(LOG_WARNING, "Thin %s is now %i%% full.\n", device, percent);
+ /* Try to extend the thin data, in accord with user-set policies */
+ if (!_extend(state)) {
+ syslog(LOG_ERR, "Failed to extend thin %s.\n", device);
+ state->data_percent_check = 0;
+ _umount(dmt, device);
+ }
+ /* FIXME: hmm READ-ONLY switch should happen in error path */
+ }
+out:
+ if (tps)
+ dm_pool_free(state->mem, tps);
+
+ dmeventd_lvm2_unlock();
+}
+
+int register_device(const char *device,
+ const char *uuid __attribute__((unused)),
+ int major __attribute__((unused)),
+ int minor __attribute__((unused)),
+ void **private)
+{
+ struct dm_pool *statemem = NULL;
+ struct dso_state *state;
+
+ if (!dmeventd_lvm2_init())
+ goto bad;
+
+ if (!(statemem = dm_pool_create("thin_pool_state", 2048)) ||
+ !(state = dm_pool_zalloc(statemem, sizeof(*state))) ||
+ !dmeventd_lvm2_command(statemem, state->cmd_str,
+ sizeof(state->cmd_str),
+ "lvextend --use-policies",
+ device)) {
+ if (statemem)
+ dm_pool_destroy(statemem);
+ dmeventd_lvm2_exit();
+ goto bad;
+ }
+
+ state->mem = statemem;
+ state->metadata_percent_check = CHECK_MINIMUM;
+ state->data_percent_check = CHECK_MINIMUM;
+ *private = state;
+
+ syslog(LOG_INFO, "Monitoring thin %s.\n", device);
+
+ return 1;
+bad:
+ syslog(LOG_ERR, "Failed to monitor thin %s.\n", device);
+
+ return 0;
+}
+
+int unregister_device(const char *device,
+ const char *uuid __attribute__((unused)),
+ int major __attribute__((unused)),
+ int minor __attribute__((unused)),
+ void **private)
+{
+ struct dso_state *state = *private;
+
+ syslog(LOG_INFO, "No longer monitoring thin %s.\n", device);
+ dm_pool_destroy(state->mem);
+ dmeventd_lvm2_exit();
+
+ return 1;
+}