From f90b7dca2a3e862ca64ced14165181c70938b6d7 Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Sun, 18 Feb 2024 12:23:14 -0500 Subject: [PATCH] session: run shutdown script, clean up activation env before exit --- docs/labwc-config.5.scd | 23 ++++++++--- include/common/string-helpers.h | 19 +++++++++ include/config/session.h | 6 +++ src/common/string-helpers.c | 71 +++++++++++++++++++++++++++++++++ src/config/session.c | 52 +++++++++++++++++++----- src/main.c | 2 + 6 files changed, 157 insertions(+), 16 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index ec887f72..57e76bdc 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -8,7 +8,7 @@ labwc - configuration files Labwc uses openbox-3.6 specification for configuration and theming, but does not support all options. The following files form the basis of the labwc -configuration: rc.xml, menu.xml, autostart and environment. +configuration: rc.xml, menu.xml, autostart, shutdown and environment. No configuration files are needed to start and run labwc. @@ -34,11 +34,8 @@ alternative. The configuration directory location can be override with the -C command line option. -All configuration and theme files except autostart are re-loaded on receiving -signal SIGHUP. - -The *autostart* file is executed as a shell script. This is the place for -executing clients for handling background images, panels and similar. +All configuration and theme files except autostart and shutdown are re-loaded on +receiving signal SIGHUP. The *environment* file is parsed as *variable=value* and sets environment variables accordingly. It is recommended to specify keyboard layout settings and @@ -48,6 +45,20 @@ sourced prior to running openbox. Note: Tilde (~) and environment variables in the value are expanded, but subshell syntax and apostrophes are ignored. +The *autostart* file is executed as a shell script after labwc has read its +configuration and set variables defined in the environment file. Additionally, +the environment variables WAYLAND_DISPLAY and (when labwc is built with Xwayland +support) DISPLAY will be defined. This is the place for executing clients for +handling background images, panels and other tasks that should run automatically +when labwc launches. + +The *shutdown* file is executed as a shell script when labwc is preparing to +terminate itself. All environment variables, including WAYLAND_DISPLAY and +DISPLAY, will be available to the script. However, because the script runs +asynchronously with other termination tasks, the shutdown file should not assume +that the display will be usable. This file is useful to perform any custom +operations necessary to finalize a labwc session. + The *menu.xml* file defines the context/root-menus and is described in labwc-menu(5). diff --git a/include/common/string-helpers.h b/include/common/string-helpers.h index 4e204302..6d0ec905 100644 --- a/include/common/string-helpers.h +++ b/include/common/string-helpers.h @@ -43,4 +43,23 @@ void string_truncate_at_pattern(char *buf, const char *pattern); */ char *strdup_printf(const char *fmt, ...); +/** + * str_join - format and join an array of strings with a separator + * @parts: NULL-terminated array of string parts to be joined + * @fmt: printf-style format string applied to each part + * @sep: separator inserted between parts when joining + * + * A new string is allocated to hold the joined result. The user must free the + * returned string. Returns NULL on error. + * + * Each part of the array is converted via the equivalent of sprintf(output, + * fmt, part), so fmt should include a single "%s" format specification. If fmt + * is NULL, a default "%s" will be used to copy each part verbatim. + * + * The separator is arbitrary. When the separator is NULL, a single space will + * be used. + */ +char *str_join(const char * const parts[], + const char *restrict fmt, const char *restrict sep); + #endif /* LABWC_STRING_HELPERS_H */ diff --git a/include/config/session.h b/include/config/session.h index d1efbb60..6de5f05d 100644 --- a/include/config/session.h +++ b/include/config/session.h @@ -15,4 +15,10 @@ void session_environment_init(void); */ void session_autostart_init(void); +/** + * session_shutdown - run session shutdown file as shell script + * Note: Same as `sh ~/.config/labwc/shutdown` (or equivalent XDG config dir) + */ +void session_shutdown(void); + #endif /* LABWC_SESSION_H */ diff --git a/src/common/string-helpers.c b/src/common/string-helpers.c index 37fdf4d0..0a1a8c81 100644 --- a/src/common/string-helpers.c +++ b/src/common/string-helpers.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include #include @@ -83,3 +84,73 @@ strdup_printf(const char *fmt, ...) } return p; } + +char * +str_join(const char * const parts[], + const char *restrict fmt, const char *restrict sep) +{ + assert(parts); + + if (!fmt) { + fmt = "%s"; + } + + if (!sep) { + sep = " "; + } + + size_t size = 0; + size_t n_parts = 0; + + size_t sep_len = strlen(sep); + + /* Count the length of each formatted string */ + for (const char *const *s = parts; *s; ++s) { + int n = snprintf(NULL, 0, fmt, *s); + if (n < 0) { + return NULL; + } + size += (size_t)n; + ++n_parts; + } + + if (n_parts < 1) { + return NULL; + } + + /* Need (n_parts - 1) separators, plus one NULL terminator */ + size += (n_parts - 1) * sep_len + 1; + + /* Concatenate the strings and separators */ + char *buf = xzalloc(size); + char *p = buf; + for (const char *const *s = parts; *s; ++s) { + int n = 0; + + if (p != buf) { + n = snprintf(p, size, "%s", sep); + if (n < 0 || (size_t)n >= size) { + p = NULL; + break; + } + size -= (size_t)n; + p += (size_t)n; + } + + n = snprintf(p, size, fmt, *s); + if (n < 0 || (size_t)n >= size) { + p = NULL; + break; + } + size -= (size_t)n; + p += (size_t)n; + } + + if (!p) { + free(buf); + return NULL; + } + + return buf; +} + diff --git a/src/config/session.c b/src/config/session.c index c87b0865..bc9f6d47 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #define _POSIX_C_SOURCE 200809L +#include #include #include #include @@ -9,11 +10,19 @@ #include "common/buf.h" #include "common/dir.h" #include "common/file-helpers.h" +#include "common/mem.h" #include "common/spawn.h" #include "common/string-helpers.h" #include "config/session.h" #include "labwc.h" +static const char *const env_vars[] = { + "DISPLAY", + "WAYLAND_DISPLAY", + "XDG_CURRENT_DESKTOP", + NULL, +}; + static void process_line(char *line) { @@ -65,7 +74,7 @@ read_environment_file(const char *filename) } static void -update_activation_env(const char *env_keys) +update_activation_env(bool initialize) { if (!getenv("DBUS_SESSION_BUS_ADDRESS")) { /* Prevent accidentally auto-launching a dbus session */ @@ -75,13 +84,22 @@ update_activation_env(const char *env_keys) } wlr_log(WLR_INFO, "Updating dbus execution environment"); - char *cmd = strdup_printf("dbus-update-activation-environment %s", env_keys); + char *env_keys = str_join(env_vars, "%s", " "); + char *env_unset_keys = initialize ? NULL : str_join(env_vars, "%s=", " "); + + char *cmd = + strdup_printf("dbus-update-activation-environment %s", + initialize ? env_keys : env_unset_keys); spawn_async_no_shell(cmd); free(cmd); - cmd = strdup_printf("systemctl --user import-environment %s", env_keys); + cmd = strdup_printf("systemctl --user %s %s", + initialize ? "import-environment" : "unset-environment", env_keys); spawn_async_no_shell(cmd); free(cmd); + + free(env_keys); + free(env_unset_keys); } void @@ -120,14 +138,11 @@ session_environment_init(void) paths_destroy(&paths); } -void -session_autostart_init(void) +static void +run_session_script(const char *script) { - /* Update dbus and systemd user environment, each may fail gracefully */ - update_activation_env("DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP"); - struct wl_list paths; - paths_config_create(&paths, "autostart"); + paths_config_create(&paths, script); bool should_merge_config = rc.merge_config; struct wl_list *(*iter)(struct wl_list *list); @@ -138,7 +153,7 @@ session_autostart_init(void) if (!file_exists(path->string)) { continue; } - wlr_log(WLR_INFO, "run autostart file %s", path->string); + wlr_log(WLR_INFO, "run session script %s", path->string); char *cmd = strdup_printf("sh %s", path->string); spawn_async_no_shell(cmd); free(cmd); @@ -149,3 +164,20 @@ session_autostart_init(void) } paths_destroy(&paths); } + +void +session_autostart_init(void) +{ + /* Update dbus and systemd user environment, each may fail gracefully */ + update_activation_env(/* initialize */ true); + run_session_script("autostart"); +} + +void +session_shutdown(void) +{ + run_session_script("shutdown"); + + /* Clear the dbus and systemd user environment, each may fail gracefully */ + update_activation_env(/* initialize */ false); +} diff --git a/src/main.c b/src/main.c index 9bfe3a1d..d4a12a80 100644 --- a/src/main.c +++ b/src/main.c @@ -178,6 +178,8 @@ main(int argc, char *argv[]) wl_display_run(server.wl_display); + session_shutdown(); + server_finish(&server); menu_finish(&server); -- 2.52.0