From 4d0763f8021d1009bd2365b8f04cb5c68c8c0924 Mon Sep 17 00:00:00 2001 From: Norbert Lange Date: Thu, 24 Oct 2024 00:49:31 +0200 Subject: [PATCH] Implement dbus-broker-session tool This is a functional replacement for dbus-run-session. Exit codes and behavior is very similar, the implementation is mostly shared with dbus-broker-launch, adding some hooks to start and supervise the application. --- src/launch/main.c | 212 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 199 insertions(+), 13 deletions(-) diff --git a/src/launch/main.c b/src/launch/main.c index ed08e85c..66018392 100644 --- a/src/launch/main.c +++ b/src/launch/main.c @@ -2,13 +2,17 @@ * D-Bus Broker Launch Main Entry */ +#include "launch/launcher.h" +#include "util/error.h" #include #include #include #include +#include #include -#include "launch/launcher.h" -#include "util/error.h" + +#include +#include enum { _MAIN_SUCCESS, @@ -21,6 +25,8 @@ static const char * main_arg_configfile = NULL; static bool main_arg_user_scope = false; static int main_fd_listen = -1; +#define SESSION_TOOL "dbus-broker-session" + static void help(void) { printf("%s [GLOBALS...] ...\n\n" "Linux D-Bus Message Broker Launcher\n\n" @@ -32,7 +38,16 @@ static void help(void) { , program_invocation_short_name); } -static int parse_argv(int argc, char *argv[]) { +static void help_session(void) { + printf("%s [GLOBALS...] ...\n\n" + "Initiate a D-Bus session with a new session controller\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --config-file PATH Specify path to configuration file\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[], bool as_session) { enum { ARG_VERSION = 0x100, ARG_VERBOSE, @@ -49,16 +64,22 @@ static int parse_argv(int argc, char *argv[]) { { "scope", required_argument, NULL, ARG_SCOPE }, {} }; + static const struct option options_session[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "config-file", required_argument, NULL, ARG_CONFIG, }, + {} + }; int c; - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "h", as_session ? options_session : options, NULL)) >= 0) { switch (c) { case 'h': - help(); + as_session ? help_session() : help(); return MAIN_EXIT; case ARG_VERSION: - printf("dbus-broker-launch %d\n", PACKAGE_VERSION); + printf("%s %d\n", as_session ? SESSION_TOOL : "dbus-broker-launch", PACKAGE_VERSION); return MAIN_EXIT; /* noop for backward compatibility */ @@ -93,9 +114,17 @@ static int parse_argv(int argc, char *argv[]) { } } - if (optind != argc) { - fprintf(stderr, "%s: invalid arguments -- '%s'\n", program_invocation_name, argv[optind]); - return MAIN_FAILED; + if (as_session) + { + if (optind >= argc) { + fprintf(stderr, "%s: a non-option argument is required\n", program_invocation_name); + return MAIN_FAILED; + } + } else { + if (optind != argc) { + fprintf(stderr, "%s: invalid arguments -- '%s'\n", program_invocation_name, argv[optind]); + return MAIN_FAILED; + } } return 0; @@ -140,7 +169,17 @@ static int inherit_fds(void) { return 0; } -static int run(void) { +struct app_data +{ + char **argv; + pid_t pid; + int exit_code; + Launcher *launcher; +}; +typedef struct app_data app_data; +static int launcher_on_controller_start(sd_event_source *source, void *userdata); + +static int run(app_data *app) { _c_cleanup_(launcher_freep) Launcher *launcher = NULL; int r; @@ -148,15 +187,22 @@ static int run(void) { if (r) return error_fold(r); + if (app) { + app->launcher = launcher; + r = sd_event_add_defer(launcher->event, NULL, launcher_on_controller_start, app); + if (r) + return error_fold(r); + } + r = launcher_run(launcher); return error_fold(r); } -int main(int argc, char **argv) { +static int launch_main(int argc, char **argv) { sigset_t mask_new, mask_old; int r; - r = parse_argv(argc, argv); + r = parse_argv(argc, argv, false); if (r) goto exit; @@ -171,7 +217,7 @@ int main(int argc, char **argv) { sigaddset(&mask_new, SIGHUP); sigprocmask(SIG_BLOCK, &mask_new, &mask_old); - r = run(); + r = run(NULL); sigprocmask(SIG_SETMASK, &mask_old, NULL); exit: @@ -180,3 +226,143 @@ int main(int argc, char **argv) { fprintf(stderr, "Exiting due to fatal error: %d\n", r); return (r == 0 || r == MAIN_EXIT) ? 0 : 1; } + +static int exit_event_loop(sd_event_source *source, int r, void *userdata) { + app_data *app = userdata; + app->exit_code = r; + return sd_event_exit(sd_event_source_get_event(source), 0); +} + +static int launcher_on_controller_exit(sd_event_source *source, const siginfo_t *si, void *userdata) { + app_data *app = userdata; + app->pid = -1; + return exit_event_loop(source, (si->si_code == CLD_EXITED) ? si->si_status : 128 + si->si_status, userdata); +} + +static int launcher_on_controller_start(sd_event_source *source, void *userdata) { + app_data *app = userdata; + Launcher *launcher = app->launcher; + + pid_t pid; + sigset_t sigs; + posix_spawnattr_t spawnat; + sigemptyset ( &sigs); + posix_spawnattr_init(&spawnat); + posix_spawnattr_setflags(&spawnat, POSIX_SPAWN_SETSIGMASK); + posix_spawnattr_setsigmask(&spawnat, &sigs); + int r = posix_spawnp(&pid, app->argv[0], NULL, &spawnat, app->argv, environ); + posix_spawnattr_destroy(&spawnat); + if (r) { + error_fold(r); + exit_event_loop(source, 1, userdata); + } + + app->pid = pid; + r = sd_event_add_child(launcher->event, NULL, pid, WEXITED, launcher_on_controller_exit, app); + + return error_fold(r); +} + +static int open_socket(int *pfd, struct sockaddr_un *p_addr) { + struct sockaddr_un addr = {AF_UNIX}; + unsigned long random_bytes; + int fd, r; + void *random = (void*)getauxval(AT_RANDOM); + + c_assert(random); + c_memcpy(&random_bytes, random, sizeof(random_bytes)); + + fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + if (fd < 0) + return error_fold(fd); + *pfd = fd; + + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, "/tmp/dbus-%02lx", random_bytes); + r = bind(fd, (const struct sockaddr*)&addr, sizeof(addr)); + if (r) + return error_fold(r); + *p_addr = addr; + r = listen(fd, 4096); + if (r) + return error_fold(r); + return fd; +} + +static int prepare_session(struct sockaddr_un *socket_path) { + static const char *const unset_env[] = { + "DBUS_SESSION_BUS_PID", + "DBUS_SESSION_BUS_WINDOWID", + "DBUS_STARTER_ADDRESS", + "DBUS_STARTER_BUS_TYPE", + }; + char buffer[sizeof(socket_path->sun_path) + 16]; + int fd = -1; + int r = open_socket(&fd, socket_path); + + if (r < 0) { + if (fd >= 0) + close(fd); + return r; + } + main_fd_listen = fd; + for (unsigned i = 0; i < C_ARRAY_SIZE(unset_env); ++i) + unsetenv(unset_env[i]); + + snprintf(buffer, sizeof(buffer), "unix:path=%s", socket_path->sun_path); + r = setenv("DBUS_SESSION_BUS_ADDRESS", buffer, 1); + return error_fold(r); +} + +static int session_main(int argc, char **argv) { + /* + * Returns 127 if bus could not be spawned + * returns 127 if app could not be forked + * returns 127 on commandline sparsing + * returns 1 if app exec fails + * returns 128 + signo if app exited by signal + * returns app exit_code else + */ + // https://gitlab.freedesktop.org/dbus/dbus/-/blob/master/tools/dbus-run-session.c + sigset_t mask_new, mask_old; + struct sockaddr_un socket_path = {0}; + app_data app = {}; + int r; + + main_arg_user_scope = true; + r = parse_argv(argc, argv, true); + if (r) + goto exit; + + app.argv = &argv[optind]; + r = prepare_session(&socket_path); + if (r) + goto exit; + + sigemptyset(&mask_new); + sigaddset(&mask_new, SIGCHLD); + sigaddset(&mask_new, SIGTERM); + sigaddset(&mask_new, SIGINT); + sigaddset(&mask_new, SIGHUP); + + sigprocmask(SIG_BLOCK, &mask_new, &mask_old); + r = run(&app); + sigprocmask(SIG_SETMASK, &mask_old, NULL); + + if (app.pid > 0) + kill(app.pid, SIGTERM); + if (socket_path.sun_path[0] != '\0') + unlink(socket_path.sun_path); +exit: + r = error_trace(r); + if (r < 0) + fprintf(stderr, "Exiting due to fatal error: %d\n", r); + + return r == 0 ? app.exit_code : 127; +} + +int main(int argc, char **argv) { + const char *p_last_path = strrchr(argv[0], '/'); + if (strcmp(p_last_path ? p_last_path + 1 : argv[0], SESSION_TOOL) == 0) + return session_main(argc, argv); + return launch_main(argc, argv); +}