[libvirt] [PATCH 2/2] Add API for calling systemd-machined's DBus API

Daniel P. Berrange berrange at redhat.com
Thu Jul 18 13:27:10 UTC 2013


From: "Daniel P. Berrange" <berrange at 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 at 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 at 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 at 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




More information about the libvir-list mailing list