From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> 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@xxxxxxxxxx> --- .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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ + +#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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list