dbus-broker/enable-dbus-broker-to-reexecute.patch
h30032433 007153d3fd fix enable-dbus-broker-to-reexecute.patch and backport-launch-config-use-AT_RANDOM-for-XML-hash-salt.patch
(cherry picked from commit e14ca780e3b7fe9abb3b747db6f5ed30a075d75b)
2024-05-27 10:56:30 +08:00

2154 lines
81 KiB
Diff

From 4923cf6d2da3fe8e100cec1949da91db72b4bd17 Mon Sep 17 00:00:00 2001
From: rpm-build <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 <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <unistd.h>
#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 <c-stdaux.h>
#include <stdlib.h>
+#include <systemd/sd-event.h>
#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<o>)(y<s>)(y<s>)])())",
+ 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 <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
+#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, tmp_str_length[i]);
+ 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 <c-stdaux.h>
#include <stdlib.h>
#include <sys/types.h>
+#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 <getopt.h>
#include <stdlib.h>
#include <systemd/sd-daemon.h>
+#include <systemd/sd-bus.h>
#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 <unistd.h>
#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 <c-rbtree.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#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;
+ }
+ 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;
+ }
+ rule_str_list_cur = stpcpy(rule_str_list_cur, rule_str);
+ }
+
+ 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 <stdlib.h>
+#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 ret_length) {
+ 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_length < i) {
+ **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, int ret_length);
+char *extract_list_element(char *string, char **ret);
/**
* string_compare() - compare two strings
--
2.30.2