summaryrefslogtreecommitdiff
path: root/libdaemon
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2013-03-05 01:47:43 -0800
committerAnas Nashif <anas.nashif@intel.com>2013-03-05 01:47:43 -0800
commit44a3c2255bc480c82f34db156553a595606d8a0b (patch)
tree5e6df96a6c6e40207cb3a711860e16b543918c0d /libdaemon
parent8bd28eea831fd5215c12e6fcecc8e9a772398ed9 (diff)
downloaddevice-mapper-44a3c2255bc480c82f34db156553a595606d8a0b.tar.gz
device-mapper-44a3c2255bc480c82f34db156553a595606d8a0b.tar.bz2
device-mapper-44a3c2255bc480c82f34db156553a595606d8a0b.zip
Imported Upstream version 2.02.98upstream/2.02.98upstream/1.02.77
Diffstat (limited to 'libdaemon')
-rw-r--r--libdaemon/Makefile.in31
-rw-r--r--libdaemon/client/Makefile.in20
-rw-r--r--libdaemon/client/config-util.c329
-rw-r--r--libdaemon/client/config-util.h67
-rw-r--r--libdaemon/client/daemon-client.c188
-rw-r--r--libdaemon/client/daemon-client.h112
-rw-r--r--libdaemon/client/daemon-io.c92
-rw-r--r--libdaemon/client/daemon-io.h31
-rw-r--r--libdaemon/server/Makefile.in22
-rw-r--r--libdaemon/server/daemon-log.c180
-rw-r--r--libdaemon/server/daemon-log.h33
-rw-r--r--libdaemon/server/daemon-server.c539
-rw-r--r--libdaemon/server/daemon-server.h158
13 files changed, 1802 insertions, 0 deletions
diff --git a/libdaemon/Makefile.in b/libdaemon/Makefile.in
new file mode 100644
index 0000000..dbe6516
--- /dev/null
+++ b/libdaemon/Makefile.in
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2004-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@
+
+.PHONY: client server
+
+SUBDIRS += client
+
+ifeq ("@BUILD_LVMETAD@", "yes")
+ SUBDIRS += server
+server: client
+endif
+
+ifeq ($(MAKECMDGOALS),distclean)
+ SUBDIRS = client server
+endif
+
+include $(top_builddir)/make.tmpl
diff --git a/libdaemon/client/Makefile.in b/libdaemon/client/Makefile.in
new file mode 100644
index 0000000..d608816
--- /dev/null
+++ b/libdaemon/client/Makefile.in
@@ -0,0 +1,20 @@
+# Copyright (C) 2011 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+LIB_STATIC = libdaemonclient.a
+SOURCES = daemon-io.c config-util.c daemon-client.c
+
+include $(top_builddir)/make.tmpl
diff --git a/libdaemon/client/config-util.c b/libdaemon/client/config-util.c
new file mode 100644
index 0000000..1c83134
--- /dev/null
+++ b/libdaemon/client/config-util.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2011-2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dm-logging.h"
+#include "config-util.h"
+#include "libdevmapper.h"
+
+int buffer_append_vf(struct buffer *buf, va_list ap)
+{
+ char *append;
+ char *next;
+ int keylen;
+ int64_t value;
+ char *string;
+ char *block;
+
+ while ((next = va_arg(ap, char *))) {
+ append = NULL;
+ if (!strchr(next, '=')) {
+ log_error(INTERNAL_ERROR "Bad format string at '%s'", next);
+ goto fail;
+ }
+ keylen = strchr(next, '=') - next;
+ if (strstr(next, "%d") || strstr(next, "%" PRId64)) {
+ value = va_arg(ap, int64_t);
+ if (dm_asprintf(&append, "%.*s= %" PRId64 "\n", keylen, next, value) < 0)
+ goto fail;
+ } else if (strstr(next, "%s")) {
+ string = va_arg(ap, char *);
+ if (dm_asprintf(&append, "%.*s= \"%s\"\n", keylen, next, string) < 0)
+ goto fail;
+ } else if (strstr(next, "%b")) {
+ if (!(block = va_arg(ap, char *)))
+ continue;
+ if (dm_asprintf(&append, "%.*s%s", keylen, next, block) < 0)
+ goto fail;
+ } else if (dm_asprintf(&append, "%s", next) < 0)
+ goto fail;
+
+ if (!append ||
+ !buffer_append(buf, append))
+ goto fail;
+
+ dm_free(append);
+ }
+
+ return 1;
+fail:
+ dm_free(append);
+ return 0;
+}
+
+int buffer_append_f(struct buffer *buf, ...)
+{
+ int res;
+ va_list ap;
+
+ va_start(ap, buf);
+ res = buffer_append_vf(buf, ap);
+ va_end(ap);
+
+ return res;
+}
+
+int set_flag(struct dm_config_tree *cft, struct dm_config_node *parent,
+ const char *field, const char *flag, int want)
+{
+ struct dm_config_value *value = NULL, *pred = NULL;
+ struct dm_config_node *node = dm_config_find_node(parent->child, field);
+ struct dm_config_value *new;
+
+ if (node)
+ value = node->v;
+
+ while (value && value->type != DM_CFG_EMPTY_ARRAY && strcmp(value->v.str, flag)) {
+ pred = value;
+ value = value->next;
+ }
+
+ if (value && want)
+ return 1;
+
+ if (!value && !want)
+ return 1;
+
+ if (value && !want) {
+ if (pred) {
+ pred->next = value->next;
+ } else if (value == node->v && value->next) {
+ node->v = value->next;
+ } else {
+ node->v->type = DM_CFG_EMPTY_ARRAY;
+ }
+ }
+
+ if (!value && want) {
+ if (!node) {
+ if (!(node = dm_config_create_node(cft, field)))
+ return 0;
+ node->sib = parent->child;
+ if (!(node->v = dm_config_create_value(cft)))
+ return 0;
+ node->v->type = DM_CFG_EMPTY_ARRAY;
+ node->parent = parent;
+ parent->child = node;
+ }
+ if (!(new = dm_config_create_value(cft))) {
+ /* FIXME error reporting */
+ return 0;
+ }
+ new->type = DM_CFG_STRING;
+ new->v.str = flag;
+ new->next = node->v;
+ node->v = new;
+ }
+
+ return 1;
+}
+
+static void chain_node(struct dm_config_node *cn,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib)
+{
+ cn->parent = parent;
+ cn->sib = NULL;
+
+ if (parent && parent->child && !pre_sib) { /* find the last one */
+ pre_sib = parent->child;
+ while (pre_sib && pre_sib->sib)
+ pre_sib = pre_sib->sib;
+ }
+
+ if (parent && !parent->child)
+ parent->child = cn;
+ if (pre_sib) {
+ cn->sib = pre_sib->sib;
+ pre_sib->sib = cn;
+ }
+
+}
+
+struct dm_config_node *make_config_node(struct dm_config_tree *cft,
+ const char *key,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib)
+{
+ struct dm_config_node *cn;
+
+ if (!(cn = dm_config_create_node(cft, key)))
+ return NULL;
+
+ cn->v = NULL;
+ cn->child = NULL;
+
+ chain_node(cn, parent, pre_sib);
+
+ return cn;
+}
+
+struct dm_config_node *make_text_node(struct dm_config_tree *cft,
+ const char *key,
+ const char *value,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib)
+{
+ struct dm_config_node *cn;
+
+ if (!(cn = make_config_node(cft, key, parent, pre_sib)) ||
+ !(cn->v = dm_config_create_value(cft)))
+ return NULL;
+
+ cn->v->type = DM_CFG_STRING;
+ cn->v->v.str = value;
+ return cn;
+}
+
+struct dm_config_node *make_int_node(struct dm_config_tree *cft,
+ const char *key,
+ int64_t value,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib)
+{
+ struct dm_config_node *cn;
+
+ if (!(cn = make_config_node(cft, key, parent, pre_sib)) ||
+ !(cn->v = dm_config_create_value(cft)))
+ return NULL;
+
+ cn->v->type = DM_CFG_INT;
+ cn->v->v.i = value;
+ return cn;
+}
+
+struct dm_config_node *config_make_nodes_v(struct dm_config_tree *cft,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib,
+ va_list ap)
+{
+ const char *next;
+ struct dm_config_node *first = NULL;
+ struct dm_config_node *cn;
+ const char *fmt, *key;
+
+ while ((next = va_arg(ap, char *))) {
+ cn = NULL;
+ fmt = strchr(next, '=');
+
+ if (!fmt) {
+ log_error(INTERNAL_ERROR "Bad format string '%s'", fmt);
+ return_NULL;
+ }
+ fmt += 2;
+
+ key = dm_pool_strdup(cft->mem, next);
+ *strchr(key, '=') = 0;
+
+ if (!strcmp(fmt, "%d") || !strcmp(fmt, "%" PRId64)) {
+ int64_t value = va_arg(ap, int64_t);
+ if (!(cn = make_int_node(cft, key, value, parent, pre_sib)))
+ return 0;
+ } else if (!strcmp(fmt, "%s")) {
+ char *value = va_arg(ap, char *);
+ if (!(cn = make_text_node(cft, key, value, parent, pre_sib)))
+ return 0;
+ } else if (!strcmp(fmt, "%t")) {
+ struct dm_config_tree *tree = va_arg(ap, struct dm_config_tree *);
+ cn = dm_config_clone_node(cft, tree->root, 1);
+ if (!cn)
+ return 0;
+ cn->key = key;
+ chain_node(cn, parent, pre_sib);
+ } else {
+ log_error(INTERNAL_ERROR "Bad format string '%s'", fmt);
+ return_NULL;
+ }
+ if (!first)
+ first = cn;
+ if (cn)
+ pre_sib = cn;
+ }
+
+ return first;
+}
+
+struct dm_config_node *config_make_nodes(struct dm_config_tree *cft,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib,
+ ...)
+{
+ struct dm_config_node *res;
+ va_list ap;
+
+ va_start(ap, pre_sib);
+ res = config_make_nodes_v(cft, parent, pre_sib, ap);
+ va_end(ap);
+
+ return res;
+}
+
+int buffer_realloc(struct buffer *buf, int needed)
+{
+ char *new;
+ int alloc = buf->allocated;
+ if (alloc < needed)
+ alloc = needed;
+
+ buf->allocated += alloc;
+ new = realloc(buf->mem, buf->allocated);
+ if (new)
+ buf->mem = new;
+ else { /* utter failure */
+ dm_free(buf->mem);
+ buf->mem = 0;
+ buf->allocated = buf->used = 0;
+ return 0;
+ }
+ return 1;
+}
+
+int buffer_append(struct buffer *buf, const char *string)
+{
+ int len = strlen(string);
+
+ if ((buf->allocated - buf->used <= len) &&
+ !buffer_realloc(buf, len + 1))
+ return 0;
+
+ strcpy(buf->mem + buf->used, string);
+ buf->used += len;
+ return 1;
+}
+
+int buffer_line(const char *line, void *baton)
+{
+ struct buffer *buf = baton;
+ if (!buffer_append(buf, line))
+ return 0;
+ if (!buffer_append(buf, "\n"))
+ return 0;
+ return 1;
+}
+
+void buffer_destroy(struct buffer *buf)
+{
+ dm_free(buf->mem);
+ buffer_init(buf);
+}
+
+void buffer_init(struct buffer *buf)
+{
+ buf->allocated = buf->used = 0;
+ buf->mem = 0;
+}
diff --git a/libdaemon/client/config-util.h b/libdaemon/client/config-util.h
new file mode 100644
index 0000000..f03bcab
--- /dev/null
+++ b/libdaemon/client/config-util.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011-2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DAEMON_CONFIG_UTIL_H
+#define _LVM_DAEMON_CONFIG_UTIL_H
+
+#include "configure.h"
+#include "libdevmapper.h"
+
+#include <stdarg.h>
+
+struct buffer {
+ int allocated;
+ int used;
+ char *mem;
+};
+
+int buffer_append_vf(struct buffer *buf, va_list ap);
+int buffer_append_f(struct buffer *buf, ...);
+int buffer_append(struct buffer *buf, const char *string);
+void buffer_init(struct buffer *buf);
+void buffer_destroy(struct buffer *buf);
+int buffer_realloc(struct buffer *buf, int required);
+
+int buffer_line(const char *line, void *baton);
+
+int set_flag(struct dm_config_tree *cft, struct dm_config_node *parent,
+ const char *field, const char *flag, int want);
+
+struct dm_config_node *make_config_node(struct dm_config_tree *cft,
+ const char *key,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib);
+
+struct dm_config_node *make_text_node(struct dm_config_tree *cft,
+ const char *key,
+ const char *value,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib);
+
+struct dm_config_node *make_int_node(struct dm_config_tree *cft,
+ const char *key,
+ int64_t value,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib);
+
+struct dm_config_node *config_make_nodes_v(struct dm_config_tree *cft,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib,
+ va_list ap);
+struct dm_config_node *config_make_nodes(struct dm_config_tree *cft,
+ struct dm_config_node *parent,
+ struct dm_config_node *pre_sib,
+ ...);
+
+#endif /* _LVM_DAEMON_SHARED_H */
diff --git a/libdaemon/client/daemon-client.c b/libdaemon/client/daemon-client.c
new file mode 100644
index 0000000..a010cc0
--- /dev/null
+++ b/libdaemon/client/daemon-client.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2011-2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "daemon-io.h"
+#include "config-util.h"
+#include "daemon-client.h"
+#include "dm-logging.h"
+
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h> // ENOMEM
+
+daemon_handle daemon_open(daemon_info i) {
+ daemon_handle h = { .protocol_version = 0, .error = 0 };
+ daemon_reply r = { .cft = NULL };
+ struct sockaddr_un sockaddr = { .sun_family = AF_UNIX };
+
+ if ((h.socket_fd = socket(PF_UNIX, SOCK_STREAM /* | SOCK_NONBLOCK */, 0)) < 0)
+ goto error;
+
+ if (!dm_strncpy(sockaddr.sun_path, i.socket, sizeof(sockaddr.sun_path))) {
+ fprintf(stderr, "%s: daemon socket path too long.\n", i.socket);
+ goto error;
+ }
+
+ if (connect(h.socket_fd,(struct sockaddr *) &sockaddr, sizeof(sockaddr)))
+ goto error;
+
+ r = daemon_send_simple(h, "hello", NULL);
+ if (r.error || strcmp(daemon_reply_str(r, "response", "unknown"), "OK"))
+ goto error;
+
+ h.protocol = daemon_reply_str(r, "protocol", NULL);
+ if (h.protocol)
+ h.protocol = dm_strdup(h.protocol); /* keep around */
+ h.protocol_version = daemon_reply_int(r, "version", 0);
+
+ if (i.protocol && (!h.protocol || strcmp(h.protocol, i.protocol)))
+ goto error;
+ if (i.protocol_version && h.protocol_version != i.protocol_version)
+ goto error;
+
+ daemon_reply_destroy(r);
+ return h;
+
+error:
+ h.error = errno;
+ if (h.socket_fd >= 0)
+ if (close(h.socket_fd))
+ log_sys_error("close", "daemon_open");
+ if (r.cft)
+ daemon_reply_destroy(r);
+ h.socket_fd = -1;
+ return h;
+}
+
+daemon_reply daemon_send(daemon_handle h, daemon_request rq)
+{
+ struct buffer buffer;
+ daemon_reply reply = { .cft = NULL, .error = 0 };
+ assert(h.socket_fd >= 0);
+ buffer = rq.buffer;
+
+ if (!buffer.mem)
+ dm_config_write_node(rq.cft->root, buffer_line, &buffer);
+
+ assert(buffer.mem);
+ if (!buffer_write(h.socket_fd, &buffer))
+ reply.error = errno;
+
+ if (buffer_read(h.socket_fd, &reply.buffer)) {
+ reply.cft = dm_config_from_string(reply.buffer.mem);
+ if (!reply.cft)
+ reply.error = EPROTO;
+ } else
+ reply.error = errno;
+
+ if (buffer.mem != rq.buffer.mem)
+ buffer_destroy(&buffer);
+
+ return reply;
+}
+
+void daemon_reply_destroy(daemon_reply r) {
+ if (r.cft)
+ dm_config_destroy(r.cft);
+ buffer_destroy(&r.buffer);
+}
+
+daemon_reply daemon_send_simple_v(daemon_handle h, const char *id, va_list ap)
+{
+ static const daemon_reply err = { .error = ENOMEM, .buffer = { 0, 0, NULL }, .cft = NULL };
+ daemon_request rq = { .cft = NULL };
+ daemon_reply repl;
+
+ if (!buffer_append_f(&rq.buffer, "request = %s", id, NULL) ||
+ !buffer_append_vf(&rq.buffer, ap)) {
+ buffer_destroy(&rq.buffer);
+ return err;
+ }
+
+ repl = daemon_send(h, rq);
+ buffer_destroy(&rq.buffer);
+
+ return repl;
+}
+
+daemon_reply daemon_send_simple(daemon_handle h, const char *id, ...)
+{
+ daemon_reply r;
+ va_list ap;
+
+ va_start(ap, id);
+ r = daemon_send_simple_v(h, id, ap);
+ va_end(ap);
+
+ return r;
+}
+
+void daemon_close(daemon_handle h)
+{
+ dm_free((char *)h.protocol);
+}
+
+daemon_request daemon_request_make(const char *id)
+{
+ daemon_request r;
+ r.cft = NULL;
+ buffer_init(&r.buffer);
+
+ if (!(r.cft = dm_config_create()))
+ goto bad;
+
+ if (!(r.cft->root = make_text_node(r.cft, "request", id, NULL, NULL)))
+ goto bad;
+
+ return r;
+bad:
+ if (r.cft)
+ dm_config_destroy(r.cft);
+ r.cft = NULL;
+ return r;
+}
+
+int daemon_request_extend_v(daemon_request r, va_list ap)
+{
+ if (!r.cft)
+ return 0;
+
+ if (!config_make_nodes_v(r.cft, NULL, r.cft->root, ap))
+ return 0;
+
+ return 1;
+}
+
+int daemon_request_extend(daemon_request r, ...)
+{
+ int res;
+ va_list ap;
+
+ va_start(ap, r);
+ res = daemon_request_extend_v(r, ap);
+ va_end(ap);
+
+ return res;
+}
+
+void daemon_request_destroy(daemon_request r) {
+ if (r.cft)
+ dm_config_destroy(r.cft);
+ buffer_destroy(&r.buffer);
+}
+
diff --git a/libdaemon/client/daemon-client.h b/libdaemon/client/daemon-client.h
new file mode 100644
index 0000000..6ba65e6
--- /dev/null
+++ b/libdaemon/client/daemon-client.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011-2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DAEMON_COMMON_CLIENT_H
+#define _LVM_DAEMON_COMMON_CLIENT_H
+
+#include "libdevmapper.h"
+#include "config-util.h"
+
+typedef struct {
+ int socket_fd; /* the fd we use to talk to the daemon */
+ const char *protocol;
+ int protocol_version; /* version of the protocol the daemon uses */
+ int error;
+} daemon_handle;
+
+typedef struct {
+ const char *path; /* the binary of the daemon */
+ const char *socket; /* path to the comms socket */
+ unsigned autostart:1; /* start the daemon if not running? */
+
+ /*
+ * If the following are not NULL/0, an attempt to talk to a daemon which
+ * uses a different protocol or version will fail.
+ */
+ const char *protocol;
+ int protocol_version;
+} daemon_info;
+
+typedef struct {
+ struct buffer buffer;
+ /*
+ * The request looks like this:
+ * request = "id"
+ * arg_foo = "something"
+ * arg_bar = 3
+ * arg_wibble {
+ * something_special = "here"
+ * amount = 75
+ * knobs = [ "twiddle", "tweak" ]
+ * }
+ */
+ struct dm_config_tree *cft;
+} daemon_request;
+
+typedef struct {
+ int error; /* 0 for success */
+ struct buffer buffer;
+ struct dm_config_tree *cft; /* parsed reply, if available */
+} daemon_reply;
+
+/*
+ * Open the communication channel to the daemon. If the daemon is not running,
+ * it may be autostarted based on the binary path provided in the info (this
+ * will only happen if autostart is set to true). If the call fails for any
+ * reason, daemon_handle_valid(h) for the response will return false. Otherwise,
+ * the connection is good to start serving requests.
+ */
+daemon_handle daemon_open(daemon_info i);
+
+/*
+ * Send a request to the daemon, waiting for the reply. All communication with
+ * the daemon is synchronous. The function handles the IO details and parses the
+ * response, handling common error conditions. See "daemon_reply" for details.
+ *
+ * In case the request contains a non-NULL buffer pointer, this buffer is sent
+ * *verbatim* to the server. In this case, the cft pointer may be NULL (but will
+ * be ignored even if non-NULL). If the buffer is NULL, the cft is required to
+ * be a valid pointer, and is used to build up the request.
+ */
+daemon_reply daemon_send(daemon_handle h, daemon_request r);
+
+/*
+ * A simple interface to daemon_send. This function just takes the command id
+ * and possibly a list of parameters (of the form "name = %?", "value"). The
+ * type (string, integer) of the value is indicated by a character substituted
+ * for ? in %?: d for integer, s for string.
+ */
+daemon_reply daemon_send_simple(daemon_handle h, const char *id, ...);
+daemon_reply daemon_send_simple_v(daemon_handle h, const char *id, va_list ap);
+
+daemon_request daemon_request_make(const char *id);
+int daemon_request_extend(daemon_request r, ...);
+int daemon_request_extend_v(daemon_request r, va_list ap);
+void daemon_request_destroy(daemon_request r);
+
+void daemon_reply_destroy(daemon_reply r);
+
+static inline int64_t daemon_reply_int(daemon_reply r, const char *path, int64_t def) {
+ return dm_config_find_int64(r.cft->root, path, def);
+}
+
+static inline const char *daemon_reply_str(daemon_reply r, const char *path, const char *def) {
+ return dm_config_find_str(r.cft->root, path, def);
+}
+
+
+/* Shut down the communication to the daemon. Compulsory. */
+void daemon_close(daemon_handle h);
+
+#endif
diff --git a/libdaemon/client/daemon-io.c b/libdaemon/client/daemon-io.c
new file mode 100644
index 0000000..37f2744
--- /dev/null
+++ b/libdaemon/client/daemon-io.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011-2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "daemon-io.h"
+#include "libdevmapper.h"
+
+/*
+ * Read a single message from a (socket) filedescriptor. Messages are delimited
+ * by blank lines. This call will block until all of a message is received. The
+ * memory will be allocated from heap. Upon error, all memory is freed and the
+ * buffer pointer is set to NULL.
+ *
+ * See also write_buffer about blocking (read_buffer has identical behaviour).
+ */
+int buffer_read(int fd, struct buffer *buffer) {
+ if (!buffer_realloc(buffer, 32)) /* ensure we have some space */
+ goto fail;
+
+ while (1) {
+ int result = read(fd, buffer->mem + buffer->used, buffer->allocated - buffer->used);
+ if (result > 0) {
+ buffer->used += result;
+ if (!strncmp((buffer->mem) + buffer->used - 4, "\n##\n", 4)) {
+ *(buffer->mem + buffer->used - 4) = 0;
+ buffer->used -= 4;
+ break; /* success, we have the full message now */
+ }
+ if (buffer->used - buffer->allocated < 32)
+ if (!buffer_realloc(buffer, 1024))
+ goto fail;
+ continue;
+ }
+ if (result == 0) {
+ errno = ECONNRESET;
+ goto fail; /* we should never encounter EOF here */
+ }
+ if (result < 0 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
+ goto fail;
+ /* TODO call select here if we encountered EAGAIN/EWOULDBLOCK/EINTR */
+ }
+ return 1;
+fail:
+ return 0;
+}
+
+/*
+ * Write a buffer to a filedescriptor. Keep trying. Blocks (even on
+ * SOCK_NONBLOCK) until all of the write went through.
+ *
+ * TODO use select on EWOULDBLOCK/EAGAIN/EINTR to avoid useless spinning
+ */
+int buffer_write(int fd, struct buffer *buffer) {
+ struct buffer terminate = { .mem = (char *) "\n##\n", .used = 4 };
+ int done = 0;
+ int written = 0;
+ struct buffer *use = buffer;
+write:
+ while (1) {
+ int result = write(fd, use->mem + written, use->used - written);
+ if (result > 0)
+ written += result;
+ if (result < 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
+ return 0; /* too bad */
+ if (written == use->used) {
+ if (done)
+ return 1;
+ else
+ break; /* done */
+ }
+ }
+
+ use = &terminate;
+ written = 0;
+ done = 1;
+ goto write;
+}
diff --git a/libdaemon/client/daemon-io.h b/libdaemon/client/daemon-io.h
new file mode 100644
index 0000000..1f55af7
--- /dev/null
+++ b/libdaemon/client/daemon-io.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011-2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DAEMON_IO_H
+#define _LVM_DAEMON_IO_H
+
+#include "configure.h"
+#include "config-util.h"
+#include "libdevmapper.h"
+
+#define _REENTRANT
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+/* TODO function names */
+
+int buffer_read(int fd, struct buffer *buffer);
+int buffer_write(int fd, struct buffer *buffer);
+
+#endif /* _LVM_DAEMON_SHARED_H */
diff --git a/libdaemon/server/Makefile.in b/libdaemon/server/Makefile.in
new file mode 100644
index 0000000..1c4a38e
--- /dev/null
+++ b/libdaemon/server/Makefile.in
@@ -0,0 +1,22 @@
+# Copyright (C) 2011 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
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+LIB_STATIC = libdaemonserver.a
+SOURCES = daemon-server.c daemon-log.c
+
+include $(top_builddir)/make.tmpl
+
+LIBS += $(DAEMON_LIBS)
diff --git a/libdaemon/server/daemon-log.c b/libdaemon/server/daemon-log.c
new file mode 100644
index 0000000..6f5f4dc
--- /dev/null
+++ b/libdaemon/server/daemon-log.c
@@ -0,0 +1,180 @@
+#include "daemon-server.h"
+#include "daemon-log.h"
+#include <syslog.h>
+#include <assert.h>
+
+struct backend {
+ int id;
+ void (*log)(log_state *s, void **state, int type, const char *message);
+};
+
+static void log_syslog(log_state *s, void **state, int type, const char *message)
+{
+ int prio;
+
+ if (!*state) { /* initialize */
+ *state = (void *)1;
+ openlog(s->name, LOG_PID, LOG_DAEMON);
+ }
+
+ switch (type) {
+ case DAEMON_LOG_INFO: prio = LOG_INFO; break;
+ case DAEMON_LOG_WARN: prio = LOG_WARNING; break;
+ case DAEMON_LOG_FATAL: prio = LOG_CRIT; break;
+ default: prio = LOG_DEBUG; break;
+ }
+
+ syslog(prio, "%s", message);
+}
+
+static void log_stderr(log_state *s, void **state, int type, const char *message)
+{
+ const char *prefix;
+
+ switch (type) {
+ case DAEMON_LOG_INFO: prefix = "I: "; break;
+ case DAEMON_LOG_WARN: prefix = "W: " ; break;
+ case DAEMON_LOG_ERROR: /* fall through */
+ case DAEMON_LOG_FATAL: prefix = "E: " ; break;
+ default: prefix = ""; break;
+ }
+
+ fprintf(stderr, "%s%s\n", prefix, message);
+}
+
+struct backend backend[] = {
+ { DAEMON_LOG_OUTLET_SYSLOG, log_syslog },
+ { DAEMON_LOG_OUTLET_STDERR, log_stderr },
+ { 0, 0 }
+};
+
+void daemon_log(log_state *s, int type, const char *message) {
+ int i = 0;
+ while ( backend[i].id ) {
+ if ( (s->log_config[type] & backend[i].id) == backend[i].id )
+ backend[i].log( s, &s->backend_state[i], type, message );
+ ++ i;
+ }
+}
+
+static int _type_interesting(log_state *s, int type) {
+ int i = 0;
+ while ( backend[i].id ) {
+ if ( (s->log_config[type] & backend[i].id) == backend[i].id )
+ return 1;
+ ++ i;
+ }
+ return 0;
+}
+
+void daemon_logf(log_state *s, int type, const char *fmt, ...) {
+ char *buf;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (dm_vasprintf(&buf, fmt, ap) >= 0) {
+ daemon_log(s, type, buf);
+ dm_free(buf);
+ } /* else return_0 */
+ va_end(ap);
+}
+
+struct log_line_baton {
+ log_state *s;
+ int type;
+ const char *prefix;
+};
+
+static int _log_line(const char *line, void *baton) {
+ struct log_line_baton *b = baton;
+ daemon_logf(b->s, b->type, "%s%s", b->prefix, line);
+ return 0;
+}
+
+void daemon_log_cft(log_state *s, int type, const char *prefix, const struct dm_config_node *n)
+{
+ struct log_line_baton b = { .s = s, .type = type, .prefix = prefix };
+
+ if (!_type_interesting(s, type))
+ return;
+
+ dm_config_write_node(n, &_log_line, &b);
+}
+
+void daemon_log_multi(log_state *s, int type, const char *prefix, const char *msg)
+{
+ struct log_line_baton b = { .s = s, .type = type, .prefix = prefix };
+ char *buf;
+ char *pos;
+
+ if (!_type_interesting(s, type))
+ return;
+
+ buf = dm_strdup(msg);
+ pos = buf;
+
+ if (!buf)
+ return; /* _0 */
+
+ while (pos) {
+ char *next = strchr(pos, '\n');
+ if (next)
+ *next = 0;
+ _log_line(pos, &b);
+ pos = next ? next + 1 : 0;
+ }
+ dm_free(buf);
+}
+
+void daemon_log_enable(log_state *s, int outlet, int type, int enable)
+{
+ assert(type < 32);
+ if (enable)
+ s->log_config[type] |= outlet;
+ else
+ s->log_config[type] &= ~outlet;
+}
+
+static int _parse_one(log_state *s, int outlet, const char *type, int enable)
+{
+ int i;
+ if (!strcmp(type, "all"))
+ for (i = 0; i < 32; ++i)
+ daemon_log_enable(s, outlet, i, enable);
+ else if (!strcmp(type, "wire"))
+ daemon_log_enable(s, outlet, DAEMON_LOG_WIRE, enable);
+ else if (!strcmp(type, "debug"))
+ daemon_log_enable(s, outlet, DAEMON_LOG_DEBUG, enable);
+ else
+ return 0;
+
+ return 1;
+}
+
+int daemon_log_parse(log_state *s, int outlet, const char *types, int enable)
+{
+ char *buf;
+ char *pos;
+
+ if (!types || !types[0])
+ return 1;
+
+ if (!(buf = dm_strdup(types)))
+ return 0;
+
+ pos = buf;
+ while (pos) {
+ char *next = strchr(pos, ',');
+ if (next)
+ *next = 0;
+ if (!_parse_one(s, outlet, pos, enable)) {
+ dm_free(buf);
+ return 0;
+ }
+ pos = next ? next + 1 : 0;
+ }
+
+ dm_free(buf);
+
+ return 1;
+}
diff --git a/libdaemon/server/daemon-log.h b/libdaemon/server/daemon-log.h
new file mode 100644
index 0000000..e2e11c5
--- /dev/null
+++ b/libdaemon/server/daemon-log.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DAEMON_LOG_H
+#define _LVM_DAEMON_LOG_H
+
+enum { DAEMON_LOG_FATAL = 0 /* usually preceding daemon death */
+ , DAEMON_LOG_ERROR = 1 /* something serious has happened */
+ , DAEMON_LOG_WARN = 2 /* something unusual has happened */
+ , DAEMON_LOG_INFO = 3 /* thought you might be interested */
+ , DAEMON_LOG_WIRE = 4 /* dump traffic on client sockets */
+ , DAEMON_LOG_DEBUG = 5 /* unsorted debug stuff */
+};
+
+#define DEBUGLOG(s, x...) daemon_logf((s)->log, DAEMON_LOG_DEBUG, x)
+#define DEBUGLOG_cft(s, i, n) daemon_log_cft((s)->log, DAEMON_LOG_DEBUG, i, n)
+#define WARN(s, x...) daemon_logf((s)->log, DAEMON_LOG_WARN, x)
+#define INFO(s, x...) daemon_logf((s)->log, DAEMON_LOG_INFO, x)
+#define ERROR(s, x...) daemon_logf((s)->log, DAEMON_LOG_ERROR, x)
+#define FATAL(s, x...) daemon_logf((s)->log, DAEMON_LOG_FATAL, x)
+
+#endif
diff --git a/libdaemon/server/daemon-server.c b/libdaemon/server/daemon-server.c
new file mode 100644
index 0000000..3711419
--- /dev/null
+++ b/libdaemon/server/daemon-server.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2011-2012 Red Hat, Inc.
+ *
+ * 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 "daemon-io.h"
+#include "config-util.h"
+#include "daemon-server.h"
+#include "daemon-log.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <syslog.h> /* FIXME. For the global closelog(). */
+
+#if 0
+/* Create a device monitoring thread. */
+static int _pthread_create(pthread_t *t, void *(*fun)(void *), void *arg, int stacksize)
+{
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ /*
+ * We use a smaller stack since it gets preallocated in its entirety
+ */
+ pthread_attr_setstacksize(&attr, stacksize);
+ return pthread_create(t, &attr, fun, arg);
+}
+#endif
+
+static volatile sig_atomic_t _shutdown_requested = 0;
+static int _systemd_activation = 0;
+
+static void _exit_handler(int sig __attribute__((unused)))
+{
+ _shutdown_requested = 1;
+}
+
+#ifdef linux
+
+#include <stddef.h>
+
+/*
+ * 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_SOCKET_SERVER SD_LISTEN_FDS_START
+
+# include <stdio.h>
+
+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 _protect_against_oom_killer(void)
+{
+ struct stat st;
+
+ if (stat(OOM_ADJ_FILE, &st) == -1) {
+ if (errno != ENOENT)
+ perror(OOM_ADJ_FILE ": stat failed");
+
+ /* 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);
+ }
+
+ return _set_oom_adj(OOM_ADJ_FILE, OOM_SCORE_ADJ_MIN);
+}
+
+union sockaddr_union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+};
+
+static int _handle_preloaded_socket(int fd, const char *path)
+{
+ struct stat st_fd;
+ union sockaddr_union sockaddr = { .sa.sa_family = 0 };
+ int type = 0;
+ socklen_t len = sizeof(type);
+ size_t path_len = strlen(path);
+
+ if (fd < 0)
+ return 0;
+
+ if (fstat(fd, &st_fd) < 0 || !S_ISSOCK(st_fd.st_mode))
+ return 0;
+
+ if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) < 0 ||
+ len != sizeof(type) || type != SOCK_STREAM)
+ return 0;
+
+ len = sizeof(sockaddr);
+ if (getsockname(fd, &sockaddr.sa, &len) < 0 ||
+ len < sizeof(sa_family_t) ||
+ sockaddr.sa.sa_family != PF_UNIX)
+ return 0;
+
+ if (!(len >= offsetof(struct sockaddr_un, sun_path) + path_len + 1 &&
+ memcmp(path, sockaddr.un.sun_path, path_len) == 0))
+ return 0;
+
+ return 1;
+}
+
+static int _systemd_handover(struct daemon_state *ds)
+{
+ const char *e;
+ char *p;
+ unsigned long env_pid, env_listen_fds;
+ int r = 0;
+
+ /* 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 1 and the fd must be a socket! */
+ 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 != 1)
+ goto out;
+
+ /* Check and handle the socket passed in */
+ if ((r = _handle_preloaded_socket(SD_FD_SOCKET_SERVER, ds->socket_path)))
+ ds->socket_fd = SD_FD_SOCKET_SERVER;
+
+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 int _open_socket(daemon_state s)
+{
+ int fd = -1;
+ struct sockaddr_un sockaddr = { .sun_family = AF_UNIX };
+ mode_t old_mask;
+
+ (void) dm_prepare_selinux_context(s.socket_path, S_IFSOCK);
+ old_mask = umask(0077);
+
+ /* Open local socket */
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("Can't create local socket.");
+ goto error;
+ }
+
+ /* Set Close-on-exec & non-blocking */
+ if (fcntl(fd, F_SETFD, 1))
+ fprintf(stderr, "setting CLOEXEC on socket fd %d failed: %s\n", fd, strerror(errno));
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK))
+ fprintf(stderr, "setting O_NONBLOCK on socket fd %d failed: %s\n", fd, strerror(errno));
+
+ fprintf(stderr, "[D] creating %s\n", s.socket_path);
+ if (!dm_strncpy(sockaddr.sun_path, s.socket_path, sizeof(sockaddr.sun_path))) {
+ fprintf(stderr, "%s: daemon socket path too long.\n", s.socket_path);
+ goto error;
+ }
+
+ if (bind(fd, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) {
+ perror("can't bind local socket.");
+ goto error;
+ }
+ if (listen(fd, 1) != 0) {
+ perror("listen local");
+ goto error;
+ }
+
+out:
+ umask(old_mask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+ return fd;
+
+error:
+ if (fd >= 0) {
+ if (close(fd))
+ perror("close failed");
+ if (unlink(s.socket_path))
+ perror("unlink failed");
+ fd = -1;
+ }
+ goto out;
+}
+
+static void remove_lockfile(const char *file)
+{
+ if (unlink(file))
+ perror("unlink failed");
+}
+
+static void _daemonise(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) && !_shutdown_requested) {
+ tval.tv_sec = 0;
+ tval.tv_usec = 250000; /* .25 sec */
+ select(0, NULL, NULL, NULL, &tval);
+ }
+
+ if (_shutdown_requested) /* Child has signaled it is ok - we can exit now */
+ exit(0);
+
+ /* Problem with child. Determine what it is by exit code */
+ fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(child_status));
+ exit(WEXITSTATUS(child_status));
+ }
+
+ if (chdir("/"))
+ exit(1);
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
+ fd = 256; /* just have to guess */
+ else
+ fd = rlim.rlim_cur;
+
+ for (--fd; fd >= 0; fd--) {
+#ifdef linux
+ /* Do not close fds preloaded by systemd! */
+ if (_systemd_activation && fd == SD_FD_SOCKET_SERVER)
+ continue;
+#endif
+ (void) close(fd);
+ }
+
+ if ((open("/dev/null", O_RDONLY) < 0) ||
+ (open("/dev/null", O_WRONLY) < 0) ||
+ (open("/dev/null", O_WRONLY) < 0))
+ exit(1);
+
+ setsid();
+}
+
+response daemon_reply_simple(const char *id, ...)
+{
+ va_list ap;
+ response res = { .cft = NULL };
+
+ va_start(ap, id);
+
+ buffer_init(&res.buffer);
+ if (!buffer_append_f(&res.buffer, "response = %s", id, NULL)) {
+ res.error = ENOMEM;
+ goto end;
+ }
+ if (!buffer_append_vf(&res.buffer, ap)) {
+ res.error = ENOMEM;
+ goto end;
+ }
+
+end:
+ va_end(ap);
+ return res;
+}
+
+struct thread_baton {
+ daemon_state s;
+ client_handle client;
+};
+
+static response builtin_handler(daemon_state s, client_handle h, request r)
+{
+ const char *rq = daemon_request_str(r, "request", "NONE");
+ response res = { .error = EPROTO };
+
+ if (!strcmp(rq, "hello")) {
+ return daemon_reply_simple("OK", "protocol = %s", s.protocol ?: "default",
+ "version = %" PRId64, (int64_t) s.protocol_version, NULL);
+ }
+
+ buffer_init(&res.buffer);
+ return res;
+}
+
+static void *client_thread(void *baton)
+{
+ struct thread_baton *b = baton;
+ request req;
+ response res;
+
+ buffer_init(&req.buffer);
+
+ while (1) {
+ if (!buffer_read(b->client.socket_fd, &req.buffer))
+ goto fail;
+
+ req.cft = dm_config_from_string(req.buffer.mem);
+
+ if (!req.cft)
+ fprintf(stderr, "error parsing request:\n %s\n", req.buffer.mem);
+ else
+ daemon_log_cft(b->s.log, DAEMON_LOG_WIRE, "<- ", req.cft->root);
+
+ res = builtin_handler(b->s, b->client, req);
+
+ if (res.error == EPROTO) /* Not a builtin, delegate to the custom handler. */
+ res = b->s.handler(b->s, b->client, req);
+
+ if (!res.buffer.mem) {
+ dm_config_write_node(res.cft->root, buffer_line, &res.buffer);
+ if (!buffer_append(&res.buffer, "\n\n"))
+ goto fail;
+ dm_config_destroy(res.cft);
+ }
+
+ if (req.cft)
+ dm_config_destroy(req.cft);
+ buffer_destroy(&req.buffer);
+
+ daemon_log_multi(b->s.log, DAEMON_LOG_WIRE, "-> ", res.buffer.mem);
+ buffer_write(b->client.socket_fd, &res.buffer);
+
+ buffer_destroy(&res.buffer);
+ }
+fail:
+ /* TODO what should we really do here? */
+ if (close(b->client.socket_fd))
+ perror("close");
+ buffer_destroy(&req.buffer);
+ dm_free(baton);
+ return NULL;
+}
+
+static int handle_connect(daemon_state s)
+{
+ struct thread_baton *baton;
+ struct sockaddr_un sockaddr;
+ client_handle client = { .thread_id = 0 };
+ socklen_t sl = sizeof(sockaddr);
+
+ client.socket_fd = accept(s.socket_fd, (struct sockaddr *) &sockaddr, &sl);
+ if (client.socket_fd < 0)
+ return 0;
+
+ if (!(baton = malloc(sizeof(struct thread_baton))))
+ return 0;
+
+ baton->s = s;
+ baton->client = client;
+
+ if (pthread_create(&baton->client.thread_id, NULL, client_thread, baton))
+ return 0;
+
+ pthread_detach(baton->client.thread_id);
+
+ return 1;
+}
+
+void daemon_start(daemon_state s)
+{
+ int failed = 0;
+ log_state _log = { { 0 } };
+
+ /*
+ * Switch to C locale to avoid reading large locale-archive file used by
+ * some glibc (on some distributions it takes over 100MB). Some daemons
+ * need to use mlockall().
+ */
+ if (setenv("LANG", "C", 1))
+ perror("Cannot set LANG to C");
+
+#ifdef linux
+ _systemd_activation = _systemd_handover(&s);
+#endif
+
+ if (!s.foreground)
+ _daemonise();
+
+ s.log = &_log;
+ s.log->name = s.name;
+
+ /* Log important things to syslog by default. */
+ daemon_log_enable(s.log, DAEMON_LOG_OUTLET_SYSLOG, DAEMON_LOG_FATAL, 1);
+ daemon_log_enable(s.log, DAEMON_LOG_OUTLET_SYSLOG, DAEMON_LOG_ERROR, 1);
+
+ if (s.pidfile) {
+ (void) dm_prepare_selinux_context(s.pidfile, S_IFREG);
+
+ /*
+ * NB. Take care to not keep stale locks around. Best not exit(...)
+ * after this point.
+ */
+ if (dm_create_lockfile(s.pidfile) == 0)
+ exit(1);
+
+ (void) dm_prepare_selinux_context(NULL, 0);
+ }
+
+ /* Set normal exit signals to request shutdown instead of dying. */
+ signal(SIGINT, &_exit_handler);
+ signal(SIGHUP, &_exit_handler);
+ signal(SIGQUIT, &_exit_handler);
+ signal(SIGTERM, &_exit_handler);
+ signal(SIGALRM, &_exit_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+#ifdef linux
+ /* Systemd has adjusted oom killer for us already */
+ if (s.avoid_oom && !_systemd_activation && !_protect_against_oom_killer())
+ ERROR(&s, "Failed to protect against OOM killer");
+#endif
+
+ if (!_systemd_activation && s.socket_path) {
+ s.socket_fd = _open_socket(s);
+ if (s.socket_fd < 0)
+ failed = 1;
+ }
+
+ /* Signal parent, letting them know we are ready to go. */
+ if (!s.foreground)
+ kill(getppid(), SIGTERM);
+
+ if (s.daemon_init)
+ if (!s.daemon_init(&s))
+ failed = 1;
+
+ while (!_shutdown_requested && !failed) {
+ fd_set in;
+ FD_ZERO(&in);
+ FD_SET(s.socket_fd, &in);
+ if (select(FD_SETSIZE, &in, NULL, NULL, NULL) < 0 && errno != EINTR)
+ perror("select error");
+ if (FD_ISSET(s.socket_fd, &in))
+ if (!_shutdown_requested && !handle_connect(s))
+ ERROR(&s, "Failed to handle a client connection.");
+ }
+
+ /* If activated by systemd, do not unlink the socket - systemd takes care of that! */
+ if (!_systemd_activation && s.socket_fd >= 0)
+ if (unlink(s.socket_path))
+ perror("unlink error");
+
+ if (s.daemon_fini)
+ if (!s.daemon_fini(&s))
+ failed = 1;
+
+ INFO(&s, "%s shutting down", s.name);
+
+ closelog(); /* FIXME */
+ if (s.pidfile)
+ remove_lockfile(s.pidfile);
+ if (failed)
+ exit(1);
+}
diff --git a/libdaemon/server/daemon-server.h b/libdaemon/server/daemon-server.h
new file mode 100644
index 0000000..f184853
--- /dev/null
+++ b/libdaemon/server/daemon-server.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2011-2012 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DAEMON_COMMON_SERVER_H
+#define _LVM_DAEMON_COMMON_SERVER_H
+
+#include "daemon-client.h"
+
+typedef struct {
+ int socket_fd; /* the fd we use to talk to the client */
+ pthread_t thread_id;
+ char *read_buf;
+ void *private; /* this holds per-client state */
+} client_handle;
+
+typedef struct {
+ struct dm_config_tree *cft;
+ struct buffer buffer;
+} request;
+
+typedef struct {
+ int error;
+ struct dm_config_tree *cft;
+ struct buffer buffer;
+} response;
+
+struct daemon_state;
+
+/*
+ * Craft a simple reply, without the need to construct a config_tree. See
+ * daemon_send_simple in daemon-client.h for the description of the parameters.
+ */
+response daemon_reply_simple(const char *id, ...);
+
+static inline int daemon_request_int(request r, const char *path, int def) {
+ if (!r.cft)
+ return def;
+ return dm_config_find_int(r.cft->root, path, def);
+}
+
+static inline const char *daemon_request_str(request r, const char *path, const char *def) {
+ if (!r.cft)
+ return def;
+ return dm_config_find_str(r.cft->root, path, def);
+}
+
+/*
+ * The callback. Called once per request issued, in the respective client's
+ * thread. It is presented by a parsed request (in the form of a config tree).
+ * The output is a new config tree that is serialised and sent back to the
+ * client. The client blocks until the request processing is done and reply is
+ * sent.
+ */
+typedef response (*handle_request)(struct daemon_state s, client_handle h, request r);
+
+typedef struct {
+ uint32_t log_config[32];
+ void *backend_state[32];
+ const char *name;
+} log_state;
+
+typedef struct daemon_state {
+ /*
+ * The maximal stack size for individual daemon threads. This is
+ * essential for daemons that need to be locked into memory, since
+ * pthread's default is 10M per thread.
+ */
+ int thread_stack_size;
+
+ /* Flags & attributes affecting the behaviour of the daemon. */
+ unsigned avoid_oom:1;
+ unsigned foreground:1;
+ const char *name;
+ const char *pidfile;
+ const char *socket_path;
+ const char *protocol;
+ int protocol_version;
+
+ handle_request handler;
+ int (*daemon_init)(struct daemon_state *st);
+ int (*daemon_fini)(struct daemon_state *st);
+
+ /* Global runtime info maintained by the framework. */
+ int socket_fd;
+
+ log_state *log;
+ void *private; /* the global daemon state */
+} daemon_state;
+
+/*
+ * Start serving the requests. This does all the daemonisation, socket setup
+ * work and so on. This function takes over the process, and upon failure, it
+ * will terminate execution. It may be called at most once.
+ */
+void daemon_start(daemon_state s);
+
+/*
+ * Take over from an already running daemon. This function handles connecting
+ * to the running daemon and telling it we are going to take over. The takeover
+ * request may be customised by passing in a non-NULL request.
+ *
+ * The takeover sequence: the old daemon stops accepting new clients, then it
+ * waits until all current client connections are closed. When that happens, it
+ * serializes its current state and sends that as a reply, which is then
+ * returned by this function (therefore, this function won't return until the
+ * previous instance has shut down).
+ *
+ * The daemon, after calling daemon_takeover is expected to set up its
+ * daemon_state using the reply from this function and call daemon_start as
+ * usual.
+ */
+daemon_reply daemon_takeover(daemon_info i, daemon_request r);
+
+/* Call this to request a clean shutdown of the daemon. Async safe. */
+void daemon_stop(void);
+
+enum { DAEMON_LOG_OUTLET_SYSLOG = 1,
+ DAEMON_LOG_OUTLET_STDERR = 2,
+ DAEMON_LOG_OUTLET_SOCKET = 4 };
+
+/* Log a message of a given type. */
+void daemon_log(log_state *s, int type, const char *message);
+
+/* Log a config (sub)tree, using a given message type, each line prefixed with "prefix". */
+void daemon_log_cft(log_state *s, int type, const char *prefix,
+ const struct dm_config_node *n);
+
+/* Log a multi-line block, prefixing each line with "prefix". */
+void daemon_log_multi(log_state *s, int type, const char *prefix, const char *message);
+
+/* Log a formatted message as "type". See also daemon-log.h. */
+void daemon_logf(log_state *s, int type, const char *format, ...);
+
+/*
+ * Configure log_state to send messages of type "type" to the log outlet
+ * "outlet", iff "enable" is true.
+ */
+void daemon_log_enable(log_state *s, int outlet, int type, int enable);
+
+/*
+ * Set up logging on a given outlet using a list of message types (comma
+ * separated) to log using that outlet. The list is expected to look like this,
+ * "all,wire,debug". Returns 0 upon encountering an unknown message type.
+ */
+int daemon_log_parse(log_state *s, int outlet, const char *types, int enable);
+
+#endif