mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-04-28 09:35:22 +00:00
switch from libsystemd's dbus to dbus-1
This is purely so that we can do static linking. Linking against libsystemd makes that a challenge because while it's perfectly simple to do, distros tend not to provide a libsystemd.a. Tools that want to (a) link against liblxc and (b) have a statically linked binary to bind into a minimal container are ill served by this. So link against libdbus-1. .github/workflows/build.yml: switch to dbus-1. src/lxc/cgroups/cgfsng.c: replace the unpriv_systemd_create_scope(), start_scope, and enter_scope() systemd code with dbus-1 code. src/tests/oss-fuzz.sh: update from libsystemd-dev to libdbus-1-dev src/tests/oss-fuzz.sh: disable dbus .github/workflows/*: update from libsystemd-dev to libdbus-1-dev meson.build and meson_options.txt: switch from sd_bus to dbus lxc.spec.in: add dbus-1 to BuildRequires Signed-off-by: Serge Hallyn <serge@hallyn.com> Changelog: 03/13: use custom iter type so we can cleanup more easily... Changelog: 03/13: initialize each dbus_iter to { 0 } as mihalicyn suggested.
This commit is contained in:
parent
4ea0b361f1
commit
820d2a2b3a
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq gcc clang meson llvm
|
||||
sudo apt-get install -qq libapparmor-dev libcap-dev libseccomp-dev libselinux1-dev linux-libc-dev libpam0g-dev docbook2x libsystemd-dev
|
||||
sudo apt-get install -qq libapparmor-dev libcap-dev libseccomp-dev libselinux1-dev linux-libc-dev libpam0g-dev docbook2x libdbus-1-dev
|
||||
|
||||
- name: Compiler version
|
||||
env:
|
||||
|
3
.github/workflows/cifuzz.yml
vendored
3
.github/workflows/cifuzz.yml
vendored
@ -20,6 +20,9 @@ jobs:
|
||||
matrix:
|
||||
sanitizer: [address, undefined, memory]
|
||||
steps:
|
||||
- name: Install dependencies not yet listed in ubuntu pkg source
|
||||
run: |
|
||||
sudo apt-get install -qq libdbus-1-dev
|
||||
- name: Build Fuzzers (${{ matrix.sanitizer }})
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
|
2
.github/workflows/coverity.yml
vendored
2
.github/workflows/coverity.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq gcc clang meson
|
||||
sudo apt-get install -qq libapparmor-dev libcap-dev libseccomp-dev libselinux1-dev linux-libc-dev libpam0g-dev docbook2x libsystemd-dev
|
||||
sudo apt-get install -qq libapparmor-dev libcap-dev libseccomp-dev libselinux1-dev linux-libc-dev libpam0g-dev docbook2x libdbus-1-dev
|
||||
|
||||
- name: Compiler version
|
||||
run: |
|
||||
|
2
.github/workflows/sanitizers.sh
vendored
2
.github/workflows/sanitizers.sh
vendored
@ -18,7 +18,7 @@ apt-get install --yes --no-install-recommends \
|
||||
libpam0g-dev libseccomp-dev libselinux1-dev libtool linux-libc-dev \
|
||||
llvm lsb-release make openssl pkg-config python3-all-dev \
|
||||
python3-setuptools rsync squashfs-tools uidmap unzip uuid-runtime \
|
||||
wget xz-utils systemd-coredump libsystemd-dev
|
||||
wget xz-utils systemd-coredump libdbus-1-dev
|
||||
apt-get remove --yes lxc-utils liblxc-common liblxc1 liblxc-dev
|
||||
|
||||
ARGS="-Dprefix=/usr -Dtests=true -Dpam-cgroup=false -Dwerror=true -Dio-uring-event-loop=false -Db_lto_mode=default -Db_lundef=false"
|
||||
|
2
.github/workflows/sanitizers.yml
vendored
2
.github/workflows/sanitizers.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq gcc clang meson llvm
|
||||
sudo apt-get install -qq libapparmor-dev libcap-dev libseccomp-dev libselinux1-dev linux-libc-dev libpam0g-dev docbook2x libsystemd-dev
|
||||
sudo apt-get install -qq libapparmor-dev libcap-dev libseccomp-dev libselinux1-dev linux-libc-dev libpam0g-dev docbook2x libdbus-1-dev
|
||||
|
||||
- name: Compiler version
|
||||
env:
|
||||
|
@ -74,7 +74,7 @@ Requires: libcgroup
|
||||
%endif
|
||||
# Note for Suse. The "docbook2X" BuildRequires does properly
|
||||
# match docbook2x on Suse in a case insensitive manner
|
||||
BuildRequires: libcap libcap-devel docbook2X graphviz libxslt pkgconfig
|
||||
BuildRequires: libcap libcap-devel docbook2X graphviz libxslt pkgconfig dbus-1
|
||||
|
||||
#
|
||||
# Additional packages for openSUSE and SUSE
|
||||
|
58
meson.build
58
meson.build
@ -163,7 +163,7 @@ want_oss_fuzz = get_option('oss-fuzz')
|
||||
want_seccomp = get_option('seccomp')
|
||||
want_thread_safety = get_option('thread-safety')
|
||||
want_memfd_rexec = get_option('memfd-rexec')
|
||||
want_sd_bus = get_option('sd-bus')
|
||||
want_dbus = get_option('dbus')
|
||||
|
||||
srcconf.set_quoted('DEFAULT_CGROUP_PATTERN', cgrouppattern)
|
||||
if coverity
|
||||
@ -276,54 +276,13 @@ else
|
||||
srcconf.set10('HAVE_LIBURING', false)
|
||||
endif
|
||||
|
||||
if not want_sd_bus.disabled()
|
||||
has_sd_bus = true
|
||||
sd_bus_optional = want_sd_bus.auto()
|
||||
|
||||
libsystemd = dependency('libsystemd', required: not sd_bus_optional)
|
||||
if not libsystemd.found()
|
||||
if not sd_bus_optional
|
||||
error('missing required libsystemd dependency')
|
||||
endif
|
||||
|
||||
has_sd_bus = false
|
||||
endif
|
||||
|
||||
if not cc.has_header('systemd/sd-bus.h')
|
||||
if not sd_bus_optional
|
||||
error('libsystemd misses required systemd/sd-bus.h header')
|
||||
endif
|
||||
|
||||
has_sd_bus = false
|
||||
endif
|
||||
|
||||
if not cc.has_header('systemd/sd-event.h')
|
||||
if not sd_bus_optional
|
||||
error('libsystemd misses required systemd/sd-event.h header')
|
||||
endif
|
||||
|
||||
has_sd_bus = false
|
||||
endif
|
||||
|
||||
if not cc.has_function('sd_bus_call_method_async', prefix: '#include <systemd/sd-bus.h>', dependencies: libsystemd)
|
||||
if not sd_bus_optional
|
||||
error('libsystemd misses required sd_bus_call_method_async function')
|
||||
endif
|
||||
|
||||
has_sd_bus = false
|
||||
endif
|
||||
|
||||
if has_sd_bus
|
||||
liblxc_dependencies += libsystemd
|
||||
if want_oss_fuzz
|
||||
oss_fuzz_dependencies += libsystemd
|
||||
endif
|
||||
endif
|
||||
|
||||
srcconf.set10('HAVE_LIBSYSTEMD', has_sd_bus)
|
||||
if want_dbus
|
||||
libdbus = dependency('dbus-1', required: true)
|
||||
pkgconfig_libs += libdbus
|
||||
liblxc_dependencies += libdbus
|
||||
srcconf.set10('HAVE_DBUS', libdbus.found())
|
||||
else
|
||||
has_sd_bus = false
|
||||
srcconf.set10('HAVE_LIBSYSTEMD', false)
|
||||
srcconf.set10('HAVE_DBUS', false)
|
||||
endif
|
||||
|
||||
## Time EPOCH.
|
||||
@ -809,8 +768,6 @@ endforeach
|
||||
found_headers = []
|
||||
missing_headers = []
|
||||
foreach tuple: [
|
||||
['systemd/sd-bus.h'],
|
||||
['systemd/sd-event.h'],
|
||||
['sys/resource.h'],
|
||||
['sys/memfd.h'],
|
||||
['sys/personality.h'],
|
||||
@ -849,7 +806,6 @@ foreach tuple: [
|
||||
['pam'],
|
||||
['openssl'],
|
||||
['liburing'],
|
||||
['libsystemd'],
|
||||
]
|
||||
|
||||
if tuple.length() >= 2
|
||||
|
@ -27,9 +27,6 @@ option('systemd-unitdir', type : 'string', value: '',
|
||||
option('io-uring-event-loop', type: 'boolean', value: 'false',
|
||||
description: 'Enable io-uring based event loop')
|
||||
|
||||
option('sd-bus', type: 'feature', value: 'auto',
|
||||
description: 'Enable linking against sd-bus')
|
||||
|
||||
# was --{disable,enable}-doc in autotools
|
||||
option('man', type: 'boolean', value: 'true',
|
||||
description: 'build and install manpages')
|
||||
@ -123,3 +120,7 @@ option('memfd-rexec', type : 'boolean', value : 'true',
|
||||
|
||||
option('distrosysconfdir', type : 'string', value: '',
|
||||
description: 'relative path to sysconfdir for distro default configuration')
|
||||
|
||||
option('dbus', type: 'boolean', value: 'true',
|
||||
description: 'use dbus')
|
||||
|
||||
|
@ -59,9 +59,8 @@
|
||||
#include "strlcat.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBSYSTEMD
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#if HAVE_DBUS
|
||||
#include <dbus/dbus.h>
|
||||
#endif
|
||||
|
||||
lxc_log_define(cgfsng, cgroup);
|
||||
@ -963,102 +962,400 @@ static bool check_cgroup_dir_config(struct lxc_conf *conf)
|
||||
#define SYSTEMD_SCOPE_UNSUPP 1
|
||||
#define SYSTEMD_SCOPE_SUCCESS 0
|
||||
|
||||
#if HAVE_LIBSYSTEMD
|
||||
struct sd_callback_data {
|
||||
char *scope_name;
|
||||
bool job_complete;
|
||||
};
|
||||
|
||||
static int systemd_jobremoved_callback(sd_bus_message *m, void *userdata, sd_bus_error *error)
|
||||
{
|
||||
char *path, *unit, *result;
|
||||
struct sd_callback_data *sd_data = userdata;
|
||||
uint32_t id;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
|
||||
if (r < 0)
|
||||
return log_error(-1, "bad message received in callback: %s", strerror(-r));
|
||||
|
||||
if (sd_data->scope_name && strcmp(unit, sd_data->scope_name) != 0)
|
||||
return log_trace(-1, "unit was '%s' not '%s'", unit, sd_data->scope_name);
|
||||
if (strcmp(result, "done") == 0) {
|
||||
sd_data->job_complete = true;
|
||||
return log_info(1, "job is done");
|
||||
}
|
||||
return log_debug(0, "result was '%s', not 'done'", result);
|
||||
}
|
||||
|
||||
#if HAVE_DBUS
|
||||
#define DESTINATION "org.freedesktop.systemd1"
|
||||
#define PATH "/org/freedesktop/systemd1"
|
||||
#define INTERFACE "org.freedesktop.systemd1.Manager"
|
||||
#define MEMBER "StartTransientUnit"
|
||||
static bool start_scope(sd_bus *bus, struct sd_callback_data *data, struct sd_event *event)
|
||||
|
||||
static bool dbus_threads_initialized = false;
|
||||
|
||||
static void _dbus_connection_free(DBusConnection **conn) {
|
||||
if (*conn) {
|
||||
dbus_connection_unref(*conn);
|
||||
*conn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void _dbus_message_free(DBusMessage **message)
|
||||
{
|
||||
__attribute__((__cleanup__(sd_bus_error_free))) sd_bus_error error = SD_BUS_ERROR_NULL;;
|
||||
__attribute__((__cleanup__(sd_bus_message_unrefp))) sd_bus_message *reply = NULL;
|
||||
__attribute__((__cleanup__(sd_bus_message_unrefp))) sd_bus_message *m = NULL;
|
||||
char *path = NULL;
|
||||
int r;
|
||||
if (*message) {
|
||||
dbus_message_unref(*message);
|
||||
*message = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_message_new_method_call(bus, &m,
|
||||
DESTINATION, PATH, INTERFACE, MEMBER);
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed creating sdbus message");
|
||||
static bool systemd_cgroup_scope_ready(DBusConnection *connection, const char *scope_name)
|
||||
{
|
||||
__attribute__((__cleanup__(_dbus_message_free))) DBusMessage* message = NULL;
|
||||
DBusMessageIter iter;
|
||||
char *unit, *result;
|
||||
|
||||
r = sd_bus_message_append(m, "ss", data->scope_name, "fail");
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed setting systemd scope name");
|
||||
dbus_connection_read_write(connection, 0);
|
||||
message = dbus_connection_pop_message(connection);
|
||||
if (!message)
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "(sv)");
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed allocating sdbus msg properties");
|
||||
if (!dbus_message_is_signal(message, INTERFACE, "JobRemoved"))
|
||||
return false;
|
||||
|
||||
r = sd_bus_message_append(m, "(sv)(sv)(sv)",
|
||||
"PIDs", "au", 1, getpid(),
|
||||
"Delegate", "b", 1,
|
||||
"CollectMode", "s", "inactive-or-failed");
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed setting properties on sdbus message");
|
||||
TRACE("got a JobRemoved signal.");
|
||||
// "uoss" -> &id, &path, &unit, &result)
|
||||
if (!dbus_message_iter_init(message, &iter)) // id
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed closing sdbus message properties");
|
||||
if (!dbus_message_iter_next(&iter)) // path
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
r = sd_bus_message_append(m, "a(sa(sv))", 0);
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed appending aux boilerplate\n");
|
||||
if (!dbus_message_iter_next(&iter)) // unit
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
dbus_message_iter_get_basic(&iter, &unit);
|
||||
if (strcmp(unit, scope_name) != 0)
|
||||
return log_debug(false, "unit was '%s' not '%s'", unit, scope_name);
|
||||
|
||||
r = sd_bus_call(NULL, m, 0, &error, &reply);
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed sending sdbus message: %s", error.message);
|
||||
if (!dbus_message_iter_next(&iter)) // result
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
dbus_message_iter_get_basic(&iter, &result);
|
||||
if (strcmp(result, "done") != 0)
|
||||
return log_debug(false, "JobRemoved signal received, but result was '%s' not done", result);
|
||||
|
||||
/* Parse the response message */
|
||||
r = sd_bus_message_read(reply, "o", &path);
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed to parse response message: %s", strerror(-r));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Now spin up a mini-event-loop to wait for the "job completed" message */
|
||||
int tries = 0;
|
||||
struct dbus_iter {
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter *parent;
|
||||
bool set;
|
||||
};
|
||||
|
||||
while (!data->job_complete) {
|
||||
r = sd_event_run(event, 1000 * 1000);
|
||||
if (r < 0) {
|
||||
log_debug(stderr, "Error waiting for JobRemoved: %s\n", strerror(-r));
|
||||
continue;
|
||||
static bool open_dbus_container(DBusMessageIter *parent, int type, const char *sig, struct dbus_iter *sub)
|
||||
{
|
||||
if (!dbus_message_iter_open_container(parent, type, sig, &sub->iter))
|
||||
return false;
|
||||
sub->set = true;
|
||||
sub->parent = parent;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool close_dbus_container(struct dbus_iter *sub)
|
||||
{
|
||||
sub->set = false;
|
||||
return dbus_message_iter_close_container(sub->parent, &sub->iter);
|
||||
}
|
||||
|
||||
static void abandon_dbus_container(struct dbus_iter *sub)
|
||||
{
|
||||
if (!sub->set)
|
||||
return;
|
||||
dbus_message_iter_abandon_container(sub->parent, &sub->iter);
|
||||
sub->set = false;
|
||||
}
|
||||
|
||||
static bool dbus_append_array(struct DBusMessageIter *parent, const uint32_t *value, unsigned int len)
|
||||
{
|
||||
__attribute__((__cleanup__(abandon_dbus_container))) struct dbus_iter iter_array = { 0 };
|
||||
unsigned int i;
|
||||
|
||||
if (!open_dbus_container(parent, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32_AS_STRING, &iter_array))
|
||||
return log_debug(false, "Dbus error opening array container");
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!dbus_message_iter_append_basic(&iter_array.iter, DBUS_TYPE_UINT32, &(value[i]))) {
|
||||
return log_debug(false, "Dbus error appending u32 to array");
|
||||
}
|
||||
if (data->job_complete || tries == 5)
|
||||
}
|
||||
|
||||
if (!close_dbus_container(&iter_array))
|
||||
return log_debug(false, "Dbus error closing array container");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// systemd wants ssa(sv)a(sa(sv)) ... so after the a(sv) we have to
|
||||
// append an empty a(sa(sv)).
|
||||
static bool sd_boilerplate(DBusMessageIter *iter)
|
||||
{
|
||||
DBusMessageIter array_iter;
|
||||
|
||||
if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
|
||||
DBUS_STRUCT_BEGIN_CHAR_AS_STRING
|
||||
DBUS_TYPE_STRING_AS_STRING
|
||||
DBUS_TYPE_ARRAY_AS_STRING
|
||||
DBUS_STRUCT_BEGIN_CHAR_AS_STRING
|
||||
DBUS_TYPE_STRING_AS_STRING
|
||||
DBUS_TYPE_VARIANT_AS_STRING
|
||||
DBUS_STRUCT_END_CHAR_AS_STRING
|
||||
DBUS_STRUCT_END_CHAR_AS_STRING,
|
||||
&array_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!dbus_message_iter_close_container(iter, &array_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool start_scope(DBusConnection *connection, const char *scope_name)
|
||||
{
|
||||
const char *fail_name = "fail",
|
||||
*pids_name = "PIDs",
|
||||
*delegate_str = "Delegate",
|
||||
*collect_str = "CollectMode",
|
||||
*inactive_str = "inactive-or-failed";
|
||||
__attribute__((__cleanup__(abandon_dbus_container))) struct dbus_iter array_iter = { 0 };
|
||||
__attribute__((__cleanup__(abandon_dbus_container))) struct dbus_iter struct_iter = { 0 };
|
||||
__attribute__((__cleanup__(abandon_dbus_container))) struct dbus_iter v_iter = { 0 };
|
||||
DBusMessageIter iter;
|
||||
DBusPendingCall* pending;
|
||||
__attribute__((__cleanup__(_dbus_message_free))) DBusMessage* message = NULL;
|
||||
uint32_t pid_uint;
|
||||
dbus_bool_t bool_true = true;
|
||||
|
||||
message = dbus_message_new_method_call(DESTINATION, PATH, INTERFACE, "StartTransientUnit");
|
||||
if (!message)
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
dbus_message_iter_init_append (message, &iter);
|
||||
// ss scope_name, fail
|
||||
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope_name))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail_name))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
// a (sv):
|
||||
// "PIDs", "au", getpid(),
|
||||
// "Delegate", b, 1
|
||||
// CollectMode, s, inactive-or-failed
|
||||
if (!open_dbus_container(&iter, DBUS_TYPE_ARRAY,
|
||||
DBUS_STRUCT_BEGIN_CHAR_AS_STRING
|
||||
DBUS_TYPE_STRING_AS_STRING
|
||||
DBUS_TYPE_VARIANT_AS_STRING
|
||||
DBUS_STRUCT_END_CHAR_AS_STRING,
|
||||
&array_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
// "PIDs", "au", getpid()
|
||||
if (!open_dbus_container(&array_iter.iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!dbus_message_iter_append_basic(&struct_iter.iter, DBUS_TYPE_STRING, &pids_name))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!open_dbus_container(&struct_iter.iter, DBUS_TYPE_VARIANT,
|
||||
DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT32_AS_STRING,
|
||||
&v_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
pid_uint = getpid();
|
||||
if (!dbus_append_array(&v_iter.iter, &pid_uint, 1))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!close_dbus_container(&v_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!close_dbus_container(&struct_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
// "Delegate", b, 1
|
||||
if (!open_dbus_container(&array_iter.iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!dbus_message_iter_append_basic(&struct_iter.iter, DBUS_TYPE_STRING, &delegate_str))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!open_dbus_container(&struct_iter.iter, DBUS_TYPE_VARIANT, DBUS_TYPE_BOOLEAN_AS_STRING, &v_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!dbus_message_iter_append_basic(&v_iter.iter, DBUS_TYPE_BOOLEAN, &bool_true))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!close_dbus_container(&v_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!close_dbus_container(&struct_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
// CollectMode, s, inactive-or-failed
|
||||
if (!open_dbus_container(&array_iter.iter, DBUS_TYPE_STRUCT, NULL, &struct_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!dbus_message_iter_append_basic(&struct_iter.iter, DBUS_TYPE_STRING, &collect_str))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!open_dbus_container(&struct_iter.iter, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &v_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!dbus_message_iter_append_basic(&v_iter.iter, DBUS_TYPE_STRING, &inactive_str))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!close_dbus_container(&v_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!close_dbus_container(&struct_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!close_dbus_container(&array_iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!sd_boilerplate(&iter))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
// send it
|
||||
if (!dbus_connection_send_with_reply(connection, message, &pending, -1))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!pending)
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
dbus_connection_flush(connection);
|
||||
|
||||
dbus_pending_call_block(pending);
|
||||
|
||||
dbus_pending_call_unref(pending);
|
||||
|
||||
// Wait on a signal telling us the async scope request is handled
|
||||
// TODO add a timeout
|
||||
while (true) {
|
||||
if (systemd_cgroup_scope_ready(connection, scope_name))
|
||||
break;
|
||||
if (r > 0) {
|
||||
log_trace(stderr, "Debug: we processed an event (%d), but not the one we wanted\n", r);
|
||||
continue;
|
||||
}
|
||||
if (r == 0) // timeout
|
||||
tries++;
|
||||
nanosleep((const struct timespec[]){{0, 1000}}, NULL);
|
||||
continue;
|
||||
}
|
||||
if (!data->job_complete) {
|
||||
return log_error(false, "Error: %s job was never removed", data->scope_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static DBusConnection *open_systemd(void)
|
||||
{
|
||||
__do_free char *user_bus = NULL;
|
||||
char *s = NULL;
|
||||
DBusMessageIter iter;
|
||||
DBusError dbus_error;
|
||||
DBusConnection *connection = NULL;
|
||||
__attribute__((__cleanup__(_dbus_message_free))) DBusMessage* message = NULL;
|
||||
DBusPendingCall* pending;
|
||||
|
||||
dbus_error_init(&dbus_error);
|
||||
user_bus = strdup("unix:path=/run/user/1000/bus"); // TODO get from $DBUS_SESSION_BUS_ADDRESS
|
||||
if (!user_bus) {
|
||||
return log_error(NULL, "Failed opening user dbus");
|
||||
}
|
||||
|
||||
connection = dbus_connection_open(user_bus, &dbus_error);
|
||||
if (!connection) {
|
||||
DEBUG("Failed opening dbus connection: %s: %s",
|
||||
dbus_error.name, dbus_error.message);
|
||||
dbus_error_free(&dbus_error);
|
||||
return NULL;
|
||||
}
|
||||
dbus_error_free(&dbus_error);
|
||||
|
||||
TRACE("Saying hello to systemd");
|
||||
//message = dbus_message_new_method_call(DESTINATION, PATH, INTERFACE, "Hello");
|
||||
message = dbus_message_new_method_call("org.freedesktop.DBus",
|
||||
"/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus",
|
||||
"Hello");
|
||||
if (!message) {
|
||||
ERROR("Failed saying hello to systemd");
|
||||
goto bad;
|
||||
}
|
||||
if (!dbus_connection_send_with_reply(connection, message, &pending, -1)) {
|
||||
ERROR("Failed sending hello message to systemd");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!pending) {
|
||||
ERROR("pending was NULL after saying hello to systemd");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
dbus_connection_flush(connection);
|
||||
|
||||
dbus_message_unref(message);
|
||||
message = NULL;
|
||||
|
||||
TRACE("Waiting systemd Hello for reply");
|
||||
|
||||
dbus_pending_call_block(pending);
|
||||
|
||||
message = dbus_pending_call_steal_reply(pending);
|
||||
if (!message) {
|
||||
ERROR("Failed stealing reply from systemd");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
dbus_pending_call_unref(pending);
|
||||
|
||||
if (!dbus_message_iter_init(message, &iter)) {
|
||||
ERROR("Failed parsing reply from systemd");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&iter)) {
|
||||
ERROR("systemd's reply was %d not DBUS_TYPE_STRING (%d)", dbus_message_iter_get_arg_type(&iter), DBUS_TYPE_STRING);
|
||||
goto bad;
|
||||
}
|
||||
dbus_message_iter_get_basic(&iter, &s);
|
||||
TRACE("reply came from systemd: '%s'", s);
|
||||
|
||||
return connection;
|
||||
|
||||
bad:
|
||||
dbus_connection_unref(connection);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool enter_scope(char *scope_name, pid_t pid)
|
||||
{
|
||||
const char *init_name = "/init";
|
||||
__attribute__((__cleanup__(_dbus_connection_free))) DBusConnection *connection = NULL;
|
||||
__attribute__((__cleanup__(_dbus_message_free))) DBusMessage* message = NULL;
|
||||
DBusMessageIter iter;
|
||||
DBusPendingCall* pending;
|
||||
uint32_t pid_uint = pid;
|
||||
|
||||
if (!dbus_threads_initialized) {
|
||||
/* tell dbus to do struct locking for thread safety */
|
||||
dbus_threads_init_default();
|
||||
dbus_threads_initialized = true;
|
||||
}
|
||||
|
||||
TRACE("enter_scope: calling open_systemd");
|
||||
connection = open_systemd();
|
||||
if (connection == NULL)
|
||||
return log_error(false, "Failed opening dbus connection");
|
||||
|
||||
TRACE("enter_scope: subscribing to signals");
|
||||
message = dbus_message_new_method_call(DESTINATION, PATH, INTERFACE, "AttachProcessesToUnit");
|
||||
if (!message)
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
dbus_message_iter_init_append (message, &iter);
|
||||
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope_name))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &init_name))
|
||||
return log_debug(false, "Dbus error...");
|
||||
if (!dbus_append_array(&iter, &pid_uint, 1))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!dbus_connection_send_with_reply(connection, message, &pending, DBUS_TIMEOUT_INFINITE))
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
if (!pending)
|
||||
return log_debug(false, "Dbus error...");
|
||||
|
||||
dbus_connection_flush(connection);
|
||||
|
||||
dbus_pending_call_block(pending);
|
||||
|
||||
dbus_message_unref(message);
|
||||
message = NULL;
|
||||
|
||||
message = dbus_pending_call_steal_reply(pending);
|
||||
if (!message)
|
||||
return log_debug(false, "Dbus error - NULL reply");
|
||||
|
||||
dbus_pending_call_unref(pending);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1079,73 +1376,6 @@ static bool string_pure_unified_system(char *contents)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only call get_current_unified_cgroup() when we are in a pure
|
||||
* unified (v2-only) cgroup
|
||||
*/
|
||||
static char *get_current_unified_cgroup(void)
|
||||
{
|
||||
__do_free char *buf = NULL;
|
||||
__do_free_string_list char **list = NULL;
|
||||
char *p;
|
||||
|
||||
buf = read_file_at(-EBADF, "/proc/self/cgroup", PROTECT_OPEN, 0);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
if (!string_pure_unified_system(buf))
|
||||
return NULL;
|
||||
|
||||
// 0::/user.slice/user-1000.slice/session-136.scope
|
||||
// Get past the "0::"
|
||||
p = buf;
|
||||
if (strnequal(p, "0::", STRLITERALLEN("0::")))
|
||||
p += STRLITERALLEN("0::");
|
||||
|
||||
return strdup(p);
|
||||
}
|
||||
|
||||
static bool pure_unified_system(void)
|
||||
{
|
||||
__do_free char *buf = NULL;
|
||||
|
||||
buf = read_file_at(-EBADF, "/proc/self/cgroup", PROTECT_OPEN, 0);
|
||||
if (!buf)
|
||||
return false;
|
||||
|
||||
return string_pure_unified_system(buf);
|
||||
}
|
||||
|
||||
#define MEMBER_JOIN "AttachProcessesToUnit"
|
||||
static bool enter_scope(char *scope_name, pid_t pid)
|
||||
{
|
||||
__attribute__((__cleanup__(sd_bus_unrefp))) sd_bus *bus = NULL;
|
||||
__attribute__((__cleanup__(sd_bus_error_free))) sd_bus_error error = SD_BUS_ERROR_NULL;;
|
||||
__attribute__((__cleanup__(sd_bus_message_unrefp))) sd_bus_message *reply = NULL;
|
||||
__attribute__((__cleanup__(sd_bus_message_unrefp))) sd_bus_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = sd_bus_open_user(&bus);
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed to connect to user bus: %s", strerror(-r));
|
||||
|
||||
r = sd_bus_message_new_method_call(bus, &m,
|
||||
DESTINATION, PATH, INTERFACE, MEMBER_JOIN);
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed creating sdbus message");
|
||||
|
||||
r = sd_bus_message_append(m, "ssau", scope_name, "/init", 1, pid);
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed setting systemd scope name");
|
||||
|
||||
|
||||
r = sd_bus_call(NULL, m, 0, &error, &reply);
|
||||
if (r < 0)
|
||||
return log_error(false, "Failed sending sdbus message: %s", error.message);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool enable_controllers_delegation(int fd_dir, char *cg)
|
||||
{
|
||||
__do_free char *rbuf = NULL;
|
||||
@ -1182,6 +1412,43 @@ static bool enable_controllers_delegation(int fd_dir, char *cg)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only call get_current_unified_cgroup() when we are in a pure
|
||||
* unified (v2-only) cgroup
|
||||
*/
|
||||
static char *get_current_unified_cgroup(void)
|
||||
{
|
||||
__do_free char *buf = NULL;
|
||||
__do_free_string_list char **list = NULL;
|
||||
char *p;
|
||||
|
||||
buf = read_file_at(-EBADF, "/proc/self/cgroup", PROTECT_OPEN, 0);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
if (!string_pure_unified_system(buf))
|
||||
return NULL;
|
||||
|
||||
// 0::/user.slice/user-1000.slice/session-136.scope
|
||||
// Get past the "0::"
|
||||
p = buf;
|
||||
if (strnequal(p, "0::", STRLITERALLEN("0::")))
|
||||
p += STRLITERALLEN("0::");
|
||||
|
||||
return strdup(p);
|
||||
}
|
||||
|
||||
static bool pure_unified_system(void)
|
||||
{
|
||||
__do_free char *buf = NULL;
|
||||
|
||||
buf = read_file_at(-EBADF, "/proc/self/cgroup", PROTECT_OPEN, 0);
|
||||
if (!buf)
|
||||
return false;
|
||||
|
||||
return string_pure_unified_system(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* systemd places us in say .../lxc-1.scope. We create lxc-1.scope/init,
|
||||
* move ourselves to there, then enable controllers in lxc-1.scope
|
||||
@ -1212,19 +1479,24 @@ static bool move_and_delegate_unified(char *parent_cgroup)
|
||||
return enable_controllers_delegation(fd_parent, parent_cgroup);
|
||||
}
|
||||
|
||||
#define JOBREMOVED_RULE \
|
||||
"type='signal',sender='" DESTINATION "',path='" PATH \
|
||||
"',interface='" INTERFACE "',member='JobRemoved'"
|
||||
|
||||
static int unpriv_systemd_create_scope(struct cgroup_ops *ops, struct lxc_conf *conf)
|
||||
{
|
||||
__do_free char *full_scope_name = NULL;
|
||||
__do_free char *user_bus = NULL;
|
||||
__do_free char *fs_cg_path = NULL;
|
||||
sd_event *event = NULL;
|
||||
__attribute__((__cleanup__(sd_bus_unrefp))) sd_bus *bus = NULL; // free the bus before the names it references, just to be sure
|
||||
struct sd_callback_data sd_data;
|
||||
int idx = 0;
|
||||
size_t len;
|
||||
int r;
|
||||
__attribute__((__cleanup__(_dbus_message_free))) DBusMessage* message = NULL;
|
||||
DBusError dbus_error;
|
||||
int idx = 0, r;
|
||||
__attribute__((__cleanup__(_dbus_connection_free))) DBusConnection *connection = NULL;
|
||||
unsigned int len;
|
||||
|
||||
if (geteuid() == 0)
|
||||
return log_info(SYSTEMD_SCOPE_UNSUPP, "Running privileged, not using a systemd unit");
|
||||
|
||||
// Pure_unified_layout() can't be used as that info is not yet setup. At
|
||||
// the same time, we don't want to calculate current cgroups until after
|
||||
// we optionally enter a new systemd user scope. So let's just do a quick
|
||||
@ -1233,32 +1505,37 @@ static int unpriv_systemd_create_scope(struct cgroup_ops *ops, struct lxc_conf *
|
||||
if (!pure_unified_system())
|
||||
return log_info(SYSTEMD_SCOPE_UNSUPP, "Not in unified layout, not using a systemd unit");
|
||||
|
||||
r = sd_bus_open_user(&bus);
|
||||
if (r < 0)
|
||||
return log_error(SYSTEMD_SCOPE_FAILED, "Failed to connect to user bus: %s", strerror(-r));
|
||||
if (!dbus_threads_initialized) {
|
||||
/* tell dbus to do struct locking for thread safety */
|
||||
dbus_threads_init_default();
|
||||
dbus_threads_initialized = true;
|
||||
}
|
||||
|
||||
r = sd_bus_call_method_async(bus, NULL, DESTINATION, PATH, INTERFACE, "Subscribe", NULL, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error(SYSTEMD_SCOPE_FAILED, "Failed to subscribe to signals: %s", strerror(-r));
|
||||
connection = open_systemd();
|
||||
if (connection == NULL)
|
||||
return log_error(false, "Failed opening dbus connection");
|
||||
|
||||
sd_data.job_complete = false;
|
||||
sd_data.scope_name = NULL;
|
||||
r = sd_bus_match_signal(bus,
|
||||
NULL, // no slot
|
||||
DESTINATION, PATH, INTERFACE, "JobRemoved",
|
||||
systemd_jobremoved_callback, &sd_data);
|
||||
if (r < 0)
|
||||
return log_error(SYSTEMD_SCOPE_FAILED, "Failed to register systemd event loop signal handler: %s", strerror(-r));
|
||||
message = dbus_message_new_method_call(DESTINATION, PATH, INTERFACE, "Subscribe");
|
||||
if (!message)
|
||||
return log_error(SYSTEMD_SCOPE_FAILED, "Failed subscribing to dbus signals");
|
||||
|
||||
// NEXT: create and attach event
|
||||
r = sd_event_new(&event);
|
||||
if (r < 0)
|
||||
return log_error(SYSTEMD_SCOPE_FAILED, "Failed allocating new event: %s\n", strerror(-r));
|
||||
r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
|
||||
if (r < 0) {
|
||||
// bus won't clean up event since the attach failed
|
||||
sd_event_unrefp(&event);
|
||||
return log_error(SYSTEMD_SCOPE_FAILED, "Failed attaching event: %s\n", strerror(-r));
|
||||
dbus_error_init(&dbus_error);
|
||||
|
||||
if (!dbus_connection_send(connection, message, NULL)) {
|
||||
INFO("error sending signal subscribe message");
|
||||
return log_error(SYSTEMD_SCOPE_FAILED, "error sending signal subscribe message");
|
||||
}
|
||||
|
||||
dbus_connection_flush(connection);
|
||||
|
||||
// subscribe to JobRemoved signal from systemd. The start_scope()
|
||||
// function will listen for this over connection.
|
||||
dbus_bus_add_match(connection, JOBREMOVED_RULE, &dbus_error);
|
||||
dbus_connection_flush(connection);
|
||||
if (dbus_error_is_set(&dbus_error)) {
|
||||
ERROR("unpriv_systemd_create_scope: MATCH ERROR (%s)", dbus_error.message);
|
||||
dbus_error_free(&dbus_error);
|
||||
return SYSTEMD_SCOPE_FAILED;
|
||||
}
|
||||
|
||||
// "lxc-" + (conf->name) + "-NN" + ".scope" + '\0'
|
||||
@ -1268,11 +1545,11 @@ static int unpriv_systemd_create_scope(struct cgroup_ops *ops, struct lxc_conf *
|
||||
return syserror("Out of memory");
|
||||
|
||||
do {
|
||||
TRACE("unpriv_systemd_create_scope: trying idx %d", idx);
|
||||
r = strnprintf(full_scope_name, len, "lxc-%s-%d.scope", conf->name, idx);
|
||||
if (r < 0)
|
||||
return log_error_errno(-1, errno, "Failed to build scope name for \"%s\"", conf->name);
|
||||
sd_data.scope_name = full_scope_name;
|
||||
if (start_scope(bus, &sd_data, event)) {
|
||||
return log_error_errno(SYSTEMD_SCOPE_FAILED, errno, "Failed to build scope name for \"%s\"", conf->name);
|
||||
if (start_scope(connection, full_scope_name)) {
|
||||
conf->cgroup_meta.systemd_scope = get_current_unified_cgroup();
|
||||
if (!conf->cgroup_meta.systemd_scope)
|
||||
return log_trace(SYSTEMD_SCOPE_FAILED, "Out of memory");
|
||||
@ -1286,13 +1563,14 @@ static int unpriv_systemd_create_scope(struct cgroup_ops *ops, struct lxc_conf *
|
||||
|
||||
return SYSTEMD_SCOPE_FAILED; // failed, let's try old-school after all
|
||||
}
|
||||
#else /* !HAVE_LIBSYSTEMD */
|
||||
#else /* HAVE_DBUS */
|
||||
|
||||
static int unpriv_systemd_create_scope(struct cgroup_ops *ops, struct lxc_conf *conf)
|
||||
{
|
||||
TRACE("unpriv_systemd_create_scope: no systemd support");
|
||||
return SYSTEMD_SCOPE_UNSUPP; // not supported
|
||||
return SYSTEMD_SCOPE_UNSUPP;
|
||||
}
|
||||
#endif /* HAVE_LIBSYSTEMD */
|
||||
|
||||
#endif /* HAVE_DBUS */
|
||||
|
||||
// Return a duplicate of cgroup path @cg without leading /, so
|
||||
// that caller can own+free it and be certain it's not abspath.
|
||||
@ -2558,12 +2836,10 @@ static int cgroup_attach_move_into_leaf(const struct lxc_conf *conf,
|
||||
__do_close int sk = *sk_fd, target_fd0 = -EBADF, target_fd1 = -EBADF;
|
||||
char pidstr[INTTYPE_TO_STRLEN(int64_t) + 1];
|
||||
size_t pidstr_len;
|
||||
#if HAVE_LIBSYSTEMD
|
||||
__do_free char *scope = NULL;
|
||||
#endif
|
||||
ssize_t ret;
|
||||
|
||||
#if HAVE_LIBSYSTEMD
|
||||
#if HAVE_DBUS
|
||||
scope = lxc_cmd_get_systemd_scope(conf->name, lxcpath);
|
||||
if (scope) {
|
||||
TRACE("%s:%s is running under systemd-created scope '%s'. Attaching...", lxcpath, conf->name, scope);
|
||||
@ -2575,6 +2851,7 @@ static int cgroup_attach_move_into_leaf(const struct lxc_conf *conf,
|
||||
TRACE("%s:%s is not running under a systemd-created scope", lxcpath, conf->name);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unprivileged) {
|
||||
ret = lxc_abstract_unix_recv_two_fds(sk, &target_fd0, &target_fd1);
|
||||
if (ret < 0)
|
||||
|
@ -24,7 +24,7 @@ mkdir -p $OUT
|
||||
apt-get update -qq
|
||||
apt-get install --yes --no-install-recommends \
|
||||
build-essential docbook2x doxygen git \
|
||||
wget xz-utils systemd-coredump pkgconf libsystemd-dev
|
||||
wget xz-utils systemd-coredump pkgconf libdbus-1-dev
|
||||
apt-get remove --yes lxc-utils liblxc-common liblxc1 liblxc-dev
|
||||
|
||||
# make sure we have a new enough meson version
|
||||
@ -47,6 +47,7 @@ meson setup san_build \
|
||||
-Db_lto=false \
|
||||
-Db_pie=false \
|
||||
-Dthread-safety=false \
|
||||
-Ddbus=false \
|
||||
-Doss-fuzz=true
|
||||
ninja -C san_build -v
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user