diff --git a/glfw/glfw.py b/glfw/glfw.py index db3651266..8733983c6 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -64,7 +64,6 @@ class Env: vcs_rev: str = '' binary_arch: BinaryArch = BinaryArch() native_optimizations: bool = False - has_systemd: bool = False primary_version: int = 0 secondary_version: int = 0 xt_version: str = '' @@ -123,7 +122,6 @@ class Env: ans.vcs_rev = self.vcs_rev ans.binary_arch = self.binary_arch ans.native_optimizations = self.native_optimizations - ans.has_systemd = self.has_systemd ans.primary_version = self.primary_version ans.secondary_version = self.secondary_version ans.xt_version = self.xt_version diff --git a/kitty/child.py b/kitty/child.py index a208a69ce..f1a507fed 100644 --- a/kitty/child.py +++ b/kitty/child.py @@ -349,7 +349,7 @@ class Child: fast_data_types.systemd_move_pid_into_new_scope(pid, f'kitty-{ppid}-{self.id}.scope', f'kitty child process: {pid} launched by: {ppid}') except NotImplementedError: pass - except (RuntimeError, OSError) as err: + except OSError as err: log_error("Could not move child process into a systemd scope: " + str(err)) return pid diff --git a/kitty/systemd.c b/kitty/systemd.c index 1c75929e0..65d9e2fb6 100644 --- a/kitty/systemd.c +++ b/kitty/systemd.c @@ -7,27 +7,90 @@ #include "data-types.h" #include "cleanup.h" +#include -#ifdef KITTY_HAS_SYSTEMD -#include -#include +#define FUNC(name, restype, ...) typedef restype (*name##_func)(__VA_ARGS__); static name##_func name = NULL +#define LOAD_FUNC(name) {\ + *(void **) (&name) = dlsym(systemd.lib, #name); \ + if (!name) { \ + const char* error = dlerror(); \ + if (error != NULL) { \ + log_error("Failed to load the function %s with error: %s", #name, error); return; \ + } \ + } \ +} + +typedef struct sd_bus sd_bus; static struct { + void *lib; sd_bus *user_bus; - bool initialized; + bool initialized, functions_loaded, ok; } systemd = {0}; +typedef struct { + const char *name; + const char *message; + int _need_free; +} sd_bus_error; +typedef struct sd_bus_message sd_bus_message; + +FUNC(sd_bus_default_user, int, sd_bus**); +FUNC(sd_bus_message_unref, sd_bus_message*, sd_bus_message*); +FUNC(sd_bus_error_free, void, sd_bus_error*); +FUNC(sd_bus_unref, sd_bus*, sd_bus*); +FUNC(sd_bus_message_new_method_call, int, sd_bus *, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member); +FUNC(sd_bus_message_append, int, sd_bus_message *m, const char *types, ...); +FUNC(sd_bus_message_open_container, int, sd_bus_message *m, char type, const char *contents); +FUNC(sd_bus_message_close_container, int, sd_bus_message *m); +FUNC(sd_pid_get_user_slice, int, pid_t pid, char **slice); +FUNC(sd_bus_call, int, sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply); + static void ensure_initialized(void) { - if (!systemd.initialized) { - systemd.initialized = true; - int ret = sd_bus_default_user(&systemd.user_bus); - if (ret < 0) { log_error("Failed to open systemd user bus with error: %s", strerror(-ret)); } + if (systemd.initialized) return; + systemd.initialized = true; + + const char* libnames[] = { +#if defined(_KITTY_SYSTEMD_LIBRARY) + _KITTY_SYSTEMD_LIBRARY, +#else + "libsystemd.so", + // some installs are missing the .so symlink, so try the full name + "libsystemd.so.0", + "libsystemd.so.0.38.0", +#endif + NULL + }; + for (int i = 0; libnames[i]; i++) { + systemd.lib = dlopen(libnames[i], RTLD_LAZY); + if (systemd.lib) break; } + if (systemd.lib == NULL) { + log_error("Failed to load %s with error: %s\n", libnames[0], dlerror()); + return; + } + LOAD_FUNC(sd_bus_default_user); + LOAD_FUNC(sd_bus_message_unref); + LOAD_FUNC(sd_bus_error_free); + LOAD_FUNC(sd_bus_unref); + LOAD_FUNC(sd_bus_message_new_method_call); + LOAD_FUNC(sd_bus_message_append); + LOAD_FUNC(sd_bus_message_open_container); + LOAD_FUNC(sd_bus_message_close_container); + LOAD_FUNC(sd_pid_get_user_slice); + LOAD_FUNC(sd_bus_call); + systemd.functions_loaded = true; + + int ret = sd_bus_default_user(&systemd.user_bus); + if (ret < 0) { log_error("Failed to open systemd user bus with error: %s", strerror(-ret)); return; } + systemd.ok = true; } -#define RAII_bus_error(name) __attribute__((cleanup(sd_bus_error_free))) sd_bus_error name = SD_BUS_ERROR_NULL; -#define RAII_message(name) __attribute__((cleanup(sd_bus_message_unrefp))) sd_bus_message *name = NULL; +static inline void err_cleanup(sd_bus_error *p) { sd_bus_error_free(p); } +#define RAII_bus_error(name) __attribute__((cleanup(err_cleanup))) sd_bus_error name = {0}; +static inline void msg_cleanup(sd_bus_message **p) { sd_bus_message_unref(*p); } +#define RAII_message(name) __attribute__((cleanup(msg_cleanup))) sd_bus_message *name = NULL; #define SYSTEMD_DESTINATION "org.freedesktop.systemd1" #define SYSTEMD_PATH "/org/freedesktop/systemd1" @@ -55,11 +118,6 @@ set_reply_error(const char* func_name, int r, const sd_bus_error *err) { static bool move_pid_into_new_scope(pid_t pid, const char* scope_name, const char *description) { - ensure_initialized(); - if (!systemd.user_bus) { - PyErr_SetString(PyExc_RuntimeError, "Could not connect to systemd user bus"); - return false; - } pid_t parent_pid = getpid(); RAII_bus_error(err); RAII_message(m); RAII_message(reply); int r; @@ -115,22 +173,32 @@ move_pid_into_new_scope(pid_t pid, const char* scope_name, const char *descripti static void finalize(void) { - if (systemd.user_bus) { - sd_bus_unref(systemd.user_bus); - } + if (systemd.user_bus) sd_bus_unref(systemd.user_bus); + if (systemd.lib) dlclose(systemd.lib); memset(&systemd, 0, sizeof(systemd)); } -#endif +static bool +ensure_initialized_and_useable(void) { + ensure_initialized(); + if (!systemd.ok) { + if (!systemd.lib) PyErr_SetString(PyExc_NotImplementedError, "Could not load libsystemd"); + else if (!systemd.functions_loaded) PyErr_SetString(PyExc_NotImplementedError, "Could not load libsystemd functions"); + else PyErr_SetString(PyExc_NotImplementedError, "Could not connect to systemd user bus"); + return false; + } + return true; +} static PyObject* systemd_move_pid_into_new_scope(PyObject *self UNUSED, PyObject *args) { long pid; const char *scope_name, *description; if (!PyArg_ParseTuple(args, "lss", &pid, &scope_name, &description)) return NULL; -#ifdef KITTY_HAS_SYSTEMD - move_pid_into_new_scope(pid, scope_name, description); -#else +#ifdef __APPLE__ PyErr_SetString(PyExc_NotImplementedError, "not supported on this platform"); +#else + if (!ensure_initialized_and_useable()) return NULL; + move_pid_into_new_scope(pid, scope_name, description); #endif if (PyErr_Occurred()) return NULL; Py_RETURN_NONE; @@ -145,9 +213,7 @@ static PyMethodDef module_methods[] = { bool init_systemd_module(PyObject *module) { -#ifdef KITTY_HAS_SYSTEMD register_at_exit_cleanup_func(SYSTEMD_CLEANUP_FUNC, finalize); -#endif if (PyModule_AddFunctions(module, module_methods) != 0) return false; return true; diff --git a/setup.py b/setup.py index 755e39b1b..7d0172534 100755 --- a/setup.py +++ b/setup.py @@ -197,6 +197,7 @@ class Options: egl_library: Optional[str] = os.getenv('KITTY_EGL_LIBRARY') startup_notification_library: Optional[str] = os.getenv('KITTY_STARTUP_NOTIFICATION_LIBRARY') canberra_library: Optional[str] = os.getenv('KITTY_CANBERRA_LIBRARY') + systemd_library: Optional[str] = os.getenv('KITTY_SYSTEMD_LIBRARY') fontconfig_library: Optional[str] = os.getenv('KITTY_FONTCONFIG_LIBRARY') building_arch: str = '' @@ -454,6 +455,7 @@ def init_env( egl_library: Optional[str] = None, startup_notification_library: Optional[str] = None, canberra_library: Optional[str] = None, + systemd_library: Optional[str] = None, fontconfig_library: Optional[str] = None, extra_logging: Iterable[str] = (), extra_include_dirs: Iterable[str] = (), @@ -545,6 +547,7 @@ def init_env( add_lpath('glfw/egl_context.c', '_GLFW_EGL_LIBRARY', egl_library) add_lpath('kitty/desktop.c', '_KITTY_STARTUP_NOTIFICATION_LIBRARY', startup_notification_library) add_lpath('kitty/desktop.c', '_KITTY_CANBERRA_LIBRARY', canberra_library) + add_lpath('kitty/systemd.c', '_KITTY_SYSTEMD_LIBRARY', systemd_library) add_lpath('kitty/fontconfig.c', '_KITTY_FONTCONFIG_LIBRARY', fontconfig_library) for path in extra_include_dirs: @@ -630,11 +633,6 @@ def kitty_env(args: Options) -> Env: else: cflags.extend(pkg_config('fontconfig', '--cflags-only-I')) platform_libs = [] - with suppress(SystemExit, subprocess.CalledProcessError): - cflags.extend(pkg_config('libsystemd', '--cflags-only-I', fatal=False)) - systemd_libs = pkg_config('libsystemd', '--libs') - platform_libs.extend(systemd_libs) - ans.has_systemd = True cflags.extend(pkg_config('harfbuzz', '--cflags-only-I')) platform_libs.extend(pkg_config('harfbuzz', '--libs')) pylib = get_python_flags(args, cflags) @@ -730,8 +728,6 @@ def get_source_specific_defines(env: Env, src: str) -> Tuple[str, List[str], Opt return src, ['3rdparty/base64',], base64_defines(env.binary_arch.isa) if src == 'kitty/screen.c': return src, [], [f'PRIMARY_VERSION={env.primary_version}', f'SECONDARY_VERSION={env.secondary_version}', f'XT_VERSION="{env.xt_version}"'] - if src == 'kitty/systemd.c': - return src, [], (['KITTY_HAS_SYSTEMD'] if env.has_systemd else None) if src == 'kitty/fast-file-copy.c': return src, [], (['HAS_COPY_FILE_RANGE'] if env.has_copy_file_range else None) try: @@ -1000,7 +996,7 @@ def init_env_from_args(args: Options, native_optimizations: bool = False) -> Non global env env = init_env( args.debug, args.sanitize, native_optimizations, args.link_time_optimization, args.profile, - args.egl_library, args.startup_notification_library, args.canberra_library, args.fontconfig_library, + args.egl_library, args.startup_notification_library, args.canberra_library, args.systemd_library, args.fontconfig_library, args.extra_logging, args.extra_include_dirs, args.ignore_compiler_warnings, args.building_arch, args.extra_library_dirs, verbose=args.verbose > 0, vcs_rev=args.vcs_rev, ) @@ -1956,6 +1952,13 @@ def option_parser() -> argparse.ArgumentParser: # {{{ help='The filename argument passed to dlopen for libcanberra.' ' This can be used to change the name of the loaded library or specify an absolute path.' ) + p.add_argument( + '--systemd-library', + type=str, + default=Options.systemd_library, + help='The filename argument passed to dlopen for libsystemd.' + ' This can be used to change the name of the loaded library or specify an absolute path.' + ) p.add_argument( '--fontconfig-library', type=str,