summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2016-08-05 13:08:38 -0500
committerDavid Teigland <teigland@redhat.com>2016-08-11 11:45:49 -0500
commit2183d5451ddd7a0f911738bf63c6a1c8819b5eca (patch)
treedb934f89651b2026027e0a64dea50809f11a9f19
parentfc93085c7a7716671c3567d367dd2c75544a058b (diff)
downloadlvm2-dev-dct-lvmetad-helper4.tar.gz
lvmetad: add helper process and uevent monitordev-dct-lvmetad-helper4
so that lvmetad can keep its cache updated (and autoactivate LVs) by itself without the assistance of externally generated pvscan commands. - the lvm udev rule sets the LVM_DO_PVSCAN label on devs that should be scanned. - the lvmetad uevent monitor watches for devs with the LVM_DO_PVSCAN label. - when an LVM_DO_PVSCAN event is seen, the lvmetad helper is told to run pvscan --cache -aay on that device. TODO: error and failure handling -- if anything fails related to the helper or uevents, lvmetad should simply exit.
-rw-r--r--daemons/lvmetad/Makefile.in4
-rw-r--r--daemons/lvmetad/lvmetactl.c34
-rw-r--r--daemons/lvmetad/lvmetad-core.c59
-rw-r--r--daemons/lvmetad/lvmetad-helper.c535
-rw-r--r--daemons/lvmetad/lvmetad-internal.h89
-rw-r--r--libdaemon/server/daemon-server.c14
-rw-r--r--libdaemon/server/daemon-server.h5
7 files changed, 706 insertions, 34 deletions
diff --git a/daemons/lvmetad/Makefile.in b/daemons/lvmetad/Makefile.in
index fa3bdf1da..9e039c93e 100644
--- a/daemons/lvmetad/Makefile.in
+++ b/daemons/lvmetad/Makefile.in
@@ -15,7 +15,7 @@ srcdir = @srcdir@
top_srcdir = @top_srcdir@
top_builddir = @top_builddir@
-SOURCES = lvmetad-core.c
+SOURCES = lvmetad-core.c lvmetad-helper.c
SOURCES2 = testclient.c
TARGETS = lvmetad lvmetactl
@@ -31,7 +31,7 @@ include $(top_builddir)/make.tmpl
INCLUDES += -I$(top_srcdir)/libdaemon/server
LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper
-LIBS += $(PTHREAD_LIBS)
+LIBS += $(PTHREAD_LIBS) -ludev
LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS)
CLDFLAGS += -L$(top_builddir)/libdaemon/server
diff --git a/daemons/lvmetad/lvmetactl.c b/daemons/lvmetad/lvmetactl.c
index dd2ee1e6d..1e42335cc 100644
--- a/daemons/lvmetad/lvmetactl.c
+++ b/daemons/lvmetad/lvmetactl.c
@@ -12,6 +12,8 @@
#include "lvmetad-client.h"
+#define ARGS_SIZE 128 /* FIXME: get from header */
+
daemon_handle h;
static void print_reply(daemon_reply reply)
@@ -45,6 +47,7 @@ int main(int argc, char **argv)
printf("lvmetactl set_global_disable 0|1\n");
printf("lvmetactl set_vg_version <uuid> <name> <version>\n");
printf("lvmetactl vg_lock_type <uuid>\n");
+ printf("lvmetactl helper_run <path> <args>\n");
return -1;
}
@@ -237,6 +240,37 @@ int main(int argc, char **argv)
NULL);
printf("%s\n", reply.buffer.mem);
+ } else if (!strcmp(cmd, "helper_run")) {
+ char args[ARGS_SIZE];
+ int i;
+
+ memset(args, 0, ARGS_SIZE);
+
+ if (argc < 3) {
+ printf("helper_run <path> [<args>]\n");
+ return -1;
+ }
+
+ if (argc > 3) {
+ for (i = 3; i < argc; i++) {
+ if (strlen(argv[i]) + strlen(args) + 2 > ARGS_SIZE) {
+ printf("args too long\n");
+ return -1;
+ }
+ strcat(args, argv[i]);
+ strcat(args, " ");
+ }
+ }
+
+ reply = daemon_send_simple(h, "helper_run",
+ "token = %s", "skip",
+ "pid = " FMTd64, (int64_t)getpid(),
+ "cmd = %s", "lvmetactl",
+ "runpath = %s", argv[2],
+ "runargs = %s", args[0] ? args : "",
+ NULL);
+ printf("%s\n", reply.buffer.mem);
+
} else {
printf("unknown command\n");
goto out_close;
diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c
index ebaca7872..84c015580 100644
--- a/daemons/lvmetad/lvmetad-core.c
+++ b/daemons/lvmetad/lvmetad-core.c
@@ -22,6 +22,7 @@
#include "daemon-server.h"
#include "daemon-log.h"
#include "lvm-version.h"
+#include "lvmetad-internal.h"
#include "lvmetad-client.h"
#include <assert.h>
@@ -203,38 +204,10 @@ struct vg_info {
#define GLFL_DISABLE_REASON_LVM1 0x00000008
#define GLFL_DISABLE_REASON_DUPLICATES 0x00000010
#define GLFL_DISABLE_REASON_VGRESTORE 0x00000020
-
#define GLFL_DISABLE_REASON_ALL (GLFL_DISABLE_REASON_DIRECT | GLFL_DISABLE_REASON_LVM1 | GLFL_DISABLE_REASON_DUPLICATES | GLFL_DISABLE_REASON_VGRESTORE)
#define VGFL_INVALID 0x00000001
-#define CMD_NAME_SIZE 32
-
-typedef struct {
- daemon_idle *idle;
- log_state *log; /* convenience */
- const char *log_config;
-
- struct dm_hash_table *pvid_to_pvmeta;
- struct dm_hash_table *device_to_pvid; /* shares locks with above */
-
- struct dm_hash_table *vgid_to_metadata;
- struct dm_hash_table *vgid_to_vgname;
- struct dm_hash_table *vgid_to_outdated_pvs;
- struct dm_hash_table *vgid_to_info;
- struct dm_hash_table *vgname_to_vgid;
- struct dm_hash_table *pvid_to_vgid;
- char token[128];
- char update_cmd[CMD_NAME_SIZE];
- int update_pid;
- int update_timeout;
- uint64_t update_begin;
- uint32_t flags; /* GLFL_ */
- pthread_mutex_t token_lock;
- pthread_mutex_t info_lock;
- pthread_rwlock_t cache_lock;
-} lvmetad_state;
-
static uint64_t _monotonic_seconds(void)
{
struct timespec ts;
@@ -2655,7 +2628,9 @@ static response dump(lvmetad_state *s)
return res;
}
-static response handler(daemon_state s, client_handle h, request r)
+/* called in context of a per-connection thread */
+
+static response client_handler(daemon_state s, client_handle h, request r)
{
response res;
lvmetad_state *state = s.private;
@@ -2822,7 +2797,8 @@ static response handler(daemon_state s, client_handle h, request r)
}
if (!strcmp(rq, "set_global_info") ||
- !strcmp(rq, "get_global_info")) {
+ !strcmp(rq, "get_global_info") ||
+ !strcmp(rq, "helper_run")) {
pthread_mutex_lock(&state->info_lock);
info_lock = 1;
goto do_rq;
@@ -2872,6 +2848,11 @@ static response handler(daemon_state s, client_handle h, request r)
else if (!strcmp(rq, "dump"))
res = dump(state);
+ else if (!strcmp(rq, "helper_run")) {
+ send_helper_request(&s, r);
+ res = daemon_reply_simple("OK", NULL );
+ }
+
else
res = reply_fail("request not implemented");
@@ -2909,6 +2890,9 @@ static int init(daemon_state *s)
if (ls->idle)
ls->idle->is_idle = 1;
+ if (ls->enable_udev_monitor)
+ setup_udev_monitor(s);
+
return 1;
}
@@ -2943,6 +2927,8 @@ static void usage(const char *prog, FILE *file)
" -V Show version of lvmetad\n"
" -h Show this help information\n"
" -f Don't fork, run in the foreground\n"
+ " -u 0|1 Enable udev monitor and running pvscan on new devs\n"
+ " -a 0|1 Enable LV autoactivation with pvscan\n"
" -l Logging message levels (all,fatal,error,warn,info,wire,debug)\n"
" -p Set path to the pidfile\n"
" -s Set path to the socket to listen on\n"
@@ -2958,7 +2944,7 @@ int main(int argc, char *argv[])
daemon_state s = {
.daemon_fini = fini,
.daemon_init = init,
- .handler = handler,
+ .handler = client_handler,
.name = "lvmetad",
.pidfile = getenv("LVM_LVMETAD_PIDFILE") ? : LVMETAD_PIDFILE,
.private = &ls,
@@ -2968,7 +2954,7 @@ int main(int argc, char *argv[])
};
// use getopt_long
- while ((opt = getopt(argc, argv, "?fhVl:p:s:t:")) != EOF) {
+ while ((opt = getopt(argc, argv, "?fhVl:p:s:t:u:a:")) != EOF) {
switch (opt) {
case 'h':
usage(argv[0], stdout);
@@ -2997,13 +2983,22 @@ int main(int argc, char *argv[])
if (di.max_timeouts)
s.idle = ls.idle = &di;
break;
+ case 'u':
+ ls.enable_udev_monitor = atoi(optarg);
+ break;
+ case 'a':
+ ls.enable_autoactivate = atoi(optarg);
+ break;
case 'V':
printf("lvmetad version: " LVM_VERSION "\n");
exit(1);
}
}
+ setup_helper(&s);
+
daemon_start(s);
return 0;
}
+
diff --git a/daemons/lvmetad/lvmetad-helper.c b/daemons/lvmetad/lvmetad-helper.c
new file mode 100644
index 000000000..1dbb86440
--- /dev/null
+++ b/daemons/lvmetad/lvmetad-helper.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _XOPEN_SOURCE 500 /* pthread */
+#define _GNU_SOURCE
+#define _REENTRANT
+
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <grp.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "libudev.h"
+
+#include "tool.h"
+
+#include "daemon-io.h"
+#include "daemon-server.h"
+#include "daemon-log.h"
+#include "lvm-version.h"
+#include "lvmetad-internal.h"
+#include "lvmetad-client.h"
+
+/*
+ * lvmetad_main is the main process that:
+ * . forks threads to handle client (command) connections
+ * . receives client requests from socket
+ * . reads/writes cache state
+ * . receives uevent messages
+ * . sends lvmetad_helper pvscan requests for uevents
+ * . sends lvmetad_helper client requests from lvmetactl
+ *
+ * lvmetad_helper is the helper process that:
+ * . receives pvscan requests from lvmetad_main
+ * . forks/execs pvscan commands for each request
+ */
+
+#define MAX_AV_COUNT 8
+
+static int _log_debug_stderr;
+
+#define log_debug(fmt, args...) \
+do { \
+ if (_log_debug_stderr) \
+ fprintf(stderr, fmt "\n", ##args); \
+} while (0)
+
+static void run_path(struct helper_msg *hm)
+{
+ char arg[HELPER_ARGS_LEN];
+ char *args = hm->args;
+ char *av[MAX_AV_COUNT + 1]; /* +1 for NULL */
+ int av_count = 0;
+ int i, arg_len, args_len;
+
+ for (i = 0; i < MAX_AV_COUNT + 1; i++)
+ av[i] = NULL;
+
+ av[av_count++] = strdup(hm->path);
+
+ if (!args[0])
+ goto run;
+
+ /* this should already be done, but make sure */
+ args[HELPER_ARGS_LEN - 1] = '\0';
+
+ memset(&arg, 0, sizeof(arg));
+ arg_len = 0;
+ args_len = strlen(args);
+
+ for (i = 0; i < args_len; i++) {
+ if (!args[i])
+ break;
+
+ if (av_count == MAX_AV_COUNT)
+ break;
+
+ if (args[i] == '\\') {
+ if (i == (args_len - 1))
+ break;
+ i++;
+
+ if (args[i] == '\\') {
+ arg[arg_len++] = args[i];
+ continue;
+ }
+ if (isspace(args[i])) {
+ arg[arg_len++] = args[i];
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if (isalnum(args[i]) || ispunct(args[i])) {
+ arg[arg_len++] = args[i];
+ } else if (isspace(args[i])) {
+ if (arg_len)
+ av[av_count++] = strdup(arg);
+
+ memset(arg, 0, sizeof(arg));
+ arg_len = 0;
+ } else {
+ break;
+ }
+ }
+
+ if ((av_count < MAX_AV_COUNT) && arg_len) {
+ av[av_count++] = strdup(arg);
+ }
+run:
+ execvp(av[0], av);
+}
+
+static int read_from_main(int fd, struct helper_msg *hm)
+{
+ int rv;
+ retry:
+ rv = read(fd, hm, sizeof(struct helper_msg));
+ if (rv == -1 && errno == EINTR)
+ goto retry;
+
+ if (rv != sizeof(struct helper_msg))
+ return -1;
+ return 0;
+}
+
+static int send_to_main(int fd, int type)
+{
+ struct helper_status hs;
+ int rv;
+
+ memset(&hs, 0, sizeof(hs));
+
+ hs.type = type;
+
+ rv = write(fd, &hs, sizeof(hs));
+
+ if (rv == sizeof(hs))
+ return 0;
+ return -1;
+}
+
+#define INACTIVE_TIMEOUT_MS 10000
+#define ACTIVE_TIMEOUT_MS 1000
+
+int run_helper(int in_fd, int out_fd, int debug_stderr)
+{
+ char name[16];
+ struct pollfd pollfd;
+ struct helper_msg hm;
+ unsigned int fork_count = 0;
+ unsigned int wait_count = 0;
+ int timeout = INACTIVE_TIMEOUT_MS;
+ int rv, pid, status;
+
+ _log_debug_stderr = debug_stderr;
+
+ memset(name, 0, sizeof(name));
+ sprintf(name, "%s", "lvmetad_helper");
+ prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
+
+ memset(&pollfd, 0, sizeof(pollfd));
+ pollfd.fd = in_fd;
+ pollfd.events = POLLIN;
+
+ /* Tell the main process we've started. */
+ send_to_main(out_fd, HELPER_STARTED);
+
+ while (1) {
+ rv = poll(&pollfd, 1, timeout);
+ if (rv == -1 && errno == EINTR)
+ continue;
+
+ if (rv < 0)
+ exit(0);
+
+ memset(&hm, 0, sizeof(hm));
+
+ if (pollfd.revents & POLLIN) {
+ rv = read_from_main(in_fd, &hm);
+ if (rv)
+ continue;
+
+ if (hm.type == HELPER_MSG_RUNPATH) {
+ pid = fork();
+ if (!pid) {
+ run_path(&hm);
+ exit(-1);
+ }
+
+ fork_count++;
+
+ /*
+ log_debug("helper fork %d count %d %d %s %s",
+ pid, fork_count, wait_count,
+ hm.path, hm.args);
+ */
+ }
+ }
+
+ if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL))
+ exit(0);
+
+ /* collect child exits until no more children exist (ECHILD)
+ or none are ready (WNOHANG) */
+
+ while (1) {
+ rv = waitpid(-1, &status, WNOHANG);
+ if (rv > 0) {
+ wait_count++;
+
+ /*
+ log_debug("helper wait %d count %d %d",
+ rv, fork_count, wait_count);
+ */
+ continue;
+ }
+
+ /* no more children to wait for or no children
+ have exited */
+
+ if (rv < 0 && errno == ECHILD)
+ timeout = INACTIVE_TIMEOUT_MS;
+ else
+ timeout = ACTIVE_TIMEOUT_MS;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void close_helper(daemon_state *s)
+{
+ lvmetad_state *ls = s->private;
+
+ close(ls->helper_pr_fd);
+ close(ls->helper_pw_fd);
+ ls->helper_pr_fd = -1;
+ ls->helper_pw_fd = -1;
+ s->helper_fd = -1;
+ s->helper_handler = NULL;
+}
+
+static void _send_helper_msg(daemon_state *s, struct helper_msg *hm)
+{
+ lvmetad_state *ls = s->private;
+ int rv;
+
+ retry:
+ rv = write(ls->helper_pw_fd, hm, sizeof(struct helper_msg));
+ if (rv == -1 && errno == EINTR)
+ goto retry;
+
+ if (rv == -1 && errno == EAGAIN) {
+ return;
+ }
+
+ /* helper exited or closed fd, quit using helper */
+ if (rv == -1 && errno == EPIPE) {
+ ERROR(s, "send_helper EPIPE");
+ close_helper(s);
+ return;
+ }
+
+ if (rv != sizeof(struct helper_msg)) {
+ /* this shouldn't happen */
+ ERROR(s, "send_helper error %d %d", rv, errno);
+ close_helper(s);
+ return;
+ }
+}
+
+/* send a request to helper process */
+
+void send_helper_request(daemon_state *s, request r)
+{
+ struct helper_msg hm = { 0 };
+ char *runpath = daemon_request_str(r, "runpath", NULL);
+ char *runargs = daemon_request_str(r, "runargs", NULL);
+
+ hm.type = HELPER_MSG_RUNPATH;
+ memcpy(hm.path, runpath, HELPER_PATH_LEN);
+ if (runargs)
+ memcpy(hm.args, runargs, HELPER_ARGS_LEN);
+
+ _send_helper_msg(s, &hm);
+}
+
+static void send_helper_pvscan_cache_dev(daemon_state *s, dev_t devt)
+{
+ lvmetad_state *ls = s->private;
+ struct helper_msg hm = { 0 };
+
+ hm.type = HELPER_MSG_RUNPATH;
+ sprintf(hm.path, "pvscan");
+ snprintf(hm.args, HELPER_ARGS_LEN-1, "--cache %s --major %d --minor %d",
+ ls->enable_autoactivate ? "-aay" : "",
+ major(devt), minor(devt));
+
+ _send_helper_msg(s, &hm);
+}
+
+static void send_helper_pvscan_cache_all(daemon_state *s)
+{
+ lvmetad_state *ls = s->private;
+ struct helper_msg hm = { 0 };
+
+ hm.type = HELPER_MSG_RUNPATH;
+ sprintf(hm.path, "pvscan");
+ snprintf(hm.args, HELPER_ARGS_LEN-1, "--cache %s",
+ ls->enable_autoactivate ? "-aay" : "");
+
+ _send_helper_msg(s, &hm);
+}
+
+/*
+ * called in context of main lvmetad process
+ * handles a message from helper process
+ */
+
+static int helper_handler(daemon_state *s)
+{
+ lvmetad_state *ls = s->private;
+ struct helper_status hs;
+ int rv;
+
+ memset(&hs, 0, sizeof(hs));
+
+ rv = read(ls->helper_pr_fd, &hs, sizeof(hs));
+ if (!rv || rv == -EAGAIN)
+ return -1;
+ if (rv < 0) {
+ ERROR(s, "handle_helper rv %d errno %d", rv, errno);
+ goto fail;
+ }
+ if (rv != sizeof(hs)) {
+ ERROR(s, "handle_helper recv size %d", rv);
+ goto fail;
+ }
+
+ DEBUGLOG(s, "helper message type %d status %d", hs.type, hs.status);
+
+ /*
+ * Run initial pvscan --cache to populate lvmetad cache.
+ *
+ * (Upon receiving HELPER_STARTED we know that the helper
+ * is ready to handle to running commands.)
+ *
+ * The udev monitor is enabled before this, so there should
+ * be no gap where new devs could be missed. It's possible
+ * that new devs added during lvmetad startup could be scanned
+ * by this initial pvscan --cache, and then scanned again
+ * individually because of the monitor. This possible repetition
+ * is harmless.
+ */
+ if (hs.type == HELPER_STARTED)
+ send_helper_pvscan_cache_all(s);
+
+ return 0;
+
+ fail:
+ ERROR(s, "close helper connection");
+ close_helper(s);
+ return -1;
+}
+
+/* create helper process */
+
+int setup_helper(daemon_state *s)
+{
+ lvmetad_state *ls = s->private;
+ int pid;
+ int pw_fd = -1; /* parent write */
+ int cr_fd = -1; /* child read */
+ int pr_fd = -1; /* parent read */
+ int cw_fd = -1; /* child write */
+ int pfd[2];
+
+ /* don't allow the main daemon thread to block */
+ if (pipe2(pfd, O_NONBLOCK | O_CLOEXEC))
+ return -errno;
+
+ /* only available on rhel7 */
+ /* fcntl(pfd[1], F_SETPIPE_SZ, 1024*1024); */
+
+ cr_fd = pfd[0];
+ pw_fd = pfd[1];
+
+ if (pipe2(pfd, O_NONBLOCK | O_CLOEXEC)) {
+ close(cr_fd);
+ close(pw_fd);
+ return -errno;
+ }
+
+ pr_fd = pfd[0];
+ cw_fd = pfd[1];
+
+ pid = fork();
+ if (pid < 0) {
+ close(cr_fd);
+ close(pw_fd);
+ close(pr_fd);
+ close(cw_fd);
+ return -errno;
+ }
+
+ if (pid) {
+ close(cr_fd);
+ close(cw_fd);
+ ls->helper_pw_fd = pw_fd;
+ ls->helper_pr_fd = pr_fd;
+ ls->helper_pid = pid;
+ s->helper_fd = pr_fd; /* libdaemon uses helper_fd in select */
+ s->helper_handler = helper_handler;
+ return 0;
+ } else {
+ close(pr_fd);
+ close(pw_fd);
+ run_helper(cr_fd, cw_fd, (s->foreground && strstr(ls->log_config, "debug")));
+ exit(0);
+ }
+}
+
+/*
+ * called in context of main lvmetad process
+ * handles a message from udev monitor
+ * sends a message to lvmetad helper to scan a device
+ */
+
+static int monitor_handler(daemon_state *s)
+{
+ lvmetad_state *ls = s->private;
+ struct udev_device *dev;
+ const char *name;
+ dev_t devt;
+
+ dev = udev_monitor_receive_device(ls->udev_mon);
+ if (!dev)
+ return 0;
+
+ name = udev_device_get_devnode(dev);
+ devt = udev_device_get_devnum(dev);
+
+ DEBUGLOG(s, "monitor scan %d:%d %s", major(devt), minor(devt), name ?: "");
+ send_helper_pvscan_cache_dev(s, devt);
+
+ udev_device_unref(dev);
+ return 0;
+}
+
+/* create udev monitor */
+
+void setup_udev_monitor(daemon_state *s)
+{
+ lvmetad_state *ls = s->private;
+ int fd;
+ int ret;
+
+ /* FIXME: add error handling/cleanup */
+
+ ls->udevh = udev_new();
+ if (!ls->udevh) {
+ ERROR(s, "Failed to monitor udev: new.");
+ return;
+ }
+
+ ls->udev_mon = udev_monitor_new_from_netlink(ls->udevh, "udev");
+ if (!ls->udev_mon) {
+ ERROR(s, "Failed to monitor udev: netlink.");
+ return;
+ }
+
+ ret = udev_monitor_filter_add_match_subsystem_devtype(ls->udev_mon, "block", "disk");
+ if (ret < 0) {
+ ERROR(s, "Failed to monitor udev: devtype.");
+ return;
+ }
+
+ ret = udev_monitor_filter_add_match_tag(ls->udev_mon, "LVM_DO_PVSCAN");
+ if (ret < 0) {
+ ERROR(s, "Failed to monitor udev: tag.");
+ return;
+ }
+
+ ret = udev_monitor_enable_receiving(ls->udev_mon);
+ if (ret < 0) {
+ ERROR(s, "Failed to monitor udev: receive.");
+ return;
+ }
+
+ /* udev_monitor_set_receive_buffer_size? */
+
+ fd = udev_monitor_get_fd(ls->udev_mon);
+ if (fd < 0) {
+ ERROR(s, "Failed to monitor udev: fd.");
+ return;
+ }
+
+ ls->udev_fd = fd;
+ s->monitor_fd = fd;
+ s->monitor_handler = monitor_handler;
+}
+
diff --git a/daemons/lvmetad/lvmetad-internal.h b/daemons/lvmetad/lvmetad-internal.h
new file mode 100644
index 000000000..8308f9e68
--- /dev/null
+++ b/daemons/lvmetad/lvmetad-internal.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _LVM_LVMETAD_INTERNAL_H
+#define _LVM_LVMETAD_INTERNAL_H
+
+#define CMD_NAME_SIZE 32
+
+typedef struct {
+ daemon_idle *idle;
+ log_state *log; /* convenience */
+ const char *log_config;
+ int enable_udev_monitor;
+ int enable_autoactivate;
+
+ struct dm_hash_table *pvid_to_pvmeta;
+ struct dm_hash_table *device_to_pvid; /* shares locks with above */
+
+ struct dm_hash_table *vgid_to_metadata;
+ struct dm_hash_table *vgid_to_vgname;
+ struct dm_hash_table *vgid_to_outdated_pvs;
+ struct dm_hash_table *vgid_to_info;
+ struct dm_hash_table *vgname_to_vgid;
+ struct dm_hash_table *pvid_to_vgid;
+ char token[128];
+ char update_cmd[CMD_NAME_SIZE];
+ int update_pid;
+ int update_timeout;
+ uint64_t update_begin;
+ uint32_t flags; /* GLFL_ */
+ pthread_mutex_t token_lock;
+ pthread_mutex_t info_lock;
+ pthread_rwlock_t cache_lock;
+
+ int helper_pid;
+ int helper_pw_fd; /* parent write to send message to helper */
+ int helper_pr_fd; /* parent read to recv message from helper */
+
+ struct udev *udevh;
+ struct udev_monitor *udev_mon;
+ int udev_fd;
+} lvmetad_state;
+
+/*
+ * helper process
+ * recvs 512 byte helper_msg on in_fd
+ * sends 4 byte helper_status on out_fd
+ */
+
+/* max length of path and args, includes terminate \0 byte */
+
+#define HELPER_PATH_LEN 128
+#define HELPER_ARGS_LEN 128
+#define HELPER_MSG_LEN 512
+#define HELPER_MSG_RUNPATH 1
+
+struct helper_msg {
+ uint8_t type;
+ uint8_t pad1;
+ uint16_t pad2;
+ uint32_t flags;
+ int pid;
+ int unused;
+ char path[HELPER_PATH_LEN]; /* 128 */
+ char args[HELPER_ARGS_LEN]; /* 128 */
+ char pad[240];
+};
+
+#define HELPER_STARTED 1
+
+struct helper_status {
+ uint8_t type;
+ uint8_t status;
+ uint16_t len;
+};
+
+void close_helper(daemon_state *s);
+void send_helper_request(daemon_state *s, request r);
+int setup_helper(daemon_state *s);
+void setup_udev_monitor(daemon_state *s);
+
+#endif
diff --git a/libdaemon/server/daemon-server.c b/libdaemon/server/daemon-server.c
index 4d6546633..00c23f5f6 100644
--- a/libdaemon/server/daemon-server.c
+++ b/libdaemon/server/daemon-server.c
@@ -630,12 +630,26 @@ void daemon_start(daemon_state s)
_reset_timeout(s);
FD_ZERO(&in);
FD_SET(s.socket_fd, &in);
+
+ if (s.helper_handler)
+ FD_SET(s.helper_fd, &in);
+ if (s.monitor_handler)
+ FD_SET(s.monitor_fd, &in);
+
if (select(FD_SETSIZE, &in, NULL, NULL, _get_timeout(s)) < 0 && errno != EINTR)
perror("select error");
if (FD_ISSET(s.socket_fd, &in)) {
timeout_count = 0;
handle_connect(s);
}
+ if (FD_ISSET(s.helper_fd, &in)) {
+ timeout_count = 0;
+ s.helper_handler(&s);
+ }
+ if (FD_ISSET(s.monitor_fd, &in)) {
+ timeout_count = 0;
+ s.monitor_handler(&s);
+ }
_reap(s, 0);
diff --git a/libdaemon/server/daemon-server.h b/libdaemon/server/daemon-server.h
index 2b9ceac36..ee8ca9c79 100644
--- a/libdaemon/server/daemon-server.h
+++ b/libdaemon/server/daemon-server.h
@@ -108,10 +108,15 @@ typedef struct daemon_state {
int (*daemon_init)(struct daemon_state *st);
int (*daemon_fini)(struct daemon_state *st);
int (*daemon_main)(struct daemon_state *st);
+ int (*helper_handler)(struct daemon_state *st);
+ int (*monitor_handler)(struct daemon_state *st);
/* Global runtime info maintained by the framework. */
int socket_fd;
+ int helper_fd; /* used in select if helper_handler is set */
+ int monitor_fd; /* used in select if monitor_handler is set */
+
log_state *log;
struct thread_state *threads;