diff --git a/add-unit-test-for-dbus-broker.patch b/add-unit-test-for-dbus-broker.patch new file mode 100644 index 0000000..9effae2 --- /dev/null +++ b/add-unit-test-for-dbus-broker.patch @@ -0,0 +1,578 @@ +From 2c51932c86b5dfc3a81b9203875ca45216f45c3a Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Fri, 10 Mar 2023 11:46:16 +0800 +Subject: [PATCH 2/2] add unit test for dbus-broker + +--- + test/dbus/meson.build | 6 ++ + test/dbus/test-reexecute.c | 121 +++++++++++++++++++++++++++++++ + test/dbus/test-serialize.c | 144 +++++++++++++++++++++++++++++++++++++ + test/dbus/util-broker.c | 138 ++++++++++++++++++++++++++++++----- + test/dbus/util-broker.h | 12 +++- + 5 files changed, 401 insertions(+), 20 deletions(-) + create mode 100755 test/dbus/test-reexecute.c + create mode 100755 test/dbus/test-serialize.c + +diff --git a/test/dbus/meson.build b/test/dbus/meson.build +index c111283..c2d1d79 100644 +--- a/test/dbus/meson.build ++++ b/test/dbus/meson.build +@@ -67,6 +67,12 @@ test('Client Lifetime', test_lifetime) + test_matches = executable('test-matches', ['test-matches.c'], dependencies: [ dep_test ]) + test('Signals and Matches', test_matches) + ++test_serialize = executable('test-serialize', ['test-serialize.c'], dependencies: [ dep_test ]) ++test('Serialize and Deserialize', test_serialize) ++ ++test_reexecute = executable('test-reexecute', ['test-reexecute.c'], dependencies: [ dep_test ]) ++test('Reexecute', test_reexecute) ++ + if use_reference_test + dbus_bin = dep_dbus.get_pkgconfig_variable('bindir') + '/dbus-daemon' + +diff --git a/test/dbus/test-reexecute.c b/test/dbus/test-reexecute.c +new file mode 100755 +index 0000000..43025e7 +--- /dev/null ++++ b/test/dbus/test-reexecute.c +@@ -0,0 +1,121 @@ ++/* ++ * Reexecute Tests ++ */ ++ ++#undef NDEBUG ++#include ++#include ++#include ++#include "util-broker.h" ++#include "util/string.h" ++#include "syslog.h" ++ ++#define PATH_LENGTH_MAX 4096 ++#define TEST_ARG_MAX 12 ++ ++static void test_send_reexecute() { ++ Broker *broker = NULL; ++ int r, sync_pairs[2], sync_value; ++ ++ r = socketpair(AF_UNIX, SOCK_STREAM, 0, sync_pairs); ++ assert(r >= 0); ++ ++ util_broker_new(&broker); ++ broker->test_reexec = true; ++ util_broker_spawn(broker); ++ pid_t pid = fork(); ++ if (pid != 0) { ++ close(sync_pairs[1]); ++ /* Wait the child process exits. */ ++ read(sync_pairs[0], &sync_value, sizeof(sync_value)); ++ util_broker_terminate(broker); ++ util_broker_free(broker); ++ return; ++ } ++ ++ close(sync_pairs[0]); ++ _c_cleanup_(sd_bus_flush_close_unrefp) sd_bus *server = NULL, *cmd_bus = NULL; ++ _c_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; ++ const char *unique_name = NULL, *owner = NULL, *reply_str = NULL; ++ ++ /* RequestName before reexecuting. */ ++ { ++ util_broker_connect(broker, &server); ++ r = sd_bus_get_unique_name(server, &unique_name); ++ c_assert(r >= 0); ++ r = sd_bus_call_method(server, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", ++ "RequestName", NULL, NULL, "su", "com.example.foo", 0); ++ c_assert(r >= 0); ++ } ++ ++ /* Make broker reexecute. */ ++ { ++ util_broker_connect(broker, &cmd_bus); ++ r = sd_bus_call_method(cmd_bus, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", ++ "Reexecute", NULL, &reply, ""); ++ c_assert(r >= 0); ++ ++ r = sd_bus_message_read(reply, "s", &reply_str); ++ c_assert(r >= 0); ++ c_assert(!strcmp(reply_str, "OK")); ++ ++ sd_bus_flush_close_unref(cmd_bus); ++ } ++ ++ /* Wait to make sure dbus-broker has exited. */ ++ sleep(1); ++ ++ /* GetNameOwner after reexecuting. */ ++ { ++ r = sd_bus_call_method(server, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", ++ "GetNameOwner", NULL, &reply, "s", "com.example.foo"); ++ c_assert(r >= 0); ++ ++ r = sd_bus_message_read(reply, "s", &owner); ++ c_assert(r >= 0); ++ c_assert(!strcmp(owner, unique_name)); ++ syslog(LOG_INFO, "Got the right owner!"); ++ } ++ ++ /* Clean. ++ * We can't call util_broker_terminate, because the forked process ++ * is single thread, util_broker_thread doesn't exist and can't do ++ * this for us. ++ */ ++ ++ broker->pipe_fds[0] = c_close(broker->pipe_fds[0]); ++ broker->pipe_fds[1] = c_close(broker->pipe_fds[1]); ++ broker->listener_fd = c_close(broker->listener_fd); ++ util_broker_free(broker); ++ write(sync_pairs[1], &sync_value, sizeof(sync_value)); ++} ++ ++static void test_generate_args_string() { ++ char *args[TEST_ARG_MAX]; ++ for (int i = 0; i < TEST_ARG_MAX; i++) { ++ args[i] = NULL; ++ } ++ int i = 0; ++ generate_args_string(false, args, TEST_ARG_MAX, &i, "--log", "1"); ++ c_assert(i == 0 && args[i] == NULL); ++ generate_args_string(true, args, TEST_ARG_MAX, &i, "--controller", "2"); ++ c_assert(i == 2 && !strcmp(args[0], "--controller") && !strcmp(args[1], "2")); ++ generate_args_string(true, args, TEST_ARG_MAX, &i, "--machine-id", "3"); ++ c_assert(i == 4 && !strcmp(args[2], "--machine-id") && !strcmp(args[3], "3")); ++ generate_args_string(true, args, TEST_ARG_MAX, &i, "--max-bytes", "123456"); ++ c_assert(i == 6 && !strcmp(args[4], "--max-bytes") && !strcmp(args[5], "123456")); ++ generate_args_string(true, args, TEST_ARG_MAX, &i, "--max-fds", "12"); ++ c_assert(i == 8 && !strcmp(args[6], "--max-fds") && !strcmp(args[7], "12")); ++ generate_args_string(true, args, TEST_ARG_MAX, &i, "--max-matches", "12345abcde"); ++ c_assert(i == 10 && !strcmp(args[8], "--max-matches") && !strcmp(args[9], "12345abcde")); ++ generate_args_string(true, args, TEST_ARG_MAX, &i, "--reexec", "13"); ++ c_assert(i == 10 && args[10] == NULL && args[11] == NULL); ++} ++ ++int main(int argc, char **argv) { ++ test_generate_args_string(); ++ /* Reexecute can only be run under privileged user */ ++ if (getuid() == 0) ++ test_send_reexecute(); ++ return 0; ++} +diff --git a/test/dbus/test-serialize.c b/test/dbus/test-serialize.c +new file mode 100755 +index 0000000..e287647 +--- /dev/null ++++ b/test/dbus/test-serialize.c +@@ -0,0 +1,144 @@ ++/* ++ * Serialize and Deserialize Tests ++ */ ++ ++#undef NDEBUG ++#include ++#include ++#include "util/serialize.h" ++#include "util/string.h" ++ ++static void test_basic() { ++ FILE *f = NULL; ++ _c_cleanup_(c_freep) char *buf = malloc(LINE_LENGTH_MAX); ++ int mem_fd; ++ ++ mem_fd = state_file_init(&f); ++ c_assert(mem_fd > 0); ++ ++ c_assert(!serialize_basic(f, "test_u", "%u", 1234)); ++ c_assert(!serialize_basic(f, "test_d", "%d", -1234)); ++ c_assert(!serialize_basic(f, "test_s", "%s", "test")); ++ c_assert(!serialize_basic(f, "test_uds", "%u;%d;%s", 1234, -1234, "test")); ++ ++ fseeko(f, 0, SEEK_SET); ++ ++ if (fgets(buf, LINE_LENGTH_MAX, f) != NULL) ++ c_assert(!strcmp(buf, "test_u=1234\n")); ++ if (fgets(buf, LINE_LENGTH_MAX, f) != NULL) ++ c_assert(!strcmp(buf, "test_d=-1234\n")); ++ if (fgets(buf, LINE_LENGTH_MAX, f) != NULL) ++ c_assert(!strcmp(buf, "test_s=test\n")); ++ if (fgets(buf, LINE_LENGTH_MAX, f) != NULL) ++ c_assert(!strcmp(buf, "test_uds=1234;-1234;test\n")); ++ ++ c_assert(!fgets(buf, LINE_LENGTH_MAX, f)); ++} ++ ++static void test_extract_world_inlist() { ++ char *list1 = ";;a;b;;c;;;"; ++ char *list2 = "a;;bb;ccc;;;"; ++ char *res1[] = {"a", "b", "c"}; ++ char *res2[] = {"a", "bb", "ccc"}; ++ char *res = malloc(10); ++ ++ int i = 0; ++ while (true) { ++ list1 = extract_word_inlist(list1, &res); ++ if (!list1) ++ break; ++ c_assert(!strcmp(res, res1[i++])); ++ } ++ ++ i = 0; ++ while (true) { ++ list2 = extract_word_inlist(list2, &res); ++ if (!list2) ++ break; ++ c_assert(!strcmp(res, res2[i++])); ++ } ++ free(res); ++} ++ ++static void test_extract_list_element() { ++ char *list1 = "[{}]"; ++ char *list2 = "[{a}{b}{c}]"; ++ char *list3 = "[{a}{{{b}}{}}{c}]"; ++ char *res2_3[] = {"a", "b", "c"}; ++ char *res = NULL; ++ ++ int i = 0; ++ while (true) { ++ list1 = extract_list_element(list1, &res); ++ if (!list1) ++ break; ++ c_assert(!res); ++ free(res); ++ res = NULL; ++ } ++ ++ if (res) { ++ free(res); ++ res = NULL; ++ } ++ ++ i = 0; ++ while (true) { ++ list2 = extract_list_element(list2, &res); ++ if (!list2) ++ break; ++ c_assert(!strcmp(res, res2_3[i++])); ++ free(res); ++ res = NULL; ++ } ++ if (res) { ++ free(res); ++ res = NULL; ++ } ++ ++ i = 0; ++ while (true) { ++ list3 = extract_list_element(list3, &res); ++ if (!list3) ++ break; ++ c_assert(!strcmp(res, res2_3[i++])); ++ free(res); ++ res = NULL; ++ } ++ if (res) { ++ free(res); ++ res = NULL; ++ } ++ return; ++} ++ ++static void test_serialize_validate_rule() { ++ const char *rule_str_ok[3] = {"[{123}{234}{.a-*(*&^@#*^)}]", "[{123}]", "[]"}; ++ const char *rule_str_bad[8] = {"oaeu", "[{{]", "[{123}}", "{123}{345}", "[{{11111}}]", "[aoeuaou]", "[{{}}]", "[aa{b}aa{b}]"}; ++ ++ for (int i = 0; i < 3; i++) ++ c_assert(serialize_validate_rule(rule_str_ok[i]) == true); ++ ++ for (int i = 0; i < 8; i++) ++ c_assert(serialize_validate_rule(rule_str_bad[i]) == false); ++} ++ ++static void test_serialize_validate_sasl() { ++ const char *sasl_str_ok[2] = {"[{0}{0}{1}]", "[{1}{1}{3}"}; ++ const char *sasl_str_bad[6] = {"[{-1}{}{}]", "foo", "[{-1}{0}{-1}]", "[{5}{1}{0}]", "[123", "123323"}; ++ ++ for (int i = 0; i < 2; i++) ++ c_assert(serialize_validate_sasl(sasl_str_ok[i]) == true); ++ ++ for (int i = 0; i < 6; i++) ++ c_assert(serialize_validate_sasl(sasl_str_bad[i]) == false); ++} ++ ++int main(int argc, char **argv) { ++ test_basic(); ++ test_extract_world_inlist(); ++ test_extract_list_element(); ++ test_serialize_validate_rule(); ++ test_serialize_validate_sasl(); ++ return 0; ++} +diff --git a/test/dbus/util-broker.c b/test/dbus/util-broker.c +index 0bdcefc..5d33b52 100644 +--- a/test/dbus/util-broker.c ++++ b/test/dbus/util-broker.c +@@ -15,6 +15,7 @@ + #include + #include + #include "dbus/protocol.h" ++#include "util/proc.h" + #include "util/syscall.h" + #include "util-broker.h" + +@@ -35,7 +36,8 @@ void util_event_new(sd_event **eventp) { + } + + static int util_event_sigchld(sd_event_source *source, const siginfo_t *si, void *userdata) { +- int status; ++ int status, r; ++ Broker* broker = userdata; + + if (si->si_code == CLD_EXITED) + status = si->si_status; +@@ -47,6 +49,70 @@ static int util_event_sigchld(sd_event_source *source, const siginfo_t *si, void + return sd_event_exit(sd_event_source_get_event(source), status); + } + ++static int util_event_sigchld_reexec(sd_event_source *source, const struct signalfd_siginfo *ssi, void *userdata) { ++ int status, r; ++ Broker* broker = userdata; ++ ++ if (ssi->ssi_pid == broker->child_pid) ++ return sd_event_source_set_enabled(broker->defer_event_source, SD_EVENT_ONESHOT); ++} ++ ++static int util_event_defer(sd_event_source *source, void *userdata) { ++ Broker *broker = userdata; ++ sd_bus *bus = broker->bus; ++ /* new_bus shares the dbus fd (pair[0] or lc_fd) with broker->bus. ++ * Both of them use this fd to communicate with broker, which use ++ * pair[1]. So we can't close new_bus here, or we will fail when ++ * closing broker->bus. ++ * And, before sending messages to the broker by new_bus, we should ++ * detach broker->bus. If not, the reply message will be processed ++ * by broker->bus, but broker->bus have no idea why this message even ++ * exist, because it doesn't send any message. ++ */ ++ sd_bus_detach_event(bus); ++ sd_bus *new_bus; ++ _c_cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; ++ int r = 0; ++ ++ create_broker_listener(broker); ++ ++ r = sd_bus_new(&new_bus); ++ assert(r >= 0); ++ ++ r = sd_bus_set_fd(new_bus, broker->lc_fd, broker->lc_fd); ++ c_assert(r >= 0); ++ ++ r = sd_bus_start(new_bus); ++ assert(r >= 0); ++ ++ r = sd_bus_message_new_method_call(new_bus, ++ &message, ++ NULL, ++ "/org/bus1/DBus/Broker", ++ "org.bus1.DBus.Broker", ++ "AddListener"); ++ c_assert(r >= 0); ++ ++ r = sd_bus_message_append(message, ++ "oh", ++ "/org/bus1/DBus/Listener/0", ++ broker->listener_fd); ++ c_assert(r >= 0); ++ ++ r = util_append_policy(message); ++ c_assert(r >= 0); ++ ++ r = sd_bus_call(new_bus, message, -1, NULL, NULL); ++ c_assert(r >= 0); ++ ++ /* close the new listener_fd just like what we have done in util_broker_thread. */ ++ broker->listener_fd = c_close(broker->listener_fd); ++ ++ /* We still need broker->bus to deal some message, reattach it. */ ++ sd_bus_attach_event(bus, sd_event_source_get_event(source), SD_EVENT_PRIORITY_NORMAL); ++ return 0; ++} ++ + #define POLICY_T_BATCH \ + "bt" \ + "a(btbs)" \ +@@ -59,7 +125,7 @@ static int util_event_sigchld(sd_event_source *source, const siginfo_t *si, void + "a(ss)" \ + "b" + +-static int util_append_policy(sd_bus_message *m) { ++int util_append_policy(sd_bus_message *m) { + int r; + + r = sd_bus_message_open_container(m, 'v', "(" POLICY_T ")"); +@@ -165,18 +231,49 @@ static int util_method_reload_config(sd_bus_message *message, void *userdata, sd + return sd_bus_reply_method_return(message, NULL); + } + ++static int util_method_reexecute(sd_bus_message *message, void *userdata, sd_bus_error *error) { ++ /* Careful: Don't return 0, or we will get ECONNRESET in libsystemd. */ ++ /* backtrace: ++ bus_process_object ++ ↳ object_find_and_run ++ ↳ method_callbacks_run ++ ↳ r = c->vtable->x.method.handler(m, u, &error); ++ */ ++ return sd_bus_reply_method_return(message, NULL); ++} ++ + const sd_bus_vtable util_vtable[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_METHOD("ReloadConfig", NULL, NULL, util_method_reload_config, 0), ++ SD_BUS_METHOD("Reexecute", NULL, NULL, util_method_reexecute, 0), + + SD_BUS_VTABLE_END + }; + +-void util_fork_broker(sd_bus **busp, sd_event *event, int listener_fd, pid_t *pidp) { +- _c_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; ++int create_broker_listener(Broker *broker) { ++ int r; ++ ++ broker->listener_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); ++ c_assert(broker->listener_fd >= 0); ++ ++ r = bind(broker->listener_fd, (struct sockaddr *)&broker->address, offsetof(struct sockaddr_un, sun_path)); ++ c_assert(r >= 0); ++ ++ r = getsockname(broker->listener_fd, (struct sockaddr *)&broker->address, &broker->n_address); ++ c_assert(r >= 0); ++ ++ r = listen(broker->listener_fd, 256); ++ c_assert(r >= 0); ++ ++ return 0; ++} ++ ++void util_fork_broker(sd_bus **busp, sd_event *event, int listener_fd, pid_t *pidp, int *lc_fd, Broker* broker) { ++ sd_bus *bus = NULL; + _c_cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; + _c_cleanup_(c_freep) char *fdstr = NULL; ++ sd_event_source *defer_event_source = NULL; + int r, pair[2]; + pid_t pid; + +@@ -214,7 +311,13 @@ void util_fork_broker(sd_bus **busp, sd_event *event, int listener_fd, pid_t *pi + if (pidp) + *pidp = pid; + +- r = sd_event_add_child(event, NULL, pid, WEXITED, util_event_sigchld, NULL); ++ r = sd_event_add_child(event, NULL, pid, WCONTINUED | WEXITED, util_event_sigchld, broker); ++ c_assert(r >= 0); ++ ++ r = sd_event_add_defer(event, &defer_event_source, util_event_defer, broker); ++ c_assert(r >= 0); ++ broker->defer_event_source = defer_event_source; ++ r = sd_event_source_set_enabled(defer_event_source, SD_EVENT_OFF); + c_assert(r >= 0); + + r = sd_bus_new(&bus); +@@ -223,11 +326,12 @@ void util_fork_broker(sd_bus **busp, sd_event *event, int listener_fd, pid_t *pi + /* consumes the fd */ + r = sd_bus_set_fd(bus, pair[0], pair[0]); + c_assert(r >= 0); ++ *lc_fd = pair[0]; + + r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL); + c_assert(r >= 0); + +- r = sd_bus_add_object_vtable(bus, NULL, "/org/bus1/DBus/Controller", "org.bus1.DBus.Controller", util_vtable, NULL); ++ r = sd_bus_add_object_vtable(bus, NULL, "/org/bus1/DBus/Controller", "org.bus1.DBus.Controller", util_vtable, broker); + c_assert(r >= 0); + + r = sd_bus_start(bus); +@@ -381,10 +485,16 @@ static void *util_broker_thread(void *userdata) { + r = sd_event_add_signal(event, NULL, SIGUSR1, util_event_sigusr1, broker); + c_assert(r >= 0); + ++ if (broker->test_reexec) { ++ r = sd_event_add_signal(event, NULL, SIGCHLD, util_event_sigchld_reexec, broker); ++ c_assert(r >= 0); ++ } ++ + if (broker->listener_fd >= 0) { +- util_fork_broker(&bus, event, broker->listener_fd, &broker->child_pid); ++ util_fork_broker(&bus, event, broker->listener_fd, &broker->child_pid, &broker->lc_fd, broker); + /* dbus-broker reports its controller in GetConnectionUnixProcessID */ + broker->pid = getpid(); ++ broker->bus = bus; + broker->listener_fd = c_close(broker->listener_fd); + } else { + c_assert(broker->listener_fd < 0); +@@ -478,18 +588,7 @@ void util_broker_spawn(Broker *broker) { + * run and babysit the broker. + */ + +- broker->listener_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); +- c_assert(broker->listener_fd >= 0); +- +- r = bind(broker->listener_fd, (struct sockaddr *)&broker->address, offsetof(struct sockaddr_un, sun_path)); +- c_assert(r >= 0); +- +- r = getsockname(broker->listener_fd, (struct sockaddr *)&broker->address, &broker->n_address); +- c_assert(r >= 0); +- +- r = listen(broker->listener_fd, 256); +- c_assert(r >= 0); +- ++ create_broker_listener(broker); + r = pthread_create(&broker->thread, NULL, util_broker_thread, broker); + c_assert(r >= 0); + } +@@ -512,6 +611,7 @@ void util_broker_terminate(Broker *broker) { + + r = pthread_join(broker->thread, &value); + c_assert(!r); ++ + c_assert(!value); + + c_assert(broker->listener_fd < 0); +diff --git a/test/dbus/util-broker.h b/test/dbus/util-broker.h +index a3f5f49..fa6de9d 100644 +--- a/test/dbus/util-broker.h ++++ b/test/dbus/util-broker.h +@@ -20,6 +20,10 @@ struct Broker { + pthread_t thread; + struct sockaddr_un address; + socklen_t n_address; ++ sd_bus *bus; ++ sd_event_source *defer_event_source; ++ bool test_reexec; ++ int lc_fd; /* launcher controller fd */ + int listener_fd; + int pipe_fds[2]; + pid_t pid; +@@ -29,6 +33,10 @@ struct Broker { + #define BROKER_NULL { \ + .address.sun_family = AF_UNIX, \ + .n_address = sizeof(struct sockaddr_un), \ ++ .bus = NULL, \ ++ .defer_event_source = NULL, \ ++ .test_reexec = false, \ ++ .lc_fd = -1, \ + .listener_fd = -1, \ + .pipe_fds[0] = -1, \ + .pipe_fds[1] = -1, \ +@@ -37,7 +45,9 @@ struct Broker { + /* misc */ + + void util_event_new(sd_event **eventp); +-void util_fork_broker(sd_bus **busp, sd_event *event, int listener_fd, pid_t *pidp); ++int util_append_policy(sd_bus_message *m); ++int create_broker_listener(Broker *broker); ++void util_fork_broker(sd_bus **busp, sd_event *event, int listener_fd, pid_t *pidp, int *lc_fd, Broker *broker); + void util_fork_daemon(sd_event *event, int pipe_fd, pid_t *pidp); + + /* broker */ +-- +2.30.2 + diff --git a/dbus-broker.spec b/dbus-broker.spec index d7cc02f..fb279bc 100644 --- a/dbus-broker.spec +++ b/dbus-broker.spec @@ -1,6 +1,6 @@ Name: dbus-broker Version: 29 -Release: 5 +Release: 6 Summary: Linux D-Bus Message Broker License: Apache License 2.0 URL: https://github.com/bus1/dbus-broker @@ -8,6 +8,8 @@ Source0: https://github.com/bus1/dbus-broker/releases/download/v%{version}/%{na Patch0001: backport-CVE-2022-31213.patch Patch0002: backport-CVE-2022-31212.patch +Patch0003: enable-dbus-broker-to-reexecute.patch +Patch0004: add-unit-test-for-dbus-broker.patch BuildRequires: cmake gcc glibc-devel meson python-docutils dbus BuildRequires: pkgconfig(expat) pkgconfig(libsystemd) pkgconfig(libselinux) @@ -67,6 +69,10 @@ fi if [ $1 == 1 ] && [ -x /usr/bin/systemctl ]; then systemctl --no-reload enable dbus-broker.service &>/dev/null || : fi +# Reexec after upgrading +if [ $1 == 2 ]; then + dbus-broker-launch --reexec &>/dev/null || : +fi %postun %systemd_user_postun dbus-broker.service @@ -81,6 +87,9 @@ fi %{_userunitdir}/dbus-broker.service %changelog +* Wed Jun 7 2023 hongjinghao - 29-6 +- Enable dbus-broker to reexecute without disconnection + * Thu Dec 01 2022 licunlong - 29-5 - Optimize installing and removing dbus-broker diff --git a/enable-dbus-broker-to-reexecute.patch b/enable-dbus-broker-to-reexecute.patch new file mode 100644 index 0000000..8b6fa2c --- /dev/null +++ b/enable-dbus-broker-to-reexecute.patch @@ -0,0 +1,2153 @@ +From 4923cf6d2da3fe8e100cec1949da91db72b4bd17 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Fri, 10 Mar 2023 11:46:09 +0800 +Subject: [PATCH 1/2] enable dbus-broker to reexecute + +--- + src/broker/broker.c | 149 ++++++++++++- + src/broker/broker.h | 34 ++- + src/broker/controller-dbus.c | 61 ++++++ + src/broker/controller.c | 16 ++ + src/broker/controller.h | 5 + + src/broker/main.c | 36 +++- + src/broker/main.h | 1 + + src/bus/driver.c | 41 ++++ + src/bus/listener.c | 6 +- + src/bus/match.c | 9 + + src/bus/match.h | 12 ++ + src/bus/peer.c | 275 +++++++++++++++++++++++- + src/bus/peer.h | 8 +- + src/dbus/connection.c | 2 +- + src/dbus/connection.h | 1 + + src/dbus/socket.c | 2 +- + src/dbus/socket.h | 1 + + src/launch/launcher.c | 213 +++++++++++++++--- + src/launch/launcher.h | 6 +- + src/launch/main.c | 119 +++++++++- + src/meson.build | 1 + + src/units/system/dbus-broker.service.in | 2 + + src/util/proc.c | 1 + + src/util/serialize.c | 234 ++++++++++++++++++++ + src/util/serialize.h | 39 ++++ + src/util/string.c | 79 +++++++ + src/util/string.h | 3 + + 27 files changed, 1305 insertions(+), 51 deletions(-) + create mode 100644 src/util/serialize.c + create mode 100644 src/util/serialize.h + +diff --git a/src/broker/broker.c b/src/broker/broker.c +index 49f2680..51c7e8f 100644 +--- a/src/broker/broker.c ++++ b/src/broker/broker.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include "broker/broker.h" + #include "broker/controller.h" + #include "broker/main.h" +@@ -21,7 +22,9 @@ + #include "util/error.h" + #include "util/log.h" + #include "util/proc.h" ++#include "util/serialize.h" + #include "util/sockopt.h" ++#include "util/string.h" + #include "util/user.h" + + static int broker_dispatch_signals(DispatchFile *file) { +@@ -40,12 +43,112 @@ static int broker_dispatch_signals(DispatchFile *file) { + return DISPATCH_E_EXIT; + } + +-int broker_new(Broker **brokerp, const char *machine_id, int log_fd, int controller_fd, uint64_t max_bytes, uint64_t max_fds, uint64_t max_matches, uint64_t max_objects) { ++static int serialize_broker(Broker *broker) { ++ FILE *f = NULL; ++ int mem_fd; ++ mem_fd = state_file_init(&f); ++ if (mem_fd < 0) ++ return error_fold(mem_fd); ++ ++ (void) serialize_basic(f, "max_ids", "%d", broker->bus.peers.ids); ++ (void) serialize_peers(f, broker); ++ fseeko(f, 0, SEEK_SET); ++ ++ return mem_fd; ++} ++ ++static int broker_execv_with_args(Broker *broker, int mem_fd) { ++ _c_cleanup_(c_freep) char *str_mem_fd = NULL, *str_log = NULL, *str_controller = NULL; ++ _c_cleanup_(c_freep) char *str_max_bytes = NULL, *str_max_fds = NULL, *str_max_matches = NULL; ++ int r; ++ /* Generating args */ ++ r = asprintf(&str_mem_fd, "%d", mem_fd); ++ if (r < 0) ++ return error_fold(r); ++ r = asprintf(&str_log, "%d", broker->log_fd); ++ if (r < 0) ++ return error_fold(r); ++ r = asprintf(&str_controller, "%d", broker->controller_fd); ++ if (r < 0) ++ return error_fold(r); ++ r = asprintf(&str_max_bytes, "%lu", broker->max_bytes); ++ if (r < 0) ++ return error_fold(r); ++ r = asprintf(&str_max_fds, "%lu", broker->max_fds + 1); ++ if (r < 0) ++ return error_fold(r); ++ r = asprintf(&str_max_matches, "%lu", broker->max_matches); ++ if (r < 0) ++ return error_fold(r); ++ ++ /* execv */ ++ char *args[OPTION_NUM_MAX]; ++ int i = 0; ++ args[i++] = broker->bin_path; ++ generate_args_string(broker->log_fd > 0, args, OPTION_NUM_MAX, &i, "--log", str_log); ++ generate_args_string(true, args, OPTION_NUM_MAX, &i, "--controller", str_controller); ++ generate_args_string(true, args, OPTION_NUM_MAX, &i, "--machine-id", broker->machine_id); ++ generate_args_string(true, args, OPTION_NUM_MAX, &i, "--max-bytes", str_max_bytes); ++ generate_args_string(true, args, OPTION_NUM_MAX, &i, "--max-fds", str_max_fds); ++ generate_args_string(true, args, OPTION_NUM_MAX, &i, "--max-matches", str_max_matches); ++ generate_args_string(true, args, OPTION_NUM_MAX, &i, "--reexec", str_mem_fd); ++ if (broker->arg_audit && i + 2 < OPTION_NUM_MAX) ++ args[i++] = "--audit"; ++ args[i++] = NULL; ++ ++ log_append_here(&broker->log, LOG_INFO, 0, NULL); ++ r = log_commitf(&broker->log, "Broker now reexecuting..."); ++ if (r) ++ return error_fold(r); ++ execv(broker->bin_path, args); ++ return 0; ++} ++ ++static void set_broker_from_arg(Broker *broker, BrokerArg *broker_arg) { ++ broker->arg_audit = broker_arg->arg_audit; ++ broker->bin_path = broker_arg->bin_path; ++ broker->machine_id = broker_arg->machine_id; ++ broker->log_fd = broker_arg->log_fd; ++ broker->controller_fd = broker_arg->controller_fd; ++ broker->mem_fd = broker_arg->mem_fd; ++ broker->max_bytes = broker_arg->max_bytes; ++ broker->max_fds = broker_arg->max_fds; ++ broker->max_matches = broker_arg->max_matches; ++ broker->max_objects = broker_arg->max_objects; ++} ++ ++static int broker_reexecute(Broker *broker) { ++ int mem_fd; ++ int r; ++ ++ log_append_here(&broker->log, LOG_INFO, 0, NULL); ++ r = log_commitf(&broker->log, "Serializing broker.\n"); ++ if (r) ++ return error_fold(r); ++ ++ /* serialize */ ++ mem_fd = serialize_broker(broker); ++ if (mem_fd < 0) { ++ log_append_here(&broker->log, LOG_INFO, errno, DBUS_BROKER_CATALOG_BROKER_EXITED); ++ r = log_commitf(&broker->log, "Failed to serialize broker.\n"); ++ if (r < 0) ++ return error_fold(r); ++ } ++ ++ kill(broker->launcher_pid, SIGCHLD); ++ return broker_execv_with_args(broker, mem_fd); ++} ++ ++int broker_new(Broker **brokerp, BrokerArg *broker_arg) { + _c_cleanup_(broker_freep) Broker *broker = NULL; + struct ucred ucred; + socklen_t z; + sigset_t sigmask; + int r, log_type; ++ int log_fd = broker_arg->log_fd, controller_fd = broker_arg->controller_fd; ++ const char* machine_id = broker_arg->machine_id; ++ uint64_t max_bytes = broker_arg->max_bytes, max_fds = broker_arg->max_fds; ++ uint64_t max_matches = broker_arg->max_matches, max_objects = broker_arg->max_objects; + + if (log_fd >= 0) { + z = sizeof(log_type); +@@ -67,6 +170,10 @@ int broker_new(Broker **brokerp, const char *machine_id, int log_fd, int control + broker->bus = (Bus)BUS_NULL(broker->bus); + broker->dispatcher = (DispatchContext)DISPATCH_CONTEXT_NULL(broker->dispatcher); + broker->signals_fd = -1; ++ broker->reexec_serial = -1; ++ broker->do_reexec = false; ++ broker->launcher_pid = getppid(); ++ set_broker_from_arg(broker, broker_arg); + broker->signals_file = (DispatchFile)DISPATCH_FILE_NULL(broker->signals_file); + broker->controller = (Controller)CONTROLLER_NULL(broker->controller); + +@@ -200,7 +307,6 @@ int broker_run(Broker *broker) { + sigemptyset(&signew); + sigaddset(&signew, SIGTERM); + sigaddset(&signew, SIGINT); +- + sigprocmask(SIG_BLOCK, &signew, &sigold); + + r = connection_open(&broker->controller.connection); +@@ -209,6 +315,12 @@ int broker_run(Broker *broker) { + else if (r) + return error_fold(r); + ++ if (broker->mem_fd) { ++ r = deserialize_broker(broker, broker->mem_fd); ++ if (r) ++ return error_trace(r); ++ } ++ + do { + r = dispatch_context_dispatch(&broker->dispatcher); + if (r == DISPATCH_E_EXIT) +@@ -217,8 +329,20 @@ int broker_run(Broker *broker) { + r = MAIN_FAILED; + else + r = error_fold(r); ++ ++ if (broker->do_reexec) ++ r = MAIN_REEXEC; ++ + } while (!r); + ++ Peer *peeri; ++ c_rbtree_for_each_entry(peeri, &broker->bus.peers.peer_tree, registry_node) { ++ socket_dispatch_write(&peeri->connection.socket); ++ } ++ ++ if (r == MAIN_REEXEC) ++ (void) broker_reexecute(broker); ++ + peer_registry_flush(&broker->bus.peers); + + k = broker_log_metrics(broker); +@@ -248,3 +372,24 @@ int broker_reload_config(Broker *broker, User *sender_user, uint64_t sender_id, + + return 0; + } ++ ++int deserialize_broker(Broker *broker, int mem_fd) { ++ FILE *f = NULL; ++ int max_ids_length = ID_LENGTH_MAX + strlen("max_ids="); ++ _c_cleanup_(c_freep) char *buf = malloc(max_ids_length); ++ ++ errno = 0; ++ f = fdopen(mem_fd, "r"); ++ if (!f) ++ return error_trace(-errno); ++ ++ while (fgets(buf, max_ids_length, f) != NULL) { ++ char *max_ids = string_prefix(buf, "max_ids="); ++ if (max_ids) { ++ broker->bus.peers.ids = atoi(max_ids); ++ break; ++ } ++ } ++ ++ return 0; ++} +diff --git a/src/broker/broker.h b/src/broker/broker.h +index a38f597..a6bca1a 100644 +--- a/src/broker/broker.h ++++ b/src/broker/broker.h +@@ -6,11 +6,14 @@ + + #include + #include ++#include + #include "broker/controller.h" + #include "bus/bus.h" + #include "util/dispatch.h" + #include "util/log.h" + ++#define OPTION_NUM_MAX 20 ++ + enum { + _BROKER_E_SUCCESS, + +@@ -18,14 +21,42 @@ enum { + }; + + typedef struct Broker Broker; ++typedef struct BrokerArg BrokerArg; + typedef struct User User; + ++struct BrokerArg { ++ const char *bin_path; ++ const char *machine_id; ++ bool arg_audit; ++ int log_fd; ++ int controller_fd; ++ int mem_fd; ++ uint64_t max_bytes; ++ uint64_t max_fds; ++ uint64_t max_matches; ++ uint64_t max_objects; ++}; ++ + struct Broker { ++ sd_event *event; + Log log; + Bus bus; + DispatchContext dispatcher; + + int signals_fd; ++ int reexec_serial; ++ bool arg_audit; ++ bool do_reexec; ++ const char *bin_path; ++ const char *machine_id; ++ int log_fd; ++ int controller_fd; ++ int mem_fd; ++ uint64_t max_bytes; ++ uint64_t max_fds; ++ uint64_t max_matches; ++ uint64_t max_objects; ++ pid_t launcher_pid; + DispatchFile signals_file; + + Controller controller; +@@ -33,7 +64,7 @@ struct Broker { + + /* broker */ + +-int broker_new(Broker **brokerp, const char *machine_id, int log_fd, int controller_fd, uint64_t max_bytes, uint64_t max_fds, uint64_t max_matches, uint64_t max_objects); ++int broker_new(Broker **brokerp, BrokerArg *broker_arg); + Broker *broker_free(Broker *broker); + + int broker_run(Broker *broker); +@@ -58,3 +89,4 @@ static inline Broker *BROKER(Bus *bus) { + */ + return c_container_of(bus, Broker, bus); + } ++int deserialize_broker(Broker *broker, int mem_fd); +diff --git a/src/broker/controller-dbus.c b/src/broker/controller-dbus.c +index 449151d..3c3ab7d 100644 +--- a/src/broker/controller-dbus.c ++++ b/src/broker/controller-dbus.c +@@ -11,6 +11,7 @@ + #include "broker/broker.h" + #include "broker/controller.h" + #include "bus/policy.h" ++#include "bus/peer.h" + #include "dbus/connection.h" + #include "dbus/message.h" + #include "dbus/protocol.h" +@@ -271,8 +272,18 @@ static int controller_method_add_listener(Controller *controller, const char *_p + policy = NULL; + fdlist_steal(fds, fd_index); + ++ /* reply PID to launcher */ + c_dvar_write(out_v, "()"); + ++ /* We choose to recover peer when adding listener. Because if before this, ++ * we do not know configs or polices of the new version. If after this, i.e. ++ * in listener_dispatch, the recover can't be done unless some new clients ++ * try to establish new connection. */ ++ if (controller->broker->mem_fd) { ++ r = peer_recover_with_fd(controller->broker->mem_fd, listener); ++ if (r) ++ return error_trace(r); ++ } + return 0; + } + +@@ -520,6 +531,12 @@ static int controller_dispatch_reply(Controller *controller, uint32_t serial, co + ControllerReload *reload; + int r; + ++ /* Set broker->do_reexec to true if we get the reply from launcher. */ ++ if (controller->broker->reexec_serial != -1 && controller->broker->reexec_serial == serial) { ++ controller->broker->do_reexec = true; ++ return 0; ++ } ++ + reload = controller_find_reload(controller, serial); + if (!reload) + return CONTROLLER_E_UNEXPECTED_REPLY; +@@ -781,3 +798,47 @@ int controller_dbus_send_reload(Controller *controller, User *user, uint32_t ser + + return 0; + } ++ ++/** ++ * controller_dbus_send_reexecute() - XXX ++ */ ++int controller_dbus_send_reexecute(Controller *controller, User *user, uint32_t serial) { ++ static const CDVarType type[] = { ++ C_DVAR_T_INIT( ++ CONTROLLER_T_MESSAGE( ++ C_DVAR_T_TUPLE0 ++ ) ++ ) ++ }; ++ _c_cleanup_(c_dvar_deinit) CDVar var = C_DVAR_INIT; ++ _c_cleanup_(message_unrefp) Message *message = NULL; ++ _c_cleanup_(c_freep) void *data = NULL; ++ size_t n_data; ++ int r; ++ ++ c_dvar_begin_write(&var, (__BYTE_ORDER == __BIG_ENDIAN), type, 1); ++ c_dvar_write(&var, "((yyyyuu[(y)(y)(y)])())", ++ c_dvar_is_big_endian(&var) ? 'B' : 'l', DBUS_MESSAGE_TYPE_METHOD_CALL, 0, 1, 0, serial, ++ DBUS_MESSAGE_FIELD_PATH, c_dvar_type_o, "/org/bus1/DBus/Controller", ++ DBUS_MESSAGE_FIELD_INTERFACE, c_dvar_type_s, "org.bus1.DBus.Controller", ++ DBUS_MESSAGE_FIELD_MEMBER, c_dvar_type_s, "Reexecute"); ++ ++ r = c_dvar_end_write(&var, &data, &n_data); ++ if (r) ++ return error_origin(r); ++ ++ r = message_new_outgoing(&message, data, n_data); ++ if (r) ++ return error_fold(r); ++ data = NULL; ++ ++ r = connection_queue(&controller->connection, user, message); ++ if (r) { ++ if (r == CONNECTION_E_QUOTA) ++ return CONTROLLER_E_QUOTA; ++ ++ return error_fold(r); ++ } ++ ++ return 0; ++} +diff --git a/src/broker/controller.c b/src/broker/controller.c +index 450c4ad..6a67077 100644 +--- a/src/broker/controller.c ++++ b/src/broker/controller.c +@@ -373,6 +373,22 @@ int controller_request_reload(Controller *controller, + return 0; + } + ++/** ++* controller_request_reexecute() - XXX ++*/ ++int controller_request_reexecute(Controller *controller, ++ User *sender_user, ++ uint64_t sender_id, ++ uint32_t sender_serial) { ++ int r; ++ ++ r = controller_dbus_send_reexecute(controller, sender_user, sender_serial); ++ if (r) ++ return error_trace(r); ++ ++ return 0; ++} ++ + /** + * controller_find_name() - XXX + */ +diff --git a/src/broker/controller.h b/src/broker/controller.h +index a353003..ea680bc 100644 +--- a/src/broker/controller.h ++++ b/src/broker/controller.h +@@ -139,6 +139,10 @@ int controller_request_reload(Controller *controller, + User *user, + uint64_t sender_id, + uint32_t sender_serial); ++int controller_request_reexecute(Controller *controller, ++ User *user, ++ uint64_t sender_id, ++ uint32_t sender_serial); + ControllerName *controller_find_name(Controller *controller, const char *path); + ControllerListener *controller_find_listener(Controller *controller, const char *path); + ControllerReload *controller_find_reload(Controller *controller, uint32_t serial); +@@ -146,6 +150,7 @@ ControllerReload *controller_find_reload(Controller *controller, uint32_t serial + int controller_dbus_dispatch(Controller *controller, Message *message); + int controller_dbus_send_activation(Controller *controller, const char *path, uint64_t serial); + int controller_dbus_send_reload(Controller *controller, User *user, uint32_t serial); ++int controller_dbus_send_reexecute(Controller *controller, User *user, uint32_t serial); + int controller_dbus_send_environment(Controller *controller, const char * const *env, size_t n_env); + + C_DEFINE_CLEANUP(Controller *, controller_deinit); +diff --git a/src/broker/main.c b/src/broker/main.c +index 292b2bb..2b0e91f 100644 +--- a/src/broker/main.c ++++ b/src/broker/main.c +@@ -18,8 +18,10 @@ + + bool main_arg_audit = false; + int main_arg_controller = 3; ++int main_reexec_fd = 0; + int main_arg_log = -1; + const char *main_arg_machine_id = NULL; ++const char *main_bin_path = NULL; + uint64_t main_arg_max_bytes = 512 * 1024 * 1024; + uint64_t main_arg_max_fds = 128; + uint64_t main_arg_max_matches = 16 * 1024; +@@ -52,6 +54,7 @@ static int parse_argv(int argc, char *argv[]) { + ARG_MAX_FDS, + ARG_MAX_MATCHES, + ARG_MAX_OBJECTS, ++ ARG_REEXEC, + }; + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, +@@ -64,10 +67,14 @@ static int parse_argv(int argc, char *argv[]) { + { "max-fds", required_argument, NULL, ARG_MAX_FDS }, + { "max-matches", required_argument, NULL, ARG_MAX_MATCHES }, + { "max-objects", required_argument, NULL, ARG_MAX_OBJECTS }, ++ { "reexec", required_argument, NULL, ARG_REEXEC }, + {} + }; + int r, c; + ++ /* save the binary path, we use it when reexecuting. */ ++ main_bin_path = argv[0] ? : BINDIR "/dbus-broker"; ++ + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + switch (c) { + case 'h': +@@ -158,6 +165,21 @@ static int parse_argv(int argc, char *argv[]) { + + break; + ++ case ARG_REEXEC: { ++ unsigned long vul; ++ char *end; ++ ++ errno = 0; ++ vul = strtoul(optarg, &end, 10); ++ if (errno != 0 || *end || optarg == end || vul > INT_MAX) { ++ fprintf(stderr, "%s: invalid controller file-descriptor -- '%s'\n", program_invocation_name, optarg); ++ return MAIN_FAILED; ++ } ++ ++ main_reexec_fd = vul; ++ break; ++ } ++ + case '?': + /* getopt_long() prints warning */ + return MAIN_FAILED; +@@ -252,9 +274,21 @@ static int setup(void) { + + static int run(void) { + _c_cleanup_(broker_freep) Broker *broker = NULL; ++ BrokerArg broker_arg = { ++ bin_path: main_bin_path, ++ machine_id: main_arg_machine_id, ++ arg_audit: main_arg_audit, ++ log_fd: main_arg_log, ++ controller_fd: main_arg_controller, ++ mem_fd: main_reexec_fd, ++ max_bytes: main_arg_max_bytes, ++ max_fds: main_arg_max_fds, ++ max_matches: main_arg_max_matches, ++ max_objects: main_arg_max_objects, ++ }; + int r; + +- r = broker_new(&broker, main_arg_machine_id, main_arg_log, main_arg_controller, main_arg_max_bytes, main_arg_max_fds, main_arg_max_matches, main_arg_max_objects); ++ r = broker_new(&broker, &broker_arg); + if (!r) + r = broker_run(broker); + +diff --git a/src/broker/main.h b/src/broker/main.h +index 81afb3d..37c822b 100644 +--- a/src/broker/main.h ++++ b/src/broker/main.h +@@ -10,6 +10,7 @@ + enum { + _MAIN_SUCCESS, + MAIN_EXIT, ++ MAIN_REEXEC, + MAIN_FAILED, + }; + +diff --git a/src/bus/driver.c b/src/bus/driver.c +index b9774d6..874cc73 100644 +--- a/src/bus/driver.c ++++ b/src/bus/driver.c +@@ -19,6 +19,7 @@ + #include "dbus/protocol.h" + #include "dbus/socket.h" + #include "util/error.h" ++#include "util/proc.h" + #include "util/selinux.h" + #include "util/string.h" + +@@ -1592,6 +1593,45 @@ static int driver_method_reload_config(Peer *peer, const char *path, CDVar *in_v + return 0; + } + ++static int driver_method_reexecute(Peer *peer, const char *path, CDVar *in_v, uint32_t serial, CDVar *out_v) { ++ int r; ++ ++ if (peer->user->uid != 0) { ++ r = driver_send_error(peer, serial, "org.freedesktop.DBus.Error.AccessDenied", "Only root can trigger reexecuting"); ++ if (r < 0) ++ return error_fold(r); ++ return 0; ++ } ++ ++ /* verify the input argument */ ++ c_dvar_read(in_v, "()"); ++ ++ r = driver_end_read(in_v); ++ if (r) ++ return error_trace(r); ++ ++ r = controller_request_reexecute(&(BROKER(peer->bus))->controller, peer->user, peer->id, serial); ++ if (r) { ++ if (r == CONTROLLER_E_SERIAL_EXHAUSTED || ++ r == CONTROLLER_E_QUOTA) ++ return BROKER_E_FORWARD_FAILED; ++ ++ return error_fold(r); ++ } ++ ++ /* Remember the sender_serial in reexec_serial. When we got reply from ++ * launcher, we compare the reply serial with this reexec_serial in ++ * controller_dispatch_reply. */ ++ BROKER(peer->bus)->reexec_serial = serial; ++ c_dvar_write(out_v, "(s)", "OK"); ++ ++ r = driver_send_reply(peer, out_v, serial); ++ if (r) ++ return error_trace(r); ++ ++ return 0; ++} ++ + static int driver_method_get_id(Peer *peer, const char *path, CDVar *in_v, uint32_t serial, CDVar *out_v) { + char buffer[sizeof(peer->bus->guid) * 2 + 1] = {}; + int r; +@@ -2285,6 +2325,7 @@ static const DriverMethod driver_methods[] = { + { "UpdateActivationEnvironment", true, "/org/freedesktop/DBus", driver_method_update_activation_environment, driver_type_in_apss, driver_type_out_unit }, + { "GetNameOwner", true, NULL, driver_method_get_name_owner, driver_type_in_s, driver_type_out_s }, + { "ReloadConfig", true, NULL, driver_method_reload_config, c_dvar_type_unit, driver_type_out_unit }, ++ { "Reexecute", true, NULL, driver_method_reexecute, c_dvar_type_unit, driver_type_out_s }, + { "GetId", true, NULL, driver_method_get_id, c_dvar_type_unit, driver_type_out_s }, + { }, + }; +diff --git a/src/bus/listener.c b/src/bus/listener.c +index 14be80b..7e87e0c 100644 +--- a/src/bus/listener.c ++++ b/src/bus/listener.c +@@ -23,7 +23,8 @@ static int listener_dispatch(DispatchFile *file) { + if (!(dispatch_file_events(file) & EPOLLIN)) + return 0; + +- fd = accept4(listener->socket_fd, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK); ++ /* Don't close fd after exec */ ++ fd = accept4(listener->socket_fd, NULL, NULL, SOCK_NONBLOCK); + if (fd < 0) { + if (errno == EAGAIN) { + /* +@@ -43,7 +44,8 @@ static int listener_dispatch(DispatchFile *file) { + } + } + +- r = peer_new_with_fd(&peer, listener->bus, listener->policy, listener->guid, file->context, fd); ++ r = peer_new_with_fd(&peer, listener->bus, listener->policy, listener->guid, ++ file->context, fd, -1); + if (r == PEER_E_QUOTA || r == PEER_E_CONNECTION_REFUSED) + /* + * The user has too many open connections, or a policy disallows it to +diff --git a/src/bus/match.c b/src/bus/match.c +index abf7c66..38f4b60 100644 +--- a/src/bus/match.c ++++ b/src/bus/match.c +@@ -934,6 +934,15 @@ void match_owner_deinit(MatchOwner *owner) { + c_assert(!c_list_is_linked(&owner->destinations_link)); + } + ++void rule_string_deinit(CList *rule_string_list) { ++ RuleString *rs; ++ while (rs = c_list_first_entry(rule_string_list, RuleString, rule_string_link)) { ++ c_list_unlink(&rs->rule_string_link); ++ free(rs->rule_string); ++ free(rs); ++ } ++} ++ + /** + * match_owner_get_stats() - XXX + */ +diff --git a/src/bus/match.h b/src/bus/match.h +index a6b39ea..59eaf7f 100644 +--- a/src/bus/match.h ++++ b/src/bus/match.h +@@ -21,6 +21,7 @@ typedef struct MatchRegistryByPath MatchRegistryByPath; + typedef struct MatchRegistry MatchRegistry; + typedef struct MatchRule MatchRule; + typedef struct MessageMetadata MessageMetadata; ++typedef struct RuleString RuleString; + + #define MATCH_RULE_LENGTH_MAX (1024UL) /* taken from dbus-daemon(1) */ + +@@ -161,6 +162,16 @@ struct MatchRegistry { + .monitor_tree = C_RBTREE_INIT, \ + } + ++struct RuleString { ++ char *rule_string; ++ CList rule_string_link; ++}; ++ ++#define RULE_STRING_INIT(_x) { \ ++ .rule_string = NULL, \ ++ .rule_string_link = C_LIST_INIT((_x).rule_string_link), \ ++ } ++ + /* rules */ + + MatchRule *match_rule_user_ref(MatchRule *rule); +@@ -175,6 +186,7 @@ C_DEFINE_CLEANUP(MatchRule *, match_rule_user_unref); + + void match_owner_init(MatchOwner *owner); + void match_owner_deinit(MatchOwner *owner); ++void rule_string_deinit(CList *rule_string_list); + + void match_owner_get_stats(MatchOwner *owner, unsigned int *n_bytesp, unsigned int *n_matchesp); + void match_owner_move(MatchOwner *to, MatchOwner *from); +diff --git a/src/bus/peer.c b/src/bus/peer.c +index ce4584c..3f72dbf 100644 +--- a/src/bus/peer.c ++++ b/src/bus/peer.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include "broker/controller.h" + #include "bus/bus.h" + #include "bus/driver.h" + #include "bus/match.h" +@@ -25,7 +26,9 @@ + #include "util/fdlist.h" + #include "util/log.h" + #include "util/metrics.h" ++#include "util/serialize.h" + #include "util/sockopt.h" ++#include "util/string.h" + #include "util/user.h" + + static int peer_dispatch_connection(Peer *peer, uint32_t events) { +@@ -227,6 +230,7 @@ int peer_dispatch(DispatchFile *file) { + + if (!connection_is_running(&peer->connection)) + peer_free(peer); ++ + } + + /* Careful: @peer might be deallocated here */ +@@ -254,7 +258,8 @@ int peer_new_with_fd(Peer **peerp, + PolicyRegistry *policy, + const char guid[], + DispatchContext *dispatcher, +- int fd) { ++ int fd, ++ int peer_id) { + _c_cleanup_(peer_freep) Peer *peer = NULL; + _c_cleanup_(user_unrefp) User *user = NULL; + _c_cleanup_(c_freep) gid_t *gids = NULL; +@@ -269,7 +274,11 @@ int peer_new_with_fd(Peer **peerp, + if (r < 0) + return error_origin(-errno); + +- r = user_registry_ref_user(&bus->users, &user, ucred.uid); ++ if (ucred.pid == getppid()) ++ r = user_registry_ref_user(&bus->users, &user, bus->user->uid); ++ else ++ r = user_registry_ref_user(&bus->users, &user, ucred.uid); ++ + if (r < 0) + return error_fold(r); + +@@ -324,7 +333,10 @@ int peer_new_with_fd(Peer **peerp, + if (r < 0) + return error_fold(r); + +- peer->id = bus->peers.ids++; ++ if (peer_id < 0) ++ peer->id = bus->peers.ids++; ++ else ++ peer->id = peer_id; + slot = c_rbtree_find_slot(&bus->peers.peer_tree, peer_compare, &peer->id, &parent); + c_assert(slot); /* peer->id is guaranteed to be unique */ + c_rbtree_add(&bus->peers.peer_tree, parent, slot, &peer->registry_node); +@@ -353,6 +365,7 @@ Peer *peer_free(Peer *peer) { + reply_owner_deinit(&peer->owned_replies); + reply_registry_deinit(&peer->replies); + match_owner_deinit(&peer->owned_matches); ++ rule_string_deinit(&peer->rule_string_list); + match_registry_deinit(&peer->name_owner_changed_matches); + match_registry_deinit(&peer->sender_matches); + name_owner_deinit(&peer->owned_names); +@@ -376,7 +389,6 @@ int peer_spawn(Peer *peer) { + } + + void peer_register(Peer *peer) { +- c_assert(!peer->registered); + c_assert(!peer->monitor); + + peer->registered = true; +@@ -460,6 +472,24 @@ void peer_release_name_ownership(Peer *peer, NameOwnership *ownership, NameChang + name_ownership_release(ownership, change); + } + ++static int peer_link_rule_string(Peer *peer, const char *rule_string) { ++ RuleString *rs; ++ ++ rs = malloc(sizeof(RuleString)); ++ if (!rs) ++ return error_origin(-ENOMEM); ++ *rs = (RuleString)RULE_STRING_INIT(*rs); ++ ++ rs->rule_string = strdup(rule_string); ++ if (!rs->rule_string) { ++ free(rs); ++ return error_origin(-ENOMEM); ++ } ++ ++ c_list_link_tail(&peer->rule_string_list, &rs->rule_string_link); ++ return 0; ++} ++ + static int peer_link_match(Peer *peer, MatchRule *rule, bool monitor) { + Address addr; + Peer *sender, *owner; +@@ -591,6 +621,10 @@ int peer_add_match(Peer *peer, const char *rule_string) { + _c_cleanup_(match_rule_user_unrefp) MatchRule *rule = NULL; + int r; + ++ r = peer_link_rule_string(peer, rule_string); ++ if (r < 0) ++ return error_fold(r); ++ + r = match_owner_ref_rule(&peer->owned_matches, &rule, peer->user, rule_string, false); + if (r) { + if (r == MATCH_E_QUOTA) +@@ -811,7 +845,7 @@ int peer_queue_unicast(PolicySnapshot *sender_policy, NameSet *sender_names, Rep + + int peer_queue_reply(Peer *sender, const char *destination, uint32_t reply_serial, Message *message) { + _c_cleanup_(reply_slot_freep) ReplySlot *slot = NULL; +- Peer *receiver; ++ Peer *receiver = NULL; + Address addr; + int r; + +@@ -820,10 +854,13 @@ int peer_queue_reply(Peer *sender, const char *destination, uint32_t reply_seria + return PEER_E_UNEXPECTED_REPLY; + + slot = reply_slot_get_by_id(&sender->replies, addr.id, reply_serial); +- if (!slot) +- return PEER_E_UNEXPECTED_REPLY; ++ if (slot) ++ receiver = c_container_of(slot->owner, Peer, owned_replies); ++ else ++ receiver = peer_registry_find_peer(&sender->bus->peers, addr.id); + +- receiver = c_container_of(slot->owner, Peer, owned_replies); ++ if (!receiver) ++ return PEER_E_UNEXPECTED_REPLY; + + r = connection_queue(&receiver->connection, NULL, message); + if (r) { +@@ -880,3 +917,225 @@ Peer *peer_registry_find_peer(PeerRegistry *registry, uint64_t id) { + + return peer && peer->registered ? peer : NULL; + } ++ ++static int peer_deserialize_key_members(char *peer_str, int *fd, uint64_t *id, pid_t *pid, ++ char **name, char **match_rule, char **sasl) { ++ int tmp_str_length[_PEER_INDEX_MAX] = {FD_LENGTH_MAX, ID_LENGTH_MAX, PID_LENGTH_MAX, ++ UID_LENGTH_MAX, NAME_LENGTH_MAX, LINE_LENGTH_MAX, ++ SASL_LENGTH_MAX}; ++ for (int i = 0; i < _PEER_INDEX_MAX; i++) { ++ char *tmp_str = malloc(tmp_str_length[i]); ++ peer_str = extract_word_inlist(peer_str, &tmp_str); ++ if (strlen(tmp_str) <= 0) { ++ return error_origin(-EINVAL); ++ } ++ switch (i) { ++ case PEER_INDEX_FD: ++ *fd = atoi(tmp_str); ++ break; ++ case PEER_INDEX_ID: ++ *id = atoi(tmp_str); ++ break; ++ case PEER_INDEX_PID: ++ *pid = atoi(tmp_str); ++ break; ++ case PEER_INDEX_UID: ++ break; ++ case PEER_INDEX_NAME: ++ *name = strndup(tmp_str, NAME_LENGTH_MAX - 1); ++ if (!name) ++ return error_origin(-ENOMEM); ++ break; ++ case PEER_INDEX_MATCH_RULE: ++ *match_rule = strndup(tmp_str, LINE_LENGTH_MAX - 1); ++ if (!match_rule) ++ return error_origin(-ENOMEM); ++ break; ++ case PEER_INDEX_SASL: ++ *sasl = strndup(tmp_str, SASL_LENGTH_MAX - 1); ++ if (!sasl) ++ return error_origin(-ENOMEM); ++ break; ++ default: ++ break; ++ } ++ if (tmp_str) { ++ free(tmp_str); ++ tmp_str = NULL; ++ } ++ } ++ return 0; ++} ++ ++static int peer_recover_match_rule(Peer *peer, char *match_rule) { ++ char *rule_str = NULL; ++ char *match_rule_cur = match_rule; ++ ++ while (true) { ++ match_rule_cur = extract_list_element(match_rule_cur, &rule_str); ++ if (!match_rule_cur) ++ break; ++ if (!rule_str) ++ break; ++ peer_add_match(peer, rule_str); ++ free(rule_str); ++ rule_str = NULL; ++ } ++ if (rule_str) { ++ free(rule_str); ++ rule_str = NULL; ++ } ++ return 0; ++} ++ ++static int peer_recover_sasl(Peer *peer, char *sasl) { ++ int sasl_index = SASL_INDEX_SERVER_STATE; ++ char *sasl_str = NULL; ++ char *sasl_cur = sasl; ++ ++ while (sasl_index < _SASL_INDEX_MAX) { ++ sasl_cur = extract_list_element(sasl_cur, &sasl_str); ++ if (!sasl_cur) ++ break; ++ if (!sasl_str) ++ break; ++ if (sasl_index == SASL_INDEX_SERVER_STATE) ++ peer->connection.sasl_server.state = atoi(sasl_str); ++ else if (sasl_index == SASL_INDEX_SERVER_FDSALLOWED) ++ peer->connection.sasl_server.fds_allowed = atoi(sasl_str); ++ else if (sasl_index == SASL_INDEX_CLIENT_STATE) ++ peer->connection.sasl_client.state = atoi(sasl_str); ++ sasl_index++; ++ free(sasl_str); ++ sasl_str = NULL; ++ } ++ ++ if (sasl_str) { ++ free(sasl_str); ++ sasl_str = NULL; ++ } ++ return 0; ++} ++ ++static int peer_recover_full(ControllerListener *controller_listener, int fd, uint64_t id, pid_t pid, ++ char *name, char *match_rule, char *sasl) { ++ Listener *listener = &controller_listener->listener; ++ Peer *peer; ++ int r; ++ ++ /* Recover: fd and id */ ++ r = peer_new_with_fd(&peer, listener->bus, listener->policy, listener->guid, ++ listener->socket_file.context, fd, id); ++ c_list_link_tail(&listener->peer_list, &peer->listener_link); ++ r = peer_spawn(peer); ++ if (r) ++ return error_fold(r); ++ ++ /* Recover: sasl */ ++ peer_recover_sasl(peer, sasl); ++ if (r < 0) ++ return error_fold(r); ++ ++ /* register and mark as recovered */ ++ peer_register(peer); ++ peer->connection.recovered = 1; ++ ++ /* Recover: pid */ ++ peer->pid = pid; ++ ++ /* Recover: name */ ++ NameChange change = NAME_CHANGE_INIT; ++ r = name_registry_request_name(&peer->bus->names, ++ &peer->owned_names, ++ peer->user, ++ name, ++ 0, ++ &change); ++ name_change_deinit(&change); ++ if (r != 0 && r != NAME_E_IN_QUEUE) ++ return error_fold(r); ++ ++ /* Recover: match_rule */ ++ r = peer_recover_match_rule(peer, match_rule); ++ if (r < 0) ++ return error_fold(r); ++ return 0; ++} ++ ++char* free_key_member(char *member) { ++ if (member) { ++ free(member); ++ } ++ return NULL; ++} ++ ++int peer_recover_with_fd(int mem_fd, ControllerListener *controller_listener) { ++ FILE *f = NULL; ++ _c_cleanup_(c_freep) char *buf = malloc(LINE_LENGTH_MAX); ++ int r; ++ ++ errno = 0; ++ f = fdopen(mem_fd, "r"); ++ if (!f) ++ return error_trace(-errno); ++ ++ fseek(f, 0, SEEK_SET); ++ while (fgets(buf, LINE_LENGTH_MAX, f) != NULL) { ++ char *peer_str = string_prefix(buf, "peer="); ++ if (!peer_str) ++ continue; ++ ++ /* Deserialize key members */ ++ int fd = 0; ++ uint64_t id = 0; ++ pid_t pid = 0; ++ char *name = NULL, *match_rule = NULL, *sasl = NULL, *pid_path = NULL; ++ r = peer_deserialize_key_members(peer_str, &fd, &id, &pid, &name, &match_rule, &sasl); ++ if (r < 0) { ++ if (fd > 0) ++ close(fd); ++ name = free_key_member(name); ++ match_rule = free_key_member(match_rule); ++ sasl = free_key_member(sasl); ++ pid_path = free_key_member(pid_path); ++ continue; ++ } ++ /* If we can't find the pid, skip */ ++ r = asprintf(&pid_path, "/proc/%"PRIu32, (uint32_t)pid); ++ if (r < 0) { ++ if (fd > 0) ++ close(fd); ++ name = free_key_member(name); ++ match_rule = free_key_member(match_rule); ++ sasl = free_key_member(sasl); ++ pid_path = free_key_member(pid_path); ++ continue; ++ } ++ if (access(pid_path, F_OK) != 0) { ++ if (fd > 0) ++ close(fd); ++ name = free_key_member(name); ++ match_rule = free_key_member(match_rule); ++ sasl = free_key_member(sasl); ++ pid_path = free_key_member(pid_path); ++ continue; ++ } ++ ++ r = peer_recover_full(controller_listener, fd, id, pid, name, match_rule, sasl); ++ if (r < 0) { ++ if (fd > 0) ++ close(fd); ++ name = free_key_member(name); ++ match_rule = free_key_member(match_rule); ++ sasl = free_key_member(sasl); ++ pid_path = free_key_member(pid_path); ++ continue; ++ } ++ name = free_key_member(name); ++ match_rule = free_key_member(match_rule); ++ sasl = free_key_member(sasl); ++ pid_path = free_key_member(pid_path); ++ } ++ fclose(f); ++ return 0; ++} +diff --git a/src/bus/peer.h b/src/bus/peer.h +index b9e30c5..cb5feff 100644 +--- a/src/bus/peer.h ++++ b/src/bus/peer.h +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include "broker/controller.h" + #include "bus/match.h" + #include "bus/name.h" + #include "bus/policy.h" +@@ -75,6 +76,7 @@ struct Peer { + MatchRegistry sender_matches; + MatchRegistry name_owner_changed_matches; + MatchOwner owned_matches; ++ CList rule_string_list; + ReplyRegistry replies; + ReplyOwner owned_replies; + }; +@@ -90,6 +92,7 @@ struct Peer { + .sender_matches = MATCH_REGISTRY_INIT((_x).sender_matches), \ + .name_owner_changed_matches = MATCH_REGISTRY_INIT((_x).name_owner_changed_matches), \ + .owned_matches = MATCH_OWNER_INIT((_x).owned_matches), \ ++ .rule_string_list = C_LIST_INIT((_x).rule_string_list), \ + .replies = REPLY_REGISTRY_INIT, \ + .owned_replies = REPLY_OWNER_INIT((_x).owned_replies), \ + } +@@ -101,7 +104,8 @@ struct PeerRegistry { + + #define PEER_REGISTRY_INIT {} + +-int peer_new_with_fd(Peer **peerp, Bus *bus, PolicyRegistry *policy, const char guid[], DispatchContext *dispatcher, int fd); ++int peer_new_with_fd(Peer **peerp, Bus *bus, PolicyRegistry *policy, const char guid[], ++ DispatchContext *dispatcher, int fd, int peer_id); + Peer *peer_free(Peer *peer); + + int peer_dispatch(DispatchFile *file); +@@ -130,6 +134,8 @@ void peer_registry_deinit(PeerRegistry *registry); + void peer_registry_flush(PeerRegistry *registry); + Peer *peer_registry_find_peer(PeerRegistry *registry, uint64_t id); + ++int peer_recover_with_fd(int mem_fd, ControllerListener *listener); ++ + static inline bool peer_is_registered(Peer *peer) { + return peer->registered; + } +diff --git a/src/dbus/connection.c b/src/dbus/connection.c +index 1a331f2..7648403 100644 +--- a/src/dbus/connection.c ++++ b/src/dbus/connection.c +@@ -244,7 +244,7 @@ int connection_dequeue(Connection *connection, Message **messagep) { + size_t n_input; + int r; + +- if (_c_unlikely_(!connection->authenticated)) { ++ if (_c_unlikely_(!connection->authenticated) && connection->recovered == 0) { + do { + r = socket_dequeue_line(&connection->socket, &input, &n_input); + if (r) +diff --git a/src/dbus/connection.h b/src/dbus/connection.h +index d694fc6..4c7343a 100644 +--- a/src/dbus/connection.h ++++ b/src/dbus/connection.h +@@ -33,6 +33,7 @@ struct Connection { + SASLClient sasl_client; + + bool server : 1; ++ bool recovered : 1; + bool authenticated : 1; + }; + +diff --git a/src/dbus/socket.c b/src/dbus/socket.c +index c0a9aff..58e784b 100644 +--- a/src/dbus/socket.c ++++ b/src/dbus/socket.c +@@ -732,7 +732,7 @@ static int socket_dispatch_read(Socket *socket) { + charge_fds); + } + +-static int socket_dispatch_write(Socket *socket) { ++int socket_dispatch_write(Socket *socket) { + SocketBuffer *buffer, *safe; + struct mmsghdr msgs[SOCKET_MMSG_MAX]; + struct msghdr *msg; +diff --git a/src/dbus/socket.h b/src/dbus/socket.h +index 67a49ae..077458f 100644 +--- a/src/dbus/socket.h ++++ b/src/dbus/socket.h +@@ -72,6 +72,7 @@ int socket_queue_line(Socket *socket, User *user, const char *line, size_t n); + int socket_queue(Socket *socket, User *user, Message *message); + + int socket_dispatch(Socket *socket, uint32_t event); ++int socket_dispatch_write(Socket *socket); + void socket_shutdown(Socket *socket); + void socket_close(Socket *socket); + void socket_get_stats(Socket *socket, +diff --git a/src/launch/launcher.c b/src/launch/launcher.c +index 3fd69e0..69adb19 100644 +--- a/src/launch/launcher.c ++++ b/src/launch/launcher.c +@@ -28,7 +28,10 @@ + #include "util/error.h" + #include "util/log.h" + #include "util/misc.h" ++#include "util/proc.h" + #include "util/string.h" ++#include "util/serialize.h" ++#include "util/syscall.h" + + /* + * These are the default limits used when spawning dbus-broker. They are +@@ -43,6 +46,18 @@ static const uint64_t main_max_match_rules_per_connection = 256; + + static const char * main_arg_broker = BINDIR "/dbus-broker"; + ++static int bus_method_reload_config(sd_bus_message *message, void *userdata, sd_bus_error *error); ++static int bus_method_reexecute(sd_bus_message *message, void *userdata, sd_bus_error *error); ++ ++const sd_bus_vtable launcher_vtable[] = { ++ SD_BUS_VTABLE_START(0), ++ ++ SD_BUS_METHOD("ReloadConfig", NULL, NULL, bus_method_reload_config, 0), ++ SD_BUS_METHOD("Reexecute", NULL, NULL, bus_method_reexecute, 0), ++ ++ SD_BUS_VTABLE_END ++}; ++ + static sd_bus *bus_close_unref(sd_bus *bus) { + /* + * It is not sufficient to simply call sd_bus_unref(), as messages +@@ -100,6 +115,79 @@ static void log_append_service_user(Log *log, const char *user) { + } + + static int launcher_reload_config(Launcher *launcher); ++static int launcher_on_message(sd_bus_message *m, void *userdata, sd_bus_error *error); ++ ++static int launcher_execv_with_args(Launcher *launcher) { ++ _c_cleanup_(c_freep) char *str_controller_fd = NULL, *str_listen_pid = NULL, *str_broker_pid = NULL; ++ pid_t pid; ++ int r; ++ ++ pid = getpid(); ++ ++ /* Generate used strings */ ++ r = asprintf(&str_controller_fd, "%d", launcher->controller_fd); ++ if (r < 0) ++ return error_fold(r); ++ r = asprintf(&str_broker_pid, "%d", launcher->broker_pid); ++ if (r < 0) ++ return error_fold(r); ++ ++ /* Set environment we need */ ++ r = setenv("LISTEN_FDS", "1", 1); ++ if (r < 0) ++ return error_fold(r); ++ r = setenv("LISTEN_FDNAMES", "dbus.socket", 1); ++ if (r < 0) ++ return error_fold(r); ++ r = asprintf(&str_listen_pid, "%d", pid); ++ if (r < 0) ++ return error_fold(r); ++ r = setenv("LISTEN_PID", str_listen_pid, 1); ++ if (r < 0) ++ return error_fold(r); ++ ++ /* execv */ ++ char *binary_launcher = "/usr/bin/dbus-broker-launch"; ++ char * const args[] = { ++ binary_launcher, ++ "--scope", launcher->user_scope ? "user" : "system", ++ "--controller-fd", str_controller_fd, ++ "--broker-pid", str_broker_pid, ++ launcher->audit ? "--audit" : NULL, /* note that this needs to be the last argument to work */ ++ NULL, ++ }; ++ ++ log_append_here(&launcher->log, LOG_INFO, 0, NULL); ++ r = log_commitf(&launcher->log, "Launcher now reexecuting..."); ++ if (r < 0) ++ return error_fold(r); ++ ++ execv(binary_launcher, args); ++ ++ return 0; ++} ++ ++static int launcher_reexecute(Launcher *launcher) { ++ int r = 0, controller_fd_flag = 0, listener_fd_flag = 0; ++ ++ /* Don't close controller_fd listen_fd after exec */ ++ controller_fd_flag = fcntl (launcher->controller_fd, F_GETFD, 0); ++ if (controller_fd_flag < 0) ++ return controller_fd_flag; ++ controller_fd_flag &= ~FD_CLOEXEC; ++ r = fcntl(launcher->controller_fd, F_SETFD, controller_fd_flag); ++ if (r) ++ return error_fold(r); ++ listener_fd_flag = fcntl (launcher->fd_listen, F_GETFD, 0); ++ if (listener_fd_flag < 0) ++ return listener_fd_flag; ++ listener_fd_flag &= ~FD_CLOEXEC; ++ r = fcntl(launcher->fd_listen, F_SETFD, listener_fd_flag); ++ if (r) ++ return error_fold(r); ++ ++ return launcher_execv_with_args(launcher); ++} + + static int launcher_on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + Launcher *launcher = userdata; +@@ -187,7 +275,8 @@ static int launcher_open_log(Launcher *launcher) { + return 0; + } + +-int launcher_new(Launcher **launcherp, int fd_listen, bool audit, const char *configfile, bool user_scope) { ++int launcher_new(Launcher **launcherp, int fd_listen, bool audit, const char *configfile, ++ bool user_scope, int controller_fd, pid_t broker_pid) { + _c_cleanup_(launcher_freep) Launcher *launcher = NULL; + int r; + +@@ -199,7 +288,10 @@ int launcher_new(Launcher **launcherp, int fd_listen, bool audit, const char *co + launcher->fd_listen = fd_listen; + launcher->uid = -1; + launcher->gid = -1; ++ launcher->broker_pid = broker_pid; ++ launcher->controller_fd = controller_fd; + launcher->audit = audit; ++ launcher->broker_reexecuted = 0; + launcher->user_scope = user_scope; + + if (configfile) +@@ -266,7 +358,7 @@ static noreturn void launcher_run_child(Launcher *launcher, int fd_log, int fd_c + str_max_fds[C_DECIMAL_MAX(uint64_t)], + str_max_matches[C_DECIMAL_MAX(uint64_t)]; + const char * const argv[] = { +- "dbus-broker", ++ main_arg_broker, + "--log", + str_log, + "--controller", +@@ -290,12 +382,6 @@ static noreturn void launcher_run_child(Launcher *launcher, int fd_log, int fd_c + goto exit; + } + +- r = prctl(PR_SET_PDEATHSIG, SIGTERM); +- if (r) { +- r = error_origin(-errno); +- goto exit; +- } +- + r = fcntl(fd_log, F_GETFD); + if (r < 0) { + r = error_origin(-errno); +@@ -361,10 +447,37 @@ static int launcher_on_child_exit(sd_event_source *source, const siginfo_t *si, + if (r) + return error_fold(r); + +- return sd_event_exit(sd_event_source_get_event(source), ++ /* Don't exit from sd_event loop even caught sigchld. Instead, return the errno in si. */ ++ if (launcher->broker_reexecuted > 0) { ++ if (si->si_errno) ++ return si->si_errno; ++ return launcher_reexecute(launcher); ++ } else ++ return sd_event_exit(sd_event_source_get_event(source), + (si->si_code == CLD_EXITED) ? si->si_status : EXIT_FAILURE); + } + ++static int launcher_on_sigchld(sd_event_source *source, const struct signalfd_siginfo *si, void *userdata) { ++ Launcher *launcher = userdata; ++ int r; ++ ++ if (si->ssi_pid != launcher->broker_pid) ++ return 0; ++ ++ if (launcher->broker_reexecuted > 0) { ++ if (si->ssi_errno) ++ return si->ssi_errno; ++ log_append_here(&launcher->log, LOG_INFO, 0, NULL); ++ r = log_commitf(&launcher->log, "Caught SIGCHLD from broker, it is trying to reexecute.\n"); ++ if (r < 0) { ++ return error_fold(r); ++ } ++ return launcher_reexecute(launcher); ++ } else ++ return sd_event_exit(sd_event_source_get_event(source), ++ (si->ssi_code == CLD_EXITED) ? si->ssi_status : EXIT_FAILURE); ++} ++ + static int launcher_fork(Launcher *launcher, int fd_controller) { + pid_t pid; + int r; +@@ -376,6 +489,7 @@ static int launcher_fork(Launcher *launcher, int fd_controller) { + if (!pid) + launcher_run_child(launcher, log_get_fd(&launcher->log), fd_controller); + ++ launcher->broker_pid = pid; + r = sd_event_add_child(launcher->event, NULL, pid, WEXITED, launcher_on_child_exit, launcher); + if (r < 0) + return error_origin(-errno); +@@ -1101,7 +1215,8 @@ static int launcher_load_policy(Launcher *launcher, ConfigRoot *root, Policy *po + return 0; + } + +-static int launcher_add_listener(Launcher *launcher, Policy *policy, uint32_t *system_console_users, size_t n_system_console_users) { ++static int launcher_add_listener(Launcher *launcher, Policy *policy, uint32_t *system_console_users, ++ size_t n_system_console_users) { + _c_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + +@@ -1303,13 +1418,22 @@ static int bus_method_reload_config(sd_bus_message *message, void *userdata, sd_ + return sd_bus_reply_method_return(message, NULL); + } + +-const sd_bus_vtable launcher_vtable[] = { +- SD_BUS_VTABLE_START(0), ++static int bus_method_reexecute(sd_bus_message *message, void *userdata, sd_bus_error *error) { ++ Launcher *launcher = userdata; ++ int r; + +- SD_BUS_METHOD("ReloadConfig", NULL, NULL, bus_method_reload_config, 0), ++ launcher->broker_reexecuted = 1; ++ r = sd_event_source_set_enabled(launcher->dirwatch_src, SD_EVENT_OFF); ++ if (r < 0) ++ return error_trace(r); + +- SD_BUS_VTABLE_END +-}; ++ log_append_here(&launcher->log, LOG_INFO, 0, NULL); ++ r = log_commitf(&launcher->log, "Launcher has received Reexecute message."); ++ if (r < 0) ++ return error_fold(r); ++ ++ return sd_bus_reply_method_return(message, NULL); ++} + + int launcher_run(Launcher *launcher) { + _c_cleanup_(config_root_freep) ConfigRoot *root = NULL; +@@ -1371,26 +1495,45 @@ int launcher_run(Launcher *launcher) { + return 0; + } + +- c_assert(launcher->fd_listen >= 0); ++ if (!launcher->controller_fd) { ++ c_assert(launcher->fd_listen >= 0); + +- r = socketpair(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, controller); +- if (r < 0) +- return error_origin(-errno); ++ r = socketpair(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, controller); ++ if (r < 0) ++ return error_origin(-errno); + +- /* consumes FD controller[0] */ +- r = sd_bus_set_fd(launcher->bus_controller, controller[0], controller[0]); +- if (r < 0) { +- close(controller[0]); +- close(controller[1]); +- return error_origin(r); +- } ++ /* consumes FD controller[0] */ ++ r = sd_bus_set_fd(launcher->bus_controller, controller[0], controller[0]); ++ if (r < 0) { ++ close(controller[0]); ++ close(controller[1]); ++ return error_origin(r); ++ } + +- /* consumes FD controller[1] */ +- r = launcher_fork(launcher, controller[1]); +- if (r) { +- close(controller[1]); +- return error_trace(r); ++ launcher->controller_fd = controller[0]; ++ ++ /* consumes FD controller[1] */ ++ r = launcher_fork(launcher, controller[1]); ++ if (r) { ++ close(controller[1]); ++ return error_trace(r); ++ } ++ } else { ++ /* Disable the dirwatch during reexecuting. */ ++ r = sd_event_source_set_enabled(launcher->dirwatch_src, SD_EVENT_OFF); ++ if (r < 0) { ++ close(launcher->controller_fd); ++ return error_origin(r); ++ } ++ r = sd_bus_set_fd(launcher->bus_controller, launcher->controller_fd, launcher->controller_fd); ++ if (r < 0) { ++ close(launcher->controller_fd); ++ return error_origin(r); ++ } + } ++ r = sd_event_add_signal(launcher->event, NULL, SIGCHLD, launcher_on_sigchld, launcher); ++ if (r < 0) ++ return error_origin(r); + + r = sd_bus_add_object_vtable(launcher->bus_controller, NULL, "/org/bus1/DBus/Controller", "org.bus1.DBus.Controller", launcher_vtable, launcher); + if (r < 0) +@@ -1412,6 +1555,11 @@ int launcher_run(Launcher *launcher) { + if (r) + return error_trace(r); + ++ /* Reexecuting is over, reenable the source. */ ++ r = sd_event_source_set_enabled(launcher->dirwatch_src, SD_EVENT_ON); ++ if (r) ++ return error_trace(r); ++ + r = launcher_connect(launcher); + if (r) + return error_trace(r); +@@ -1434,10 +1582,13 @@ int launcher_run(Launcher *launcher) { + return error_fold(r); + } + ++ log_append_here(&launcher->log, LOG_INFO, 0, NULL); + r = log_commitf(&launcher->log, "Ready\n"); + if (r) + return error_fold(r); + ++ (void) sd_bus_emit_signal(launcher->bus_regular, "/org/bus1/DBus/Controller", "org.bus1.DBus.Controller", "Ready", ""); ++ + r = sd_event_loop(launcher->event); + if (r < 0) + return error_origin(r); +diff --git a/src/launch/launcher.h b/src/launch/launcher.h +index bc42a56..6a50c42 100644 +--- a/src/launch/launcher.h ++++ b/src/launch/launcher.h +@@ -29,6 +29,7 @@ struct Launcher { + Log log; + int fd_listen; + bool audit; ++ int broker_reexecuted; + bool user_scope; + char *configfile; + Dirwatch *dirwatch; +@@ -38,13 +39,16 @@ struct Launcher { + uint64_t service_ids; + uint32_t uid; + uint32_t gid; ++ uint32_t controller_fd; + uint64_t max_bytes; + uint64_t max_fds; + uint64_t max_matches; ++ pid_t broker_pid; + bool at_console; + }; + +-int launcher_new(Launcher **launcherp, int listen_fd, bool audit, const char *configfile, bool user_scope); ++int launcher_new(Launcher **launcherp, int listen_fd, bool audit, const char *configfile, ++ bool user_scope, int controller_fd, int broker_pid); + Launcher *launcher_free(Launcher *launcher); + + C_DEFINE_CLEANUP(Launcher *, launcher_free); +diff --git a/src/launch/main.c b/src/launch/main.c +index ed08e85..672c99e 100644 +--- a/src/launch/main.c ++++ b/src/launch/main.c +@@ -7,8 +7,10 @@ + #include + #include + #include ++#include + #include "launch/launcher.h" + #include "util/error.h" ++#include "util/proc.h" + + enum { + _MAIN_SUCCESS, +@@ -20,6 +22,9 @@ static bool main_arg_audit = false; + static const char * main_arg_configfile = NULL; + static bool main_arg_user_scope = false; + static int main_fd_listen = -1; ++static bool main_arg_cmd_reexec = false; ++static int main_arg_controller_fd = 0; ++static pid_t main_arg_broker_pid = -1; + + static void help(void) { + printf("%s [GLOBALS...] ...\n\n" +@@ -29,6 +34,7 @@ static void help(void) { + " --audit Enable audit support\n" + " --config-file PATH Specify path to configuration file\n" + " --scope SCOPE Scope of message bus\n" ++ " --reexec Restart dbus with peers connected\n" + , program_invocation_short_name); + } + +@@ -39,14 +45,20 @@ static int parse_argv(int argc, char *argv[]) { + ARG_AUDIT, + ARG_CONFIG, + ARG_SCOPE, ++ ARG_CONTROLLER_FD, ++ ARG_BROKER_PID, ++ ARG_REEXEC, + }; + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "verbose", no_argument, NULL, ARG_VERBOSE }, + { "audit", no_argument, NULL, ARG_AUDIT }, +- { "config-file", required_argument, NULL, ARG_CONFIG, }, ++ { "config-file", required_argument, NULL, ARG_CONFIG }, + { "scope", required_argument, NULL, ARG_SCOPE }, ++ { "controller-fd", required_argument, NULL, ARG_CONTROLLER_FD }, ++ { "broker-pid", required_argument, NULL, ARG_BROKER_PID }, ++ { "reexec", no_argument, NULL, ARG_REEXEC }, + {} + }; + int c; +@@ -84,6 +96,40 @@ static int parse_argv(int argc, char *argv[]) { + } + break; + ++ case ARG_CONTROLLER_FD: { ++ unsigned long vul; ++ char *end; ++ ++ errno = 0; ++ vul = strtoul(optarg, &end, 10); ++ if (errno != 0 || *end || optarg == end || vul > INT_MAX) { ++ fprintf(stderr, "%s: invalid controller fd -- '%s'\n", program_invocation_name, optarg); ++ return MAIN_FAILED; ++ } ++ ++ main_arg_controller_fd = vul; ++ break; ++ } ++ ++ case ARG_BROKER_PID: { ++ unsigned long vul; ++ char *end; ++ ++ errno = 0; ++ vul = strtoul(optarg, &end, 10); ++ if (errno != 0 || *end || optarg == end || vul > INT_MAX) { ++ fprintf(stderr, "%s: invalid broker pid -- '%s'\n", program_invocation_name, optarg); ++ return MAIN_FAILED; ++ } ++ ++ main_arg_broker_pid = (pid_t) vul; ++ break; ++ } ++ ++ case ARG_REEXEC: ++ main_arg_cmd_reexec = true; ++ break; ++ + case '?': + /* getopt_long() prints warning */ + return MAIN_FAILED; +@@ -144,7 +190,8 @@ static int run(void) { + _c_cleanup_(launcher_freep) Launcher *launcher = NULL; + int r; + +- r = launcher_new(&launcher, main_fd_listen, main_arg_audit, main_arg_configfile, main_arg_user_scope); ++ r = launcher_new(&launcher, main_fd_listen, main_arg_audit, main_arg_configfile, ++ main_arg_user_scope, main_arg_controller_fd, main_arg_broker_pid); + if (r) + return error_fold(r); + +@@ -152,6 +199,71 @@ static int run(void) { + return error_fold(r); + } + ++static int ready_signal_dispatch(sd_bus_message *message, void *userdata, sd_bus_error *errorp) { ++ int *reexec_ready = (int *) userdata; ++ *reexec_ready = 1; ++ return 0; ++} ++ ++static int trigger_reexecute(void) { ++ _c_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; ++ _c_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; ++ _c_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; ++ int r, reexec_ready = 0; ++ ++ r = sd_bus_open_system(&bus); ++ if (r < 0) ++ return error_origin(r); ++ ++ r = sd_bus_match_signal(bus, NULL, NULL, "/org/bus1/DBus/Controller", ++ "org.bus1.DBus.Controller", ++ "Ready", ++ ready_signal_dispatch, ++ &reexec_ready); ++ if (r < 0) { ++ fprintf(stderr, "Failed to add match signal: %s\n", strerror(-r)); ++ goto finish; ++ } ++ ++ r = sd_bus_message_new_method_call(bus, &m, "org.freedesktop.DBus", ++ "/org/freedesktop/DBus", ++ "org.freedesktop.DBus", ++ "Reexecute"); ++ if (r < 0) ++ return error_origin(r); ++ ++ r = sd_bus_call(bus, m, 0, &error, NULL); ++ ++ if (r < 0) { ++ fprintf(stderr, "Failed to reexecute dbus-broker due to fatal error: %s\n", strerror(-r)); ++ goto finish; ++ } ++ ++ for (;;) { ++ r = sd_bus_process(bus, NULL); ++ if (r < 0) { ++ fprintf(stderr, "Failed to process bus: %s\n", strerror(-r)); ++ goto finish; ++ } ++ ++ if (reexec_ready) ++ break; ++ ++ if (r > 0) ++ continue; ++ ++ r = sd_bus_wait(bus, (uint64_t) -1); ++ if (r < 0) { ++ fprintf(stderr, "Failed to wait on bus: %s\n", strerror(-r)); ++ goto finish; ++ } ++ } ++ ++ r = 0; ++finish: ++ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} ++ + int main(int argc, char **argv) { + sigset_t mask_new, mask_old; + int r; +@@ -160,6 +272,9 @@ int main(int argc, char **argv) { + if (r) + goto exit; + ++ if (main_arg_cmd_reexec) ++ return trigger_reexecute(); ++ + r = inherit_fds(); + if (r) + goto exit; +diff --git a/src/meson.build b/src/meson.build +index c363cbc..7db1ecb 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -41,6 +41,7 @@ sources_bus = [ + 'util/metrics.c', + 'util/misc.c', + 'util/proc.c', ++ 'util/serialize.c', + 'util/sockopt.c', + 'util/string.c', + 'util/systemd.c', +diff --git a/src/units/system/dbus-broker.service.in b/src/units/system/dbus-broker.service.in +index 4155630..9912572 100644 +--- a/src/units/system/dbus-broker.service.in ++++ b/src/units/system/dbus-broker.service.in +@@ -16,6 +16,8 @@ PrivateTmp=true + PrivateDevices=true + ExecStart=@bindir@/dbus-broker-launch --scope system --audit + ExecReload=@bindir@/busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus ReloadConfig ++Restart=always ++RestartSec=1 + + [Install] + Alias=dbus.service +diff --git a/src/util/proc.c b/src/util/proc.c +index 06d4e27..b502da1 100644 +--- a/src/util/proc.c ++++ b/src/util/proc.c +@@ -8,6 +8,7 @@ + #include + #include "util/error.h" + #include "util/proc.h" ++#include "util/serialize.h" + + int proc_get_seclabel(pid_t pid, char **labelp, size_t *n_labelp) { + _c_cleanup_(c_fclosep) FILE *f = NULL; +diff --git a/src/util/serialize.c b/src/util/serialize.c +new file mode 100644 +index 0000000..c027999 +--- /dev/null ++++ b/src/util/serialize.c +@@ -0,0 +1,234 @@ ++/* ++* Serialize Helpers ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include "bus/peer.h" ++#include "util/error.h" ++#include "util/proc.h" ++#include "util/serialize.h" ++#include "util/syscall.h" ++ ++bool serialize_validate_nameowner(const char *nameowner_ship_str) { ++ if (nameowner_ship_str == NULL) ++ return false; ++ ++ return dbus_validate_name(nameowner_ship_str, strlen(nameowner_ship_str)); ++} ++ ++bool serialize_validate_sasl(const char *sasl_str_list) { ++ int cli_stat = -1, fd_allowed = -1, server_stat = -1; ++ ++ if (sasl_str_list == NULL) ++ return false; ++ ++ sscanf(sasl_str_list, "[{%d}{%d}{%d}]", &cli_stat, &fd_allowed, &server_stat); ++ if (cli_stat < SASL_CLIENT_STATE_INIT || cli_stat > SASL_CLIENT_STATE_UNIX_FD) ++ return false; ++ ++ if (server_stat < SASL_SERVER_STATE_INIT || server_stat > SASL_SERVER_STATE_NEGOTIATED_FDS) ++ return false; ++ ++ if (fd_allowed != 0 && fd_allowed != 1) ++ return false; ++ ++ return true; ++} ++ ++bool serialize_validate_rule(const char* rule_str) { ++ if (rule_str == NULL) ++ return false; ++ ++ int len = strlen(rule_str); ++ if (len < 2) ++ return false; ++ ++ if (rule_str[0] != '[' || rule_str[len - 1] != ']') ++ return false; ++ ++ if (len == 2) ++ return true; ++ ++ /* found_left: Whether we have found left curly bracket */ ++ bool found_left = false; ++ for (int i = 1; i < len - 1; i++) { ++ char cur = rule_str[i]; ++ ++ if (!found_left && cur != '{') ++ return false; ++ if (found_left && cur == '{') ++ return false; ++ if (cur == '{' || cur == '}') ++ found_left = !found_left; ++ } ++ ++ return !found_left; ++} ++ ++int state_file_init(FILE **ret) { ++ int mem_fd; ++ FILE *f = NULL; ++ mem_fd = syscall_memfd_create("launcher-state", 0); ++ if (mem_fd < 0) ++ return mem_fd; ++ ++ errno = 0; ++ f = fdopen(mem_fd, "w+"); ++ if (!f) { ++ return error_trace(-errno); ++ } ++ *ret = f; ++ return mem_fd; ++} ++ ++int serialize_basic(FILE *f, char *key, const char *format, ...) { ++ va_list args; ++ _c_cleanup_(c_freep) char *buf = malloc(LINE_LENGTH_MAX); ++ int r; ++ ++ va_start(args, format); ++ r = vsnprintf(buf, LINE_LENGTH_MAX, format, args); ++ va_end(args); ++ ++ if (r < 0 || strlen(key) + r + 2 > LINE_LENGTH_MAX) { ++ return -EINVAL; ++ } ++ ++ fputs(key, f); ++ fputc('=', f); ++ fputs(buf, f); ++ fputc('\n', f); ++ ++ return 0; ++} ++ ++int serialize_peers(FILE *f, Broker *broker) { ++ Peer *peeri; ++ int r = 0; ++ ++ _c_cleanup_(c_freep) char *fd_str = malloc(FD_LENGTH_MAX), *id_str = malloc(ID_LENGTH_MAX); ++ _c_cleanup_(c_freep) char *pid_str = malloc(PID_LENGTH_MAX), *uid_str = malloc(UID_LENGTH_MAX); ++ _c_cleanup_(c_freep) char *rule_str = malloc(MATCH_RULE_LENGTH_MAX), *sasl_str = malloc(SASL_ELEMENT_LENGTH_MAX); ++ _c_cleanup_(c_freep) char *rule_str_list = malloc(LINE_LENGTH_MAX), *sasl_str_list = malloc(SASL_LENGTH_MAX); ++ ++ c_rbtree_for_each_entry(peeri, &broker->bus.peers.peer_tree, registry_node) { ++ _c_cleanup_(c_freep) char *nameowner_ship_str = malloc(NAME_LENGTH_MAX); ++ memset(nameowner_ship_str, 0, NAME_LENGTH_MAX); ++ char *rule_str_list_cur = rule_str_list, *sasl_str_list_cur = sasl_str_list; ++ int left_length = LINE_LENGTH_MAX; ++ bool skip_this_peer = false; ++ ++ /* Skip dbus-broker-launch */ ++ if (peeri->pid == broker->launcher_pid) { ++ close(peeri->connection.socket.fd); ++ continue; ++ } ++ ++ /* Serialize fd, id, pid and uid. */ ++ (void) snprintf(fd_str, FD_LENGTH_MAX, "%d", peeri->connection.socket.fd); ++ (void) snprintf(id_str, ID_LENGTH_MAX, "%lu", peeri->id); ++ (void) snprintf(pid_str, PID_LENGTH_MAX, "%d", peeri->pid); ++ (void) snprintf(uid_str, UID_LENGTH_MAX, "%u", peeri->user->uid); ++ /* 1 * strlen('peer=') + 4 * strlen(';') + 1 * strlen('\0') = 10 */ ++ left_length -= strlen(fd_str) + strlen(id_str) + strlen(pid_str) + strlen(uid_str) + 10; ++ ++ /* Serialize requested names. */ ++ NameOwnership *nameowner_shipi = NULL; ++ c_rbtree_for_each_entry(nameowner_shipi, &peeri->owned_names.ownership_tree, owner_node) { ++ if (name_ownership_is_primary(nameowner_shipi) && nameowner_shipi->name->name){ ++ (void) snprintf(nameowner_ship_str, NAME_LENGTH_MAX, "%s", nameowner_shipi->name->name); ++ left_length -= strlen(nameowner_ship_str) + strlen(";"); ++ } ++ } ++ ++ /* Asign a well-known name for connection. */ ++ if (strlen(nameowner_ship_str) == 0) ++ snprintf(nameowner_ship_str, NAME_LENGTH_MAX, "local.client.fd%d", peeri->connection.socket.fd); ++ ++ if (!serialize_validate_nameowner(nameowner_ship_str)) { ++ log_append_here(&broker->log, LOG_WARNING, 0, NULL); ++ r = log_commitf(&broker->log, "NameOwner string %s is invalid, skipping.\n", ++ nameowner_ship_str); ++ if (r < 0) ++ return error_fold(r); ++ close(peeri->connection.socket.fd); ++ continue; ++ } ++ ++ /* Serialize match rule strings. */ ++ rule_str_list_cur = stpcpy(rule_str_list_cur, "["); ++ left_length -= 1; ++ RuleString *rsi; ++ c_list_for_each_entry(rsi, &peeri->rule_string_list, rule_string_link) { ++ (void) snprintf(rule_str, MATCH_RULE_LENGTH_MAX, "{%s}", rsi->rule_string); ++ char *arg0 = strstr(rule_str, "arg0"); ++ if (arg0 && !strncmp(arg0 + strlen("arg0"), "=':1", strlen("=':1"))) { ++ continue; ++ } ++ rule_str_list_cur = stpcpy(rule_str_list_cur, rule_str); ++ left_length -= strlen(rule_str); ++ /* Besides the next rule_str, we should also keep MATCH_RULE_LENGTH_MAX ++ * bytes for sasl_str. sasl_str usually doesn't need that much space, ++ * just be sure. */ ++ if (left_length <= 2 * MATCH_RULE_LENGTH_MAX) { ++ skip_this_peer = true; ++ break; ++ } ++ } ++ ++ if (skip_this_peer) { ++ log_append_here(&broker->log, LOG_WARNING, 0, NULL); ++ r = log_commitf(&broker->log, "Failed to serialize peer: %s, skipping.\n", ++ nameowner_ship_str); ++ if (r < 0) ++ return error_fold(r); ++ close(peeri->connection.socket.fd); ++ continue; ++ } ++ ++ rule_str_list_cur = stpcpy(rule_str_list_cur, "]"); ++ if (!serialize_validate_rule(rule_str_list)) { ++ log_append_here(&broker->log, LOG_WARNING, 0, NULL); ++ r = log_commitf(&broker->log, "Rule string %s is invalid, skipping.\n", ++ rule_str_list); ++ if (r < 0) ++ return error_fold(r); ++ close(peeri->connection.socket.fd); ++ continue; ++ } ++ ++ /* Serialize SASL state and fds_allowed. */ ++ sasl_str_list_cur = stpcpy(sasl_str_list_cur, "["); ++ (void) snprintf(sasl_str, SASL_ELEMENT_LENGTH_MAX, "{%d}", peeri->connection.sasl_server.state); ++ sasl_str_list_cur = stpcpy(sasl_str_list_cur, sasl_str); ++ (void) snprintf(sasl_str, SASL_ELEMENT_LENGTH_MAX, "{%d}", peeri->connection.sasl_server.fds_allowed); ++ sasl_str_list_cur = stpcpy(sasl_str_list_cur, sasl_str); ++ (void) snprintf(sasl_str, SASL_ELEMENT_LENGTH_MAX, "{%d}", peeri->connection.sasl_client.state); ++ sasl_str_list_cur = stpcpy(sasl_str_list_cur, sasl_str); ++ sasl_str_list_cur = stpcpy(sasl_str_list_cur, "]"); ++ if (!serialize_validate_sasl(sasl_str_list)) { ++ log_append_here(&broker->log, LOG_WARNING, 0, NULL); ++ r = log_commitf(&broker->log, "SASL string %s is invalid, skipping.\n", ++ sasl_str_list); ++ if (r < 0) ++ return error_fold(r); ++ close(peeri->connection.socket.fd); ++ continue; ++ } ++ ++ /* Write all. */ ++ (void) serialize_basic(f, "peer", "%s;%s;%s;%s;%s;%s;%s", ++ fd_str, ++ id_str, ++ pid_str, ++ uid_str, ++ nameowner_ship_str, ++ rule_str_list, ++ sasl_str_list); ++ } ++ return 0; ++} +\ No newline at end of file +diff --git a/src/util/serialize.h b/src/util/serialize.h +new file mode 100644 +index 0000000..5a5758d +--- /dev/null ++++ b/src/util/serialize.h +@@ -0,0 +1,39 @@ ++#pragma once ++ ++#include ++#include "broker/broker.h" ++ ++#define LINE_LENGTH_MAX 51200 ++#define FD_LENGTH_MAX 12 ++#define PID_LENGTH_MAX 12 ++#define UID_LENGTH_MAX 12 ++#define ID_LENGTH_MAX 21 ++#define SASL_ELEMENT_LENGTH_MAX 5 ++#define SASL_LENGTH_MAX 20 ++#define NAME_LENGTH_MAX 256 ++ ++enum { ++ PEER_INDEX_FD, ++ PEER_INDEX_ID, ++ PEER_INDEX_PID, ++ PEER_INDEX_UID, ++ PEER_INDEX_NAME, ++ PEER_INDEX_MATCH_RULE, ++ PEER_INDEX_SASL, ++ _PEER_INDEX_MAX, ++}; ++ ++enum { ++ SASL_INDEX_SERVER_STATE, ++ SASL_INDEX_SERVER_FDSALLOWED, ++ SASL_INDEX_CLIENT_STATE, ++ _SASL_INDEX_MAX, ++}; ++ ++bool serialize_validate_nameowner(const char *nameowner_ship_str); ++bool serialize_validate_sasl(const char *sasl_str_list); ++bool serialize_validate_rule(const char* rule_str); ++ ++int state_file_init(FILE **ret); ++int serialize_basic(FILE *f, char *key, const char *format, ...); ++int serialize_peers(FILE *f, Broker *broker); +\ No newline at end of file +diff --git a/src/util/string.c b/src/util/string.c +index dfbdced..4016f98 100644 +--- a/src/util/string.c ++++ b/src/util/string.c +@@ -55,3 +55,82 @@ int util_strtou64(uint64_t *valp, const char *string) { + + return 0; + } ++ ++void generate_args_string(bool valid_arg, char **ret, int size, int *cur_i, char *option, char *val) { ++ int i = *cur_i; ++ ++ if (!valid_arg) ++ return; ++ ++ if (i + 3 >= size) ++ return; ++ ++ ret[i++] = option; ++ ret[i++] = val; ++ *cur_i = i; ++} ++ ++/* This extract value in @string to @ret. ++@string: string splited by ";" ++@ret: value between ";"" ++input example: 1;2;3 ++output example: 1 => 2 => 3 (one by one) */ ++char *extract_word_inlist(char *string, char **ret) { ++ int i = 0, length = strlen(string); ++ bool found_value = false; ++ while (i < length) { ++ if (string[i] != ';') ++ found_value = true; ++ else { ++ if (found_value) ++ break; ++ else { ++ string++; ++ length--; ++ continue; ++ } ++ } ++ i++; ++ } ++ if (!found_value) { ++ **ret = 0; ++ return NULL; ++ } ++ c_assert(i >= 0); ++ *ret = strncpy(*ret, string, i); ++ *(*ret + i) = '\0'; ++ return string + i; ++} ++ ++/* Like extract_word_inlist, see example below: ++input example: [{a}{b}{c}] ++output example: a => b => c (one by one). */ ++char *extract_list_element(char *string, char **ret) ++{ ++ if (ret == NULL) ++ return NULL; ++ ++ if (!string || strlen(string) <= 2) ++ return NULL; ++ int i = 0, pi = 0; ++ bool valid_left = false; ++ while (i < strlen(string)) { ++ if (string[i] == '{') { ++ pi = i + 1; ++ valid_left = true; ++ } else if (string[i] == '}') { ++ valid_left = (i == pi ? false : valid_left); ++ if (valid_left) ++ break; ++ } ++ i++; ++ } ++ if (!valid_left) { ++ if (*ret) ++ **ret = 0; ++ return NULL; ++ } ++ c_assert(i >= pi); ++ *ret = strndup(string + pi, i - pi); ++ return string + i + 1; ++} +diff --git a/src/util/string.h b/src/util/string.h +index 14f7171..f1c7d10 100644 +--- a/src/util/string.h ++++ b/src/util/string.h +@@ -17,6 +17,9 @@ enum { + + int util_strtou32(uint32_t *valp, const char *string); + int util_strtou64(uint64_t *valp, const char *string); ++void generate_args_string(bool valid_arg, char **ret, int size, int *cur_i, char *option, char *val); ++char *extract_word_inlist(char *string, char **ret); ++char *extract_list_element(char *string, char **ret); + + /** + * string_compare() - compare two strings +-- +2.30.2 +