From: "Daniel P. Berrange" <berrange(a)redhat.com>
To register virtual machines and containers with systemd-machined,
and thus have cgroups auto-created, we need to talk over DBus.
This is somewhat tedious code, so introduce a dedicated function
to isolate the DBus call in one place.
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
.gitignore | 1 +
include/libvirt/virterror.h | 2 +
src/Makefile.am | 1 +
src/libvirt_private.syms | 4 ++
src/util/virerror.c | 7 +++
src/util/virerror.h | 11 ++++
src/util/virsystemd.c | 139 ++++++++++++++++++++++++++++++++++++++++++++
src/util/virsystemd.h | 36 ++++++++++++
tests/Makefile.am | 22 ++++++-
tests/testutils.h | 2 +
tests/virsystemdmock.c | 77 ++++++++++++++++++++++++
tests/virsystemdtest.c | 131 +++++++++++++++++++++++++++++++++++++++++
12 files changed, 430 insertions(+), 3 deletions(-)
create mode 100644 src/util/virsystemd.c
create mode 100644 src/util/virsystemd.h
create mode 100644 tests/virsystemdmock.c
create mode 100644 tests/virsystemdtest.c
diff --git a/.gitignore b/.gitignore
index 851c6e4..4c79de3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -204,6 +204,7 @@
/tests/virshtest
/tests/virstoragetest
/tests/virstringtest
+/tests/virsystemdtest
/tests/virtimetest
/tests/viruritest
/tests/vmx2xmltest
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 5f78856..c1960c8 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -119,6 +119,7 @@ typedef enum {
VIR_FROM_CGROUP = 54, /* Error from cgroups */
VIR_FROM_ACCESS = 55, /* Error from access control manager */
+ VIR_FROM_SYSTEMD = 56, /* Error from systemd code */
# ifdef VIR_ENUM_SENTINELS
VIR_ERR_DOMAIN_LAST
@@ -294,6 +295,7 @@ typedef enum {
VIR_ERR_RESOURCE_BUSY = 87, /* resource is already in use */
VIR_ERR_ACCESS_DENIED = 88, /* operation on the object/resource
was denied */
+ VIR_ERR_DBUS_SERVICE = 89, /* error from a dbus service */
} virErrorNumber;
/**
diff --git a/src/Makefile.am b/src/Makefile.am
index d9e703f..e4d05a0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -131,6 +131,7 @@ UTIL_SOURCES = \
util/virstoragefile.c util/virstoragefile.h \
util/virstring.h util/virstring.c \
util/virsysinfo.c util/virsysinfo.h \
+ util/virsystemd.c util/virsystemd.h \
util/virthread.c util/virthread.h \
util/virthreadpthread.h \
util/virthreadwin32.h \
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 0128264..a9b65fd 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1911,6 +1911,10 @@ virSysinfoRead;
virSysinfoSetup;
+# util/virsystemd.h
+virSystemdCreateMachine;
+
+
# util/virthread.h
virCondBroadcast;
virCondDestroy;
diff --git a/src/util/virerror.c b/src/util/virerror.c
index ce3ab85..36d256b 100644
--- a/src/util/virerror.c
+++ b/src/util/virerror.c
@@ -123,6 +123,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
"Cgroup",
"Access Manager", /* 55 */
+ "Systemd",
)
@@ -1243,6 +1244,12 @@ virErrorMsg(virErrorNumber error, const char *info)
else
errmsg = _("access denied: %s");
break;
+ case VIR_ERR_DBUS_SERVICE:
+ if (info == NULL)
+ errmsg = _("error from service");
+ else
+ errmsg = _("error from service: %s");
+ break;
}
return errmsg;
}
diff --git a/src/util/virerror.h b/src/util/virerror.h
index 332a5eb..6ea456b 100644
--- a/src/util/virerror.h
+++ b/src/util/virerror.h
@@ -145,6 +145,17 @@ void virReportSystemErrorFull(int domcode,
0, 0, \
(fmt), __VA_ARGS__)
+# define virReportDBusServiceError(message, name) \
+ virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__, \
+ VIR_FROM_THIS, \
+ VIR_ERR_DBUS_SERVICE, \
+ VIR_ERR_ERROR, \
+ __FUNCTION__, \
+ name, \
+ NULL, \
+ 0, 0, \
+ "%s", message);
+
void virReportOOMErrorFull(int domcode,
const char *filename,
const char *funcname,
diff --git a/src/util/virsystemd.c b/src/util/virsystemd.c
new file mode 100644
index 0000000..25165a3
--- /dev/null
+++ b/src/util/virsystemd.c
@@ -0,0 +1,139 @@
+/*
+ * virsystemd.c: helpers for using systemd APIs
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include "virsystemd.h"
+#include "virdbus.h"
+#include "virstring.h"
+#include "viralloc.h"
+#include "virutil.h"
+
+#define VIR_FROM_THIS VIR_FROM_SYSTEMD
+
+/**
+ * virSystemdCreateMachine:
+ * @name: driver unique name of the machine
+ * @drivername: name of the virt driver
+ * @privileged: whether driver is running privileged or per user
+ * @uuid: globally unique UUID of the machine
+ * @rootdir: root directory of machine filesystem
+ * @pidleader: PID of the leader process
+ * @slice: name of the slice to place the machine in
+ */
+int virSystemdCreateMachine(const char *name,
+ const char *drivername,
+ bool privileged,
+ const unsigned char *uuid,
+ const char *rootdir,
+ pid_t pidleader,
+ bool iscontainer,
+ const char *slice)
+{
+ int ret = -1;
+ DBusConnection *conn;
+ char *machinename = NULL;
+ char *creatorname = NULL;
+ char *username = NULL;
+
+ if (!(conn = virDBusGetSystemBus()))
+ return -1;
+
+ if (privileged) {
+ if (virAsprintf(&machinename, "%s-%s", drivername, name) < 0)
+ goto cleanup;
+ } else {
+ if (!(username = virGetUserName(geteuid())))
+ goto cleanup;
+ if (virAsprintf(&machinename, "%s-%s-%s", username, drivername,
name) < 0)
+ goto cleanup;
+ }
+
+ if (virAsprintf(&creatorname, "libvirt-%s", drivername) < 0)
+ goto cleanup;
+
+ /*
+ * The systemd DBus API we're invoking has the
+ * following signature
+ *
+ * CreateMachine(in s name,
+ * in ay id,
+ * in s service,
+ * in s class,
+ * in u leader,
+ * in s root_directory,
+ * in a(sv) scope_properties,
+ * out o path);
+ *
+ * @name a host unique name for the machine. shows up
+ * in 'ps' listing & similar
+ *
+ * @id: a UUID of the machine, ideally matching /etc/machine-id
+ * for containers
+ *
+ * @service: identifier of the client ie "libvirt-lxc"
+ *
+ * @class: either the string "container" or "vm" depending
+ * on the type of machine
+ *
+ * @leader: main PID of the machine, either the host emulator
+ * process, or the 'init' PID of the container
+ *
+ * @root_directory: the root directory of the container, if
+ * this is known & visible in the host filesystem, or empty string
+ *
+ * @scope_properties:an array (not a dict!) of properties that are
+ * passed on to PID 1 when creating a scope unit for your machine.
+ * Will allow initial settings for the cgroup & similar.
+ *
+ * @path: a bus path returned for the machine object created, to
+ * allow further API calls to be made against the object.
+ */
+
+ if (virDBusCallMethod(conn,
+ NULL,
+ "org.freedesktop.machine1",
+ "/org/freedesktop/machine1",
+ "org.freedesktop.machine1.Manager",
+ "CreateMachine",
+ "sayssusa(sv)",
+ machinename,
+ 16,
+ uuid[0], uuid[1], uuid[2], uuid[3],
+ uuid[4], uuid[5], uuid[6], uuid[7],
+ uuid[8], uuid[9], uuid[10], uuid[11],
+ uuid[12], uuid[13], uuid[14], uuid[15],
+ creatorname,
+ iscontainer ? "container" : "vm",
+ (unsigned int)pidleader,
+ rootdir ? rootdir : "",
+ 1, "Slice","s",
+ slice ? slice : "") < 0)
+ goto cleanup;
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(username);
+ VIR_FREE(creatorname);
+ VIR_FREE(machinename);
+ return ret;
+}
diff --git a/src/util/virsystemd.h b/src/util/virsystemd.h
new file mode 100644
index 0000000..5bee3db
--- /dev/null
+++ b/src/util/virsystemd.h
@@ -0,0 +1,36 @@
+/*
+ * virsystemd.h: helpers for using systemd APIs
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VIR_SYSTEMD_H__
+# define __VIR_SYSTEMD_H__
+
+# include "internal.h"
+
+int virSystemdCreateMachine(const char *name,
+ const char *drivername,
+ bool privileged,
+ const unsigned char *uuid,
+ const char *rootdir,
+ pid_t pidleader,
+ bool iscontainer,
+ const char *slice);
+
+#endif /* __VIR_SYSTEMD_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1748ed1..be6347a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -129,10 +129,10 @@ test_programs = virshtest sockettest \
$(NULL)
if WITH_DBUS
-test_programs += virdbustest
+test_programs += virdbustest \
+ virsystemdtest
endif
-
if WITH_GNUTLS
test_programs += virnettlscontexttest
endif
@@ -281,6 +281,10 @@ if WITH_QEMU
test_libraries += libqemumonitortestutils.la
endif
+if WITH_DBUS
+test_libraries += virsystemdmock.la
+endif
+
if WITH_TESTS
noinst_PROGRAMS = $(test_programs) $(test_helpers)
noinst_LTLIBRARIES = $(test_libraries)
@@ -647,8 +651,20 @@ virdbustest_SOURCES = \
virdbustest.c testutils.h testutils.c
virdbustest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
virdbustest_LDADD = $(LDADDS)
+
+virsystemdtest_SOURCES = \
+ virsystemdtest.c testutils.h testutils.c
+virsystemdtest_CFLAGS = $(AM_CFLAGS)
+virsystemdtest_LDADD = $(LDADDS)
+
+virsystemdmock_la_SOURCES = \
+ virsystemdmock.c
+virsystemdmock_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+virsystemdmock_la_LDFLAGS = -module -avoid-version \
+ -rpath /evil/libtool/hack/to/force/shared/lib/creation
+
else
-EXTRA_DIST += virdbustest.c
+EXTRA_DIST += virdbustest.c virsystemdtest.c virsystemdmock.c
endif
viruritest_SOURCES = \
diff --git a/tests/testutils.h b/tests/testutils.h
index 3647487..bf5c701 100644
--- a/tests/testutils.h
+++ b/tests/testutils.h
@@ -25,6 +25,8 @@
# include <stdio.h>
# include "viralloc.h"
+# include "virfile.h"
+# include "virstring.h"
# define EXIT_AM_SKIP 77 /* tell Automake we're skipping a test */
# define EXIT_AM_HARDFAIL 99 /* tell Automake that the framework is broken */
diff --git a/tests/virsystemdmock.c b/tests/virsystemdmock.c
new file mode 100644
index 0000000..5f9cce6
--- /dev/null
+++ b/tests/virsystemdmock.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include "internal.h"
+
+#include <stdlib.h>
+
+#include <dbus/dbus.h>
+
+void dbus_connection_set_change_sigpipe(dbus_bool_t will_modify_sigpipe
ATTRIBUTE_UNUSED)
+{
+}
+
+dbus_bool_t dbus_threads_init_default(void)
+{
+ return 1;
+}
+
+DBusConnection *dbus_bus_get(DBusBusType type ATTRIBUTE_UNUSED,
+ DBusError *error ATTRIBUTE_UNUSED)
+{
+ return (DBusConnection *)0x1;
+}
+
+void dbus_connection_set_exit_on_disconnect(DBusConnection *connection ATTRIBUTE_UNUSED,
+ dbus_bool_t exit_on_disconnect
ATTRIBUTE_UNUSED)
+{
+}
+
+
+dbus_bool_t dbus_connection_set_watch_functions(DBusConnection *connection
ATTRIBUTE_UNUSED,
+ DBusAddWatchFunction add_function
ATTRIBUTE_UNUSED,
+ DBusRemoveWatchFunction remove_function
ATTRIBUTE_UNUSED,
+ DBusWatchToggledFunction toggled_function
ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED,
+ DBusFreeFunction free_data_function
ATTRIBUTE_UNUSED)
+{
+ return 1;
+}
+
+DBusMessage *dbus_connection_send_with_reply_and_block(DBusConnection *connection
ATTRIBUTE_UNUSED,
+ DBusMessage *message,
+ int timeout_milliseconds
ATTRIBUTE_UNUSED,
+ DBusError *error
ATTRIBUTE_UNUSED)
+{
+ DBusMessage *reply;
+
+ dbus_message_set_serial(message, 7);
+
+ if (getenv("FAIL_NO_SERVICE"))
+ reply = dbus_message_new_error(message,
+
"org.freedesktop.DBus.Error.ServiceUnknown",
+ "The name org.freedesktop.machine1 was not
provided by any .service files");
+ else
+ reply = dbus_message_new_method_return(message);
+
+ return reply;
+}
diff --git a/tests/virsystemdtest.c b/tests/virsystemdtest.c
new file mode 100644
index 0000000..3992722
--- /dev/null
+++ b/tests/virsystemdtest.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include "virsystemd.h"
+#include "virlog.h"
+#include "testutils.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+static int testCreateContainer(const void *opaque ATTRIBUTE_UNUSED)
+{
+ unsigned char uuid[VIR_UUID_BUFLEN] = {
+ 1, 1, 1, 1,
+ 2, 2, 2, 2,
+ 3, 3, 3, 3,
+ 4, 4, 4, 4
+ };
+ if (virSystemdCreateMachine("demo",
+ "lxc",
+ true,
+ uuid,
+ "/proc/123/root",
+ 123,
+ true,
+ "highpriority.slice") < 0) {
+ fprintf(stderr, "%s", "Failed to create LXC machine\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int testCreateMachine(const void *opaque ATTRIBUTE_UNUSED)
+{
+ unsigned char uuid[VIR_UUID_BUFLEN] = {
+ 1, 1, 1, 1,
+ 2, 2, 2, 2,
+ 3, 3, 3, 3,
+ 4, 4, 4, 4
+ };
+ if (virSystemdCreateMachine("demo",
+ "qemu",
+ false,
+ uuid,
+ NULL,
+ 123,
+ false,
+ NULL) < 0) {
+ fprintf(stderr, "%s", "Failed to create KVM machine\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int testCreateNoSystemd(const void *opaque ATTRIBUTE_UNUSED)
+{
+ unsigned char uuid[VIR_UUID_BUFLEN] = {
+ 1, 1, 1, 1,
+ 2, 2, 2, 2,
+ 3, 3, 3, 3,
+ 4, 4, 4, 4
+ };
+
+ setenv("FAIL_NO_SERVICE", "1", 1);
+
+ if (virSystemdCreateMachine("demo",
+ "qemu",
+ true,
+ uuid,
+ NULL,
+ 123,
+ false,
+ NULL) == 0) {
+ fprintf(stderr, "%s", "Unexpected create machine
success\n");
+ return -1;
+ }
+
+ virErrorPtr err = virGetLastError();
+
+ if (!err) {
+ fprintf(stderr, "No error raised");
+ return -1;
+ }
+
+ if (err->code == VIR_ERR_DBUS_SERVICE &&
+ STREQ(err->str2, "org.freedesktop.DBus.Error.ServiceUnknown"))
+ return 0;
+
+ fprintf(stderr, "Unexpected error code %d / message %s\n",
+ err->code, err->str2);
+ return -1;
+}
+
+static int
+mymain(void)
+{
+ int ret = 0;
+
+ if (virtTestRun("Test create container ", 1, testCreateContainer, NULL)
< 0)
+ ret = -1;
+ if (virtTestRun("Test create machine ", 1, testCreateMachine, NULL) <
0)
+ ret = -1;
+ if (virtTestRun("Test create nosystemd ", 1, testCreateNoSystemd, NULL)
< 0)
+ ret = -1;
+
+ return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virsystemdmock.so")
--
1.8.1.4