From a500d11f0ff287bfe1c03c0cae4eb8466663a7ad Mon Sep 17 00:00:00 2001 From: Jonathan GUILLOT Date: Wed, 10 Sep 2025 22:36:28 +0200 Subject: [PATCH] Add optional feature to notify status to systemd When enabled, the feature uses libsystemd to notify service manager about the following states: - ready - periodic alive ping (20s) - stopping It allows to use Cage in a systemd unit with following options: Type=notify WatchdogSec=30s --- cage.c | 19 +++++++++++++++++++ config.h.in | 1 + meson.build | 7 +++++++ meson_options.txt | 1 + notify.h | 24 ++++++++++++++++++++++++ notify_systemd.c | 33 +++++++++++++++++++++++++++++++++ server.h | 1 + 7 files changed, 86 insertions(+) create mode 100644 notify.h create mode 100644 notify_systemd.c diff --git a/cage.c b/cage.c index 9b7c510..2124a41 100644 --- a/cage.c +++ b/cage.c @@ -52,6 +52,7 @@ #endif #include "idle_inhibit_v1.h" +#include "notify.h" #include "output.h" #include "seat.h" #include "server.h" @@ -220,6 +221,16 @@ handle_signal(int signal, void *data) } } +static int +handle_alive_timer(void *data) +{ + struct cg_server *server = data; + + notify_set_state(CAGE_ALIVE); + wl_event_source_timer_update(server->alive_source, CAGE_ALIVE_PERIOD_MS); + return 0; +} + static void usage(FILE *file, const char *cage) { @@ -311,6 +322,7 @@ main(int argc, char *argv[]) struct wl_event_loop *event_loop = wl_display_get_event_loop(server.wl_display); struct wl_event_source *sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server); struct wl_event_source *sigterm_source = wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server); + server.alive_source = wl_event_loop_add_timer(event_loop, handle_alive_timer, &server); server.backend = wlr_backend_autocreate(event_loop, &server.session); if (!server.backend) { @@ -593,8 +605,14 @@ main(int argc, char *argv[]) } seat_center_cursor(server.seat); + + notify_set_state(CAGE_READY); + wl_event_source_timer_update(server.alive_source, CAGE_ALIVE_PERIOD_MS); + wl_display_run(server.wl_display); + notify_set_state(CAGE_STOPPING); + #if CAGE_HAS_XWAYLAND if (xwayland) { wl_list_remove(&server.new_xwayland_surface.link); @@ -623,6 +641,7 @@ end: wl_event_source_remove(sigint_source); wl_event_source_remove(sigterm_source); + wl_event_source_remove(server.alive_source); if (sigchld_source) { wl_event_source_remove(sigchld_source); } diff --git a/config.h.in b/config.h.in index 51137ec..580926d 100644 --- a/config.h.in +++ b/config.h.in @@ -2,6 +2,7 @@ #define CG_CONFIG_H #mesondefine CAGE_HAS_XWAYLAND +#mesondefine CAGE_HAS_SYSTEMD #mesondefine CAGE_VERSION diff --git a/meson.build b/meson.build index 07ca800..1ef3b3b 100644 --- a/meson.build +++ b/meson.build @@ -39,6 +39,7 @@ wlroots = dependency('wlroots-0.19', fallback: ['wlroots', 'wlroots']) wayland_protos = dependency('wayland-protocols', version: '>=1.14') wayland_server = dependency('wayland-server') xkbcommon = dependency('xkbcommon') +systemd = dependency('libsystemd', required: get_option('systemd')) math = cc.find_library('m') wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') @@ -82,6 +83,7 @@ endif conf_data = configuration_data() conf_data.set10('CAGE_HAS_XWAYLAND', have_xwayland) +conf_data.set10('CAGE_HAS_SYSTEMD', systemd.found()) conf_data.set_quoted('CAGE_VERSION', version) scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) @@ -136,6 +138,10 @@ if conf_data.get('CAGE_HAS_XWAYLAND', 0) == 1 cage_headers += 'xwayland.h' endif +if conf_data.get('CAGE_HAS_SYSTEMD', 0) == 1 + cage_sources += 'notify_systemd.c' +endif + executable( meson.project_name(), cage_sources + cage_headers, @@ -144,6 +150,7 @@ executable( wayland_server, wlroots, xkbcommon, + systemd, math, ], install: true, diff --git a/meson_options.txt b/meson_options.txt index e40a23d..16a4e14 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') +option('systemd', type: 'feature', value: 'disabled', description: 'Notify status to systemd') diff --git a/notify.h b/notify.h new file mode 100644 index 0000000..bea9bbc --- /dev/null +++ b/notify.h @@ -0,0 +1,24 @@ +#ifndef CG_NOTIFY_H +#define CG_NOTIFY_H + +#include "config.h" + +#define CAGE_ALIVE_PERIOD_MS (20000) + +enum cg_notify_state { + CAGE_READY, + CAGE_ALIVE, + CAGE_STOPPING, +}; + +#if !CAGE_HAS_SYSTEMD +static inline void +notify_set_state(enum cg_notify_state state) +{ + /* Nothing */ +} +#else +void notify_set_state(enum cg_notify_state state); +#endif + +#endif diff --git a/notify_systemd.c b/notify_systemd.c new file mode 100644 index 0000000..95e333b --- /dev/null +++ b/notify_systemd.c @@ -0,0 +1,33 @@ +/* + * Cage: A Wayland kiosk. + * + * Copyright (C) 2018-2020 Jente Hidskes + * + * See the LICENSE file accompanying this file. + */ + +#include "config.h" + +#include + +#include "notify.h" + +void +notify_set_state(enum cg_notify_state state) +{ + const char *sd_state; + + switch (state) { + case CAGE_READY: + sd_state = "READY=1"; + break; + case CAGE_ALIVE: + sd_state = "WATCHDOG=1"; + break; + case CAGE_STOPPING: + sd_state = "STOPPING=1"; + break; + } + + sd_notify(0, sd_state); +} diff --git a/server.h b/server.h index 00c2a61..9ec8a4a 100644 --- a/server.h +++ b/server.h @@ -66,6 +66,7 @@ struct cg_server { bool return_app_code; bool terminated; enum wlr_log_importance log_level; + struct wl_event_source *alive_source; }; void server_terminate(struct cg_server *server);