services: elogind: Split sleep.conf and port to define-configuration.

* 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.
(shepherd-configuration-action*): New procedure.
* doc/guix.texi (Desktop Services): Fully document configuration options.

Fixes: https://issues.guix.gnu.org/77806
Change-Id: I8767891871d83e58d64995ec986a7d01689fa6d8
Reported-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
Maxim Cournoyer 2025-04-24 22:09:35 +09:00
parent ab39ed52a0
commit f10d00e4e2
No known key found for this signature in database
GPG key ID: 1260E46482E63562
2 changed files with 556 additions and 314 deletions

View file

@ -26264,129 +26264,158 @@ seat management daemon. The value for this service is a
@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{kill-only-users} (default: @code{'()}) (type: list)
...
@item @code{kill-exclude-users} (default: @code{'("root")}) (type: list-of-string)
...
@item @code{inhibit-delay-max-seconds} (default: @code{5}) (type: integer)
...
@item @code{handle-power-key} (default: @code{'poweroff}) (type: symbol)
...
@item @code{handle-suspend-key} (default: @code{'suspend}) (type: symbol)
...
@item @code{handle-hibernate-key} (default: @code{'hibernate}) (type: symbol)
...
@item @code{handle-lid-switch} (default: @code{'suspend}) (type: symbol)
...
@item @code{handle-lid-switch-docked} (default: @code{'ignore}) (type: symbol)
...
@item @code{handle-lid-switch-external-power} (default: @code{*unspecified*}) (type: symbol)
...
@item @code{power-key-ignore-inhibited?} (default: @code{#f}) (type: boolean)
...
@item @code{suspend-key-ignore-inhibited?} (default: @code{#f}) (type: boolean)
...
@item @code{hibernate-key-ignore-inhibited?} (default: @code{#f}) (type: boolean)
...
@item @code{lid-switch-ignore-inhibited?} (default: @code{#t}) (type: boolean)
...
@item @code{holdoff-timeout-seconds} (default: @code{30}) (type: integer)
...
@item @code{idle-action} (default: @code{'ignore}) (type: symbol)
...
@item @code{idle-action-seconds} (default: @code{(* 30 60)}) (type: integer)
...
@item @code{runtime-directory-size-percent} (default: @code{10}) (type: integer)
...
@item @code{runtime-directory-size} (default: @code{#f}) (type: integer)
...
@item @code{remove-ipc?} (default: @code{#t}) (type: boolean)
...
@item @code{suspend-state} (default: @code{'("mem" "standby" "freeze")}) (type: list)
...
@item @code{suspend-mode} (default: @code{'()}) (type: list)
...
@item @code{hibernate-state} (default: @code{'("disk")}) (type: list)
...
@item @code{hibernate-mode} (default: @code{'("platform" "shutdown")}) (type: list)
...
@item @code{hybrid-sleep-state} (default: @code{'("disk")}) (type: list)
...
@item @code{hybrid-sleep-mode} (default: @code{'("suspend" "platform" "shutdown")}) (type: list)
...
@item @code{hibernate-delay-seconds} (default: @code{*unspecified*}) (type: integer)
...
@item @code{suspend-estimation-seconds} (default: @code{*unspecified*}) (type: integer)
...
@item @code{system-sleep-hook-files} (default: @code{'()}) (type: list)
@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. For example:
the @file{/etc/elogind/system-sleep} hook directory. See `Hook
directories' in the @samp{loginctl(1)} man page for more information.
@lisp
(elogind-configuration
(system-sleep-hook-files
(list (local-file "sleep-script"))))
@end lisp
See `Hook directories' in the @code{loginctl(1)} man page for more information.
@item @code{system-shutdown-hook-files} (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{allow-power-off-interrupts?} (default: @code{#f}) (type: boolean)
@itemx @code{allow-suspend-interrupts?} (default: @code{#f}) (type: boolean)
@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 or suspend action to be cancelled (interrupted) by
printing an appropriate error message to stdout.
cause a power-off action to be cancelled (interrupted) by printing an
appropriate error message to stdout.
@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{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{broadcast-power-off-interrupts?} (default: @code{#f}) (type: maybe-boolean)
Whether an interrupt of a power-off action is broadcasted.
@item @code{broadcast-suspend-interrupts?} (default: @code{#f}) (type: maybe-boolean)
Whether an interrupt of a suspend action is broadcasted.
@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{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{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{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{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{handle-suspend-key} (default: @code{suspend}) (type: maybe-action)
The action done when the suspend key is pressed. The
@item @code{handle-hibernate-key} (default: @code{hibernate}) (type: maybe-action)
The action done when the hibernate key is pressed.
@item @code{handle-lid-switch} (default: @code{suspend}) (type: maybe-action)
The action done when the lid is closed.
@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{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{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{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{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{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{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{idle-action} (default: @code{ignore}) (type: maybe-action)
Action to take when the system is idle.
@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{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{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{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{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-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{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.
@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.
@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{hibernate-state} (type: maybe-list-of-strings)
Deprecated option.
@item @code{hybrid-sleep-state} (type: maybe-list-of-strings)
Deprecated option.
@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.

View file

@ -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 @@ is a list, it recursively searches it until it locates the last item of 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 @@ and many other) available for GIO applications.")
;;; 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))
;;; Elogind configuration types.
(define-maybe boolean
(prefix elogind-))
(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 (non-negative-integer? x)
(and (exact-integer? x)
(not (negative? x))))
(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?))))
(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,21 @@ and many other) available for GIO applications.")
(chmod dest #o500)))
(mkdir-p #$output) ;in case neither directory gets created
;; Symlink the main configuration files.
(with-directory-excursion #$output
(mkdir-p "logind.conf.d")
(symlink #$(logind.conf config) "logind.conf.d/logind.conf")
(mkdir-p "sleep.conf.d")
(symlink #$(sleep.conf config) "sleep.conf.d/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 +1437,7 @@ explain how to start elogind; instead, it spawns a wrapper that waits for the
;; <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 +1494,7 @@ seats.)"
(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
@ -1299,58 +1505,65 @@ seats.)"
(session (cons pam-elogind (pam-service-session pam))))))
(shepherd-requirements '(elogind)))))
(define* (shepherd-configuration-action* files)
"Return a 'configuration' action to display FILES, which should be the names
of the service's configuration files."
(shepherd-action
(name 'configuration)
(documentation "Display the names of this service's configuration files.")
(procedure #~(lambda (_)
(format #t "~{~a~%~}" '#$files)
'#$files))))
(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*
(list (logind.conf config)
(sleep.conf config))))))))
(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))
(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)
;; 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))
;; 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)
;; 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)))))
;; 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
;; 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