Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Implement dbus-broker-session tool #380

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 199 additions & 13 deletions src/launch/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
* D-Bus Broker Launch Main Entry
*/

#include "launch/launcher.h"
#include "util/error.h"
#include <c-stdaux.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <spawn.h>
#include <systemd/sd-daemon.h>
#include "launch/launcher.h"
#include "util/error.h"

#include <sys/auxv.h>
#include <sys/un.h>

enum {
_MAIN_SUCCESS,
Expand All @@ -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"
Expand All @@ -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,
Expand All @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -140,23 +169,40 @@ 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;

r = launcher_new(&launcher, main_fd_listen, main_arg_audit, main_arg_configfile, main_arg_user_scope);
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;

Expand All @@ -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:
Expand All @@ -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);
}
Loading