Projects
home:Eustace:branches:Eulaceura:Factory
dbus-broker
_service:obs_scm:enable-dbus-broker-to-reexecut...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:obs_scm:enable-dbus-broker-to-reexecute.patch of Package dbus-broker
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 | 212 +++++++++++++++--- 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, 1304 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 7a56fa7..eba2970 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); @@ -210,7 +317,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); @@ -219,6 +325,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) @@ -227,8 +339,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); @@ -258,3 +382,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 68be2bc..504a35d 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" @@ -273,8 +274,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; } @@ -528,6 +539,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; @@ -789,3 +806,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 b9ce927..6ee2f62 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 c3d18f0..acb550e 100644 --- a/src/broker/controller.h +++ b/src/broker/controller.h @@ -153,6 +153,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); @@ -160,6 +164,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 6fff07a..ef99ccb 100644 --- a/src/bus/driver.c +++ b/src/bus/driver.c @@ -21,6 +21,7 @@ #include "dbus/socket.h" #include "util/apparmor.h" #include "util/error.h" +#include "util/proc.h" #include "util/selinux.h" #include "util/string.h" @@ -1658,6 +1659,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; @@ -2363,6 +2403,7 @@ static const DriverMethod driver_methods[] = { { "UpdateActivationEnvironment", true, "/org/freedesktop/DBus", driver_method_update_activation_environment, driver_type_in_apss, driver_type_out_unit, false }, { "GetNameOwner", true, NULL, driver_method_get_name_owner, driver_type_in_s, driver_type_out_s, false }, { "ReloadConfig", true, NULL, driver_method_reload_config, c_dvar_type_unit, driver_type_out_unit, false }, + { "Reexecute", true, NULL, driver_method_reexecute, c_dvar_type_unit, driver_type_out_s, false }, { "GetId", true, NULL, driver_method_get_id, c_dvar_type_unit, driver_type_out_s, false }, { }, }; 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 9d64d78..0a35c45 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; @@ -270,7 +275,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) return error_fold(r); @@ -337,7 +346,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); @@ -367,6 +379,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); @@ -390,7 +403,6 @@ int peer_spawn(Peer *peer) { } void peer_register(Peer *peer) { - c_assert(!peer->registered); c_assert(!peer->monitor); peer->registered = true; @@ -476,6 +488,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; @@ -609,6 +639,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) @@ -849,7 +883,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; @@ -858,10 +892,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) { @@ -918,3 +955,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 4cf202a..457c7b5 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" @@ -76,6 +77,7 @@ struct Peer { MatchRegistry sender_matches; MatchRegistry name_owner_changed_matches; MatchOwner owned_matches; + CList rule_string_list; ReplyRegistry replies; ReplyOwner owned_replies; }; @@ -89,6 +91,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), \ } @@ -100,7 +103,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); @@ -129,6 +133,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 85eedda..672f353 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 a4b7460..8b52bef 100644 --- a/src/launch/launcher.c +++ b/src/launch/launcher.c @@ -29,7 +29,10 @@ #include "util/fs.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 @@ -44,6 +47,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 @@ -101,6 +116,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; @@ -188,7 +276,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; @@ -200,7 +289,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) @@ -267,7 +359,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", @@ -291,12 +383,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); @@ -362,10 +448,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; @@ -377,6 +490,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); @@ -1102,7 +1216,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; @@ -1304,13 +1419,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; @@ -1378,26 +1502,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) @@ -1419,6 +1562,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); @@ -1446,6 +1594,8 @@ int launcher_run(Launcher *launcher) { 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 2014372..c07f1c5 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 cc5ae36..163853f 100644 --- a/src/units/system/dbus-broker.service.in +++ b/src/units/system/dbus-broker.service.in @@ -17,6 +17,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 4a876b9..a3cdf0f 100644 --- a/src/util/proc.c +++ b/src/util/proc.c @@ -9,6 +9,7 @@ #include "util/error.h" #include "util/proc.h" #include "util/string.h" +#include "util/serialize.h" /* * A file in /proc can be at most 4M minus one. If required, we start with a 4K 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 e138d9f..71b5920 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -56,6 +56,85 @@ 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; +} + int util_strtoint(int *valp, const char *string) { long val; char *end; diff --git a/src/util/string.h b/src/util/string.h index 5ceeb2f..5138409 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -18,6 +18,9 @@ enum { int util_strtou32(uint32_t *valp, const char *string); int util_strtou64(uint64_t *valp, const char *string); int util_strtoint(int *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.33.0
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2