]> git.mdlowis.com Git - proto/labwc.git/commitdiff
session: run shutdown script, clean up activation env before exit
authorAndrew J. Hesford <ajh@sideband.org>
Sun, 18 Feb 2024 17:23:14 +0000 (12:23 -0500)
committerAndrew J. Hesford <ajh@sideband.org>
Sun, 3 Mar 2024 02:30:03 +0000 (21:30 -0500)
docs/labwc-config.5.scd
include/common/string-helpers.h
include/config/session.h
src/common/string-helpers.c
src/config/session.c
src/main.c

index ec887f72242e461e2ccb0417aaceb7bfb0bd864a..57e76bdc81b3da2dbe577ceb0049d458fd0e7227 100644 (file)
@@ -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).
 
index 4e20430285558916e74797c5db967dd4fed2d93b..6d0ec9056a8139e5a05f9ceb8a68fc8c11d0c826 100644 (file)
@@ -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 */
index d1efbb60d250f047d81be0c05dfd7435e616d244..6de5f05d3cae3b7f8b936eedfb42a0d30fd24dc1 100644 (file)
@@ -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 */
index 37fdf4d07966dee1edf70c57af49d3de176325d4..0a1a8c81341809d078659c91acbddf45581e052e 100644 (file)
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
+#include <assert.h>
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -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;
+}
+
index c87b08657e824e6a622f23fcb39868c0171ffb78..bc9f6d473aff4ec195465fa6fb6c33dafb92b8ba 100644 (file)
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 #define _POSIX_C_SOURCE 200809L
+#include <assert.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #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);
+}
index 9bfe3a1dafc2398d02c019862e7179de4eac5d13..d4a12a800db4bb0d361ffffce06e8b29788c2694 100644 (file)
@@ -178,6 +178,8 @@ main(int argc, char *argv[])
 
        wl_display_run(server.wl_display);
 
+       session_shutdown();
+
        server_finish(&server);
 
        menu_finish(&server);