From a79753b98e506d1d11a653b50f3c446721d66b0e Mon Sep 17 00:00:00 2001 From: Stefano Ragni Date: Sun, 18 Apr 2021 12:01:24 +0200 Subject: [PATCH] s6-rc: switch to bare s6 based model The current form of s6-rc is unsuitable for dynamic activation on external events, such as dbus activation events. Therefore we switch to a different model based on a dedicated s6 scandir (we don't need dependency handling), separated from the rest of the services. --- README.md | 27 ++++++++++----------- activation.c | 33 +++++++++++++++++--------- controller.c | 30 +++++++++++++++++++++++ main.c | 23 ++++++++++-------- meson.build | 4 ++-- meson_options.txt | 2 +- tools/{s6rc-generator => s6-generator} | 8 ++----- 7 files changed, 82 insertions(+), 45 deletions(-) rename tools/{s6rc-generator => s6-generator} (73%) diff --git a/README.md b/README.md index 62f2eaa..f784d4e 100644 --- a/README.md +++ b/README.md @@ -13,30 +13,27 @@ implementation, and are capable of running both system and session buses. This is also a starting point to explore the feasibility of a runit/s6 controller. -## dbus-controller-s6rc +## dbus-controller-s6 -Build with `meson -Ds6-rc=enabled build`. +Build with `meson -Ds6=enabled build`. -Usage: `dbus-controller-s6rc -h`. +Usage: `dbus-controller-s6 -h`. -The [s6-rc] controller looks in a s6-rc livedir for services containing the -file `data/dbus-activatable-name` with a dbus activatable name provided by the -service. It issues `s6-rc change ` when it receives an activation +The [s6] controller looks in a s6 scandir for services containing the file +`data/dbus-activatable-name` with a dbus activatable name provided by the +service. It issues `s6-svc -u ` when it receives an activation request. -Such dbus activatable services can be autogenerated by `tools/s6rc-generator`, -which generates a s6-rc source database called `dbus` that can be included in -the list of your databases to compile with `s6-rc-compile`. - +Such dbus activatable services can be autogenerated by `tools/s6-generator`. It is typically invoked as: ``` -./s6rc-generator /usr/share/dbus-1/system-services/* -./s6rc-generator /usr/share/dbus-1/services/* +./s6-generator /usr/share/dbus-1/system-services/* +./s6-generator /usr/share/dbus-1/services/* ``` for the system and session bus, respectively. -You are free to further tweak this source database as you like, for example -removing unwanted activatable services. +You are free to further tweak this scandir as you like, for example removing +unwanted activatable services. [dbus-broker]: https://github.com/bus1/dbus-broker -[s6-rc]: https://skarnet.org/software/s6-rc +[s6]: https://skarnet.org/software/s6 diff --git a/activation.c b/activation.c index 7e99c99..c1f0141 100644 --- a/activation.c +++ b/activation.c @@ -48,25 +48,19 @@ struct service { tll(struct service) service_list = tll_init(); #define NAMEFILE "data/dbus-activatable-name" -#define SERVICEDIRS "servicedirs" #define SIZE(array) (sizeof(array)/sizeof(*array)) -void add_s6rc_servicedirs(const char* s6rc_livedir) { +void add_s6_servicedirs(const char* s6_dbuscandir) { struct dirent **namelist; int n; - size_t s6rc_livedir_len = strlen(s6rc_livedir); - char path[s6rc_livedir_len+1+SIZE(SERVICEDIRS)]; - memcpy(path, s6rc_livedir, s6rc_livedir_len); - path[s6rc_livedir_len] = '/'; - memcpy(path+s6rc_livedir_len+1, SERVICEDIRS, SIZE(SERVICEDIRS)); - n = scandir(path, &namelist, NULL, alphasort); + n = scandir(s6_dbuscandir, &namelist, NULL, alphasort); if (n == -1) { perror("scandir"); exit(EXIT_FAILURE); } - chdir(path); // TODO: remove this, the directory could be deleted by s6-rc-update + chdir(s6_dbuscandir); while (n--) { if (namelist[n]->d_name[0] != '.') { struct service service = {0}; @@ -110,16 +104,33 @@ static struct service *find_service_by_id(int id) { return NULL; } -const char* s6rc_livedir; // TODO: pass around properly +const char* s6_dbuscandir; // TODO: pass around properly int start_service(int id) { int r = -1; struct service* service = find_service_by_id(id); if (service) { char cmd[256]; - sprintf(cmd, "s6-rc -l %s change %s", s6rc_livedir, service->s6rc.s); + sprintf(cmd, "s6-svc -uwu -T 1000 %s/%s", s6_dbuscandir, service->s6rc.s); fprintf(stderr, "%s\n", cmd); r = system(cmd); } return r; } + +static int stop_service(int id) { + int r = -1; + struct service* service = find_service_by_id(id); + if (service) { + char cmd[256]; + sprintf(cmd, "s6-svc -d %s/%s", s6_dbuscandir, service->s6rc.s); + fprintf(stderr, "%s\n", cmd); + r = system(cmd); + } + return r; +} + +void stop_all_services() { + tll_foreach(service_list, it) + stop_service(it->item.id); +} diff --git a/controller.c b/controller.c index 7ea51fc..f992be6 100644 --- a/controller.c +++ b/controller.c @@ -116,3 +116,33 @@ void controller_run() { if (r < 0) handle_error("Failed to wait on bus"); } } + +#ifdef HAVE_S6 + +#include +#include + +void controller_run_signals() { + iopause_fd x[2] = { + { sd_bus_get_fd(bus_controller), IOPAUSE_READ, 0 }, + { selfpipe_init(), IOPAUSE_READ, 0 }, + }; + int r = selfpipe_trap(SIGTERM); + for (;;) { + /* Wait for the next request to process */ + r = iopause(x, 2, NULL, NULL); + if (r < 0) handle_error("Failed to wait on bus"); + if (x[0].revents & IOPAUSE_READ) + /* Process requests */ + while ((r = sd_bus_process(bus_controller, NULL))) + if (r < 0) handle_error("Failed to process bus"); + if (x[1].revents & IOPAUSE_READ) { + int c = selfpipe_read(); + break; + } + } + selfpipe_finish(); + stop_all_services(); +} + +#endif diff --git a/main.c b/main.c index d2cf498..e0a7569 100644 --- a/main.c +++ b/main.c @@ -13,8 +13,8 @@ static const char* default_dbus_socket_path = "/run/dbus/system_bus_socket"; #ifdef HAVE_S6 -static const char* default_s6rc_livedir = "/run/s6-rc"; -extern const char* s6rc_livedir; // TODO: pass around properly +static const char* default_s6_dbuscandir = "/run/dbus_activated_services"; +extern const char* s6_dbuscandir; // TODO: pass around properly #endif static const char* dummy_machine_id = "00000000000000000000000000000001"; @@ -35,7 +35,7 @@ optional arguments:\n\ -3 notify readiness on fd 3\n" #ifdef HAVE_S6 "\n\ - -l s6-rc livedir (default: %s)\n" + -a s6 scandir of dbus-activated services (default: %s)\n" #endif "\n\ -h show this help message and exit\n"; @@ -45,12 +45,12 @@ int main(int argc, char* argv[]) { bool notif = false; bool syslog = false; #ifdef HAVE_S6 - s6rc_livedir = default_s6rc_livedir; + s6_dbuscandir = default_s6_dbuscandir; #endif int opt; #ifdef HAVE_S6 - while ((opt = getopt(argc, argv, "d:hl:s3")) != -1) { + while ((opt = getopt(argc, argv, "d:ha:s3")) != -1) { #else while ((opt = getopt(argc, argv, "d:hs3")) != -1) { #endif @@ -59,12 +59,12 @@ int main(int argc, char* argv[]) { case 's': syslog = true; break; case '3': check_3_open(); notif = true; break; #ifdef HAVE_S6 - case 'l': s6rc_livedir = optarg; break; + case 'a': s6_dbuscandir = optarg; break; #endif default: printf(usage, default_dbus_socket_path #ifdef HAVE_S6 - , default_s6rc_livedir + , default_s6_dbuscandir #endif ); return EXIT_FAILURE; @@ -115,7 +115,7 @@ int main(int argc, char* argv[]) { if (logfd >= 0) close(logfd); #ifdef HAVE_S6 - add_s6rc_servicedirs(s6rc_livedir); + add_s6_servicedirs(s6_dbuscandir); #endif controller_setup(controller[0], dbus_socket_path); @@ -125,8 +125,11 @@ int main(int argc, char* argv[]) { close(3); } +#ifdef HAVE_S6 + controller_run_signals(); +#else controller_run(); - int wstatus; - waitpid(cpid, &wstatus, 0); +#endif + // do we need to do something here to make the broker exit "cleanly"? } } diff --git a/meson.build b/meson.build index 9d28b2a..0cdb3ea 100644 --- a/meson.build +++ b/meson.build @@ -27,11 +27,11 @@ add_project_arguments('-DHAVE_' + sdbus.name().to_upper() + '=1', language: 'c') executable('dbus-controller-dummy', ['controller.c', 'dbus.c', 'main.c', 'policy.c', 'syslog.c'], dependencies: [sdbus]) -if get_option('s6-rc').enabled() +if get_option('s6').enabled() tllist = dependency('tllist') cc = meson.get_compiler('c') skalibs = cc.find_library('skarnet') -executable('dbus-controller-s6rc', ['activation.c', 'controller.c', 'dbus.c', 'main.c', 'policy.c', 'syslog.c'], dependencies: [sdbus, skalibs, tllist], c_args : '-DHAVE_S6=1') +executable('dbus-controller-s6', ['activation.c', 'controller.c', 'dbus.c', 'main.c', 'policy.c', 'syslog.c'], dependencies: [sdbus, skalibs, tllist], c_args : '-DHAVE_S6=1') endif diff --git a/meson_options.txt b/meson_options.txt index 719d0ab..8c76bc4 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,2 +1,2 @@ option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') -option('s6-rc', type: 'feature', value: 'disabled') +option('s6', type: 'feature', value: 'disabled') diff --git a/tools/s6rc-generator b/tools/s6-generator similarity index 73% rename from tools/s6rc-generator rename to tools/s6-generator index 48b343a..d4e06db 100755 --- a/tools/s6rc-generator +++ b/tools/s6-generator @@ -1,13 +1,9 @@ #!/bin/sh -e -# Generate s6-rc service definitions for dbus services +# Generate s6 service definitions for dbus services # usage: pass the absolute paths of dbus service definitions, i.e. # /usr/share/dbus-1/system-services/* (for the system bus) # or /usr/share/dbus-1/services/* (for the session bus) -# The name of the dbus service which launches controller/broker -# all dbus activated services depend on it -DBUS_S6RC_SERVICE=dbus - mkdir -p 'dbus' cd 'dbus' @@ -20,8 +16,8 @@ for service in "$@"; do mkdir "$name_/data" echo "$name_" >> "$name_/data/dbus-activatable-name" echo 'longrun' >> "$name_/type" - echo "$DBUS_S6RC_SERVICE" >> "$name_/dependencies" echo '#!/bin/execlineb -P' >> "$name_/run" echo "$exec_" >> "$name_/run" chmod +x "$name_/run" + touch "$name_/down" done