summary refs log tree commit diff
path: root/gnu/services/base.scm
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/services/base.scm')
-rw-r--r--gnu/services/base.scm1145
1 files changed, 653 insertions, 492 deletions
diff --git a/gnu/services/base.scm b/gnu/services/base.scm
index 616bc42e69..9e799445d2 100644
--- a/gnu/services/base.scm
+++ b/gnu/services/base.scm
@@ -19,6 +19,7 @@
 ;;; Copyright © 2021 muradm <mail@muradm.net>
 ;;; Copyright © 2022 Guillaume Le Vaillant <glv@posteo.net>
 ;;; Copyright © 2022 Justin Veilleux <terramorpha@cock.li>
+;;; Copyright © 2022 ( <paren@disroot.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -55,17 +56,25 @@
                 #:select (file-system-packages))
   #:use-module (gnu packages admin)
   #:use-module ((gnu packages linux)
-                #:select (alsa-utils crda eudev e2fsprogs fuse gpm kbd lvm2 rng-tools))
+                #:select (alsa-utils btrfs-progs crda eudev
+                          e2fsprogs f2fs-tools fuse gpm kbd lvm2 rng-tools
+                          util-linux xfsprogs))
   #:use-module (gnu packages bash)
   #:use-module ((gnu packages base)
-                #:select (coreutils glibc glibc-utf8-locales tar))
+                #:select (coreutils glibc glibc-utf8-locales tar
+                          canonical-package))
   #:use-module ((gnu packages compression) #:select (gzip))
   #:autoload   (gnu packages guile-xyz) (guile-netlink)
   #:autoload   (gnu packages hurd) (hurd)
   #:use-module (gnu packages package-management)
   #:use-module ((gnu packages gnupg) #:select (guile-gcrypt))
-  #:use-module (gnu packages linux)
+  #:use-module ((gnu packages disk)
+                #:select (dosfstools))
+  #:use-module ((gnu packages file-systems)
+                #:select (bcachefs-tools exfat-utils jfsutils zfs))
+  #:use-module (gnu packages fonts)
   #:use-module (gnu packages terminals)
+  #:use-module ((gnu packages wm) #:select (sway))
   #:use-module ((gnu build file-systems)
                 #:select (mount-flags->bit-mask
                           swap-space->flags-bit-mask))
@@ -86,6 +95,7 @@
   #:export (fstab-service-type
             root-file-system-service
             file-system-service-type
+            file-system-utilities
             swap-service
             host-name-service
             %default-console-font
@@ -188,6 +198,7 @@
             guix-configuration-generate-substitute-key?
             guix-configuration-extra-options
             guix-configuration-log-file
+            guix-configuration-environment
 
             guix-extension
             guix-extension?
@@ -231,6 +242,8 @@
             greetd-configuration
             greetd-terminal-configuration
             greetd-agreety-session
+            greetd-wlgreet-session
+            greetd-wlgreet-sway-session
 
             %base-services))
 
@@ -488,6 +501,31 @@ upon boot."
                 (memq 'bind-mount (file-system-flags file-system))))
           file-systems))
 
+(define (file-system-type->utilities type)
+  "Return the package providing the utilities for file system TYPE, #f
+otherwise."
+  (assoc-ref
+   `(("bcachefs" . ,bcachefs-tools)
+     ("btrfs" . ,btrfs-progs)
+     ("exfat" . ,exfat-utils)
+     ("ext2" . ,e2fsprogs)
+     ("ext3" . ,e2fsprogs)
+     ("ext4" . ,e2fsprogs)
+     ("fat" . ,dosfstools)
+     ("f2fs" . ,f2fs-tools)
+     ("jfs" . ,jfsutils)
+     ("vfat" . ,dosfstools)
+     ("xfs" . ,xfsprogs)
+     ("zfs" . ,zfs))
+   type))
+
+(define (file-system-utilities file-systems)
+  "Return a list of packages containing file system utilities for
+FILE-SYSTEMS."
+  (filter-map (lambda (file-system)
+                (file-system-type->utilities (file-system-type file-system)))
+              file-systems))
+
 (define file-system-service-type
   (service-type (name 'file-systems)
                 (extensions
@@ -495,6 +533,8 @@ upon boot."
                                           file-system-shepherd-services)
                        (service-extension fstab-service-type
                                           file-system-fstab-entries)
+                       (service-extension profile-service-type
+                                          file-system-utilities)
 
                        ;; Have 'user-processes' depend on 'file-systems'.
                        (service-extension user-processes-service-type
@@ -940,148 +980,148 @@ to use as the tty.  This is primarily useful for headless systems."
                ((device-name _ ...)
                 device-name))))))))
 
-(define agetty-shepherd-service
-  (match-lambda
-    (($ <agetty-configuration> agetty tty term baud-rate auto-login
-        login-program login-pause? eight-bits? no-reset? remote? flow-control?
-        host no-issue? init-string no-clear? local-line extract-baud?
-        skip-login? no-newline? login-options chroot hangup? keep-baud? timeout
-        detect-case? wait-cr? no-hints? no-hostname? long-hostname?
-        erase-characters kill-characters chdir delay nice extra-options
-        shepherd-requirement)
-     (list
-       (shepherd-service
-         (documentation "Run agetty on a tty.")
-         (provision (list (symbol-append 'term- (string->symbol (or tty "console")))))
-
-         ;; Since the login prompt shows the host name, wait for the 'host-name'
-         ;; service to be done.  Also wait for udev essentially so that the tty
-         ;; text is not lost in the middle of kernel messages (see also
-         ;; mingetty-shepherd-service).
-         (requirement (cons* 'user-processes 'host-name 'udev
-                             shepherd-requirement))
-
-         (modules '((ice-9 match) (gnu build linux-boot)))
-         (start
-          (with-imported-modules  (source-module-closure
-                                   '((gnu build linux-boot)))
-            #~(lambda args
-                (let ((defaulted-tty #$(or tty (default-serial-port))))
-                  (apply
-                   (if defaulted-tty
-                       (make-forkexec-constructor
-                        (list #$(file-append util-linux "/sbin/agetty")
-                              #$@extra-options
-                              #$@(if eight-bits?
-                                     #~("--8bits")
-                                     #~())
-                              #$@(if no-reset?
-                                     #~("--noreset")
-                                     #~())
-                              #$@(if remote?
-                                     #~("--remote")
-                                     #~())
-                              #$@(if flow-control?
-                                     #~("--flow-control")
-                                     #~())
-                              #$@(if host
-                                     #~("--host" #$host)
-                                     #~())
-                              #$@(if no-issue?
-                                     #~("--noissue")
-                                     #~())
-                              #$@(if init-string
-                                     #~("--init-string" #$init-string)
-                                     #~())
-                              #$@(if no-clear?
-                                     #~("--noclear")
-                                     #~())
+(define (agetty-shepherd-service config)
+  (match-record config <agetty-configuration>
+    (agetty tty term baud-rate auto-login
+            login-program login-pause? eight-bits? no-reset? remote? flow-control?
+            host no-issue? init-string no-clear? local-line extract-baud?
+            skip-login? no-newline? login-options chroot hangup? keep-baud? timeout
+            detect-case? wait-cr? no-hints? no-hostname? long-hostname?
+            erase-characters kill-characters chdir delay nice extra-options
+            shepherd-requirement)
+    (list
+     (shepherd-service
+      (documentation "Run agetty on a tty.")
+      (provision (list (symbol-append 'term- (string->symbol (or tty "console")))))
+
+      ;; Since the login prompt shows the host name, wait for the 'host-name'
+      ;; service to be done.  Also wait for udev essentially so that the tty
+      ;; text is not lost in the middle of kernel messages (see also
+      ;; mingetty-shepherd-service).
+      (requirement (cons* 'user-processes 'host-name 'udev
+                          shepherd-requirement))
+
+      (modules '((ice-9 match) (gnu build linux-boot)))
+      (start
+       (with-imported-modules  (source-module-closure
+                                '((gnu build linux-boot)))
+         #~(lambda args
+             (let ((defaulted-tty #$(or tty (default-serial-port))))
+               (apply
+                (if defaulted-tty
+                    (make-forkexec-constructor
+                     (list #$(file-append util-linux "/sbin/agetty")
+                           #$@extra-options
+                           #$@(if eight-bits?
+                                  #~("--8bits")
+                                  #~())
+                           #$@(if no-reset?
+                                  #~("--noreset")
+                                  #~())
+                           #$@(if remote?
+                                  #~("--remote")
+                                  #~())
+                           #$@(if flow-control?
+                                  #~("--flow-control")
+                                  #~())
+                           #$@(if host
+                                  #~("--host" #$host)
+                                  #~())
+                           #$@(if no-issue?
+                                  #~("--noissue")
+                                  #~())
+                           #$@(if init-string
+                                  #~("--init-string" #$init-string)
+                                  #~())
+                           #$@(if no-clear?
+                                  #~("--noclear")
+                                  #~())
 ;;; FIXME This doesn't work as expected. According to agetty(8), if this option
 ;;; is not passed, then the default is 'auto'. However, in my tests, when that
 ;;; option is selected, agetty never presents the login prompt, and the
 ;;; term-ttyS0 service respawns every few seconds.
-                              #$@(if local-line
-                                     #~(#$(match local-line
-                                            ('auto "--local-line=auto")
-                                            ('always "--local-line=always")
-                                            ('never "-local-line=never")))
-                                     #~())
-                              #$@(if tty
-                                     #~()
-                                     #~("--keep-baud"))
-                              #$@(if extract-baud?
-                                     #~("--extract-baud")
-                                     #~())
-                              #$@(if skip-login?
-                                     #~("--skip-login")
-                                     #~())
-                              #$@(if no-newline?
-                                     #~("--nonewline")
-                                     #~())
-                              #$@(if login-options
-                                     #~("--login-options" #$login-options)
-                                     #~())
-                              #$@(if chroot
-                                     #~("--chroot" #$chroot)
-                                     #~())
-                              #$@(if hangup?
-                                     #~("--hangup")
-                                     #~())
-                              #$@(if keep-baud?
-                                     #~("--keep-baud")
-                                     #~())
-                              #$@(if timeout
-                                     #~("--timeout" #$(number->string timeout))
-                                     #~())
-                              #$@(if detect-case?
-                                     #~("--detect-case")
-                                     #~())
-                              #$@(if wait-cr?
-                                     #~("--wait-cr")
-                                     #~())
-                              #$@(if no-hints?
-                                     #~("--nohints?")
-                                     #~())
-                              #$@(if no-hostname?
-                                     #~("--nohostname")
-                                     #~())
-                              #$@(if long-hostname?
-                                     #~("--long-hostname")
-                                     #~())
-                              #$@(if erase-characters
-                                     #~("--erase-chars" #$erase-characters)
-                                     #~())
-                              #$@(if kill-characters
-                                     #~("--kill-chars" #$kill-characters)
-                                     #~())
-                              #$@(if chdir
-                                     #~("--chdir" #$chdir)
-                                     #~())
-                              #$@(if delay
-                                     #~("--delay" #$(number->string delay))
-                                     #~())
-                              #$@(if nice
-                                     #~("--nice" #$(number->string nice))
-                                     #~())
-                              #$@(if auto-login
-                                     (list "--autologin" auto-login)
-                                     '())
-                              #$@(if login-program
-                                     #~("--login-program" #$login-program)
-                                     #~())
-                              #$@(if login-pause?
-                                     #~("--login-pause")
-                                     #~())
-                              defaulted-tty
-                              #$@(if baud-rate
-                                     #~(#$baud-rate)
-                                     #~())
-                              #$@(if term
-                                     #~(#$term)
-                                     #~())))
-                       (const #f))                 ; never start.
-                   args)))))
-         (stop #~(make-kill-destructor)))))))
+                           #$@(if local-line
+                                  #~(#$(match local-line
+                                         ('auto "--local-line=auto")
+                                         ('always "--local-line=always")
+                                         ('never "-local-line=never")))
+                                  #~())
+                           #$@(if tty
+                                  #~()
+                                  #~("--keep-baud"))
+                           #$@(if extract-baud?
+                                  #~("--extract-baud")
+                                  #~())
+                           #$@(if skip-login?
+                                  #~("--skip-login")
+                                  #~())
+                           #$@(if no-newline?
+                                  #~("--nonewline")
+                                  #~())
+                           #$@(if login-options
+                                  #~("--login-options" #$login-options)
+                                  #~())
+                           #$@(if chroot
+                                  #~("--chroot" #$chroot)
+                                  #~())
+                           #$@(if hangup?
+                                  #~("--hangup")
+                                  #~())
+                           #$@(if keep-baud?
+                                  #~("--keep-baud")
+                                  #~())
+                           #$@(if timeout
+                                  #~("--timeout" #$(number->string timeout))
+                                  #~())
+                           #$@(if detect-case?
+                                  #~("--detect-case")
+                                  #~())
+                           #$@(if wait-cr?
+                                  #~("--wait-cr")
+                                  #~())
+                           #$@(if no-hints?
+                                  #~("--nohints?")
+                                  #~())
+                           #$@(if no-hostname?
+                                  #~("--nohostname")
+                                  #~())
+                           #$@(if long-hostname?
+                                  #~("--long-hostname")
+                                  #~())
+                           #$@(if erase-characters
+                                  #~("--erase-chars" #$erase-characters)
+                                  #~())
+                           #$@(if kill-characters
+                                  #~("--kill-chars" #$kill-characters)
+                                  #~())
+                           #$@(if chdir
+                                  #~("--chdir" #$chdir)
+                                  #~())
+                           #$@(if delay
+                                  #~("--delay" #$(number->string delay))
+                                  #~())
+                           #$@(if nice
+                                  #~("--nice" #$(number->string nice))
+                                  #~())
+                           #$@(if auto-login
+                                  (list "--autologin" auto-login)
+                                  '())
+                           #$@(if login-program
+                                  #~("--login-program" #$login-program)
+                                  #~())
+                           #$@(if login-pause?
+                                  #~("--login-pause")
+                                  #~())
+                           defaulted-tty
+                           #$@(if baud-rate
+                                  #~(#$baud-rate)
+                                  #~())
+                           #$@(if term
+                                  #~(#$term)
+                                  #~())))
+                    (const #f))                   ; never start.
+                args)))))
+      (stop #~(make-kill-destructor))))))
 
 (define agetty-service-type
   (service-type (name 'agetty)
@@ -1111,42 +1151,42 @@ the tty to run, among other things."
   (clear-on-logout? mingetty-clear-on-logout?       ;Boolean
                     (default #t)))
 
-(define mingetty-shepherd-service
-  (match-lambda
-    (($ <mingetty-configuration> mingetty tty auto-login login-program
-                                 login-pause? clear-on-logout?)
-     (list
-      (shepherd-service
-       (documentation "Run mingetty on an tty.")
-       (provision (list (symbol-append 'term- (string->symbol tty))))
-
-       ;; Since the login prompt shows the host name, wait for the 'host-name'
-       ;; service to be done.  Also wait for udev essentially so that the tty
-       ;; text is not lost in the middle of kernel messages (XXX).
-       (requirement '(user-processes host-name udev virtual-terminal))
-
-       (start  #~(make-forkexec-constructor
-                  (list #$(file-append mingetty "/sbin/mingetty")
-
-                        ;; Avoiding 'vhangup' allows us to avoid 'setfont'
-                        ;; errors down the path where various ioctls get
-                        ;; EIO--see 'hung_up_tty_ioctl' in driver/tty/tty_io.c
-                        ;; in Linux.
-                        "--nohangup" #$tty
-
-                        #$@(if clear-on-logout?
-                               #~()
-                               #~("--noclear"))
-                        #$@(if auto-login
-                               #~("--autologin" #$auto-login)
-                               #~())
-                        #$@(if login-program
-                               #~("--loginprog" #$login-program)
-                               #~())
-                        #$@(if login-pause?
-                               #~("--loginpause")
-                               #~()))))
-       (stop   #~(make-kill-destructor)))))))
+(define (mingetty-shepherd-service config)
+  (match-record config <mingetty-configuration>
+    (mingetty tty auto-login login-program
+              login-pause? clear-on-logout?)
+    (list
+     (shepherd-service
+      (documentation "Run mingetty on an tty.")
+      (provision (list (symbol-append 'term- (string->symbol tty))))
+
+      ;; Since the login prompt shows the host name, wait for the 'host-name'
+      ;; service to be done.  Also wait for udev essentially so that the tty
+      ;; text is not lost in the middle of kernel messages (XXX).
+      (requirement '(user-processes host-name udev virtual-terminal))
+
+      (start  #~(make-forkexec-constructor
+                 (list #$(file-append mingetty "/sbin/mingetty")
+
+                       ;; Avoiding 'vhangup' allows us to avoid 'setfont'
+                       ;; errors down the path where various ioctls get
+                       ;; EIO--see 'hung_up_tty_ioctl' in driver/tty/tty_io.c
+                       ;; in Linux.
+                       "--nohangup" #$tty
+
+                       #$@(if clear-on-logout?
+                              #~()
+                              #~("--noclear"))
+                       #$@(if auto-login
+                              #~("--autologin" #$auto-login)
+                              #~())
+                       #$@(if login-program
+                              #~("--loginprog" #$login-program)
+                              #~())
+                       #$@(if login-pause?
+                              #~("--loginpause")
+                              #~()))))
+      (stop   #~(make-kill-destructor))))))
 
 (define mingetty-service-type
   (service-type (name 'mingetty)
@@ -1174,7 +1214,13 @@ the tty to run, among other things."
   (name-services nscd-configuration-name-services ;list of file-like
                  (default '()))
   (glibc      nscd-configuration-glibc            ;file-like
-              (default glibc)))
+              (default (let-system (system target)
+                         ;; Unless we're cross-compiling, arrange to use nscd
+                         ;; from 'glibc-final' instead of pulling in a second
+                         ;; glibc copy.
+                         (if target
+                             glibc
+                             (canonical-package glibc))))))
 
 (define-record-type* <nscd-cache> nscd-cache make-nscd-cache
   nscd-cache?
@@ -1223,46 +1269,47 @@ the tty to run, among other things."
 (define (nscd.conf-file config)
   "Return the @file{nscd.conf} configuration file for @var{config}, an
 @code{<nscd-configuration>} object."
-  (define cache->config
-    (match-lambda
-      (($ <nscd-cache> (= symbol->string database)
-                       positive-ttl negative-ttl size check-files?
-                       persistent? shared? max-size propagate?)
-       (string-append "\nenable-cache\t" database "\tyes\n"
-
-                      "positive-time-to-live\t" database "\t"
-                      (number->string positive-ttl) "\n"
-                      "negative-time-to-live\t" database "\t"
-                      (number->string negative-ttl) "\n"
-                      "suggested-size\t" database "\t"
-                      (number->string size) "\n"
-                      "check-files\t" database "\t"
-                      (if check-files? "yes\n" "no\n")
-                      "persistent\t" database "\t"
-                      (if persistent? "yes\n" "no\n")
-                      "shared\t" database "\t"
-                      (if shared? "yes\n" "no\n")
-                      "max-db-size\t" database "\t"
-                      (number->string max-size) "\n"
-                      "auto-propagate\t" database "\t"
-                      (if propagate? "yes\n" "no\n")))))
-
-  (match config
-    (($ <nscd-configuration> log-file debug-level caches)
-     (plain-file "nscd.conf"
-                 (string-append "\
+  (define (cache->config cache)
+    (match-record cache <nscd-cache>
+      (database positive-time-to-live negative-time-to-live
+                suggested-size check-files?
+                persistent? shared? max-database-size auto-propagate?)
+      (let ((database (symbol->string database)))
+        (string-append "\nenable-cache\t" database "\tyes\n"
+
+                       "positive-time-to-live\t" database "\t"
+                       (number->string positive-time-to-live) "\n"
+                       "negative-time-to-live\t" database "\t"
+                       (number->string negative-time-to-live) "\n"
+                       "suggested-size\t" database "\t"
+                       (number->string suggested-size) "\n"
+                       "check-files\t" database "\t"
+                       (if check-files? "yes\n" "no\n")
+                       "persistent\t" database "\t"
+                       (if persistent? "yes\n" "no\n")
+                       "shared\t" database "\t"
+                       (if shared? "yes\n" "no\n")
+                       "max-db-size\t" database "\t"
+                       (number->string max-database-size) "\n"
+                       "auto-propagate\t" database "\t"
+                       (if auto-propagate? "yes\n" "no\n")))))
+
+  (match-record config <nscd-configuration>
+    (log-file debug-level caches)
+    (plain-file "nscd.conf"
+                (string-append "\
 # Configuration of libc's name service cache daemon (nscd).\n\n"
-                                (if log-file
-                                    (string-append "logfile\t" log-file)
-                                    "")
-                                "\n"
-                                (if debug-level
-                                    (string-append "debug-level\t"
-                                                   (number->string debug-level))
-                                    "")
-                                "\n"
-                                (string-concatenate
-                                 (map cache->config caches)))))))
+                               (if log-file
+                                   (string-append "logfile\t" log-file)
+                                   "")
+                               "\n"
+                               (if debug-level
+                                   (string-append "debug-level\t"
+                                                  (number->string debug-level))
+                                   "")
+                               "\n"
+                               (string-concatenate
+                                (map cache->config caches))))))
 
 (define (nscd-action-procedure nscd config option)
   ;; XXX: This is duplicated from mcron; factorize.
@@ -1290,10 +1337,11 @@ the tty to run, among other things."
              (loop)))))))
 
 (define (nscd-actions nscd config)
-  "Return Shepherd actions for NSCD."
+  "Return Shepherd actions for NSCD using CONFIG its config file."
   ;; Make this functionality available as actions because that's a simple way
   ;; to run the right 'nscd' binary with the right config file.
-  (list (shepherd-action
+  (list (shepherd-configuration-action config)
+        (shepherd-action
          (name 'statistics)
          (documentation "Display statistics about nscd usage.")
          (procedure (nscd-action-procedure nscd config "--statistics")))
@@ -1607,7 +1655,9 @@ archive' public keys, with GUIX."
   (http-proxy       guix-http-proxy               ;string | #f
                     (default #f))
   (tmpdir           guix-tmpdir                   ;string | #f
-                    (default #f)))
+                    (default #f))
+  (environment      guix-configuration-environment  ;list of strings
+                    (default '())))
 
 (define %default-guix-configuration
   (guix-configuration))
@@ -1663,7 +1713,7 @@ proxy of 'guix-daemon'...~%")
     (guix build-group build-accounts authorize-key? authorized-keys
           use-substitutes? substitute-urls max-silent-time timeout
           log-compression discover? extra-options log-file
-          http-proxy tmpdir chroot-directories)
+          http-proxy tmpdir chroot-directories environment)
     (list (shepherd-service
            (documentation "Run the Guix daemon.")
            (provision '(guix-daemon))
@@ -1752,24 +1802,23 @@ proxy of 'guix-daemon'...~%")
                            (if proxy
                                (list (string-append "http_proxy=" proxy)
                                      (string-append "https_proxy=" proxy))
-                               '()))
+                               '())
+                           '#$environment)
 
                    #:log-file #$log-file))))
            (stop #~(make-kill-destructor))))))
 
 (define (guix-accounts config)
   "Return the user accounts and user groups for CONFIG."
-  (match config
-    (($ <guix-configuration> _ build-group build-accounts)
-     (cons (user-group
-            (name build-group)
-            (system? #t)
-
-            ;; Use a fixed GID so that we can create the store with the right
-            ;; owner.
-            (id 30000))
-           (guix-build-accounts build-accounts
-                                #:group build-group)))))
+  (cons (user-group
+         (name (guix-configuration-build-group config))
+         (system? #t)
+
+         ;; Use a fixed GID so that we can create the store with the right
+         ;; owner.
+         (id 30000))
+        (guix-build-accounts (guix-configuration-build-accounts config)
+                             #:group (guix-configuration-build-group config))))
 
 (define (guix-activation config)
   "Return the activation gexp for CONFIG."
@@ -1979,7 +2028,9 @@ raise a deprecation warning if the 'compression-level' field was used."
 
 (define %guix-publish-log-rotations
   (list (log-rotation
-         (files (list "/var/log/guix-publish.log")))))
+         (files (list "/var/log/guix-publish.log"))
+         (options `("rotate 4"                    ;don't keep too many of them
+                    ,@%default-log-rotation-options)))))
 
 (define (guix-publish-activation config)
   (let ((cache (guix-publish-configuration-cache config)))
@@ -2092,95 +2143,94 @@ item of @var{packages}."
   (udev-rule "90-kvm.rules"
              "KERNEL==\"kvm\", GROUP=\"kvm\", MODE=\"0660\"\n"))
 
-(define udev-shepherd-service
+(define (udev-shepherd-service config)
   ;; Return a <shepherd-service> for UDEV with RULES.
-  (match-lambda
-    (($ <udev-configuration> udev)
-     (list
-      (shepherd-service
-       (provision '(udev))
-
-       ;; Udev needs /dev to be a 'devtmpfs' mount so that new device nodes can
-       ;; be added: see
-       ;; <http://www.linuxfromscratch.org/lfs/view/development/chapter07/udev.html>.
-       (requirement '(root-file-system))
-
-       (documentation "Populate the /dev directory, dynamically.")
-       (start
-        (with-imported-modules (source-module-closure
-                                '((gnu build linux-boot)))
-          #~(lambda ()
-              (define udevd
-                ;; 'udevd' from eudev.
-                #$(file-append udev "/sbin/udevd"))
-
-              (define (wait-for-udevd)
-                ;; Wait until someone's listening on udevd's control
-                ;; socket.
-                (let ((sock (socket AF_UNIX SOCK_SEQPACKET 0)))
-                  (let try ()
-                    (catch 'system-error
-                      (lambda ()
-                        (connect sock PF_UNIX "/run/udev/control")
-                        (close-port sock))
-                      (lambda args
-                        (format #t "waiting for udevd...~%")
-                        (usleep 500000)
-                        (try))))))
-
-              ;; Allow udev to find the modules.
-              (setenv "LINUX_MODULE_DIRECTORY"
-                      "/run/booted-system/kernel/lib/modules")
-
-              (let* ((kernel-release
-                      (utsname:release (uname)))
-                     (linux-module-directory
-                      (getenv "LINUX_MODULE_DIRECTORY"))
-                     (directory
-                      (string-append linux-module-directory "/"
-                                     kernel-release))
-                     (old-umask (umask #o022)))
-                ;; If we're in a container, DIRECTORY might not exist,
-                ;; for instance because the host runs a different
-                ;; kernel.  In that case, skip it; we'll just miss a few
-                ;; nodes like /dev/fuse.
-                (when (file-exists? directory)
-                  (make-static-device-nodes directory))
-                (umask old-umask))
-
-              (let ((pid (fork+exec-command
-                          (list udevd)
-                          #:environment-variables
-                          (cons*
-                           ;; The first one is for udev, the second one for
-                           ;; eudev.
-                           "UDEV_CONFIG_FILE=/etc/udev/udev.conf"
-                           "EUDEV_RULES_DIRECTORY=/etc/udev/rules.d"
-                           (string-append "LINUX_MODULE_DIRECTORY="
-                                          (getenv "LINUX_MODULE_DIRECTORY"))
-                           (default-environment-variables)))))
-                ;; Wait until udevd is up and running.  This appears to
-                ;; be needed so that the events triggered below are
-                ;; actually handled.
-                (wait-for-udevd)
-
-                ;; Trigger device node creation.
-                (system* #$(file-append udev "/bin/udevadm")
-                         "trigger" "--action=add")
-
-                ;; Wait for things to settle down.
-                (system* #$(file-append udev "/bin/udevadm")
-                         "settle")
-                pid))))
-       (stop #~(make-kill-destructor))
-
-       ;; When halting the system, 'udev' is actually killed by
-       ;; 'user-processes', i.e., before its own 'stop' method was called.
-       ;; Thus, make sure it is not respawned.
-       (respawn? #f)
-       ;; We need additional modules.
-       (modules `((gnu build linux-boot)        ;'make-static-device-nodes'
-                  ,@%default-modules)))))))
+  (let ((udev (udev-configuration-udev config)))
+    (list
+     (shepherd-service
+      (provision '(udev))
+
+      ;; Udev needs /dev to be a 'devtmpfs' mount so that new device nodes can
+      ;; be added: see
+      ;; <http://www.linuxfromscratch.org/lfs/view/development/chapter07/udev.html>.
+      (requirement '(root-file-system))
+
+      (documentation "Populate the /dev directory, dynamically.")
+      (start
+       (with-imported-modules (source-module-closure
+                               '((gnu build linux-boot)))
+         #~(lambda ()
+             (define udevd
+               ;; 'udevd' from eudev.
+               #$(file-append udev "/sbin/udevd"))
+
+             (define (wait-for-udevd)
+               ;; Wait until someone's listening on udevd's control
+               ;; socket.
+               (let ((sock (socket AF_UNIX SOCK_SEQPACKET 0)))
+                 (let try ()
+                   (catch 'system-error
+                     (lambda ()
+                       (connect sock PF_UNIX "/run/udev/control")
+                       (close-port sock))
+                     (lambda args
+                       (format #t "waiting for udevd...~%")
+                       (usleep 500000)
+                       (try))))))
+
+             ;; Allow udev to find the modules.
+             (setenv "LINUX_MODULE_DIRECTORY"
+                     "/run/booted-system/kernel/lib/modules")
+
+             (let* ((kernel-release
+                     (utsname:release (uname)))
+                    (linux-module-directory
+                     (getenv "LINUX_MODULE_DIRECTORY"))
+                    (directory
+                     (string-append linux-module-directory "/"
+                                    kernel-release))
+                    (old-umask (umask #o022)))
+               ;; If we're in a container, DIRECTORY might not exist,
+               ;; for instance because the host runs a different
+               ;; kernel.  In that case, skip it; we'll just miss a few
+               ;; nodes like /dev/fuse.
+               (when (file-exists? directory)
+                 (make-static-device-nodes directory))
+               (umask old-umask))
+
+             (let ((pid (fork+exec-command
+                         (list udevd)
+                         #:environment-variables
+                         (cons*
+                          ;; The first one is for udev, the second one for
+                          ;; eudev.
+                          "UDEV_CONFIG_FILE=/etc/udev/udev.conf"
+                          "EUDEV_RULES_DIRECTORY=/etc/udev/rules.d"
+                          (string-append "LINUX_MODULE_DIRECTORY="
+                                         (getenv "LINUX_MODULE_DIRECTORY"))
+                          (default-environment-variables)))))
+               ;; Wait until udevd is up and running.  This appears to
+               ;; be needed so that the events triggered below are
+               ;; actually handled.
+               (wait-for-udevd)
+
+               ;; Trigger device node creation.
+               (system* #$(file-append udev "/bin/udevadm")
+                        "trigger" "--action=add")
+
+               ;; Wait for things to settle down.
+               (system* #$(file-append udev "/bin/udevadm")
+                        "settle")
+               pid))))
+      (stop #~(make-kill-destructor))
+
+      ;; When halting the system, 'udev' is actually killed by
+      ;; 'user-processes', i.e., before its own 'stop' method was called.
+      ;; Thus, make sure it is not respawned.
+      (respawn? #f)
+      ;; We need additional modules.
+      (modules `((gnu build linux-boot)           ;'make-static-device-nodes'
+                 ,@%default-modules))))))
 
 (define udev.conf
   (computed-file "udev.conf"
@@ -2188,14 +2238,15 @@ item of @var{packages}."
                      (lambda (port)
                        (format port "udev_rules=\"/etc/udev/rules.d\"~%")))))
 
-(define udev-etc
-  (match-lambda
-    (($ <udev-configuration> udev rules)
-     `(("udev"
-        ,(file-union
-          "udev" `(("udev.conf" ,udev.conf)
-                   ("rules.d" ,(udev-rules-union (cons* udev kvm-udev-rule
-                                                        rules))))))))))
+(define (udev-etc config)
+  (match-record config <udev-configuration>
+    (udev rules)
+    `(("udev"
+       ,(file-union "udev"
+                    `(("udev.conf" ,udev.conf)
+                      ("rules.d"
+                       ,(udev-rules-union (cons* udev kvm-udev-rule
+                                                 rules)))))))))
 
 (define udev-service-type
   (service-type (name 'udev)
@@ -2205,11 +2256,11 @@ item of @var{packages}."
                        (service-extension etc-service-type udev-etc)))
                 (compose concatenate)           ;concatenate the list of rules
                 (extend (lambda (config rules)
-                          (match config
-                            (($ <udev-configuration> udev initial-rules)
-                             (udev-configuration
-                              (udev udev)
-                              (rules (append initial-rules rules)))))))
+                          (let ((initial-rules
+                                 (udev-configuration-rules config)))
+                            (udev-configuration
+                             (inherit config)
+                             (rules (append initial-rules rules))))))
                 (default-value (udev-configuration))
                 (description
                  "Run @command{udev}, which populates the @file{/dev}
@@ -2347,23 +2398,23 @@ instance."
   (options  gpm-configuration-options             ;list of strings
             (default %default-gpm-options)))
 
-(define gpm-shepherd-service
-  (match-lambda
-    (($ <gpm-configuration> gpm options)
-     (list (shepherd-service
-            (requirement '(udev))
-            (provision '(gpm))
-            ;; 'gpm' runs in the background and sets a PID file.
-            ;; Note that it requires running as "root".
-            (start #~(make-forkexec-constructor
-                      (list #$(file-append gpm "/sbin/gpm")
-                            #$@options)
-                      #:pid-file "/var/run/gpm.pid"
-                      #:pid-file-timeout 3))
-            (stop #~(lambda (_)
-                      ;; Return #f if successfully stopped.
-                      (not (zero? (system* #$(file-append gpm "/sbin/gpm")
-                                           "-k"))))))))))
+(define (gpm-shepherd-service config)
+  (match-record config <gpm-configuration>
+    (gpm options)
+    (list (shepherd-service
+           (requirement '(udev))
+           (provision '(gpm))
+           ;; 'gpm' runs in the background and sets a PID file.
+           ;; Note that it requires running as "root".
+           (start #~(make-forkexec-constructor
+                     (list #$(file-append gpm "/sbin/gpm")
+                           #$@options)
+                     #:pid-file "/var/run/gpm.pid"
+                     #:pid-file-timeout 3))
+           (stop #~(lambda (_)
+                     ;; Return #f if successfully stopped.
+                     (not (zero? (system* #$(file-append gpm "/sbin/gpm")
+                                          "-k")))))))))
 
 (define gpm-service-type
   (service-type (name 'gpm)
@@ -2443,7 +2494,15 @@ notably to select, copy, and paste text.  The default options use the
         (documentation "kmscon virtual terminal")
         (requirement '(user-processes udev dbus-system))
         (provision (list (symbol-append 'term- (string->symbol virtual-terminal))))
-        (start #~(make-forkexec-constructor #$kmscon-command))
+        (start #~(make-forkexec-constructor
+                  #$kmscon-command
+
+                  ;; The installer needs to be able to display glyphs from
+                  ;; various scripts, so give it access to unifont.
+                  ;; TODO: Make this configurable.
+                  #:environment-variables
+                  (list (string-append "XDG_DATA_DIRS="
+                                       #$font-gnu-unifont "/share"))))
         (stop #~(make-kill-destructor)))))
    (description "Start the @command{kmscon} virtual terminal emulator for the
 Linux @dfn{kernel mode setting} (KMS).")))
@@ -2616,32 +2675,64 @@ to CONFIG."
                              "/servers/socket/2")
                      #f))))
 
-(define network-set-up/linux
-  (match-lambda
-    (($ <static-networking> addresses links routes)
-     (scheme-file "set-up-network"
-                  (with-extensions (list guile-netlink)
-                    #~(begin
-                        (use-modules (ip addr) (ip link) (ip route))
-
-                        #$@(map (lambda (address)
-                                  #~(begin
-                                      (addr-add #$(network-address-device address)
-                                                #$(network-address-value address)
-                                                #:ipv6?
-                                                #$(network-address-ipv6? address))
-                                      ;; FIXME: loopback?
-                                      (link-set #$(network-address-device address)
-                                                #:multicast-on #t
-                                                #:up #t)))
-                                addresses)
-                        #$@(map (match-lambda
-                                  (($ <network-link> name type arguments)
-                                   #~(link-add #$name #$type
-                                               #:type-args '#$arguments)))
-                                links)
-                        #$@(map (lambda (route)
-                                  #~(route-add #$(network-route-destination route)
+(define (network-set-up/linux config)
+  (match-record config <static-networking>
+    (addresses links routes)
+    (scheme-file "set-up-network"
+                 (with-extensions (list guile-netlink)
+                   #~(begin
+                       (use-modules (ip addr) (ip link) (ip route))
+
+                       #$@(map (lambda (address)
+                                 #~(begin
+                                     (addr-add #$(network-address-device address)
+                                               #$(network-address-value address)
+                                               #:ipv6?
+                                               #$(network-address-ipv6? address))
+                                     ;; FIXME: loopback?
+                                     (link-set #$(network-address-device address)
+                                               #:multicast-on #t
+                                               #:up #t)))
+                               addresses)
+                       #$@(map (match-lambda
+                                 (($ <network-link> name type arguments)
+                                  #~(link-add #$name #$type
+                                              #:type-args '#$arguments)))
+                               links)
+                       #$@(map (lambda (route)
+                                 #~(route-add #$(network-route-destination route)
+                                              #:device
+                                              #$(network-route-device route)
+                                              #:ipv6?
+                                              #$(network-route-ipv6? route)
+                                              #:via
+                                              #$(network-route-gateway route)
+                                              #:src
+                                              #$(network-route-source route)))
+                               routes)
+                       #t)))))
+
+(define (network-tear-down/linux config)
+  (match-record config <static-networking>
+    (addresses links routes)
+    (scheme-file "tear-down-network"
+                 (with-extensions (list guile-netlink)
+                   #~(begin
+                       (use-modules (ip addr) (ip link) (ip route)
+                                    (netlink error)
+                                    (srfi srfi-34))
+
+                       (define-syntax-rule (false-if-netlink-error exp)
+                         (guard (c ((netlink-error? c) #f))
+                           exp))
+
+                       ;; Wrap calls in 'false-if-netlink-error' so this
+                       ;; script goes as far as possible undoing the effects
+                       ;; of "set-up-network".
+
+                       #$@(map (lambda (route)
+                                 #~(false-if-netlink-error
+                                    (route-del #$(network-route-destination route)
                                                #:device
                                                #$(network-route-device route)
                                                #:ipv6?
@@ -2649,80 +2740,47 @@ to CONFIG."
                                                #:via
                                                #$(network-route-gateway route)
                                                #:src
-                                               #$(network-route-source route)))
-                                routes)
-                        #t))))))
-
-(define network-tear-down/linux
-  (match-lambda
-    (($ <static-networking> addresses links routes)
-     (scheme-file "tear-down-network"
-                  (with-extensions (list guile-netlink)
-                    #~(begin
-                        (use-modules (ip addr) (ip link) (ip route)
-                                     (netlink error)
-                                     (srfi srfi-34))
-
-                        (define-syntax-rule (false-if-netlink-error exp)
-                          (guard (c ((netlink-error? c) #f))
-                            exp))
-
-                        ;; Wrap calls in 'false-if-netlink-error' so this
-                        ;; script goes as far as possible undoing the effects
-                        ;; of "set-up-network".
-
-                        #$@(map (lambda (route)
-                                  #~(false-if-netlink-error
-                                     (route-del #$(network-route-destination route)
-                                                #:device
-                                                #$(network-route-device route)
-                                                #:ipv6?
-                                                #$(network-route-ipv6? route)
-                                                #:via
-                                                #$(network-route-gateway route)
-                                                #:src
-                                                #$(network-route-source route))))
-                                routes)
-                        #$@(map (match-lambda
-                                  (($ <network-link> name type arguments)
-                                   #~(false-if-netlink-error
-                                      (link-del #$name))))
-                                links)
-                        #$@(map (lambda (address)
+                                               #$(network-route-source route))))
+                               routes)
+                       #$@(map (match-lambda
+                                 (($ <network-link> name type arguments)
                                   #~(false-if-netlink-error
-                                     (addr-del #$(network-address-device
-                                                  address)
-                                               #$(network-address-value address)
-                                               #:ipv6?
-                                               #$(network-address-ipv6? address))))
-                                addresses)
-                        #f))))))
+                                     (link-del #$name))))
+                               links)
+                       #$@(map (lambda (address)
+                                 #~(false-if-netlink-error
+                                    (addr-del #$(network-address-device
+                                                 address)
+                                              #$(network-address-value address)
+                                              #:ipv6?
+                                              #$(network-address-ipv6? address))))
+                               addresses)
+                       #f)))))
 
 (define (static-networking-shepherd-service config)
-  (match config
-    (($ <static-networking> addresses links routes
-                            provision requirement name-servers)
-     (let ((loopback? (and provision (memq 'loopback provision))))
-       (shepherd-service
+  (match-record config <static-networking>
+    (addresses links routes provision requirement name-servers)
+    (let ((loopback? (and provision (memq 'loopback provision))))
+      (shepherd-service
 
-        (documentation
-         "Bring up the networking interface using a static IP address.")
-        (requirement requirement)
-        (provision provision)
+       (documentation
+        "Bring up the networking interface using a static IP address.")
+       (requirement requirement)
+       (provision provision)
 
-        (start #~(lambda _
-                   ;; Return #t if successfully started.
-                   (load #$(let-system (system target)
-                             (if (string-contains (or target system) "-linux")
-                                 (network-set-up/linux config)
-                                 (network-set-up/hurd config))))))
-        (stop #~(lambda _
-                  ;; Return #f is successfully stopped.
+       (start #~(lambda _
+                  ;; Return #t if successfully started.
                   (load #$(let-system (system target)
                             (if (string-contains (or target system) "-linux")
-                                (network-tear-down/linux config)
-                                (network-tear-down/hurd config))))))
-        (respawn? #f))))))
+                                (network-set-up/linux config)
+                                (network-set-up/hurd config))))))
+       (stop #~(lambda _
+                 ;; Return #f is successfully stopped.
+                 (load #$(let-system (system target)
+                           (if (string-contains (or target system) "-linux")
+                               (network-tear-down/linux config)
+                               (network-tear-down/hurd config))))))
+       (respawn? #f)))))
 
 (define (static-networking-shepherd-services networks)
   (map static-networking-shepherd-service networks))
@@ -2835,51 +2893,152 @@ to handle."
   (extra-env greetd-agreety-extra-env (default '()))
   (xdg-env? greetd-agreety-xdg-env? (default #t)))
 
-(define greetd-agreety-tty-session-command
-  (match-lambda
-    (($ <greetd-agreety-session> _ command args extra-env)
-     (program-file
-      "agreety-tty-session-command"
-      #~(begin
-          (use-modules (ice-9 match))
-          (for-each (match-lambda ((var . val) (setenv var val)))
-                    (quote (#$@extra-env)))
-          (apply execl #$command #$command (list #$@args)))))))
-
-(define greetd-agreety-tty-xdg-session-command
-  (match-lambda
-    (($ <greetd-agreety-session> _ command args extra-env)
-     (program-file
-      "agreety-tty-xdg-session-command"
-      #~(begin
-          (use-modules (ice-9 match))
-          (let*
-              ((username (getenv "USER"))
-               (useruid (passwd:uid (getpwuid username)))
-               (useruid (number->string useruid)))
-            (setenv "XDG_SESSION_TYPE" "tty")
-            (setenv "XDG_RUNTIME_DIR" (string-append "/run/user/" useruid)))
-          (for-each (match-lambda ((var . val) (setenv var val)))
-                    (quote (#$@extra-env)))
-          (apply execl #$command #$command (list #$@args)))))))
-
-(define (make-greetd-agreety-session-command config command)
-  (let ((agreety (file-append (greetd-agreety config) "/bin/agreety")))
+(define (greetd-agreety-tty-session-command config)
+  (match-record config <greetd-agreety-session>
+    (command command-args extra-env)
+    (program-file
+     "agreety-tty-session-command"
+     #~(begin
+         (use-modules (ice-9 match))
+         (for-each (match-lambda ((var . val) (setenv var val)))
+                   (quote (#$@extra-env)))
+         (apply execl #$command #$command (list #$@command-args))))))
+
+(define (greetd-agreety-tty-xdg-session-command config)
+  (match-record config <greetd-agreety-session>
+    (command command-args extra-env)
     (program-file
-     "agreety-command"
-     #~(execl #$agreety #$agreety "-c" #$command))))
-
-(define (make-greetd-default-session-command config-or-command)
-  (cond ((greetd-agreety-session? config-or-command)
-         (cond ((greetd-agreety-xdg-env? config-or-command)
-                (make-greetd-agreety-session-command
-                 config-or-command
-                 (greetd-agreety-tty-xdg-session-command config-or-command)))
-               (#t
-                (make-greetd-agreety-session-command
-                 config-or-command
-                 (greetd-agreety-tty-session-command config-or-command)))))
-        (#t config-or-command)))
+     "agreety-tty-xdg-session-command"
+     #~(begin
+         (use-modules (ice-9 match))
+         (let*
+             ((username (getenv "USER"))
+              (useruid (passwd:uid (getpwuid username)))
+              (useruid (number->string useruid)))
+           (setenv "XDG_SESSION_TYPE" "tty")
+           (setenv "XDG_RUNTIME_DIR" (string-append "/run/user/" useruid)))
+         (for-each (match-lambda ((var . val) (setenv var val)))
+                   (quote (#$@extra-env)))
+         (apply execl #$command #$command (list #$@command-args))))))
+
+(define-gexp-compiler (greetd-agreety-session-compiler
+                       (session <greetd-agreety-session>)
+                       system target)
+  (let ((agreety (file-append (greetd-agreety session)
+                              "/bin/agreety"))
+        (command ((if (greetd-agreety-xdg-env? session)
+                      greetd-agreety-tty-xdg-session-command
+                      greetd-agreety-tty-session-command)
+                  session)))
+    (lower-object
+     (program-file "agreety-command"
+       #~(execl #$agreety #$agreety "-c" #$command)))))
+
+(define-record-type* <greetd-wlgreet-session>
+  greetd-wlgreet-session make-greetd-wlgreet-session
+  greetd-wlgreet-session?
+  (wlgreet greetd-wlgreet (default wlgreet))
+  (command greetd-wlgreet-command
+           (default (file-append sway "/bin/sway")))
+  (command-args greetd-wlgreet-command-args (default '()))
+  (output-mode greetd-wlgreet-output-mode (default "all"))
+  (scale greetd-wlgreet-scale (default 1))
+  (background greetd-wlgreet-background (default '(0 0 0 0.9)))
+  (headline greetd-wlgreet-headline (default '(1 1 1 1)))
+  (prompt greetd-wlgreet-prompt (default '(1 1 1 1)))
+  (prompt-error greetd-wlgreet-prompt-error (default '(1 1 1 1)))
+  (border greetd-wlgreet-border (default '(1 1 1 1)))
+  (extra-env greetd-wlgreet-extra-env (default '())))
+
+(define (greetd-wlgreet-wayland-session-command session)
+  (program-file "wlgreet-session-command"
+    #~(let* ((username (getenv "USER"))
+             (useruid (number->string
+                       (passwd:uid (getpwuid username))))
+             (command #$(greetd-wlgreet-command session)))
+        (use-modules (ice-9 match))
+        (setenv "XDG_SESSION_TYPE" "wayland")
+        (setenv "XDG_RUNTIME_DIR" (string-append "/run/user/" useruid))
+        (for-each (lambda (env) (setenv (car env) (cdr env)))
+                  '(#$@(greetd-wlgreet-extra-env session)))
+        (apply execl command command
+               (list #$@(greetd-wlgreet-command-args session))))))
+
+(define (make-wlgreet-config-color section-name color)
+  (match color
+    ((red green blue opacity)
+     (string-append
+      "[" section-name "]\n"
+      "red = " (number->string red) "\n"
+      "green = " (number->string green) "\n"
+      "blue = " (number->string blue) "\n"
+      "opacity = " (number->string opacity) "\n"))))
+
+(define (make-wlgreet-configuration-file session)
+  (let ((command (greetd-wlgreet-wayland-session-command session))
+        (output-mode (greetd-wlgreet-output-mode session))
+        (scale (greetd-wlgreet-scale session))
+        (background (greetd-wlgreet-background session))
+        (headline (greetd-wlgreet-headline session))
+        (prompt (greetd-wlgreet-prompt session))
+        (prompt-error (greetd-wlgreet-prompt-error session))
+        (border (greetd-wlgreet-border session)))
+    (mixed-text-file "wlgreet.toml"
+      "command = \"" command "\"\n"
+      "outputMode = \"" output-mode "\"\n"
+      "scale = " (number->string scale) "\n"
+      (apply string-append
+             (map (match-lambda
+                    ((section-name . color)
+                     (make-wlgreet-config-color section-name color)))
+                  `(("background" . ,background)
+                    ("headline" . ,headline)
+                    ("prompt" . ,prompt)
+                    ("prompt-error" . ,prompt-error)
+                    ("border" . ,border)))))))
+
+(define-record-type* <greetd-wlgreet-sway-session>
+  greetd-wlgreet-sway-session make-greetd-wlgreet-sway-session
+  greetd-wlgreet-sway-session?
+  (wlgreet-session greetd-wlgreet-sway-session-wlgreet-session       ;<greetd-wlgreet-session>
+                   (default (greetd-wlgreet-session)))
+  (sway greetd-wlgreet-sway-session-sway (default sway))             ;<package>
+  (sway-configuration greetd-wlgreet-sway-session-sway-configuration ;file-like
+                      (default (plain-file "wlgreet-sway-config" ""))))
+
+(define (make-wlgreet-sway-configuration-file session)
+  (let* ((wlgreet-session (greetd-wlgreet-sway-session-wlgreet-session session))
+         (wlgreet-config (make-wlgreet-configuration-file wlgreet-session))
+         (wlgreet (file-append (greetd-wlgreet wlgreet-session) "/bin/wlgreet"))
+         (sway-config (greetd-wlgreet-sway-session-sway-configuration session))
+         (swaymsg (file-append (greetd-wlgreet-sway-session-sway session)
+                               "/bin/swaymsg")))
+    (mixed-text-file "wlgreet-sway.conf"
+      "include " sway-config "\n"
+      "xwayland disable\n"
+      "exec \"" wlgreet " --config " wlgreet-config "; "
+      swaymsg " exit\"\n")))
+
+(define-gexp-compiler (greetd-wlgreet-sway-session-compiler
+                       (session <greetd-wlgreet-sway-session>)
+                       system target)
+  (let ((sway (file-append (greetd-wlgreet-sway-session-sway session)
+                           "/bin/sway"))
+        (config (make-wlgreet-sway-configuration-file session)))
+    (lower-object
+     (program-file "wlgreet-sway-session-command"
+       #~(let* ((log-file (open-output-file
+                           (string-append "/tmp/sway-greeter."
+                                          (number->string (getpid))
+                                          ".log")))
+                (username (getenv "USER"))
+                (useruid (number->string (passwd:uid (getpwuid username)))))
+           ;; redirect stdout/err to log-file
+           (dup2 (fileno log-file) 1)
+           (dup2 1 2)
+           (sleep 1) ;give seatd/logind some time to start up
+           (setenv "XDG_RUNTIME_DIR" (string-append "/run/user/" useruid))
+           (execl #$sway #$sway "-d" "-c" #$config))))))
 
 (define-record-type* <greetd-terminal-configuration>
   greetd-terminal-configuration make-greetd-terminal-configuration
@@ -2891,10 +3050,10 @@ to handle."
                  (default (default-log-file-name this-record)))
   (terminal-vt greetd-terminal-vt (default "7"))
   (terminal-switch greetd-terminal-switch (default #f))
+  (source-profile? greetd-source-profile? (default #t))
   (default-session-user greetd-default-session-user (default "greeter"))
   (default-session-command greetd-default-session-command
-    (default (greetd-agreety-session))
-    (sanitize make-greetd-default-session-command)))
+    (default (greetd-agreety-session))))
 
 (define (default-config-file-name config)
   (string-join (list "config-" (greetd-terminal-vt config) ".toml") ""))
@@ -2905,12 +3064,14 @@ to handle."
 (define (make-greetd-terminal-configuration-file config)
   (let*
       ((config-file-name (greetd-config-file-name config))
+       (source-profile? (greetd-source-profile? config))
        (terminal-vt (greetd-terminal-vt config))
        (terminal-switch (greetd-terminal-switch config))
        (default-session-user (greetd-default-session-user config))
        (default-session-command (greetd-default-session-command config)))
     (mixed-text-file
      config-file-name
+     "source_profile = " (if source-profile? "true" "false") "\n"
      "[terminal]\n"
      "vt = " terminal-vt "\n"
      "switch = " (if terminal-switch "true" "false") "\n"