session: run shutdown script, clean up activation env before exit

This commit is contained in:
Andrew J. Hesford 2024-02-18 12:23:14 -05:00
parent 4ddeb3cd42
commit f90b7dca2a
6 changed files with 157 additions and 16 deletions

View 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).

View 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 */

View 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 */

View 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;
}

View 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>
@ -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);
}

View file

@ -178,6 +178,8 @@ main(int argc, char *argv[])
wl_display_run(server.wl_display);
session_shutdown();
server_finish(&server);
menu_finish(&server);