2154 lines
80 KiB
Diff
2154 lines
80 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);
|
|
+ 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;
|
|
+ }
|
|
+ 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 <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 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
|
|
|