Package: guix;
Reported by: Ludovic Courtès <ludo <at> gnu.org>
Date: Mon, 14 Apr 2025 16:48:02 UTC
Severity: important
Done: Maxim Cournoyer <maxim.cournoyer <at> gmail.com>
Bug is archived. No further changes may be made.
Message #22 received at 77806 <at> debbugs.gnu.org (full text, mbox):
From: Maxim Cournoyer <maxim.cournoyer <at> gmail.com> To: 77806 <at> debbugs.gnu.org Cc: Maxim Cournoyer <maxim.cournoyer <at> gmail.com> Subject: [PATCH v2] services: elogind: Split sleep.conf and port to define-configuration. Date: Fri, 25 Apr 2025 11:10:16 +0900
* gnu/services/desktop.scm (pascal-case): New procedure. (<elogind-configuration>): Rewrite in terms of define-configuration. (elogind-configuration-file): Delete. (maybe-list-of-suspend-states?, maybe-list-of-suspend-modes?) maybe-list-of-user-names?, maybe-boolean?maybe-package?) (maybe-action?, maybe-percent?, maybe-list-of-strings?) (maybe-list-of-hibernation-modes?, maybe-non-negative-integer?) (non-negative-integer?, percent?, char-set:user-name, user-name?) (list-of-user-names?, %elogind-actions, action?, %linux-suspend-states) (string->symbol/maybe, suspend-state?, list-of-suspend-states?) (%linux-suspend-modes, suspend-mode?, list-of-suspend-modes?) (%linux-hibernation-modes, hibernation-mode?, list-of-hibernation-modes?) (elogind-deprecated-empty-serializer, list-of-file-likes?) (elogind-serialize-boolean, elogind-base-serializer, elogind-serialize-action) (elogind-serialize-non-negative-integer, elogind-serialize-percent) (elogind-list-serializer, elogind-serialize-list-of-strings) (elogind-serialize-list-of-user-names, elogind-serialize-list-of-suspend-states) (elogind-serialize-list-of-suspend-modes) (elogind-serialize-list-of-hibernation-modes) (%elogind-configuration-sleep-fields, logind.conf, sleep.conf): New procedures. (elogind-etc-directory): Create the main configuration files there too. (elogind-dbus-service): Adjust for package accessor name change. (pam-extension-procedure, elogind-shepherd-service) (elogind-service-type): Likewise. * doc/guix.texi (Desktop Services): Fully document configuration options. Fixes: bug#77806 Change-Id: I8767891871d83e58d64995ec986a7d01689fa6d8 --- doc/guix.texi | 191 +++++++----- gnu/services/desktop.scm | 628 ++++++++++++++++++++++++++------------- 2 files changed, 524 insertions(+), 295 deletions(-) diff --git a/doc/guix.texi b/doc/guix.texi index be2fbbaf5bc..7b418a40892 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -26264,129 +26264,158 @@ Desktop Services @code{<elogind-configuration>} object. @end defvar -@c TODO: field descriptions. This is best done by refactoring -@c elogind-configuration to use define-configuration which embeds the -@c descriptions in the code and then use configuration->documentation. +@c Auto-generated via (configuration->documentation 'elogind-configuration). +@c %start of fragment + @deftp {Data Type} elogind-configuration -Data type representing the configuration of @command{elogind}. +Available @code{elogind-configuration} fields are: @table @asis @item @code{elogind} (default: @code{elogind}) (type: file-like) -... +The elogind package to use. -@item @code{kill-user-processes?} (default: @code{#f}) (type: boolean) -... +@item @code{system-sleep-hook-files} (default: @code{()}) (type: list-of-file-likes) +A list of executables (file-like objects) that will be installed into +the @file{/etc/elogind/system-sleep} hook directory. See `Hook +directories' in the @samp{loginctl(1)} man page for more information. -@item @code{kill-only-users} (default: @code{'()}) (type: list) -... +@item @code{system-shutdown-hook-files} (default: @code{()}) (type: list-of-file-likes) +A list of executables (file-like objects) that will be installed into +the @file{/etc/elogind/system-shutdown/} hook directory. -@item @code{kill-exclude-users} (default: @code{'("root")}) (type: list-of-string) -... +@item @code{allow-power-off-interrupts?} (default: @code{#f}) (type: maybe-boolean) +Whether the executables in elogind's hook directories (see above) can +cause a power-off action to be cancelled (interrupted) by printing an +appropriate error message to stdout. -@item @code{inhibit-delay-max-seconds} (default: @code{5}) (type: integer) -... +@item @code{allow-suspend-interrupts?} (default: @code{#f}) (type: maybe-boolean) +Likewise as the @code{allow-power-off-interrupts?} option, but for the +suspend action. -@item @code{handle-power-key} (default: @code{'poweroff}) (type: symbol) -... +@item @code{broadcast-power-off-interrupts?} (default: @code{#f}) (type: maybe-boolean) +Whether an interrupt of a power-off action is broadcasted. -@item @code{handle-suspend-key} (default: @code{'suspend}) (type: symbol) -... +@item @code{broadcast-suspend-interrupts?} (default: @code{#f}) (type: maybe-boolean) +Whether an interrupt of a suspend action is broadcasted. -@item @code{handle-hibernate-key} (default: @code{'hibernate}) (type: symbol) -... +@item @code{kill-user-processes?} (default: @code{#f}) (type: maybe-boolean) +Whether the processes of a user should be killed when the user logs out. -@item @code{handle-lid-switch} (default: @code{'suspend}) (type: symbol) -... +@item @code{kill-only-users} (type: maybe-list-of-user-names) +Usernames whose processes should be killed, regardless the value of +@code{kill-user-processes?}. -@item @code{handle-lid-switch-docked} (default: @code{'ignore}) (type: symbol) -... +@item @code{kill-exclude-users} (default: @code{("root")}) (type: maybe-list-of-user-names) +Usernames whose processes should @emph{not} be killed, regardless the +value of @code{kill-user-processes?}. -@item @code{handle-lid-switch-external-power} (default: @code{*unspecified*}) (type: symbol) -... +@item @code{inhibit-delay-max-seconds} (default: @code{5}) (type: maybe-non-negative-integer) +The maximum time a system shutdown or sleep request is delayed due to an +inhibitor lock of type delay being active before the inhibitor is +ignored and the operation executes anyway. -@item @code{power-key-ignore-inhibited?} (default: @code{#f}) (type: boolean) -... +@item @code{handle-power-key} (default: @code{poweroff}) (type: maybe-action) +The action done when the power key is pressed. The compiled default is +@code{'poweroff}. -@item @code{suspend-key-ignore-inhibited?} (default: @code{#f}) (type: boolean) -... +@item @code{handle-suspend-key} (default: @code{suspend}) (type: maybe-action) +The action done when the suspend key is pressed. The -@item @code{hibernate-key-ignore-inhibited?} (default: @code{#f}) (type: boolean) -... +@item @code{handle-hibernate-key} (default: @code{hibernate}) (type: maybe-action) +The action done when the hibernate key is pressed. -@item @code{lid-switch-ignore-inhibited?} (default: @code{#t}) (type: boolean) -... +@item @code{handle-lid-switch} (default: @code{suspend}) (type: maybe-action) +The action done when the lid is closed. -@item @code{holdoff-timeout-seconds} (default: @code{30}) (type: integer) -... +@item @code{handle-lid-switch-docked} (default: @code{ignore}) (type: maybe-action) +The action done when the lid is closed and the device docked. -@item @code{idle-action} (default: @code{'ignore}) (type: symbol) -... +@item @code{handle-lid-switch-external-power} (default: @code{suspend}) (type: maybe-action) +The action done when the lid is closed and the device is externally +powered. -@item @code{idle-action-seconds} (default: @code{(* 30 60)}) (type: integer) -... +@item @code{power-key-ignore-inhibited?} (default: @code{#f}) (type: maybe-boolean) +Whether to ignore high-level inhibitor locks (shutdown, reboot, sleep or +idle) when the power key is pressed. -@item @code{runtime-directory-size-percent} (default: @code{10}) (type: integer) -... +@item @code{suspend-key-ignore-inhibited?} (default: @code{#f}) (type: maybe-boolean) +Whether to ignore high-level inhibitor locks (shutdown, reboot, sleep or +idle) when the suspend key is pressed. -@item @code{runtime-directory-size} (default: @code{#f}) (type: integer) -... +@item @code{hibernate-key-ignore-inhibited?} (default: @code{#f}) (type: maybe-boolean) +Whether to ignore high-level inhibitor locks (shutdown, reboot, sleep or +idle) when the hibernate key is pressed. -@item @code{remove-ipc?} (default: @code{#t}) (type: boolean) -... +@item @code{lid-switch-ignore-inhibited?} (default: @code{#f}) (type: maybe-boolean) +Whether to ignore high-level inhibitor locks (shutdown, reboot, sleep or +idle) when the lid is closed. -@item @code{suspend-state} (default: @code{'("mem" "standby" "freeze")}) (type: list) -... +@item @code{holdoff-timeout-seconds} (default: @code{30}) (type: maybe-non-negative-integer) +Specifies the number of seconds after system startup or system resume +during which elogind will hold off on reacting to lid events. -@item @code{suspend-mode} (default: @code{'()}) (type: list) -... +@item @code{idle-action} (default: @code{ignore}) (type: maybe-action) +Action to take when the system is idle. -@item @code{hibernate-state} (default: @code{'("disk")}) (type: list) -... +@item @code{idle-action-seconds} (type: maybe-non-negative-integer) +The delay after which the action configured in @code{idle-action} is +taken after the system is idle. -@item @code{hibernate-mode} (default: @code{'("platform" "shutdown")}) (type: list) -... +@item @code{runtime-directory-size-percent} (type: maybe-percent) +Set the size limit, in percent, on the @env{XDG_RUNTIME_DIR} runtime +directory for each user who logs in. This specifies the per-user size +limit relative to the amount of physical @acronym{RAM,read access +memory}. This value takes precedence over that specified via +@code{runtime-directory-size}. -@item @code{hybrid-sleep-state} (default: @code{'("disk")}) (type: list) -... +@item @code{runtime-directory-size} (type: maybe-non-negative-integer) +Set the size limit, in bytes, on the @env{XDG_RUNTIME_DIR} runtime +directory for each user who logs in. -@item @code{hybrid-sleep-mode} (default: @code{'("suspend" "platform" "shutdown")}) (type: list) -... +@item @code{remove-ipc?} (default: @code{#t}) (type: maybe-boolean) +Whether @acronym{IPC,inter-process communication} objects belonging to +the user shall be removed when the user fully logs out. -@item @code{hibernate-delay-seconds} (default: @code{*unspecified*}) (type: integer) -... +@item @code{suspend-state} (default: @code{(mem standby freeze)}) (type: maybe-list-of-suspend-states) +The suspend state values to be write to @file{/sys/power/state} by +elogind when suspending the system. They will be tried in turn, until +one is written without error. -@item @code{suspend-estimation-seconds} (default: @code{*unspecified*}) (type: integer) -... +@item @code{suspend-mode} (type: maybe-list-of-suspend-modes) +The suspend mode values to write to @file{/sys/power/mem_sleep} by +elogind when suspending the system. -@item @code{system-sleep-hook-files} (default: @code{'()}) (type: list) -A list of executables (file-like objects) that will be installed into -the @file{/etc/elogind/system-sleep/} hook directory. For example: +@item @code{suspend-estimation-seconds} (default: @code{3600}) (type: maybe-non-negative-integer) +Cause the RTC alarm to wake the system after the specified time span to +measure the system battery capacity level and estimate the battery +discharging rate, which is used for estimating the time span until the +system battery charge level goes down to 5%. This option is only used +by elogind when using the @code{'suspend-then-hibernate} action. -@lisp -(elogind-configuration - (system-sleep-hook-files - (list (local-file "sleep-script")))) -@end lisp +@item @code{hibernate-mode} (default: @code{(platform shutdown)}) (type: maybe-list-of-hibernation-modes) +The hibernation mode values to write to @file{/sys/power/disk} by +elogind when hibernating the system. -See `Hook directories' in the @code{loginctl(1)} man page for more information. +@item @code{hibernate-delay-seconds} (type: maybe-non-negative-integer) +The amount of time the system spends in suspend mode before the system +is automatically put into hibernate mode. -@item @code{system-shutdown-hook-files} (default: @code{'()}) (type: list) -A list of executables (file-like objects) that will be installed into -the @file{/etc/elogind/system-shutdown/} hook directory. +@item @code{hibernate-state} (type: maybe-list-of-strings) +Deprecated option. -@item @code{allow-power-off-interrupts?} (default: @code{#f}) (type: boolean) -@itemx @code{allow-suspend-interrupts?} (default: @code{#f}) (type: boolean) -Whether the executables in elogind's hook directories (see above) can -cause a power-off or suspend action to be cancelled (interrupted) by -printing an appropriate error message to stdout. +@item @code{hybrid-sleep-state} (type: maybe-list-of-strings) +Deprecated option. -@item @code{broadcast-power-off-interrupts?} (default: @code{#t}) (type: boolean) -@itemx @code{broadcast-suspend-interrupts?} (default: @code{#t}) (type: boolean) -Whether an interrupt of a power-off or suspend action is broadcasted. +@item @code{hybrid-sleep-mode} (type: maybe-list-of-strings) +Deprecated option. @end table + @end deftp + +@c %end of fragment + @defvar accountsservice-service-type Type for the service that runs AccountsService, a system service that can list available accounts, change their passwords, and so on. diff --git a/gnu/services/desktop.scm b/gnu/services/desktop.scm index fe034cfa8f4..e422fbc89df 100644 --- a/gnu/services/desktop.scm +++ b/gnu/services/desktop.scm @@ -37,6 +37,7 @@ ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. (define-module (gnu services desktop) + #:use-module ((gnu home services utils) #:select (object->camel-case-string)) #:use-module (gnu services) #:use-module (gnu services shepherd) #:use-module (gnu services base) @@ -228,6 +229,8 @@ (define (package-direct-input-selector tree) (loop (cdr tree) (car (assoc-ref (package-direct-inputs package) (car tree)))))))) +(define (pascal-case text) + (object->camel-case-string text 'upper)) ;;; @@ -1025,173 +1028,367 @@ (define gvfs-service-type ;;; Elogind login and seat management service. ;;; -(define-record-type* <elogind-configuration> elogind-configuration - make-elogind-configuration - elogind-configuration? - (elogind elogind-package - (default elogind)) - (kill-user-processes? elogind-kill-user-processes? - (default #f)) - (kill-only-users elogind-kill-only-users - (default '())) - (kill-exclude-users elogind-kill-exclude-users - (default '("root"))) - (inhibit-delay-max-seconds elogind-inhibit-delay-max-seconds - (default 5)) - (handle-power-key elogind-handle-power-key - (default 'poweroff)) - (handle-suspend-key elogind-handle-suspend-key - (default 'suspend)) - (handle-hibernate-key elogind-handle-hibernate-key - (default 'hibernate)) - (handle-lid-switch elogind-handle-lid-switch - (default 'suspend)) - (handle-lid-switch-docked elogind-handle-lid-switch-docked - (default 'ignore)) - (handle-lid-switch-external-power elogind-handle-lid-switch-external-power - (default *unspecified*)) - (power-key-ignore-inhibited? elogind-power-key-ignore-inhibited? - (default #f)) - (suspend-key-ignore-inhibited? elogind-suspend-key-ignore-inhibited? - (default #f)) - (hibernate-key-ignore-inhibited? elogind-hibernate-key-ignore-inhibited? - (default #f)) - (lid-switch-ignore-inhibited? elogind-lid-switch-ignore-inhibited? - (default #t)) - (holdoff-timeout-seconds elogind-holdoff-timeout-seconds - (default 30)) - (idle-action elogind-idle-action - (default 'ignore)) - (idle-action-seconds elogind-idle-action-seconds - (default (* 30 60))) - (runtime-directory-size-percent elogind-runtime-directory-size-percent - (default 10)) - (runtime-directory-size elogind-runtime-directory-size - (default #f)) - (remove-ipc? elogind-remove-ipc? - (default #t)) - - (suspend-state elogind-suspend-state - (default '("mem" "standby" "freeze"))) - (suspend-mode elogind-suspend-mode - (default '())) - (hibernate-state elogind-hibernate-state - (default '("disk"))) - (hibernate-mode elogind-hibernate-mode - (default '("platform" "shutdown"))) - (hybrid-sleep-state elogind-hybrid-sleep-state - (default '("disk"))) - (hybrid-sleep-mode elogind-hybrid-sleep-mode - (default - '("suspend" "platform" "shutdown"))) - (hibernate-delay-seconds elogind-hibernate-delay-seconds - (default *unspecified*)) - (suspend-estimation-seconds elogind-suspend-estimation-seconds - (default *unspecified*)) - (system-sleep-hook-files elogind-system-sleep-hook-files - (default '())) - (system-shutdown-hook-files elogind-system-shutdown-hook-files - (default '())) - (allow-power-off-interrupts? elogind-allow-power-off-interrupts? - (default #f)) - (allow-suspend-interrupts? elogind-allow-suspend-interrupts? - (default #f)) - (broadcast-power-off-interrupts? elogind-broadcast-power-off-interrupts? - (default #t)) - (broadcast-suspend-interrupts? elogind-broadcast-suspend-interrupts? - (default #t))) - -(define (elogind-configuration-file config) - (define (yesno x) - (match x - (#t "yes") - (#f "no") - (_ (error "expected #t or #f, instead got:" x)))) - (define char-set:user-name - (string->char-set "abcdefghijklmnopqrstuvwxyz0123456789_-")) - (define (valid-list? l pred) - (and-map (lambda (x) (string-every pred x)) l)) - (define (user-name-list users) - (unless (valid-list? users char-set:user-name) - (error "invalid user list" users)) - (string-join users " ")) - (define (enum val allowed) - (unless (memq val allowed) - (error "invalid value" val allowed)) - (symbol->string val)) - (define (non-negative-integer x) - (unless (exact-integer? x) (error "not an integer" x)) - (when (negative? x) (error "negative number not allowed" x)) - (number->string x)) - (define (maybe-non-negative-integer x) - (or (and (unspecified? x) x) - (non-negative-integer x))) - (define handle-actions - '(ignore poweroff reboot halt kexec suspend hibernate hybrid-sleep suspend-then-hibernate lock)) - (define (handle-action x) - (if (unspecified? x) - x ;let the unspecified value go through - (enum x handle-actions))) - (define (sleep-list tokens) - (unless (valid-list? tokens char-set:user-name) - (error "invalid sleep list" tokens)) - (string-join tokens " ")) - (define-syntax ini-file-clause - (syntax-rules () - ;; Produce an empty line when encountering an unspecified value. This - ;; is better than an empty string value, which can, in some cases, cause - ;; warnings such as "Failed to parse handle action setting". - ((_ config (prop (parser getter))) - (let ((value (parser (getter config)))) - (if (unspecified? value) - "" - (string-append prop "=" value "\n")))) - ((_ config str) - (if (unspecified? str) - "" - (string-append str "\n"))))) - (define-syntax-rule (ini-file config file clause ...) - (plain-file file (string-append (ini-file-clause config clause) ...))) - (ini-file - config "logind.conf" - "[Login]" - ("KillUserProcesses" (yesno elogind-kill-user-processes?)) - ("KillOnlyUsers" (user-name-list elogind-kill-only-users)) - ("KillExcludeUsers" (user-name-list elogind-kill-exclude-users)) - ("InhibitDelayMaxSec" (non-negative-integer elogind-inhibit-delay-max-seconds)) - ("HandlePowerKey" (handle-action elogind-handle-power-key)) - ("HandleSuspendKey" (handle-action elogind-handle-suspend-key)) - ("HandleHibernateKey" (handle-action elogind-handle-hibernate-key)) - ("HandleLidSwitch" (handle-action elogind-handle-lid-switch)) - ("HandleLidSwitchDocked" (handle-action elogind-handle-lid-switch-docked)) - ("HandleLidSwitchExternalPower" (handle-action elogind-handle-lid-switch-external-power)) - ("PowerKeyIgnoreInhibited" (yesno elogind-power-key-ignore-inhibited?)) - ("SuspendKeyIgnoreInhibited" (yesno elogind-suspend-key-ignore-inhibited?)) - ("HibernateKeyIgnoreInhibited" (yesno elogind-hibernate-key-ignore-inhibited?)) - ("LidSwitchIgnoreInhibited" (yesno elogind-lid-switch-ignore-inhibited?)) - ("HoldoffTimeoutSec" (non-negative-integer elogind-holdoff-timeout-seconds)) - ("IdleAction" (handle-action elogind-idle-action)) - ("IdleActionSec" (non-negative-integer elogind-idle-action-seconds)) - ("RuntimeDirectorySize" - (identity - (lambda (config) - (match (elogind-runtime-directory-size-percent config) - (#f (non-negative-integer (elogind-runtime-directory-size config))) - (percent (string-append (non-negative-integer percent) "%")))))) - ("RemoveIPC" (yesno elogind-remove-ipc?)) - "[Sleep]" - ("SuspendState" (sleep-list elogind-suspend-state)) - ("SuspendMode" (sleep-list elogind-suspend-mode)) - ("HibernateState" (sleep-list elogind-hibernate-state)) - ("HibernateMode" (sleep-list elogind-hibernate-mode)) - ("HybridSleepState" (sleep-list elogind-hybrid-sleep-state)) - ("HybridSleepMode" (sleep-list elogind-hybrid-sleep-mode)) - ("HibernateDelaySec" (maybe-non-negative-integer elogind-hibernate-delay-seconds)) - ("SuspendEstimationSec" (maybe-non-negative-integer elogind-suspend-estimation-seconds)) - ("AllowPowerOffInterrupts" (yesno elogind-allow-power-off-interrupts?)) - ("AllowSuspendInterrupts" (yesno elogind-allow-suspend-interrupts?)) - ("BroadcastPowerOffInterrupts" (yesno elogind-broadcast-power-off-interrupts?)) - ("BroadcastSuspendInterrupts" (yesno elogind-broadcast-suspend-interrupts?)))) +;;; Elogind configuration types. +(define-maybe boolean + (prefix elogind-)) + +(define (non-negative-integer? x) + (and (exact-integer? x) + (not (negative? x)))) + +(define-maybe non-negative-integer + (prefix elogind-)) + +(define (percent? x) + (and (non-negative-integer? x) + (>= x 0) + (<= x 100))) + +(define-maybe percent + (prefix elogind-)) + +(define char-set:user-name + (string->char-set "abcdefghijklmnopqrstuvwxyz0123456789_-")) + +(define (user-name? x) + (string-every char-set:user-name x)) + +(define list-of-user-names? + (list-of user-name?)) + +(define-maybe list-of-user-names + (prefix elogind-)) + +(define %elogind-actions + '( ignore poweroff reboot halt kexec suspend hibernate hybrid-sleep + suspend-then-hibernate lock factory-reset)) + +(define (action? x) + (member x %elogind-actions)) + +(define-maybe action + (prefix elogind-)) + +(define %linux-suspend-states + ;; The possible suspend states supported by the Linux kernel. + ;; See (info "(linux) Basic sysfs Interfaces for System Suspend and Hibernation"). + '(disk standby freeze mem)) + +(define (string->symbol/maybe x) + (if (string? x) + (string->symbol x) + x)) + +(define (suspend-state? x) + (member (string->symbol/maybe x) %linux-suspend-states)) + +(define list-of-suspend-states? + (list-of suspend-state?)) + +(define-maybe list-of-suspend-states + (prefix elogind-)) + +(define %linux-suspend-modes + ;; The possible suspend state variants supported by the Linux kernel. + ;; See (info "(linux) Basic sysfs Interfaces for System Suspend and Hibernation"). + '(s2idle shallow deep)) + +(define (suspend-mode? x) + (member (string->symbol/maybe x) %linux-suspend-modes)) + +(define list-of-suspend-modes? + (list-of suspend-mode?)) + +(define-maybe list-of-suspend-modes + (prefix elogind-)) + +(define %linux-hibernation-modes + ;; The possible hibernation operating modes supported by the Linux kernel. + ;; See (info "(linux) Basic sysfs Interfaces for System Suspend and Hibernation"). + '(platform shutdown reboot suspend test_resume)) + +(define (hibernation-mode? x) + (member (string->symbol/maybe x) %linux-hibernation-modes)) + +(define list-of-hibernation-modes? + (list-of hibernation-mode?)) + +(define-maybe list-of-hibernation-modes + (prefix elogind-)) + +(define (elogind-deprecated-empty-serializer name value) + (when (maybe-value-set? value) + (warn-about-deprecation name #f + #:replacement #f)) + "") + +(define list-of-file-likes? + (list-of file-like?)) + +(define-maybe list-of-strings + (prefix elogind-)) + +;;; Elogind serializers. +(define (elogind-serialize-boolean name value) + (let* ((name-str (symbol->string name)) + (name (if (string-suffix? "?" name-str) + (string-drop-right name-str 1) + name-str))) + (format #f "~a=~:[no~;yes~]~%" (pascal-case name) value))) + +(define (elogind-base-serializer name value) + (let* ((name-str (symbol->string name)) + (name (if (string-suffix? "seconds" name-str) + (string-drop-right name-str 4) ;seconds -> sec + name-str))) + (format #f "~a=~a~%" (pascal-case name) value))) + +(define elogind-serialize-action elogind-base-serializer) +(define elogind-serialize-non-negative-integer elogind-base-serializer) +(define elogind-serialize-percent elogind-base-serializer) + +(define (elogind-list-serializer name value) + (format #f "~a=~{~a~^ ~}~%" (pascal-case name) value)) + +(define elogind-serialize-list-of-strings elogind-list-serializer) +(define elogind-serialize-list-of-user-names elogind-list-serializer) +(define elogind-serialize-list-of-suspend-states elogind-list-serializer) +(define elogind-serialize-list-of-suspend-modes elogind-list-serializer) +(define elogind-serialize-list-of-hibernation-modes elogind-list-serializer) + +;;; XXX: For backward-compatible/historical reasons, the configuration object +;;; is flat, containing the fields of both the logind.conf and sleep.conf +;;; files. The list below contains the fields that should be serialized to +;;; sleep.conf. +(define %elogind-configuration-sleep-fields + '( suspend-state suspend-mode suspend-estimation-seconds + hibernate-mode hibernate-delay-seconds hibernate-state + hybrid-sleep-state hybrid-sleep-mode)) + +(define-configuration elogind-configuration + (elogind + (file-like elogind) + "The elogind package to use." + (serializer empty-serializer)) + + (system-sleep-hook-files + (list-of-file-likes '()) + "A list of executables (file-like objects) that will be installed into the +@file{/etc/elogind/system-sleep} hook directory. See `Hook directories' in +the @samp{loginctl(1)} man page for more information." + (serializer empty-serializer)) + + (system-shutdown-hook-files + (list-of-file-likes '()) + "A list of executables (file-like objects) that will be installed into the +@file{/etc/elogind/system-shutdown/} hook directory." + (serializer empty-serializer)) + + (allow-power-off-interrupts? + (maybe-boolean #f) + "Whether the executables in elogind's hook directories (see above) can + cause a power-off action to be cancelled (interrupted) by printing an + appropriate error message to stdout.") + + (allow-suspend-interrupts? + (maybe-boolean #f) + "Likewise as the @code{allow-power-off-interrupts?} option, but for the + suspend action.") + + (broadcast-power-off-interrupts? + (maybe-boolean #f) + "Whether an interrupt of a power-off action is broadcasted.") + + (broadcast-suspend-interrupts? + (maybe-boolean #f) + "Whether an interrupt of a suspend action is broadcasted.") + + ;; logind.conf options. + (kill-user-processes? + (maybe-boolean #f) + "Whether the processes of a user should be killed when the user logs + out.") + + (kill-only-users + maybe-list-of-user-names + "Usernames whose processes should be killed, regardless the value of + @code{kill-user-processes?}.") + + (kill-exclude-users + (maybe-list-of-user-names (list "root")) + "Usernames whose processes should @emph{not} be killed, regardless the + value of @code{kill-user-processes?}.") + + (inhibit-delay-max-seconds + (maybe-non-negative-integer 5) + "The maximum time a system shutdown or sleep request is delayed due to an + inhibitor lock of type delay being active before the inhibitor is ignored and + the operation executes anyway.") + + (handle-power-key + (maybe-action 'poweroff) + "The action done when the power key is pressed. The compiled default is + @code{'poweroff}.") + + (handle-suspend-key + (maybe-action 'suspend) + "The action done when the suspend key is pressed. The ") + + (handle-hibernate-key + (maybe-action 'hibernate) + "The action done when the hibernate key is pressed.") + + (handle-lid-switch + (maybe-action 'suspend) + "The action done when the lid is closed.") + + (handle-lid-switch-docked + (maybe-action 'ignore) + "The action done when the lid is closed and the device docked.") + + (handle-lid-switch-external-power + (maybe-action 'suspend) + "The action done when the lid is closed and the device is externally + powered.") + + (power-key-ignore-inhibited? + (maybe-boolean #f) + "Whether to ignore high-level inhibitor locks (shutdown, reboot, sleep or + idle) when the power key is pressed.") + + (suspend-key-ignore-inhibited? + (maybe-boolean #f) + "Whether to ignore high-level inhibitor locks (shutdown, reboot, sleep or + idle) when the suspend key is pressed.") + + (hibernate-key-ignore-inhibited? + (maybe-boolean #f) + "Whether to ignore high-level inhibitor locks (shutdown, reboot, sleep or + idle) when the hibernate key is pressed.") + + (lid-switch-ignore-inhibited? + (maybe-boolean #f) + "Whether to ignore high-level inhibitor locks (shutdown, reboot, sleep or + idle) when the lid is closed.") + + (holdoff-timeout-seconds + (maybe-non-negative-integer 30) + "Specifies the number of seconds after system startup or system resume +during which elogind will hold off on reacting to lid events.") + + (idle-action + (maybe-action 'ignore) + "Action to take when the system is idle.") + + (idle-action-seconds + maybe-non-negative-integer + "The delay after which the action configured in @code{idle-action} is +taken after the system is idle.") + + ;; XXX: Perhaps deprecate in the future and handle all the accepted forms + ;; directly in 'runtime-directory-size' instead. + (runtime-directory-size-percent + maybe-percent + "Set the size limit, in percent, on the @env{XDG_RUNTIME_DIR} runtime +directory for each user who logs in. This specifies the per-user size limit +relative to the amount of physical @acronym{RAM, read access memory}. This +value takes precedence over that specified via @code{runtime-directory-size}." + (serializer empty-serializer)) ;special cased at serialization time + + (runtime-directory-size + maybe-non-negative-integer + "Set the size limit, in bytes, on the @env{XDG_RUNTIME_DIR} runtime +directory for each user who logs in." + (serializer empty-serializer)) ;special cased at serialization time + + (remove-ipc? + (maybe-boolean #t) + "Whether @acronym{IPC, inter-process communication} objects belonging to +the user shall be removed when the user fully logs out.") + + ;; sleep.conf options. + ;; CAUTION: all sleep.conf option names must be registered in the above + ;; %ELOGIND-CONFIGURATION-SLEEP-FIELDS variable: otherwise they will be + ;; serialized to logind.conf instead of sleep.conf! + (suspend-state + (maybe-list-of-suspend-states '(mem standby freeze)) + "The suspend state values to be write to @file{/sys/power/state} by elogind + when suspending the system. They will be tried in turn, until one is written + without error.") + + (suspend-mode + (maybe-list-of-suspend-modes) + "The suspend mode values to write to @file{/sys/power/mem_sleep} by elogind + when suspending the system.") + + (suspend-estimation-seconds + (maybe-non-negative-integer (* 60 60)) ;1 hour + "Cause the RTC alarm to wake the system after the specified time span to + measure the system battery capacity level and estimate the battery discharging + rate, which is used for estimating the time span until the system battery + charge level goes down to 5%. This option is only used by elogind when using + the @code{'suspend-then-hibernate} action.") + + (hibernate-mode + (maybe-list-of-hibernation-modes '(platform shutdown)) + "The hibernation mode values to write to @file{/sys/power/disk} by elogind + when hibernating the system.") + + (hibernate-delay-seconds + maybe-non-negative-integer + "The amount of time the system spends in suspend mode before the system is + automatically put into hibernate mode.") + + ;; TODO: Remove in May 2026. + (hibernate-state + maybe-list-of-strings + "Deprecated option." + (serializer elogind-deprecated-empty-serializer)) + + ;; TODO: Remove in May 2026. + (hybrid-sleep-state + maybe-list-of-strings + "Deprecated option." + (serializer elogind-deprecated-empty-serializer)) + + ;; TODO: Remove in May 2026. + (hybrid-sleep-mode + maybe-list-of-strings + "Deprecated option." + (serializer elogind-deprecated-empty-serializer)) + + (prefix elogind-)) + +(define (logind.conf config) + (let ((logind-fields (remove (lambda (field) + (memq (configuration-field-name field) + %elogind-configuration-sleep-fields)) + elogind-configuration-fields))) + (match-record config <elogind-configuration> + (runtime-directory-size-percent runtime-directory-size) + ;; Handle the special-cased + ;; runtime-directory-size-percent/runtime-directory-size options pair. + (let ((runtime-directory-size + (if (maybe-value-set? runtime-directory-size-percent) + (format #f "~a%~%" runtime-directory-size-percent) ;10 -> 10% + runtime-directory-size))) + (mixed-text-file + "logind.conf" + "[Login]\n" + (if (maybe-value-set? runtime-directory-size) + (list "RuntimeDirectorySize=" runtime-directory-size) + "") + (serialize-configuration config logind-fields)))))) + +(define (sleep.conf config) + (let ((sleep-fields (filter (lambda (field) + (memq (configuration-field-name field) + %elogind-configuration-sleep-fields)) + elogind-configuration-fields))) + (mixed-text-file + "sleep.conf" + "[Sleep]\n" + (serialize-configuration config sleep-fields)))) (define (elogind-etc-directory config) "Return the /etc/elogind directory for CONFIG." @@ -1213,12 +1410,19 @@ (define (elogind-etc-directory config) (chmod dest #o500))) (mkdir-p #$output) ;in case neither directory gets created + + ;; Symlink the main configuration files. + (with-directory-excursion #$output + (symlink #$(logind.conf config) "logind.conf") + (symlink #$(sleep.conf config) "sleep.conf")) + (for-each (lambda (f) (copy-script f sleep-directory)) - '#$(elogind-system-sleep-hook-files config)) + '#$(elogind-configuration-system-sleep-hook-files config)) (for-each (lambda (f) (copy-script f shutdown-directory)) - '#$(elogind-system-shutdown-hook-files config)))))) + '#$(elogind-configuration-system-shutdown-hook-files + config)))))) (define (elogind-dbus-service config) "Return a @file{org.freedesktop.login1.service} file that tells D-Bus how to @@ -1231,7 +1435,7 @@ (define (elogind-dbus-service config) ;; <https://issues.guix.gnu.org/55444>. (define elogind - (elogind-package config)) + (elogind-configuration-elogind config)) (define wrapper (program-file "elogind-dbus-shepherd-sync" @@ -1288,7 +1492,7 @@ (define (pam-extension-procedure config) (define pam-elogind (pam-entry (control "required") - (module (file-append (elogind-package config) + (module (file-append (elogind-configuration-elogind config) "/lib/security/pam_elogind.so")))) (list (pam-extension @@ -1301,56 +1505,52 @@ (define (pam-extension-procedure config) (define (elogind-shepherd-service config) "Return a Shepherd service to start elogind according to @var{config}." - (define config-file - (elogind-configuration-file config)) - (list (shepherd-service (requirement '(user-processes dbus-system)) (provision '(elogind)) (start #~(make-forkexec-constructor - (list #$(file-append (elogind-package config) - "/libexec/elogind/elogind")) - #:environment-variables - (list (string-append "ELOGIND_CONF_FILE=" - #$config-file)))) + (list #$(file-append (elogind-configuration-elogind config) + "/libexec/elogind/elogind")))) (stop #~(make-kill-destructor)) - (actions (list (shepherd-configuration-action config-file)))))) + (actions (list (shepherd-configuration-action + "/etc/elogind/logind.conf")))))) (define elogind-service-type - (service-type (name 'elogind) - (extensions - (list (service-extension dbus-root-service-type - elogind-dbus-service) - (service-extension udev-service-type - (compose list elogind-package)) - (service-extension polkit-service-type - (compose list elogind-package)) - - ;; Start elogind from the Shepherd rather than waiting - ;; for bus activation. This ensures that it can handle - ;; events like lid close, etc. - (service-extension shepherd-root-service-type - elogind-shepherd-service) - - ;; Provide the 'loginctl' command. - (service-extension profile-service-type - (compose list elogind-package)) - - ;; Extend PAM with pam_elogind.so. - (service-extension pam-root-service-type - pam-extension-procedure) - - ;; Install sleep/shutdown hook files. - (service-extension etc-service-type - (lambda (config) - `(("elogind" - ,(elogind-etc-directory config))))) - - ;; We need /run/user, /run/systemd, etc. - (service-extension file-system-service-type - (const %elogind-file-systems)))) - (default-value (elogind-configuration)) - (description "Run the @command{elogind} login and seat + (service-type + (name 'elogind) + (extensions + (list (service-extension dbus-root-service-type + elogind-dbus-service) + (service-extension udev-service-type + (compose list elogind-configuration-elogind)) + (service-extension polkit-service-type + (compose list elogind-configuration-elogind)) + + ;; Start elogind from the Shepherd rather than waiting + ;; for bus activation. This ensures that it can handle + ;; events like lid close, etc. + (service-extension shepherd-root-service-type + elogind-shepherd-service) + + ;; Provide the 'loginctl' command. + (service-extension profile-service-type + (compose list elogind-configuration-elogind)) + + ;; Extend PAM with pam_elogind.so. + (service-extension pam-root-service-type + pam-extension-procedure) + + ;; Install sleep/shutdown hook files. + (service-extension etc-service-type + (lambda (config) + `(("elogind" + ,(elogind-etc-directory config))))) + + ;; We need /run/user, /run/systemd, etc. + (service-extension file-system-service-type + (const %elogind-file-systems)))) + (default-value (elogind-configuration)) + (description "Run the @command{elogind} login and seat management service. The @command{elogind} service integrates with PAM to allow other system components to know the set of logged-in users as well as their session types (graphical, console, remote, etc.). It can also clean up base-commit: 5b981a2b5c9c880634d883f3b7ae078b4fa75fdb -- 2.49.0
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.