Hi Guix, So I've been trying to patch `elogind-service-type` in order to add support for the `system-sleep` and `system-shutdown` hook directories [1]. This would give users a way to run scripts before/after suspend/hibernate, a feature that is currently missing in Guix [2]. I've run into a strange unbound-variable error that I'm not able to troubleshoot further, and I would really appreciate any guidance.
The requirements are simple - put script files in /etc/elogind/system-sleep/ or /etc/elogind/system-shutdown/ directories, and they will be run pre/post suspend/hibernate/poweroff/whatever, with arguments indicating what is happening. I'm trying to do this by adding corresponding fields to `elogind-configuration` each taking a list of local (script) files, and then having `elogind-service` extend `activation-service-type`. `elogind-activation` then takes care of installing the files into the hook directories. Here's a patch showing what I have so far: diff --git a/gnu/services/desktop.scm b/gnu/services/desktop.scm index 274aeeef9b..1237d53b6f 100644 --- a/gnu/services/desktop.scm +++ b/gnu/services/desktop.scm @@ -1084,7 +1084,10 @@ (define-record-type* <elogind-configuration> elogind-configuration (hibernate-delay-seconds elogind-hibernate-delay-seconds (default *unspecified*)) (suspend-estimation-seconds elogind-suspend-estimation-seconds - (default *unspecified*))) + (default *unspecified*)) + (system-sleep-hook-files elogind-system-sleep-hook-files (default '())) + (system-shutdown-hook-files elogind-system-shutdown-hook-files (default '()))) +; TODO AllowPowerOffInterrupts, AllowSuspendInterrupts, BroadcastPowerOffInterrupts, BroadcastSuspendInterrupts (define (elogind-configuration-file config) (define (yesno x) @@ -1174,6 +1177,19 @@ (define (elogind-configuration-file config) ("HibernateDelaySec" (maybe-non-negative-integer elogind-hibernate-delay-seconds)) ("SuspendEstimationSec" (maybe-non-negative-integer elogind-suspend-estimation-seconds)))) +(define (elogind-activation config) + "Return the activation GEXP for CONFIG." + (with-imported-modules (source-module-closure '((guix build utils) (srfi srfi-26))) + #~(let ((sleep-dir "/etc/elogind/system-sleep") + (shutdown-dir "/etc/elogind/system-shutdown")) + (use-modules (guix build utils) (srfi srfi-26)) + (for-each + (cut install-file <> sleep-dir) + '#$(elogind-system-sleep-hook-files config)) + (for-each + (cut install-file <> shutdown-dir) + '#$(elogind-system-shutdown-hook-files config))))) + (define (elogind-dbus-service config) "Return a @file{org.freedesktop.login1.service} file that tells D-Bus how to \"start\" elogind. In practice though, our elogind is started when booting by @@ -1294,6 +1310,10 @@ (define elogind-service-type (service-extension pam-root-service-type pam-extension-procedure) + ;; Install sleep/shutdown hook files. + (service-extension activation-service-type + elogind-activation) + ;; We need /run/user, /run/systemd, etc. (service-extension file-system-service-type (const %elogind-file-systems)))) And here is a test configuration file I'm running it with (a stripped-down version of my own system config): (use-modules (gnu)) (use-service-modules cups desktop networking ssh xorg) (operating-system (locale "en_US.utf8") (timezone "America/New_York") (keyboard-layout (keyboard-layout "us")) (host-name "guixy") ;; The list of user accounts ('root' is implicit). (users (cons* (user-account (name "me") (comment "Me") (group "users") (home-directory "/home/me") (supplementary-groups '("wheel" "netdev" "audio" "video"))) %base-user-accounts)) (packages %base-packages) (services (append (list (service elogind-service-type (elogind-configuration (system-sleep-hook-files (list (local-file "./test-sleep-script")))) )) %base-services)) (bootloader (bootloader-configuration (bootloader grub-bootloader) (targets (list "/dev/sda")) (keyboard-layout keyboard-layout))) (swap-devices (list (swap-space (target (uuid "f04306ed-0fcb-44ab-b3fd-1921e03f8654"))))) (file-systems (cons* (file-system (mount-point "/") (device (uuid "ddcfb0e2-7e45-4c44-86ce-ac81dd3e53ab" 'btrfs)) (type "btrfs")) %base-file-systems))) `test-sleep-script` is an executable in the same directory just containing #!/bin/sh echo "$@" >> /home/me/elogind-test-log I'm testing this by running the following in my Guix checkout (after `guix shell -D guix --pure -- ./bootstrap`, and similarly for `./configure --localstatedir=/var` and `make`): sudo $(./pre-inst-env guix system container path/to/test-config.scm) This is the output I see: system container is running as PID 12670 WARNING: (guile-user): imported module (guix build utils) overrides core binding `delete' Run 'sudo guix container exec 12670 /run/current-system/profile/bin/bash --login' or run 'sudo nsenter -a -t 12670' to get a shell into it. WARNING: (guile-user): imported module (guix build utils) overrides core binding `delete' making '/gnu/store/2jwl0mc296r71yf03q9ic75zmc39y3ni-system' the current system... populating /etc from /gnu/store/y89n82vyqlzcsn08bj34rgjjixkkfhla-etc... WARNING: (guile-user): imported module (guix build utils) overrides core binding `delete' WARNING: (guile-user): imported module (guix build utils) overrides core binding `delete' setting up privileged programs in '/run/privileged/bin'... WARNING: (guile-user): imported module (guix build utils) overrides core binding `delete' Please wait while gathering entropy to generate the key pair; this may take time... WARNING: (guile-user): imported module (guix build utils) overrides core binding `delete' Backtrace: 13 (primitive-load "/gnu/store/9v4y45fcazxp8czkabawfqyc2bx…") In gnu/build/linux-container.scm: 300:8 12 (call-with-temporary-directory #<procedure 7f46a4c38d20…>) 397:16 11 (_ "/tmp/guix-directory.RDCnGc") 62:6 10 (call-with-clean-exit #<procedure 7f46a4c43100 at gnu/b…>) In unknown file: 9 (primitive-load "/gnu/store/2jwl0mc296r71yf03q9ic75zmc3…") In ice-9/eval.scm: 619:8 8 (_ #f) In unknown file: 7 (primitive-load "/gnu/store/yz4fklxd6qpcsv4x54m6a1dk4ck…") In srfi/srfi-1.scm: 634:9 6 (for-each #<procedure primitive-load (_)> _) In unknown file: 5 (primitive-load "/gnu/store/z8yvff5ni8w91ms4glmwhyczpsk…") In ice-9/eval.scm: 619:8 4 (_ #(#<directory (guile-user) 7f46a7f16c80> "/etc/el…" …)) 159:9 3 (_ #(#<directory (guile-user) 7f46a7f16c80> "/etc/el…" …)) 163:9 2 (_ #(#<directory (guile-user) 7f46a7f16c80> "/etc/el…" …)) 223:20 1 (proc #(#<directory (guile-user) 7f46a7f16c80> "/etc…" …)) In unknown file: 0 (%resolve-variable (7 . <>) #<directory (guile-user) 7f…>) ERROR: In procedure %resolve-variable: Unbound variable: <> Any ideas on where to go from here would be most appreciated. [1] https://manpages.debian.org/unstable/elogind/loginctl.1.en.html#Hook_directories [2] https://www.reddit.com/r/GUIX/comments/s3ab3f/running_scripts_commands_on_suspend_resume/?rdt=46057