aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2014-2022 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2015 Andy Wingo <wingo@igalia.com>
;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2016 Sou Bunnbu <iyzsong@gmail.com>
;;; Copyright © 2017, 2020, 2022, 2023, 2025 Maxim Cournoyer <maxim.cournoyer@gmail.com>
;;; Copyright © 2017 Nikita <nikita@n0.is>
;;; Copyright © 2017, 2019 Hartmut Goebel <h.goebel@crazy-compilers.com>
;;; Copyright © 2018, 2020, 2022 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2018, 2023 Ricardo Wurmus <rekado@elephly.net>
;;; Copyright © 2017, 2019 Christopher Baines <mail@cbaines.net>
;;; Copyright © 2019 Tim Gesthuizen <tim.gesthuizen@yahoo.de>
;;; Copyright © 2019 David Wilson <david@daviwil.com>
;;; Copyright © 2020, 2024 Tobias Geerinckx-Rice <me@tobias.gr>
;;; Copyright © 2020 Reza Alizadeh Majd <r.majd@pantherx.org>
;;; Copyright © 2021 Brice Waegeneire <brice@waegenei.re>
;;; Copyright © 2021, 2022 muradm <mail@muradm.net>
;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
;;; Copyright © 2023 Zheng Junjie <873216071@qq.com>
;;; Copyright © 2024 45mg <45mg.writes@gmail.com>
;;; Copyright © 2024 Raven Hallsby <karl@hallsby.com>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.

(define-module (gnu services desktop)
  #:use-module (gnu services)
  #:use-module (gnu services shepherd)
  #:use-module (gnu services base)
  #:use-module (gnu services configuration)
  #:use-module (gnu services dbus)
  #:use-module (gnu services avahi)
  #:use-module (gnu services xorg)
  #:use-module (gnu services networking)
  #:use-module (gnu services sound)
  #:use-module ((gnu system file-systems)
                #:select (%control-groups
                          %elogind-file-systems
                          file-system))
  #:autoload   (gnu services sddm) (sddm-service-type)
  #:use-module (gnu system)
  #:use-module (gnu system privilege)
  #:use-module (gnu system shadow)
  #:use-module (gnu system uuid)
  #:use-module (gnu system pam)
  #:use-module (gnu packages glib)
  #:use-module (gnu packages admin)
  #:use-module (gnu packages cups)
  #:use-module (gnu packages freedesktop)
  #:use-module (gnu packages gnome)
  #:use-module (gnu packages kde)
  #:use-module (gnu packages kde-frameworks)
  #:use-module (gnu packages kde-plasma)
  #:use-module (gnu packages pulseaudio)
  #:use-module (gnu packages xfce)
  #:use-module (gnu packages avahi)
  #:use-module (gnu packages xdisorg)
  #:use-module (gnu packages scanner)
  #:use-module (gnu packages suckless)
  #:use-module (gnu packages sugar)
  #:use-module (gnu packages linux)
  #:use-module (gnu packages libusb)
  #:use-module (gnu packages lxqt)
  #:use-module (gnu packages mate)
  #:use-module (gnu packages nfs)
  #:use-module (gnu packages enlightenment)
  #:use-module (guix deprecation)
  #:use-module (guix records)
  #:use-module (guix packages)
  #:use-module (guix store)
  #:use-module (guix ui)
  #:use-module (guix utils)
  #:use-module (guix gexp)
  #:use-module (guix modules)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-26)
  #:use-module (ice-9 format)
  #:use-module (ice-9 match)
  #:export (<upower-configuration>
            upower-configuration
            upower-configuration?
            upower-configuration-upower
            upower-configuration-watts-up-pro?
            upower-configuration-poll-batteries?
            upower-configuration-ignore-lid?
            upower-configuration-use-percentage-for-policy?
            upower-configuration-percentage-low
            upower-configuration-percentage-critical
            upower-configuration-percentage-action
            upower-configuration-time-low
            upower-configuration-time-critical
            upower-configuration-time-action
            upower-configuration-critical-power-action

            upower-service-type

            udisks-configuration
            udisks-configuration?
            udisks-service  ; deprecated
            udisks-service-type

            gvfs-configuration
            gvfs-configuration?
            gvfs-service-type

            colord-service-type

            geoclue-application
            geoclue-configuration
            geoclue-configuration?
            %standard-geoclue-applications
            geoclue-service  ; deprecated
            geoclue-service-type

            bluetooth-service-type
            bluetooth-configuration
            bluetooth-configuration?
            bluetooth-service  ; deprecated

            elogind-configuration
            elogind-configuration?
            elogind-service  ; deprecated
            elogind-service-type

            %gdm-file-system
            gdm-file-system-service

            %fontconfig-file-system
            fontconfig-file-system-service

            accountsservice-service-type
            accountsservice-service  ; deprecated

            cups-pk-helper-service-type
            sane-service-type

            gnome-desktop-configuration
            gnome-desktop-configuration?
            gnome-desktop-configuration-core-services
            gnome-desktop-configuration-shell
            gnome-desktop-configuration-utilities
            gnome-desktop-configuration-extra-packages
            gnome-desktop-configuration-polkit-ignorelist
            gnome-desktop-configuration-udev-ignorelist
            gnome-desktop-service
            gnome-desktop-service-type

            mate-desktop-configuration
            mate-desktop-configuration?
            mate-desktop-service
            mate-desktop-service-type

            lxqt-desktop-configuration
            lxqt-desktop-configuration?
            lxqt-desktop-service-type

            sugar-desktop-configuration
            sugar-desktop-configuration?
            sugar-desktop-service-type

            plasma-desktop-configuration
            plasma-desktop-configuration?
            plasma-desktop-service-type

            xfce-desktop-configuration
            xfce-desktop-configuration?
            xfce-desktop-service
            xfce-desktop-service-type

            x11-socket-directory-service ;deprecated
            x11-socket-directory-service-type

            enlightenment-desktop-configuration
            enlightenment-desktop-configuration?
            enlightenment-desktop-service-type

            inputattach-configuration
            inputattach-configuration?
            inputattach-service-type

            polkit-wheel-service

            gnome-keyring-configuration
            gnome-keyring-configuration?
            gnome-keyring-service-type

            seatd-configuration
            seatd-service-type

            %desktop-services))

;;; Commentary:
;;;
;;; This module contains service definitions for a "desktop" environment.
;;;
;;; Code:


;;;
;;; Helpers.
;;;

(define (bool value)
  (if value "true\n" "false\n"))

(define (package-direct-input-selector tree)
  "Return a procedure that selects TREE from the inputs of PACKAGE.  If TREE
is a list, it recursively searches it until it locates the last item of TREE."
  (lambda (package)
    (let loop ((tree (if (pair? tree)
                         tree
                         (list tree)))
               (package package))
      (if (null? tree)
          package
          (loop (cdr tree)
                (car (assoc-ref (package-direct-inputs package)
                                (car tree))))))))


;;;
;;; Upower D-Bus service.
;;;

(define-record-type* <upower-configuration>
  upower-configuration make-upower-configuration
  upower-configuration?
  (upower                     upower-configuration-upower
                              (default upower))
  (watts-up-pro?              upower-configuration-watts-up-pro?
                              (default #f))
  (poll-batteries?            upower-configuration-poll-batteries?
                              (default #t))
  (ignore-lid?                upower-configuration-ignore-lid?
                              (default #f))
  (use-percentage-for-policy? upower-configuration-use-percentage-for-policy?
                              (default #t))
  (percentage-low             upower-configuration-percentage-low
                              (default 20))
  (percentage-critical        upower-configuration-percentage-critical
                              (default 5))
  (percentage-action          upower-configuration-percentage-action
                              (default 2))
  (time-low                   upower-configuration-time-low
                              (default 1200))
  (time-critical              upower-configuration-time-critical
                              (default 300))
  (time-action                upower-configuration-time-action
                              (default 120))
  (critical-power-action      upower-configuration-critical-power-action
                              (default 'hybrid-sleep)))

(define* upower-configuration-file
  ;; Return an upower-daemon configuration file.
  (match-lambda
    (($ <upower-configuration> upower
        watts-up-pro? poll-batteries? ignore-lid? use-percentage-for-policy?
        percentage-low percentage-critical percentage-action time-low
        time-critical time-action critical-power-action)
     (plain-file "UPower.conf"
                 (string-append
                  "[UPower]\n"
                  "EnableWattsUpPro=" (bool watts-up-pro?)
                  "NoPollBatteries=" (bool (not poll-batteries?))
                  "IgnoreLid=" (bool ignore-lid?)
                  "UsePercentageForPolicy=" (bool use-percentage-for-policy?)
                  "PercentageLow=" (number->string percentage-low) "\n"
                  "PercentageCritical=" (number->string percentage-critical) "\n"
                  "PercentageAction=" (number->string percentage-action) "\n"
                  "TimeLow=" (number->string time-low) "\n"
                  "TimeCritical=" (number->string time-critical) "\n"
                  "TimeAction=" (number->string time-action) "\n"
                  "CriticalPowerAction=" (match critical-power-action
                                           ('hybrid-sleep "HybridSleep")
                                           ('hibernate "Hibernate")
                                           ('power-off "PowerOff"))
                  "\n")))))

(define %upower-activation
  #~(begin
      (use-modules (guix build utils))
      (mkdir-p "/var/lib/upower")))

(define (upower-dbus-service config)
  (list (wrapped-dbus-service (upower-configuration-upower config)
                              "libexec/upowerd"
                              `(("UPOWER_CONF_FILE_NAME"
                                 ,(upower-configuration-file config))))))

(define (upower-shepherd-service config)
  "Return a shepherd service for UPower with CONFIG."
  (let ((upower (upower-configuration-upower config))
        (config (upower-configuration-file config)))
    (list (shepherd-service
           (documentation "Run the UPower power and battery monitor.")
           (provision '(upower-daemon))
           (requirement '(dbus-system udev))

           (start #~(make-forkexec-constructor
                     (list (string-append #$upower "/libexec/upowerd"))
                     #:environment-variables
                     (list (string-append "UPOWER_CONF_FILE_NAME="
                                          #$config))))
           (stop #~(make-kill-destructor))
           (actions (list (shepherd-configuration-action config)))))))

(define upower-service-type
  (let ((upower-package (compose list upower-configuration-upower)))
    (service-type (name 'upower)
                  (description
                   "Run @command{upowerd}, a system-wide monitor for power
consumption and battery levels, with the given configuration settings.  It
implements the @code{org.freedesktop.UPower} D-Bus interface, and is notably
used by GNOME.")
                  (extensions
                   (list (service-extension dbus-root-service-type
                                            upower-dbus-service)
                         (service-extension shepherd-root-service-type
                                            upower-shepherd-service)
                         (service-extension activation-service-type
                                            (const %upower-activation))
                         (service-extension udev-service-type
                                            upower-package)

                         ;; Make the 'upower' command visible.
                         (service-extension profile-service-type
                                            upower-package)))
                  (default-value (upower-configuration)))))


;;;
;;; GeoClue D-Bus service.
;;;

(define* (geoclue-application name #:key (allowed? #t) system? (users '()))
  "Configure default GeoClue access permissions for an application.  NAME is
the Desktop ID of the application, without the .desktop part.  If ALLOWED? is
true, the application will have access to location information by default.
The boolean SYSTEM? value indicates that an application is a system component
or not.  Finally USERS is a list of UIDs of all users for which this
application is allowed location info access.  An empty users list means all
users are allowed."
  (string-append
   "[" name "]\n"
   "allowed=" (bool allowed?)
   "system=" (bool system?)
   "users=" (string-join users ";") "\n"))

(define %standard-geoclue-applications
  (list (geoclue-application "gnome-datetime-panel" #:system? #t)
        (geoclue-application "epiphany" #:system? #f)
        (geoclue-application "firefox" #:system? #f)))

;; TODO: Use define-configuration and export accessors.
(define-record-type* <geoclue-configuration>
  geoclue-configuration make-geoclue-configuration
  geoclue-configuration?
  (geoclue geoclue-configuration-geoclue
           (default geoclue))
  (whitelist geoclue-configuration-whitelist
             (default '()))
  (wifi-geolocation-url
   geoclue-configuration-wifi-geolocation-url
   ;; Mozilla geolocation service:
   (default "https://location.services.mozilla.com/v1/geolocate?key=geoclue"))
  (submit-data? geoclue-configuration-submit-data?
                (default #f))
  (wifi-submission-url
   geoclue-configuration-wifi-submission-url
   (default "https://location.services.mozilla.com/v1/submit?key=geoclue"))
  (submission-nick geoclue-configuration-submission-nick
                   (default "geoclue"))
  (applications geoclue-configuration-applications
                (default %standard-geoclue-applications)))

(define* (geoclue-configuration-file config)
  "Return a geoclue configuration file."
  (plain-file "geoclue.conf"
              (string-append
               "[agent]\n"
               "whitelist="
               (string-join (geoclue-configuration-whitelist config)
                            ";") "\n"
               "[wifi]\n"
               "url=" (geoclue-configuration-wifi-geolocation-url config) "\n"
               "submit-data=" (bool (geoclue-configuration-submit-data? config))
               "submission-url="
               (geoclue-configuration-wifi-submission-url config) "\n"
               "submission-nick="
               (geoclue-configuration-submission-nick config)
               "\n"
               (string-join (geoclue-configuration-applications config)
                            "\n"))))

(define (geoclue-dbus-service config)
  (list (wrapped-dbus-service (geoclue-configuration-geoclue config)
                              "libexec/geoclue"
                              `(("GEOCLUE_CONFIG_FILE"
                                 ,(geoclue-configuration-file config))))))

(define %geoclue-accounts
  (list (user-group (name "geoclue") (system? #t))
        (user-account
         (name "geoclue")
         (group "geoclue")
         (system? #t)
         (comment "GeoClue daemon user")
         (home-directory "/var/empty")
         (shell "/run/current-system/profile/sbin/nologin"))))

(define geoclue-service-type
  (service-type (name 'geoclue)
                (extensions
                 (list (service-extension dbus-root-service-type
                                          geoclue-dbus-service)
                       (service-extension account-service-type
                                          (const %geoclue-accounts))))
                (description "Run the @command{geoclue} location service.
This service provides a D-Bus interface to allow applications to request
access to a user's physical location, and optionally to add information to
online location databases.")
                (default-value (geoclue-configuration))))

(define-deprecated
  (geoclue-service #:key (geoclue geoclue)
                   (whitelist '())
                   (wifi-geolocation-url
                    ;; Mozilla geolocation service:
                    "https://location.services.mozilla.com/v1/geolocate?key=geoclue")
                   (submit-data? #f)
                   (wifi-submission-url
                    "https://location.services.mozilla.com/v1/submit?key=geoclue")
                   (submission-nick "geoclue")
                   (applications %standard-geoclue-applications))
  geoclue-service-type
  "Return a service that runs the @command{geoclue} location service.  This
service provides a D-Bus interface to allow applications to request access to
a user's physical location, and optionally to add information to online
location databases.  By default, only the GNOME date-time panel and the Icecat
and Epiphany web browsers are able to ask for the user's location, and in the
case of Icecat and Epiphany, both will ask the user for permission first.  See
@uref{https://wiki.freedesktop.org/www/Software/GeoClue/, the geoclue web
site} for more information."
  (service geoclue-service-type
           (geoclue-configuration
            (geoclue geoclue)
            (whitelist whitelist)
            (wifi-geolocation-url wifi-geolocation-url)
            (submit-data? submit-data?)
            (wifi-submission-url wifi-submission-url)
            (submission-nick submission-nick)
            (applications applications))))


;;;
;;; Bluetooth.
;;;

(define-record-type* <bluetooth-configuration>
  bluetooth-configuration make-bluetooth-configuration
  bluetooth-configuration?
  (bluez bluetooth-configuration-bluez (default bluez))

  ;;; [General]
  (name bluetooth-configuration-name (default "BlueZ"))
  (class bluetooth-configuration-class (default #x000000))
  (discoverable-timeout
   bluetooth-configuration-discoverable-timeout (default 180))
  (always-pairable? bluetooth-configuration-always-pairable? (default #f))
  (pairable-timeout bluetooth-configuration-pairable-timeout (default 0))

  ;;; MAYBE: Exclude into separate <device-id> record-type?
  (device-id bluetooth-configuration-device-id (default #f))
  (reverse-service-discovery?
   bluetooth-configuration-reverse-service-discovery (default #t))
  (name-resolving? bluetooth-configuration-name-resolving? (default #t))
  (debug-keys? bluetooth-configuration-debug-keys? (default #f))

  ;;; Possible values:
  ;;; 'dual, 'bredr, 'le
  (controller-mode bluetooth-configuration-controller-mode (default 'dual))

  ;;; Possible values:
  ;;; 'off, 'single, 'multiple
  (multi-profile bluetooth-configuration-multi-profile (default 'off))
  (fast-connectable? bluetooth-configuration-fast-connectable? (default #f))

  ;;; Possible values:
  ;;; for LE mode: 'off, 'network/on, 'device
  ;;; for Dual mode: 'off, 'network/on', 'device, 'limited-network, 'limited-device
  ;;; Source: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/main.conf#n68
  (privacy bluetooth-configuration-privacy (default 'off))

  ;;; Possible values:
  ;;; 'never, 'confirm, 'always
  (just-works-repairing
   bluetooth-configuration-just-works-repairing (default 'never))
  (temporary-timeout bluetooth-configuration-temporary-timeout (default 30))
  (refresh-discovery? bluetooth-configuration-refresh-discovery (default #t))

  ;;; Possible values: #t, #f, (uuid <uuid>)
  ;;; Possible UUIDs:
  ;;; d4992530-b9ec-469f-ab01-6c481c47da1c (BlueZ Experimental Debug)
  ;;; 671b10b5-42c0-4696-9227-eb28d1b049d6 (BlueZ Experimental Simultaneous Central and Peripheral)
  ;;; 15c0a148-c273-11ea-b3de-0242ac130004 (BlueZ Experimental LL privacy)
  ;;; 330859bc-7506-492d-9370-9a6f0614037f (BlueZ Experimental Bluetooth Quality Report)
  ;;; a6695ace-ee7f-4fb9-881a-5fac66c629af (BlueZ Experimental Offload Codecs)
  ;;; Source: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/main.conf#n110
  (experimental bluetooth-configuration-experimental (default #f))
  (remote-name-request-retry-delay
   bluetooth-configuration-remote-name-request-retry-delay (default 300))

  ;;; [BR]
  (page-scan-type bluetooth-configuration-page-scan-type (default #f))
  (page-scan-interval bluetooth-configuration-page-scan-interval (default #f))
  (page-scan-window bluetooth-configuration-page-scan-window (default #f))
  (inquiry-scan-type bluetooth-configuration-inquiry-scan-type (default #f))
  (inquiry-scan-interval bluetooth-configuration-inquiry-scan-interval (default #f))
  (inquiry-scan-window bluetooth-configuration-inquiry-scan-window (default #f))
  (link-supervision-timeout bluetooth-configuration-link-supervision-timeout (default #f))
  (page-timeout bluetooth-configuration-page-timeout (default #f))
  (min-sniff-interval bluetooth-configuration-min-sniff-interval (default #f))
  (max-sniff-interval bluetooth-configuration-max-sniff-interval (default #f))

  ;;; [LE]
  (min-advertisement-interval
   bluetooth-configuration-min-advertisement-interval (default #f))
  (max-advertisement-interval
   bluetooth-configuration-max-advertisement-interval (default #f))
  (multi-advertisement-rotation-interval
   bluetooth-configuration-multi-advertisement-rotation-interval (default #f))
  (scan-interval-auto-connect
   bluetooth-configuration-scan-interval-auto-connect (default #f))
  (scan-window-auto-connect
   bluetooth-configuration-scan-window-auto-connect (default #f))
  (scan-interval-suspend
   bluetooth-configuration-scan-interval-suspend (default #f))
  (scan-window-suspend
   bluetooth-configuration-scan-window-suspend (default #f))
  (scan-interval-discovery
   bluetooth-configuration-scan-interval-discovery (default #f))
  (scan-window-discovery
   bluetooth-configuration-scan-window-discovery (default #f))
  (scan-interval-adv-monitor
   bluetooth-configuration-scan-interval-adv-monitor (default #f))
  (scan-window-adv-monitor
   bluetooth-configuration-scan-window-adv-monitor (default #f))
  (scan-interval-connect
   bluetooth-configuration-scan-interval-connect (default #f))
  (scan-window-connect
   bluetooth-configuration-scan-window-connect (default #f))
  (min-connection-interval
   bluetooth-configuration-min-connection-interval (default #f))
  (max-connection-interval
   bluetooth-configuration-max-connection-interval (default #f))
  (connection-latency
   bluetooth-configuration-connection-latency (default #f))
  (connection-supervision-timeout
   bluetooth-configuration-connection-supervision-timeout (default #f))
  (autoconnect-timeout
   bluetooth-configuration-autoconnect-timeout (default #f))
  (adv-mon-allowlist-scan-duration
   bluetooth-configuration-adv-mon-allowlist-scan-duration (default 300))
  (adv-mon-no-filter-scan-duration
   bluetooth-configuration-adv-mon-no-filter-scan-duration (default 500))
  (enable-adv-mon-interleave-scan?
   bluetooth-configuration-enable-adv-mon-interleave-scan (default #t))

  ;;; [GATT]
  ;;; Possible values: 'yes, 'no, 'always
  (cache bluetooth-configuration-cache (default 'always))

  ;;; Possible values: 7 ... 16, 0 (don't care)
  (key-size bluetooth-configuration-key-size (default 0))

  ;;; Possible values: 23 ... 517
  (exchange-mtu bluetooth-configuration-exchange-mtu (default 517))

  ;;; Possible values: 1 ... 5
  (att-channels bluetooth-configuration-att-channels (default 3))

  ;;; [AVDTP]
  ;;; Possible values: 'basic, 'ertm
  (session-mode bluetooth-configuration-session-mode (default 'basic))

  ;;; Possible values: 'basic, 'streaming
  (stream-mode bluetooth-configuration-stream-mode (default 'basic))

  ;;; [Policy]
  (reconnect-uuids bluetooth-configuration-reconnect-uuids (default '()))
  (reconnect-attempts bluetooth-configuration-reconnect-attempts (default 7))
  (reconnect-intervals bluetooth-configuration-reconnect-intervals
                       (default (list 1 2 4 8 16 32 64)))
  (auto-enable? bluetooth-configuration-auto-enable? (default #f))
  (resume-delay bluetooth-configuration-resume-delay (default 2))

  ;;; [AdvMon]
  ;;; Possible values:
  ;;; "0x00", "0xFF",
  ;;; "N = 0x00" ... "N = 0xFF"
  ;;; Source: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/src/main.conf#n286
  (rssi-sampling-period bluetooth-configuration-rssi-sampling-period
                        (default #xFF)))

(define (bluetooth-configuration-file config)
  "Return a configuration file for the systemd bluetooth service, as a string."
  (string-append
   "[General]"
   "\nName = " (bluetooth-configuration-name config)
   "\nClass = " (string-append
                 "0x"
                 (format #f "~6,'0x" (bluetooth-configuration-class config)))
   "\nDiscoverableTimeout = " (number->string
                               (bluetooth-configuration-discoverable-timeout
                                config))
   "\nAlwaysPairable = " (bool (bluetooth-configuration-always-pairable?
                                config))
   "\nPairableTimeout = " (number->string
                           (bluetooth-configuration-pairable-timeout
                            config))
   (if (bluetooth-configuration-device-id config)
       (string-append "\nDeviceID = " (bluetooth-configuration-device-id config))
       "")
   "\nReverseServiceDiscovery = " (bool
                                   (bluetooth-configuration-reverse-service-discovery
                                    config))
   "\nNameResolving = " (bool (bluetooth-configuration-name-resolving? config))
   "\nDebugKeys = " (bool (bluetooth-configuration-debug-keys? config))
   "\nControllerMode = " (symbol->string
                          (bluetooth-configuration-controller-mode config))
   "\nMultiProfile = " (symbol->string (bluetooth-configuration-multi-profile
                                        config))
   "\nFastConnectable = " (bool (bluetooth-configuration-fast-connectable? config))
   "\nPrivacy = " (symbol->string (bluetooth-configuration-privacy config))
   "\nJustWorksRepairing = " (symbol->string
                              (bluetooth-configuration-just-works-repairing config))
   "\nTemporaryTimeout = " (number->string
                            (bluetooth-configuration-temporary-timeout config))
   "\nRefreshDiscovery = " (bool (bluetooth-configuration-refresh-discovery config))
   "\nExperimental = " (let ((experimental (bluetooth-configuration-experimental config)))
                         (cond ((or (eq? experimental #t)
                                    (eq? experimental #f)) (bool experimental))
                               ((list? experimental)
                                (string-join (map uuid->string experimental) ","))))
   "\nRemoteNameRequestRetryDelay = " (number->string
                                       (bluetooth-configuration-remote-name-request-retry-delay
                                        config))
   "\n[BR]"
   (if (bluetooth-configuration-page-scan-type config)
       (string-append
        "\nPageScanType = "
        (number->string (bluetooth-configuration-page-scan-type config)))
       "")
   (if (bluetooth-configuration-page-scan-interval config)
       (string-append
        "\nPageScanInterval = "
        (number->string (bluetooth-configuration-page-scan-interval config)))
       "")
   (if (bluetooth-configuration-page-scan-window config)
       (string-append
        "\nPageScanWindow = "
        (number->string (bluetooth-configuration-page-scan-window config)))
       "")
   (if (bluetooth-configuration-inquiry-scan-type config)
       (string-append
        "\nInquiryScanType = "
        (number->string (bluetooth-configuration-inquiry-scan-type config)))
       "")
   (if (bluetooth-configuration-inquiry-scan-interval config)
       (string-append
        "\nInquiryScanInterval = "
        (number->string (bluetooth-configuration-inquiry-scan-interval config)))
       "")
   (if (bluetooth-configuration-inquiry-scan-window config)
       (string-append
        "\nInquiryScanWindow = "
        (number->string (bluetooth-configuration-inquiry-scan-window config)))
       "")
   (if (bluetooth-configuration-link-supervision-timeout config)
       (string-append
        "\nLinkSupervisionTimeout = "
        (number->string (bluetooth-configuration-link-supervision-timeout config)))
       "")
   (if (bluetooth-configuration-page-timeout config)
       (string-append
        "\nPageTimeout = "
        (number->string (bluetooth-configuration-page-timeout config)))
       "")
   (if (bluetooth-configuration-min-sniff-interval config)
       (string-append
        "\nMinSniffInterval = "
        (number->string (bluetooth-configuration-min-sniff-interval config)))
       "")
   (if (bluetooth-configuration-max-sniff-interval config)
       (string-append
        "\nMaxSniffInterval = "
        (number->string (bluetooth-configuration-max-sniff-interval config)))
       "")

   "\n[LE]"
   (if (bluetooth-configuration-min-advertisement-interval config)
       (string-append
        "\nMinAdvertisementInterval = "
        (number->string (bluetooth-configuration-min-advertisement-interval config)))
       "")
   (if (bluetooth-configuration-max-advertisement-interval config)
       (string-append
        "\nMaxAdvertisementInterval = "
        (number->string (bluetooth-configuration-max-advertisement-interval config)))
       "")
   (if (bluetooth-configuration-multi-advertisement-rotation-interval config)
       (string-append
        "\nMultiAdvertisementRotationInterval = "
        (number->string
         (bluetooth-configuration-multi-advertisement-rotation-interval config)))
       "")
   (if (bluetooth-configuration-scan-interval-auto-connect config)
       (string-append
        "\nScanIntervalAutoConnect = "
        (number->string (bluetooth-configuration-scan-interval-auto-connect config)))
       "")
   (if (bluetooth-configuration-scan-window-auto-connect config)
       (string-append
        "\nScanWindowAutoConnect = "
        (number->string (bluetooth-configuration-scan-window-auto-connect config)))
       "")
   (if (bluetooth-configuration-scan-interval-suspend config)
       (string-append
        "\nScanIntervalSuspend = "
        (number->string (bluetooth-configuration-scan-interval-suspend config)))
       "")
   (if (bluetooth-configuration-scan-window-suspend config)
       (string-append
        "\nScanWindowSuspend = "
        (number->string (bluetooth-configuration-scan-window-suspend config)))
       "")
   (if (bluetooth-configuration-scan-interval-discovery config)
       (string-append
        "\nScanIntervalDiscovery = "
        (number->string (bluetooth-configuration-scan-interval-discovery config)))
       "")
   (if (bluetooth-configuration-scan-window-discovery config)
       (string-append
        "\nScanWindowDiscovery = "
        (number->string (bluetooth-configuration-scan-window-discovery config)))
       "")
   (if (bluetooth-configuration-scan-interval-adv-monitor config)
       (string-append
        "\nScanIntervalAdvMonitor = "
        (number->string (bluetooth-configuration-scan-interval-adv-monitor config)))
       "")
   (if (bluetooth-configuration-scan-window-adv-monitor config)
       (string-append
        "\nScanWindowAdvMonitor = "
        (number->string (bluetooth-configuration-scan-window-adv-monitor config)))
       "")
   (if (bluetooth-configuration-scan-interval-connect config)
       (string-append
        "\nScanIntervalConnect = "
        (number->string (bluetooth-configuration-scan-interval-connect config)))
       "")
   (if (bluetooth-configuration-scan-window-connect config)
       (string-append
        "\nScanWindowConnect = "
        (number->string (bluetooth-configuration-scan-window-connect config)))
       "")
   (if (bluetooth-configuration-min-connection-interval config)
       (string-append
        "\nMinConnectionInterval = "
        (number->string (bluetooth-configuration-min-connection-interval config)))
       "")
   (if (bluetooth-configuration-max-connection-interval config)
       (string-append
        "\nMaxConnectionInterval = "
        (number->string (bluetooth-configuration-max-connection-interval config)))
       "")
   (if (bluetooth-configuration-connection-latency config)
       (string-append
        "\nConnectionLatency = "
        (number->string (bluetooth-configuration-connection-latency config)))
       "")
   (if (bluetooth-configuration-connection-supervision-timeout config)
       (string-append
        "\nConnectionSupervisionTimeout = "
        (number->string (bluetooth-configuration-connection-supervision-timeout config)))
       "")
   (if (bluetooth-configuration-autoconnect-timeout config)
       (string-append
        "\nAutoconnecttimeout = "
        (number->string (bluetooth-configuration-autoconnect-timeout config)))
       "")
   "\nAdvMonAllowlistScanDuration = " (number->string
                                       (bluetooth-configuration-adv-mon-allowlist-scan-duration
                                        config))
   "\nAdvMonNoFilterScanDuration = " (number->string
                                      (bluetooth-configuration-adv-mon-no-filter-scan-duration
                                       config))
   "\nEnableAdvMonInterleaveScan = " (number->string
                                      (if (eq? #t
                                               (bluetooth-configuration-enable-adv-mon-interleave-scan
                                                config))
                                          1 0))

   "\n[GATT]"
   "\nCache = " (symbol->string (bluetooth-configuration-cache config))
   "\nKeySize = " (number->string (bluetooth-configuration-key-size config))
   "\nExchangeMTU = " (number->string (bluetooth-configuration-exchange-mtu config))
   "\nChannels = " (number->string (bluetooth-configuration-att-channels config))

   "\n[AVDTP]"
   "\nSessionMode = " (symbol->string (bluetooth-configuration-session-mode config))
   "\nStreamMode = " (symbol->string (bluetooth-configuration-stream-mode config))

   "\n[Policy]"
   (let ((uuids (bluetooth-configuration-reconnect-uuids config)))
     (if (not (eq? '() uuids))
         (string-append
          "\nReconnectUUIDs = "
          (string-join (map uuid->string uuids) ","))
         ""))
   "\nReconnectAttempts = " (number->string
                             (bluetooth-configuration-reconnect-attempts config))
   "\nReconnectIntervals = " (string-join
                              (map number->string
                                   (bluetooth-configuration-reconnect-intervals
                                    config))
                              ",")
   "\nAutoEnable = " (bool (bluetooth-configuration-auto-enable?
                            config))
   "\nResumeDelay = " (number->string (bluetooth-configuration-resume-delay config))

   "\n[AdvMon]"
   "\nRSSISamplingPeriod = " (string-append
                              "0x"
                              (format #f "~2,'0x"
                                      (bluetooth-configuration-rssi-sampling-period config)))))

(define (bluetooth-directory config)
  (computed-file "etc-bluetooth"
                 #~(begin
                     (mkdir #$output)
                     (chdir #$output)
                     (call-with-output-file "main.conf"
                       (lambda (port)
                         (display #$(bluetooth-configuration-file config)
                                  port))))))

(define (bluetooth-shepherd-service config)
  "Return a shepherd service for @command{bluetoothd}."
  (shepherd-service
   (provision '(bluetooth))
   (requirement '(dbus-system udev))
   (documentation "Run the bluetoothd daemon.")
   (start #~(make-forkexec-constructor
             (list #$(file-append (bluetooth-configuration-bluez config)
                                  "/libexec/bluetooth/bluetoothd"))))
   (stop #~(make-kill-destructor))))

(define bluetooth-service-type
  (service-type
   (name 'bluetooth)
   (extensions
    (list (service-extension dbus-root-service-type
                             (compose list bluetooth-configuration-bluez))
          (service-extension udev-service-type
                             (compose list bluetooth-configuration-bluez))
          (service-extension etc-service-type
                             (lambda (config)
                               `(("bluetooth"
                                  ,(bluetooth-directory config)))))
          (service-extension shepherd-root-service-type
                             (compose list bluetooth-shepherd-service))))
   (default-value (bluetooth-configuration))
   (description "Run the @command{bluetoothd} daemon, which manages all the
Bluetooth devices and provides a number of D-Bus interfaces.")))

(define-deprecated (bluetooth-service #:key (bluez bluez) (auto-enable? #f))
  bluetooth-service-type
  "Return a service that runs the @command{bluetoothd} daemon, which manages
all the Bluetooth devices and provides a number of D-Bus interfaces.  When
AUTO-ENABLE? is true, the bluetooth controller is powered automatically at
boot, which can be useful when using a bluetooth keyboard or mouse.
"
  (service bluetooth-service-type
           (bluetooth-configuration
            (bluez bluez)
            (auto-enable? auto-enable?))))


;;;
;;; Colord D-Bus service.
;;;

(define %colord-activation
  #~(begin
      (use-modules (guix build utils))
      (mkdir-p "/var/lib/colord")
      (let ((user (getpwnam "colord")))
        (chown "/var/lib/colord"
               (passwd:uid user) (passwd:gid user)))))

(define %colord-accounts
  (list (user-group (name "colord") (system? #t))
        (user-account
         (name "colord")
         (group "colord")
         (system? #t)
         (comment "colord daemon user")
         (home-directory "/var/empty")
         (shell (file-append shadow "/sbin/nologin")))))

(define colord-service-type
  (service-type (name 'colord)
                (extensions
                 (list (service-extension account-service-type
                                          (const %colord-accounts))
                       (service-extension activation-service-type
                                          (const %colord-activation))

                       ;; Colord is a D-Bus service that dbus-daemon can
                       ;; activate.
                       (service-extension dbus-root-service-type list)

                       ;; Colord provides "color device" rules for udev.
                       (service-extension udev-service-type list)

                       ;; It provides polkit "actions".
                       (service-extension polkit-service-type list)))
                (default-value colord)
                (description
                 "Run @command{colord}, a system service with a D-Bus
interface to manage the color profiles of input and output devices such as
screens and scanners.")))


;;;
;;; UDisks.
;;;

(define-record-type* <udisks-configuration>
  udisks-configuration make-udisks-configuration
  udisks-configuration?
  (udisks   udisks-configuration-udisks
            (default udisks)))

(define %udisks-activation
  (with-imported-modules '((guix build utils))
    #~(begin
        (use-modules (guix build utils))

        (let ((run-dir "/var/run/udisks2"))
          (mkdir-p run-dir)
          (chmod run-dir #o700)))))

(define udisks-service-type
  (let ((udisks-package (lambda (config)
                          (list (udisks-configuration-udisks config)))))
    (service-type (name 'udisks)
                  (extensions
                   (list (service-extension polkit-service-type
                                            udisks-package)
                         (service-extension dbus-root-service-type
                                            udisks-package)
                         (service-extension udev-service-type
                                            udisks-package)
                         (service-extension activation-service-type
                                            (const %udisks-activation))

                         ;; Profile 'udisksctl' & co. in the system profile.
                         (service-extension profile-service-type
                                            udisks-package)))
                  (description "Run UDisks, a @dfn{disk management} daemon
that provides user interfaces with notifications and ways to mount/unmount
disks.  Programs that talk to UDisks include the @command{udisksctl} command,
part of UDisks, and GNOME Disks.")
                  (default-value (udisks-configuration)))))

(define-deprecated (udisks-service #:key (udisks udisks))
  udisks-service-type
  "Return a service for @uref{http://udisks.freedesktop.org/docs/latest/,
UDisks}, a @dfn{disk management} daemon that provides user interfaces with
notifications and ways to mount/unmount disks.  Programs that talk to UDisks
include the @command{udisksctl} command, part of UDisks, and GNOME Disks."
  (service udisks-service-type
           (udisks-configuration (udisks udisks))))



;;;
;;; GVfs virtual file system.
;;;

(define-record-type* <gvfs-configuration>
  gvfs-configuration make-gvfs-configuration
  gvfs-configuration?
  (gvfs gvfs-package (default gvfs)))

(define gvfs-service-type
  (service-type (name 'gvfs)
                (extensions
                 (list
                  (service-extension profile-service-type
                                     (compose list gvfs-package))
                  ;; Required for gvfs-udisks2-volume-monitor.
                  (service-extension udisks-service-type (const #t))))
                (description
                 "Make GVfs virtual file systems (Trash, SFTP, SMB, HTTP,
and many other) available for GIO applications.")
                (default-value (gvfs-configuration))))


;;;
;;; Elogind login and seat management service.
;;;

(define-record-type* <elogind-configuration> elogind-configuration
  make-elogind-configuration
  elogind-configuration?
  (elogind                          elogind-package
                                    (default elogind))
  (kill-user-processes?             elogind-kill-user-processes?
                                    (default #f))
  (kill-only-users                  elogind-kill-only-users
                                    (default '()))
  (kill-exclude-users               elogind-kill-exclude-users
                                    (default '("root")))
  (inhibit-delay-max-seconds        elogind-inhibit-delay-max-seconds
                                    (default 5))
  (handle-power-key                 elogind-handle-power-key
                                    (default 'poweroff))
  (handle-suspend-key               elogind-handle-suspend-key
                                    (default 'suspend))
  (handle-hibernate-key             elogind-handle-hibernate-key
                                    (default 'hibernate))
  (handle-lid-switch                elogind-handle-lid-switch
                                    (default 'suspend))
  (handle-lid-switch-docked         elogind-handle-lid-switch-docked
                                    (default 'ignore))
  (handle-lid-switch-external-power elogind-handle-lid-switch-external-power
                                    (default *unspecified*))
  (power-key-ignore-inhibited?      elogind-power-key-ignore-inhibited?
                                    (default #f))
  (suspend-key-ignore-inhibited?    elogind-suspend-key-ignore-inhibited?
                                    (default #f))
  (hibernate-key-ignore-inhibited?  elogind-hibernate-key-ignore-inhibited?
                                    (default #f))
  (lid-switch-ignore-inhibited?     elogind-lid-switch-ignore-inhibited?
                                    (default #t))
  (holdoff-timeout-seconds          elogind-holdoff-timeout-seconds
                                    (default 30))
  (idle-action                      elogind-idle-action
                                    (default 'ignore))
  (idle-action-seconds              elogind-idle-action-seconds
                                    (default (* 30 60)))
  (runtime-directory-size-percent   elogind-runtime-directory-size-percent
                                    (default 10))
  (runtime-directory-size           elogind-runtime-directory-size
                                    (default #f))
  (remove-ipc?                      elogind-remove-ipc?
                                    (default #t))

  (suspend-state                    elogind-suspend-state
                                    (default '("mem" "standby" "freeze")))
  (suspend-mode                     elogind-suspend-mode
                                    (default '()))
  (hibernate-state                  elogind-hibernate-state
                                    (default '("disk")))
  (hibernate-mode                   elogind-hibernate-mode
                                    (default '("platform" "shutdown")))
  (hybrid-sleep-state               elogind-hybrid-sleep-state
                                    (default '("disk")))
  (hybrid-sleep-mode                elogind-hybrid-sleep-mode
                                    (default
                                      '("suspend" "platform" "shutdown")))
  (hibernate-delay-seconds          elogind-hibernate-delay-seconds
                                    (default *unspecified*))
  (suspend-estimation-seconds       elogind-suspend-estimation-seconds
                                    (default *unspecified*))
  (system-sleep-hook-files          elogind-system-sleep-hook-files
                                    (default '()))
  (system-shutdown-hook-files       elogind-system-shutdown-hook-files
                                    (default '()))
  (allow-power-off-interrupts?      elogind-allow-power-off-interrupts?
                                    (default #f))
  (allow-suspend-interrupts?        elogind-allow-suspend-interrupts?
                                    (default #f))
  (broadcast-power-off-interrupts?  elogind-broadcast-power-off-interrupts?
                                    (default #t))
  (broadcast-suspend-interrupts?    elogind-broadcast-suspend-interrupts?
                                    (default #t)))

(define (elogind-configuration-file config)
  (define (yesno x)
    (match x
      (#t "yes")
      (#f "no")
      (_ (error "expected #t or #f, instead got:" x))))
  (define char-set:user-name
    (string->char-set "abcdefghijklmnopqrstuvwxyz0123456789_-"))
  (define (valid-list? l pred)
    (and-map (lambda (x) (string-every pred x)) l))
  (define (user-name-list users)
    (unless (valid-list? users char-set:user-name)
      (error "invalid user list" users))
    (string-join users " "))
  (define (enum val allowed)
    (unless (memq val allowed)
      (error "invalid value" val allowed))
    (symbol->string val))
  (define (non-negative-integer x)
    (unless (exact-integer? x) (error "not an integer" x))
    (when (negative? x) (error "negative number not allowed" x))
    (number->string x))
  (define (maybe-non-negative-integer x)
    (or (and (unspecified? x) x)
        (non-negative-integer x)))
  (define handle-actions
    '(ignore poweroff reboot halt kexec suspend hibernate hybrid-sleep suspend-then-hibernate lock))
  (define (handle-action x)
    (if (unspecified? x)
        x                               ;let the unspecified value go through
        (enum x handle-actions)))
  (define (sleep-list tokens)
    (unless (valid-list? tokens char-set:user-name)
      (error "invalid sleep list" tokens))
    (string-join tokens " "))
  (define-syntax ini-file-clause
    (syntax-rules ()
      ;; Produce an empty line when encountering an unspecified value.  This
      ;; is better than an empty string value, which can, in some cases, cause
      ;; warnings such as "Failed to parse handle action setting".
      ((_ config (prop (parser getter)))
       (let ((value (parser (getter config))))
         (if (unspecified? value)
             ""
             (string-append prop "=" value "\n"))))
      ((_ config str)
       (if (unspecified? str)
           ""
           (string-append str "\n")))))
  (define-syntax-rule (ini-file config file clause ...)
    (plain-file file (string-append (ini-file-clause config clause) ...)))
  (ini-file
   config "logind.conf"
   "[Login]"
   ("KillUserProcesses" (yesno elogind-kill-user-processes?))
   ("KillOnlyUsers" (user-name-list elogind-kill-only-users))
   ("KillExcludeUsers" (user-name-list elogind-kill-exclude-users))
   ("InhibitDelayMaxSec" (non-negative-integer elogind-inhibit-delay-max-seconds))
   ("HandlePowerKey" (handle-action elogind-handle-power-key))
   ("HandleSuspendKey" (handle-action elogind-handle-suspend-key))
   ("HandleHibernateKey" (handle-action elogind-handle-hibernate-key))
   ("HandleLidSwitch" (handle-action elogind-handle-lid-switch))
   ("HandleLidSwitchDocked" (handle-action elogind-handle-lid-switch-docked))
   ("HandleLidSwitchExternalPower" (handle-action elogind-handle-lid-switch-external-power))
   ("PowerKeyIgnoreInhibited" (yesno elogind-power-key-ignore-inhibited?))
   ("SuspendKeyIgnoreInhibited" (yesno elogind-suspend-key-ignore-inhibited?))
   ("HibernateKeyIgnoreInhibited" (yesno elogind-hibernate-key-ignore-inhibited?))
   ("LidSwitchIgnoreInhibited" (yesno elogind-lid-switch-ignore-inhibited?))
   ("HoldoffTimeoutSec" (non-negative-integer elogind-holdoff-timeout-seconds))
   ("IdleAction" (handle-action elogind-idle-action))
   ("IdleActionSec" (non-negative-integer elogind-idle-action-seconds))
   ("RuntimeDirectorySize"
    (identity
     (lambda (config)
       (match (elogind-runtime-directory-size-percent config)
         (#f (non-negative-integer (elogind-runtime-directory-size config)))
         (percent (string-append (non-negative-integer percent) "%"))))))
   ("RemoveIPC" (yesno elogind-remove-ipc?))
   "[Sleep]"
   ("SuspendState" (sleep-list elogind-suspend-state))
   ("SuspendMode" (sleep-list elogind-suspend-mode))
   ("HibernateState" (sleep-list elogind-hibernate-state))
   ("HibernateMode" (sleep-list elogind-hibernate-mode))
   ("HybridSleepState" (sleep-list elogind-hybrid-sleep-state))
   ("HybridSleepMode" (sleep-list elogind-hybrid-sleep-mode))
   ("HibernateDelaySec" (maybe-non-negative-integer elogind-hibernate-delay-seconds))
   ("SuspendEstimationSec" (maybe-non-negative-integer elogind-suspend-estimation-seconds))
   ("AllowPowerOffInterrupts" (yesno elogind-allow-power-off-interrupts?))
   ("AllowSuspendInterrupts" (yesno elogind-allow-suspend-interrupts?))
   ("BroadcastPowerOffInterrupts" (yesno elogind-broadcast-power-off-interrupts?))
   ("BroadcastSuspendInterrupts" (yesno elogind-broadcast-suspend-interrupts?))))

(define (elogind-etc-directory config)
  "Return the /etc/elogind directory for CONFIG."
  (with-imported-modules (source-module-closure '((guix build utils)))
    (computed-file
     "etc-elogind"

     #~(begin
         (use-modules (guix build utils))

         (define sleep-directory (string-append #$output "/system-sleep/"))
         (define shutdown-directory (string-append #$output "/system-shutdown/"))

         (define (copy-script file directory)
           "Copy FILE into DIRECTORY, giving rx (500) permissions."
           (let ((dest (string-append directory "/" (basename file))))
             (mkdir-p directory)
             (copy-file file dest)
             (chmod dest #o500)))

         (mkdir-p #$output)            ;in case neither directory gets created
         (for-each (lambda (f)
                     (copy-script f sleep-directory))
                   '#$(elogind-system-sleep-hook-files config))
         (for-each (lambda (f)
                     (copy-script f shutdown-directory))
                   '#$(elogind-system-shutdown-hook-files config))))))

(define (elogind-dbus-service config)
  "Return a @file{org.freedesktop.login1.service} file that tells D-Bus how to
\"start\" elogind.  In practice though, our elogind is started when booting by
shepherd.  Thus, the @code{Exec} line of this @file{.service} file does not
explain how to start elogind; instead, it spawns a wrapper that waits for the
@code{elogind} shepherd service.  This avoids a race condition where both
@command{shepherd} and @command{dbus-daemon} would attempt to start elogind."
  ;; For more info on the elogind startup race, see
  ;; <https://issues.guix.gnu.org/55444>.

  (define elogind
    (elogind-package config))

  (define wrapper
    (program-file "elogind-dbus-shepherd-sync"
                  (with-imported-modules '((gnu services herd))
                    #~(begin
                        (use-modules (gnu services herd)
                                     (srfi srfi-34))

                        (guard (c ((service-not-found-error? c)
                                   (format (current-error-port)
                                           "no elogind shepherd service~%")
                                   (exit 1))
                                  ((shepherd-error? c)
                                   (format (current-error-port)
                                           "elogind shepherd service not \
started~%")
                                   (exit 2)))
                          (wait-for-service 'elogind))))))

  (define build
    (with-imported-modules '((guix build utils))
      #~(begin
          (use-modules (guix build utils)
                       (ice-9 match))

          (define service-directory
            "/share/dbus-1/system-services")

          (mkdir-p (dirname (string-append #$output service-directory)))
          (copy-recursively (string-append #$elogind service-directory)
                            (string-append #$output service-directory))
          (symlink (string-append #$elogind "/etc") ;for etc/dbus-1
                   (string-append #$output "/etc"))
          ;; Also expose the D-Bus policy configurations (.conf) files, now
          ;; installed under '/share' instead of the legacy '/etc' prefix.
          (symlink (string-append #$elogind "/share/dbus-1/system.d")
                   (string-append #$output "/share/dbus-1/system.d"))

          ;; Replace the "Exec=" line of the 'org.freedesktop.login1.service'
          ;; file with one that refers to WRAPPER instead of elogind.
          (match (find-files #$output "\\.service$")
            ((file)
             (substitute* file
               (("Exec[[:blank:]]*=.*" _)
                (string-append "Exec=" #$wrapper "\n"))))))))

  (list (computed-file "elogind-dbus-service-wrapper" build)))

(define (pam-extension-procedure config)
  "Return an extension for PAM-ROOT-SERVICE-TYPE that ensures that all the PAM
services use 'pam_elogind.so', a module that allows elogind to keep track of
logged-in users (run 'loginctl' to see elogind's world view of users and
seats.)"
  (define pam-elogind
    (pam-entry
     (control "required")
     (module (file-append (elogind-package config)
                          "/lib/security/pam_elogind.so"))))

  (list (pam-extension
         (transformer
          (lambda (pam)
            (pam-service
             (inherit pam)
             (session (cons pam-elogind (pam-service-session pam))))))
         (shepherd-requirements '(elogind)))))

(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 '(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))))
         (stop #~(make-kill-destructor))
         (actions (list (shepherd-configuration-action config-file))))))

(define elogind-service-type
  (service-type (name 'elogind)
                (extensions
                 (list (service-extension dbus-root-service-type
                                          elogind-dbus-service)
                       (service-extension udev-service-type
                                          (compose list elogind-package))
                       (service-extension polkit-service-type
                                          (compose list elogind-package))

                       ;; Start elogind from the Shepherd rather than waiting
                       ;; for bus activation.  This ensures that it can handle
                       ;; events like lid close, etc.
                       (service-extension shepherd-root-service-type
                                          elogind-shepherd-service)

                       ;; Provide the 'loginctl' command.
                       (service-extension profile-service-type
                                          (compose list elogind-package))

                       ;; Extend PAM with pam_elogind.so.
                       (service-extension pam-root-service-type
                                          pam-extension-procedure)

                       ;; Install sleep/shutdown hook files.
                       (service-extension etc-service-type
                                          (lambda (config)
                                            `(("elogind"
                                               ,(elogind-etc-directory config)))))

                       ;; We need /run/user, /run/systemd, etc.
                       (service-extension file-system-service-type
                                          (const %elogind-file-systems))))
                (default-value (elogind-configuration))
                (description "Run the @command{elogind} login and seat
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
after users when they log out.")))

(define-deprecated (elogind-service #:key (config (elogind-configuration)))
  elogind-service-type
  "Return a service that runs 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 after users
when they log out."
  (service elogind-service-type config))


;;;
;;; Fontconfig and other desktop file-systems.
;;;

(define %fontconfig-file-system
  (file-system
    (device "none")
    (mount-point "/var/cache/fontconfig")
    (type "tmpfs")
    (flags '(read-only))
    (check? #f)))

(define %gdm-file-system
  (file-system
    (device "none")
    (mount-point "/var/lib/gdm")
    (type "tmpfs")
    (check? #f)))

;; The global fontconfig cache directory can sometimes contain stale entries,
;; possibly referencing fonts that have been GC'd, so mount it read-only.
;; As mentioned https://debbugs.gnu.org/cgi/bugreport.cgi?bug=36924#8 and
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=38046#10 and elsewhere.
(define fontconfig-file-system-service
  (simple-service 'fontconfig-file-system
                  file-system-service-type
                  (list %fontconfig-file-system)))

;; Avoid stale caches and stale user IDs being reused between system
;; reconfigurations, which would crash GDM and render the system unusable.
;; GDM doesn't require persisting anything valuable there anyway.
(define gdm-file-system-service
  (simple-service 'gdm-file-system
                  file-system-service-type
                  (list %gdm-file-system)))


;;;
;;; AccountsService service.
;;;

(define %accountsservice-activation
  #~(begin
      (use-modules (guix build utils))
      (mkdir-p "/var/lib/AccountsService")))

(define accountsservice-service-type
  (service-type (name 'accountsservice)
                (extensions
                 (list (service-extension activation-service-type
                                          (const %accountsservice-activation))
                       (service-extension dbus-root-service-type list)
                       (service-extension polkit-service-type list)))
                (default-value accountsservice)
                (description "Run AccountsService, a system service available
over D-Bus that can list available accounts, change their passwords, and so
on.  AccountsService integrates with PolicyKit to enable unprivileged users to
acquire the capability to modify their system configuration.")))

(define-deprecated
  (accountsservice-service #:key (accountsservice accountsservice))
  accountsservice-service-type
  "Return a service that runs AccountsService, a system service that
can list available accounts, change their passwords, and so on.
AccountsService integrates with PolicyKit to enable unprivileged users to
acquire the capability to modify their system configuration.
@uref{https://www.freedesktop.org/wiki/Software/AccountsService/, the
accountsservice web site} for more information."
  (service accountsservice-service-type accountsservice))


;;;
;;; cups-pk-helper service.
;;;

(define cups-pk-helper-service-type
  (service-type
   (name 'cups-pk-helper)
   (description
    "PolicyKit helper to configure CUPS with fine-grained privileges.")
   (extensions
    (list (service-extension dbus-root-service-type list)
          (service-extension polkit-service-type list)))
   (default-value cups-pk-helper)))


;;;
;;; Scanner access via SANE.
;;;

(define %sane-accounts
  ;; The '60-libsane.rules' udev rules refers to the "scanner" group.
  (list (user-group (name "scanner") (system? #t))))

(define sane-service-type
  (service-type
   (name 'sane)
   (description
    "This service provides access to scanners @i{via}
@uref{http://www.sane-project.org, SANE} by installing the necessary udev
rules.")
   (default-value sane-backends-minimal)
   (extensions
    (list (service-extension udev-service-type list)
          (service-extension account-service-type
                             (const %sane-accounts))))))



;;;
;;; GNOME desktop service.
;;;

(define-maybe/no-serialization package)

(define (extract-propagated-inputs package)
  ;; Drop input labels.  Attempt to support outputs.
  (map
   (match-lambda
     ((_ (? package? pkg)) pkg)
     ((_ (? package? pkg) output) (list pkg output)))
   (package-propagated-inputs package)))

(define-configuration/no-serialization gnome-desktop-configuration
  (core-services
   (list-of-packages (extract-propagated-inputs gnome-meta-core-services))
   "A list of packages that the GNOME Shell and applications may rely on.")
  (shell
   (list-of-packages (extract-propagated-inputs gnome-meta-core-shell))
   "A list of packages that constitute the GNOME Shell, without applications.")
  (utilities
   (list-of-packages (extract-propagated-inputs gnome-meta-core-utilities))
   "A list of packages that serve as applications to use on top of the \
GNOME Shell.")
  (gnome (maybe-package) "Deprecated.  Do not use.")
  (extra-packages
   (list-of-packages (extract-propagated-inputs gnome-essential-extras))
   "A list of GNOME-adjacent packages to also include.  This field is intended
for users to add their own packages to their GNOME experience.  Note, that it
already includes some packages that are considered essential by some (most?)
GNOME users.")
  (udev-ignorelist
   (list-of-strings '())
   "A list of regular expressions denoting udev rules or hardware file names
provided by any package that should not be installed.  By default, every udev
rule and hardware file specified by any package referenced in the other fields
are installed.")
  (polkit-ignorelist
   (list-of-strings '())
   "A list of regular expressions denoting polkit rules provided by any package
that should not be installed.  By default, every polkit rule added by any package
referenced in the other fields are installed."))

(define (gnome-package gnome name)
  "Return the package NAME among the GNOME package inputs.  NAME can be a
single name or a tree-like, e.g. @code{'(\"gnome-boxes\" \"spice-gtk\")} to
denote the spice-gtk input of the gnome-boxes input of the GNOME meta-package."
  ((package-direct-input-selector name) gnome))

(define (gnome-packages gnome names)
  "Return the package NAMES among the GNOME package inputs."
  (map (cut gnome-package gnome <>) names))

(define (gnome-udev-configuration-files config)
  "Return the GNOME udev rules and hardware files as computed from its
dependencies by filtering out the ignorelist."
  (list
   (computed-file
    "gnome-udev-configurations"
    (with-imported-modules
        (source-module-closure '((guix build utils)
                                 (guix build union)))
      #~(begin
          (use-modules (guix build utils)
                       (guix build union))
          ;; If rules.d or hwdb.d is not a proper directory but a symlink,
          ;; then it will not be possible to delete individual files in this
          ;; directory.
          (union-build #$output
                       (search-path-as-list
                        (list "lib/udev" "libexec/udev")
                        (list #$@(gnome-profile config #:transitive? #t)))
                       #:create-all-directories? #t)
          (for-each
           (lambda (pattern)
             (for-each
              delete-file-recursively
              (find-files #$output pattern)))
           (list #$@(gnome-desktop-configuration-udev-ignorelist config))))))))

(define (gnome-polkit-settings config)
  "Return the list of GNOME dependencies that provide polkit actions and
rules."
  (list
   (computed-file
    "gnome-polkit-settings"
    (with-imported-modules
        (source-module-closure '((guix build utils)
                                 (guix build union)))
      #~(let ((output (string-append #$output "/share/polkit-1")))
          (use-modules (guix build utils)
                       (guix build union))
          (mkdir-p (dirname output))
          (union-build output
                       (search-path-as-list
                        (list "share/polkit-1")
                        (list #$@(gnome-profile config #:transitive? #t)))
                       #:create-all-directories? #t)
          (for-each
           (lambda (pattern)
             (for-each
              delete-file-recursively
              (find-files output pattern)))
           (list #$@(gnome-desktop-configuration-polkit-ignorelist config))))))))

(define* (gnome-profile config #:key transitive?)
  "Return the list of the packages specified in CONFIG.  When TRANSITIVE? is
#t, also include their transitive propagated inputs.  If there are transitive
inputs using non-default outputs, they are returned as gexp-input objects."
  (define gnome-packages
    (append
     (gnome-desktop-configuration-core-services config)
     (gnome-desktop-configuration-shell config)
     (gnome-desktop-configuration-utilities config)
     (let ((gnome-meta (gnome-desktop-configuration-gnome config)))
       (if (maybe-value-set? gnome-meta)
           (begin
             (warning
              (gnome-desktop-configuration-source-location config)
              (G_ "Using a meta-package for gnome-desktop is discouraged.~%"))
             (list gnome-meta))
           (list)))
     (gnome-desktop-configuration-extra-packages config)))
  (if transitive?
      (append gnome-packages
              (append-map (compose (cut map (match-lambda ;discard labels
                                              ((_ pkg) pkg)
                                              ((_ pkg out)
                                               (gexp-input pkg out)))
                                        <>)
                                   package-transitive-propagated-inputs)
                          gnome-packages))
      gnome-packages))

(define (gnome-setuid-programs config)
  "Return the list of setuid programs found within the packages specified in
CONFIG, a <gnome-desktop-configuration> object."
  ;; spice-gtk provides polkit actions for USB redirection in GNOME Boxes; set
  ;; its usb-acl-helper script setuid automatically when the gnome-boxes or
  ;; spice-gtk packages are added to one of the gnome-desktop-configuration
  ;; fields.
  (let* ((gnome-packages (gnome-profile config #:transitive? #t))
         (spice-gtk (find (compose (cut string=? "spice-gtk" <>)
                                   package-name
                                   (match-lambda ;disregard potential output
                                     ((? package? p) p)
                                     ((? gexp-input? p)
                                      (gexp-input-thing p))))
                          gnome-packages))
         (files `(,@(if spice-gtk
                        (list (file-append
                               spice-gtk
                               "/libexec/spice-client-glib-usb-acl-helper"))
                        '()))))
    (map file-like->setuid-program files)))

(define gnome-desktop-service-type
  (service-type
   (name 'gnome-desktop)
   (extensions
    (list (service-extension udev-service-type
                             gnome-udev-configuration-files)
          (service-extension polkit-service-type
                             gnome-polkit-settings)
          (service-extension privileged-program-service-type
                             gnome-setuid-programs)
          (service-extension profile-service-type
                             gnome-profile)))
   (default-value (gnome-desktop-configuration))
   (description "Run the GNOME desktop environment.")))


;;;
;;; MATE Desktop service.
;;; TODO: Add mate-screensaver.

(define-record-type* <mate-desktop-configuration> mate-desktop-configuration
  make-mate-desktop-configuration
  mate-desktop-configuration?
  (mate-package mate-package (default mate)))

(define (mate-polkit-extension config)
  "Return the list of packages for CONFIG's MATE package that extend polkit."
  (let ((mate (mate-package config)))
    (map (lambda (input)
           ((package-direct-input-selector input) mate))
         '("mate-system-monitor"                  ;kill, renice processes
           "mate-settings-daemon"                 ;date/time settings
           "mate-power-manager"                   ;modify brightness
           "mate-control-center"                  ;RandR, display properties FIXME
           "mate-applets"))))                     ;CPU frequency scaling

(define mate-desktop-service-type
  (service-type
   (name 'mate-desktop)
   (extensions
    (list (service-extension polkit-service-type
                             mate-polkit-extension)
          (service-extension profile-service-type
                             (compose list
                                      mate-package))))
   (default-value (mate-desktop-configuration))
   (description "Run the MATE desktop environment.")))


;;;
;;; XFCE desktop service.
;;;

(define-record-type* <xfce-desktop-configuration> xfce-desktop-configuration
  make-xfce-desktop-configuration
  xfce-desktop-configuration?
  (xfce xfce-package (default xfce)))

(define (xfce-polkit-settings config)
  "Return the list of XFCE dependencies that provide polkit actions and
rules."
  (let ((xfce (xfce-package config)))
    (map (lambda (name)
           ((package-direct-input-selector name) xfce))
         '("thunar"
           "xfce4-power-manager"))))

(define (xfce-pam-services config)
  (list (unix-pam-service "xfce4-screensaver")))

(define xfce-desktop-service-type
  (service-type
   (name 'xfce-desktop)
   (extensions
    (list (service-extension polkit-service-type
                             xfce-polkit-settings)
          (service-extension pam-root-service-type
                             xfce-pam-services)
          (service-extension profile-service-type
                             (compose list xfce-package))))
   (default-value (xfce-desktop-configuration))
   (description "Run the Xfce desktop environment.")))


;;;
;;; Lxqt desktop service.
;;;

(define-record-type* <lxqt-desktop-configuration> lxqt-desktop-configuration
  make-lxqt-desktop-configuration
  lxqt-desktop-configuration?
  (lxqt lxqt-package
        (default lxqt)))

(define (lxqt-polkit-settings config)
  "Return the list of LXQt dependencies that provide polkit actions and
rules."
  (let ((lxqt (lxqt-package config)))
    (map (lambda (name)
           ((package-direct-input-selector name) lxqt))
         '("lxqt-admin"))))

(define lxqt-desktop-service-type
  (service-type
   (name 'lxqt-desktop)
   (extensions
    (list (service-extension polkit-service-type
                             lxqt-polkit-settings)
          (service-extension profile-service-type
                             (compose list lxqt-package))))
   (default-value (lxqt-desktop-configuration))
   (description "Run LXQt desktop environment.")))


;;;
;;; Sugar desktop service.
;;;

(define-record-type* <sugar-desktop-configuration> sugar-desktop-configuration
  make-sugar-desktop-configuration
  sugar-desktop-configuration?
  (sugar sugar-package (default sugar))
  (gobject-introspection
   sugar-gobject-introspection (default gobject-introspection))
  (activities
   sugar-activities (default (list sugar-help-activity))))

(define (sugar-polkit-settings config)
  "Return the list of packages that provide polkit actions and rules."
  (list (sugar-package config)))

(define sugar-desktop-service-type
  (service-type
   (name 'sugar-desktop)
   (extensions
    (list (service-extension polkit-service-type
                             sugar-polkit-settings)
          (service-extension profile-service-type
                             (lambda (config)
                               (cons* (sugar-package config)
                                      (sugar-gobject-introspection config)
                                      (sugar-activities config))))))
   (default-value (sugar-desktop-configuration))
   (description "Run the Sugar desktop environment.")))


;;;
;;; X11 socket directory service
;;;

(define x11-socket-directory-service-type
  (let ((x11-socket-directory-shepherd-service
         (shepherd-service
          (documentation "Create @file{/tmp/.X11-unix} for XWayland.")
          (requirement '(file-systems))
          (provision '(x11-socket-directory))
          (one-shot? #t)
          (start #~(lambda _
                     (let ((directory "/tmp/.X11-unix"))
                       (mkdir-p directory)
                       (chmod directory #o1777)))))))
    (service-type
     (name 'x11-socket-directory-service)
     (extensions
      (list
       (service-extension shepherd-root-service-type
                          (compose
                           list
                           (const x11-socket-directory-shepherd-service)))))
     (default-value #f) ; no default value required
     (description
      "Create @file{/tmp/.X11-unix} for XWayland.  When using X11, libxcb
takes care of creating that directory however, when using XWayland, we
need to create it beforehand."))))

(define-deprecated x11-socket-directory-service
  x11-socket-directory-service-type
  ;; Return a service that creates /tmp/.X11-unix.  When using X11, libxcb
  ;; takes care of creating that directory.  However, when using XWayland, we
  ;; need to create beforehand.  Thus, create it unconditionally here.
  (service x11-socket-directory-service-type))


;;;
;;; Enlightenment desktop service.
;;;

(define-record-type* <enlightenment-desktop-configuration>
  enlightenment-desktop-configuration make-enlightenment-desktop-configuration
  enlightenment-desktop-configuration?
  ;; <package>
  (enlightenment        enlightenment-package
                        (default enlightenment)))

(define (enlightenment-privileged-programs enlightenment-desktop-configuration)
  (match-record enlightenment-desktop-configuration
      <enlightenment-desktop-configuration>
    (enlightenment)
    (map file-like->setuid-program
         (list (file-append enlightenment
                            "/lib/enlightenment/utils/enlightenment_sys")
               (file-append enlightenment
                            "/lib/enlightenment/utils/enlightenment_system")
               (file-append enlightenment
                            "/lib/enlightenment/utils/enlightenment_ckpasswd")))))

(define enlightenment-desktop-service-type
  (service-type
   (name 'enlightenment-desktop)
   (extensions
    (list (service-extension dbus-root-service-type
                             (compose list
                                      (package-direct-input-selector
                                       "efl")
                                      enlightenment-package))
          (service-extension udev-service-type
                             (compose list
                                      (package-direct-input-selector
                                        "ddcutil")
                                      enlightenment-package))
          (service-extension privileged-program-service-type
                             enlightenment-privileged-programs)
          (service-extension profile-service-type
                             (compose list
                                      enlightenment-package))))
   (default-value (enlightenment-desktop-configuration))
   (description
    "Return a service that adds the @code{enlightenment} package to the system
profile, and extends dbus with the ability for @code{efl} to generate
thumbnails and privileges the programs which enlightenment needs to function
as expected.")))

;;;
;;; KDE Plasma desktop service.
;;;

(define-record-type* <plasma-desktop-configuration> plasma-desktop-configuration
  make-plasma-desktop-configuration
  plasma-desktop-configuration?
  (plasma-package plasma-package (default plasma)))

(define (plasma-polkit-settings config)
  "Return the list of KDE Plasma dependencies that provide polkit actions and
rules."
  (let ((plasma-plasma (plasma-package config)))
    (map (lambda (name)
           ((package-direct-input-selector name) plasma-plasma))
         '("plasma-desktop"
           "plasma-workspace"
           "plasma-disks"
           "kinfocenter"
           "libksysguard"
           "ktexteditor"
           "powerdevil"
           "plasma-firewall"))))

(define (plasma-dbus-service config)
  "Return the list of KDE Plasma dependencies that provide D-Bus services."
  (let ((plasma-plasma (plasma-package config)))
    (map (lambda (name)
           ((package-direct-input-selector name) plasma-plasma))
         '("plasma-desktop"
           "plasma-workspace"
           "kactivitymanagerd"
           "plasma-disks"
           "kinfocenter"
           "libksysguard"
           "ktexteditor"
           "powerdevil"
           "bluedevil"
           "kwallet"
           "plasma-firewall"))))

;; see https://bugs.kde.org/show_bug.cgi?id=456210
;; if `kde' no exits, fallback to `other', and then unlock lockscreen not work,
;; so add it.
(define (plasma-pam-services config)
  (list (unix-pam-service "kde")))

(define plasma-desktop-service-type
  (service-type
   (name 'plasma-desktop)
   (description "Run the KDE Plasma desktop environment.")
   (default-value (plasma-desktop-configuration))
   (extensions
    (list (service-extension polkit-service-type
                             plasma-polkit-settings)
          (service-extension dbus-root-service-type
                             plasma-dbus-service)
          (service-extension pam-root-service-type
                             plasma-pam-services)
          (service-extension profile-service-type
                             (compose list
                                      plasma-package))))))


;;;
;;; inputattach-service-type
;;;

(define-record-type* <inputattach-configuration>
  inputattach-configuration
  make-inputattach-configuration
  inputattach-configuration?
  (device-type inputattach-configuration-device-type
               (default "wacom"))
  (device inputattach-configuration-device
          (default "/dev/ttyS0"))
  (baud-rate inputattach-configuration-baud-rate
             (default #f))
  (log-file inputattach-configuration-log-file
            (default #f)))

(define inputattach-shepherd-service
  (match-lambda
    (($ <inputattach-configuration> type device baud-rate log-file)
     (let ((args (append (if baud-rate
                             (list "--baud" (number->string baud-rate))
                             '())
                         (list (string-append "--" type)
                               device))))
       (list (shepherd-service
              (provision '(inputattach))
              (requirement '(udev))
              (documentation "inputattach daemon")
              (start #~(make-forkexec-constructor
                        (cons (string-append #$inputattach
                                             "/bin/inputattach")
                              (quote #$args))
                        #:log-file #$log-file))
              (stop #~(make-kill-destructor))))))))

(define inputattach-service-type
  (service-type
   (name 'inputattach)
   (extensions
    (list (service-extension shepherd-root-service-type
                             inputattach-shepherd-service)))
   (default-value (inputattach-configuration))
   (description "Return a service that runs inputattach on a device and
dispatches events from it.")))


;;;
;;; gnome-keyring-service-type
;;;

(define-record-type* <gnome-keyring-configuration> gnome-keyring-configuration
  make-gnome-keyring-configuration
  gnome-keyring-configuration?
  (keyring gnome-keyring-package (default gnome-keyring))
  (pam-services gnome-keyring-pam-services (default '(("gdm-password" . login)
                                                      ("passwd" . passwd)))))

(define (pam-gnome-keyring config)
  (define (%pam-keyring-entry . arguments)
    (pam-entry
     (control "optional")
     (module (file-append (gnome-keyring-package config)
                          "/lib/security/pam_gnome_keyring.so"))
     (arguments arguments)))

  (list
   (pam-extension
    (transformer
     (lambda (service)
       (case (assoc-ref (gnome-keyring-pam-services config)
                        (pam-service-name service))
         ((login)
          (pam-service
           (inherit service)
           (auth (append (pam-service-auth service)
                         (list (%pam-keyring-entry))))
           (session (append (pam-service-session service)
                            (list (%pam-keyring-entry "auto_start"))))))
         ((passwd)
          (pam-service
           (inherit service)
           (password (append (pam-service-password service)
                             (list (%pam-keyring-entry))))))
         (else service)))))))

(define gnome-keyring-service-type
  (service-type
   (name 'gnome-keyring)
   (extensions (list
                (service-extension pam-root-service-type pam-gnome-keyring)))
   (default-value (gnome-keyring-configuration))
   (description "Return a service, that adds the @code{gnome-keyring} package
to the system profile and extends PAM with entries using
@code{pam_gnome_keyring.so}, unlocking a user's login keyring when they log in
or setting its password with passwd.")))


;;;
;;; polkit-wheel-service -- Allow wheel group to perform admin actions
;;;

(define polkit-wheel
  (file-union
   "polkit-wheel"
   `(("share/polkit-1/rules.d/wheel.rules"
      ,(plain-file
        "wheel.rules"
        "polkit.addAdminRule(function(action, subject) {
    return [\"unix-group:wheel\"];
});
")))))

(define polkit-wheel-service
  (simple-service 'polkit-wheel polkit-service-type (list polkit-wheel)))


;;;
;;; seatd-service-type -- minimal seat management daemon
;;;

(define (seatd-group-sanitizer group-or-name)
  (match group-or-name
    ((? user-group? group) group)
    ((? string? group-name) (user-group (name group-name) (system? #t)))
    (_ (leave (G_ "seatd: '~a' is not a valid group~%") group-or-name))))

(define-record-type* <seatd-configuration> seatd-configuration
  make-seatd-configuration
  seatd-configuration?
  (seatd seatd-package (default seatd))
  (group seatd-group                    ; string | <user-group>
         (default "seat")
         (sanitize seatd-group-sanitizer))
  (socket seatd-socket (default "/run/seatd.sock"))
  (logfile seatd-logfile (default "/var/log/seatd.log"))
  (loglevel seatd-loglevel (default "info")))

(define (seatd-shepherd-service config)
  (list (shepherd-service
         (documentation "Minimal seat management daemon")
         (requirement '())
         ;; TODO: once cgroups is separate dependency
         ;; here we should depend on it rather than elogind
         (provision '(seatd elogind))
         (start #~(make-forkexec-constructor
                   (list #$(file-append (seatd-package config) "/bin/seatd")
                         "-g" #$(user-group-name (seatd-group config)))
                   #:environment-variables
                   (list (string-append "SEATD_LOGLEVEL="
                                        #$(seatd-loglevel config))
                         (string-append "SEATD_DEFAULTPATH="
                                        #$(seatd-socket config)))
                   #:log-file #$(seatd-logfile config)))
         (stop #~(make-kill-destructor)))))

(define seatd-accounts
  (match-lambda (($ <seatd-configuration> _ group) (list group))))

(define seatd-environment
  (match-lambda
    (($ <seatd-configuration> _ _ socket)
     `(("SEATD_SOCK" . ,socket)))))

(define seatd-service-type
  (service-type
   (name 'seatd)
   (description "Seat management takes care of mediating access
to shared devices (graphics, input), without requiring the
applications needing access to be root.")
   (extensions
    (list
     (service-extension account-service-type seatd-accounts)
     (service-extension session-environment-service-type seatd-environment)
     ;; TODO: once cgroups is separate dependency we should not mount it here
     ;; for now it is mounted here, because elogind mounts it
     (service-extension file-system-service-type (const %control-groups))
     (service-extension shepherd-root-service-type seatd-shepherd-service)))
   (default-value (seatd-configuration))))


;;;
;;; The default set of desktop services.
;;;

(define* (desktop-services-for-system #:optional
                                      (system (or (%current-target-system)
                                                  (%current-system))))
  ;; List of services typically useful for a "desktop" use case.

  ;; Since GDM depends on Rust and Rust is not available on all platforms,
  ;; use SDDM as the fall-back display manager.
  ;; TODO: Switch the condition to use (supported-package? "rust") and make
  ;; a news entry about the change.
  (cons* (if (string-prefix? "x86_64" system)
             (service gdm-service-type)
             (service sddm-service-type))

         ;; Screen lockers are a pretty useful thing and these are small.
         (service screen-locker-service-type
                  (screen-locker-configuration
                   (name "slock")
                   (program (file-append slock "/bin/slock"))))
         (service screen-locker-service-type
                  (screen-locker-configuration
                   (name "xlock")
                   (program (file-append xlockmore "/bin/xlock"))))

         ;; Add udev rules for MTP devices so that non-root users can access
         ;; them.
         (simple-service 'mtp udev-service-type (list libmtp))
         ;; Add udev rules for scanners.
         (service sane-service-type)
         ;; Add polkit rules, so that non-root users in the wheel group can
         ;; perform administrative tasks (similar to "sudo").
         polkit-wheel-service

         ;; Allow desktop users to also mount NTFS and NFS file systems
         ;; without root.
         (simple-service 'mount-setuid-helpers privileged-program-service-type
                         (map file-like->setuid-program
                              (list (file-append nfs-utils "/sbin/mount.nfs")
                               (file-append ntfs-3g "/sbin/mount.ntfs-3g"))))

         ;; This is a volatile read-write file system mounted at /var/lib/gdm,
         ;; to avoid GDM stale cache and permission issues.
         gdm-file-system-service

         ;; The global fontconfig cache directory can sometimes contain
         ;; stale entries, possibly referencing fonts that have been GC'd,
         ;; so mount it read-only.
         fontconfig-file-system-service

         ;; NetworkManager and its applet.
         (service network-manager-service-type)
         (service wpa-supplicant-service-type)    ;needed by NetworkManager
         (simple-service 'network-manager-applet
                         profile-service-type
                         (list network-manager-applet))
         (service modem-manager-service-type)
         (service usb-modeswitch-service-type)

         ;; The D-Bus clique.
         (service avahi-service-type)
         (service udisks-service-type)
         (service upower-service-type)
         (service accountsservice-service-type)
         (service cups-pk-helper-service-type)
         (service colord-service-type)
         (service geoclue-service-type)
         (service polkit-service-type)
         (service elogind-service-type)
         (service dbus-root-service-type)

         (service ntp-service-type)

         (service x11-socket-directory-service-type)

         (service pulseaudio-service-type)
         (service alsa-service-type)

         %base-services))

(define-syntax %desktop-services
  (identifier-syntax (desktop-services-for-system)))

;;; desktop.scm ends here
ass='hex'>?..G.}......s..QG.Z.d.N.{..w...N 3900 b5 5a 3d 69 d2 a4 f2 f2 72 07 5b 05 41 b0 dd 4e 73 f5 6d 2d 6f bd f5 56 48 48 48 76 76 b6 5e af .Z=i....r.[.A..Ns.m-o..VHHHvv.^. 3920 0f 0d 0d 7d e0 81 07 be f9 e6 9b b6 33 6c db b6 6d d4 a8 51 6a b5 fa 37 bf f9 cd fa f5 eb af eb ...}........3l..m..Qj..7........ 3940 96 a7 2f bf fc 72 e2 c4 89 3a 9d 4e a3 d1 8c 1d 3b 76 e7 ce 9d 8e bc fa 8e b0 bf ac d5 6a 7d ef ../..r...:.N....;v...........j}. 3960 bd f7 f4 7a 7d 48 48 48 54 54 d4 9c 39 73 ce 9c 39 23 35 85 84 84 04 04 04 6c df be bd b8 b8 d8 ...z}HHHTT..9s..9#5......l...... 3980 d6 2d 35 35 35 8e f4 95 fd e3 aa dd 9e ec 98 b2 b2 b2 21 43 86 68 34 9a 89 13 27 ee dd bb 57 a3 .-555.............!C.h4...'...W. 39a0 d1 a8 d5 ea b2 b2 b2 61 c3 86 cd 9b 37 af f3 eb 07 80 6b 21 00 00 e8 b8 17 5f 7c 71 fb f6 ed 7f .......a....7.....k!....._|q.... 39c0 fb db df 46 8c 18 d1 76 fa f1 e3 c7 27 4c 98 f0 dd 77 df ad 58 b1 62 d5 aa 55 17 2e 5c 98 3c 79 ...F...v....'L...w..X.b..U..\.<y 39e0 b2 74 9e ad 54 2a 1f 7a e8 a1 6d db b6 b5 b4 b4 48 33 57 56 56 1e 38 70 60 f2 e4 c9 b6 c5 2f 5f .t..T*.z..m.....H3WVV.8p`...../_ 3a00 be bc 68 d1 a2 67 9e 79 66 f5 ea d5 17 2f 5e 4c 4e 4e 6e 6e 6e 96 9a 8a 8b 8b a7 4c 99 e2 e3 e3 ..h..g.yf..../^LNNnnn......L.... 3a20 b3 66 cd 9a b4 b4 b4 03 07 0e 4c 9d 3a d5 f6 a9 29 27 4f 9e 14 45 f1 b9 e7 9e db bc 79 73 7a 7a .f........L.:...)'O..E......yszz 3a40 ba c1 60 98 36 6d da d5 65 2f 5e bc b8 ae ae ee dd 77 df dd b1 63 47 6c 6c ac d5 6a 15 04 21 2f ..`.6m..e/^......w...cGll..j..!/ 3a60 2f 6f de bc 79 d1 d1 d1 eb d6 ad 1b 36 6c d8 2b af bc 72 5d 5d d1 d8 d8 b8 64 c9 92 7f ff fb df /o..y.......6l.+..r]]....d...... 3a80 4b 96 2c 59 b5 6a 95 af af ef 9b 6f be e9 60 55 75 75 75 1f 7c f0 c1 fb ef bf 9f 97 97 27 8a e2 K.,Y.j.....o..`Uuuu.|........'.. 3aa0 d4 a9 53 6d 99 a4 dd d6 82 82 82 82 82 82 3b ee b8 e3 27 ab b2 58 2c ab 57 af 5e b5 6a 55 56 56 ..Sm..........;...'..X,.W.^.jUVV 3ac0 96 42 a1 78 ea a9 a7 6c 4d 5f 7c f1 c5 d3 4f 3f 7d c7 1d 77 ac 5d bb 76 f8 f0 e1 57 44 32 fb aa .B.x...lM_|...O?}..w.].v...WD2.. 3ae0 ab ab a7 4d 9b e6 eb eb bb 66 cd 9a b5 6b d7 4e 98 30 a1 6d 5e b2 f3 ea b7 ab dd 65 17 2e 5c f8 ...M.....f...k.N.0.m^......e..\. 3b00 97 bf fc 25 26 26 66 ed da b5 2f bd f4 92 bb bb fb b1 63 c7 a4 a6 bc bc bc 82 82 82 84 84 04 9d ...%&&f.../.......c............. 3b20 4e 57 f0 03 6f 6f 6f 47 fa ca fe 71 65 bf 27 3b cc 60 30 34 35 35 69 b5 da 9a 9a 1a a3 d1 a8 d5 NW..oooG...qe.';.`0455i......... 3b40 6a cd 66 b3 c1 60 d0 68 34 7c 0f 00 80 6e c5 2d 40 00 3a 28 3b 3b bb ae ae 4e 10 04 37 37 b7 2b j.f..`.h4|...n.-@.:(;;...N..77.+ 3b60 9a 96 2e 5d ea e1 e1 b1 75 eb 56 e9 4e 8f a8 a8 a8 11 23 46 ec dc b9 33 29 29 49 10 84 a9 53 a7 ...]....u.V.N.....#F...3))I...S. 3b80 66 64 64 e4 e4 e4 48 ef fa 7f f8 e1 87 a2 28 26 26 26 da 16 6f 69 69 59 b8 70 e1 fd f7 df 2f 08 fdd...H.......(&&&..oiiY.p..../. 3ba0 c2 a0 41 83 7e f9 cb 5f ee d8 b1 e3 91 47 1e 11 04 61 cd 9a 35 bd 7a f5 5a b7 6e 9d 97 97 97 20 ..A.~.._.....G...a..5.z.Z.n..... 3bc0 08 de de de b3 67 cf ce cb cb bb e7 9e 7b 04 41 18 3d 7a f4 e8 d1 a3 6d eb e9 d7 af 9f 74 01 41 .....g.......{.A.=z....m.....t.A 3be0 a7 d3 b5 2d 6f e0 c0 81 af bd f6 9a 34 1c 19 19 29 0d ac 5e bd 3a 24 24 64 e5 ca 95 6e 6e 6e f7 ...-o.......4...)..^.:$$d...nnn. 3c00 de 7b ef f1 e3 c7 f7 ee dd eb 78 6f 4c 9e 3c f9 8e 3b ee 78 f5 d5 57 e3 e2 e2 c6 8c 19 f3 d5 57 .{........xoL.<..;.x..W........W 3c20 5f ed d9 b3 47 6a 6a b7 2a ab d5 ba 70 e1 c2 d0 d0 50 41 10 d2 d3 d3 ef be fb ee dd bb 77 8f 1f _...Gjj.*...p....PA..........w.. 3c40 3f de 91 d6 c0 c0 40 41 10 a4 de b8 5a 6b 6b 6b 5a 5a 9a b4 a1 d9 b3 67 cf 99 33 e7 dc b9 73 d2 ?.....@A....ZkkkZZ.....g..3...s. 3c60 73 1a d2 fe ae 5a b5 ca cd cd 2d 2e 2e ae bc bc fc a3 8f 3e 72 70 67 4b 4a 4a ea ea ea 52 52 52 s....Z....-........>rpgKJJ...RRR 3c80 6e bf fd 76 41 10 c6 8c 19 e3 f8 ab 6f 9f fd 65 4f 9e 3c b9 76 ed da c7 1f 7f 3c 35 35 55 9a 7f n..vA.......o..eO.<.v.....<55U.. 3ca0 fc f8 f1 b6 78 20 dd d9 af 54 2a 7b f4 e8 21 75 cb 15 ec f4 95 fd e3 ca 7e 4f 76 98 5e af 9f 32 ....x....T*{..!u........~Ov.^..2 3cc0 65 4a 76 76 f6 8c 19 33 0e 1e 3c 78 e4 c8 91 d4 d4 54 bd 5e 5f 51 51 e1 e9 e9 d9 99 35 03 80 7d eJvv...3..<x.....T.^_QQ.....5..} 3ce0 5c 01 00 d0 41 75 75 75 8b 17 2f be ed b6 db fe f4 a7 3f d5 d6 d6 b6 6d ca cb cb d3 eb f5 b6 fb \...Auuu../.......?....m........ 3d00 bc 07 0c 18 10 1a 1a 7a f8 f0 61 69 34 22 22 22 32 32 72 cb 96 2d d2 e8 b6 6d db c6 8d 1b d7 bb .......z..ai4"""22r..-...m...... 3d20 77 ef b6 6b b0 9d 31 87 87 87 f7 ed db d7 76 c7 45 51 51 d1 88 11 23 6c e7 70 d2 f9 59 51 51 91 w..k..1.......v.EQQ...#l.p..YQQ. 3d40 34 da da da ba 71 e3 c6 71 e3 c6 dd 76 db 6d 21 21 21 52 66 a8 aa aa ba a2 f2 07 1f 7c f0 ea dd 4....q..q...v.m!!!Rf........|... 3d60 f9 e6 9b 6f c6 8c 19 63 0b 33 71 71 71 d7 d5 1b d2 3b cd 2a 95 4a a5 52 49 a3 f5 f5 f5 0e 56 e5 ...o...c.3qqq....;.*.J.RI.....V. 3d80 e6 e6 66 7b 5b 3a 38 38 d8 d7 d7 f7 c8 91 23 0e b6 da e7 e6 e6 16 1e 1e 6e 7b 15 04 41 b0 7d 4c ..f{[:88......#.........n{..A.}L 3da0 53 71 71 f1 3d f7 dc 63 db df 2b 4e e2 ed 0b 0d 0d f5 f4 f4 5c b4 68 d1 ae 5d bb 6c 77 e0 38 f8 Sqq.=..c..+N........\.h..].lw.8. 3dc0 ea db 67 7f d9 7f fd eb 5f ad ad ad 6d b3 a2 20 08 a2 d8 05 ef 64 d9 3f ae ec f7 64 87 65 64 64 ..g....._...m........d.?...d.edd 3de0 04 07 07 27 27 27 c7 c6 c6 2e 5f be 3c 39 39 d9 df df 3f 23 23 63 d6 ac 59 6f bf fd 76 e7 77 0a ...'''...._.<99...?##c..Yo..v.w. 3e00 00 ae 85 00 00 a0 83 ee bb ef be a9 53 a7 2e 5b b6 ac aa aa ea c5 17 5f b4 4d 6f 69 69 a9 a9 a9 ............S..[......._.Moii... 3e20 d9 b1 63 47 48 1b 25 25 25 67 cf 9e b5 cd 93 9c 9c fc e9 a7 9f 5e b8 70 a1 a8 a8 a8 a4 a4 a4 ed ..cGH.%%%g...........^.p........ 3e40 fd 3f 82 20 78 78 78 f4 ea d5 cb 36 da bb 77 6f db 0d d9 b5 b5 b5 7d fa f4 b1 35 79 7b 7b 8b a2 .?..xxx....6..wo......}...5y{{.. 3e60 78 e9 d2 25 69 f4 ad b7 de 4a 4d 4d bd ff fe fb 37 6c d8 f0 e9 a7 9f fe f5 af 7f 15 04 e1 ea 9b x..%i....JMM....7l.............. 3e80 4f 7e f1 8b 5f 5c 31 a5 b5 b5 b5 ba ba fa 96 5b 6e b1 4d e9 db b7 ef 75 f5 86 74 26 ad 50 28 6c O~.._\1........[n.M....u..t&.P(l 3ea0 a3 b6 7b 9c da ad ca d3 d3 b3 ed 59 ac 8f 8f 4f db 78 60 bf d5 3e 0f 0f 0f db b2 52 6d d2 76 5b ..{........Y...O.x`..>.....Rm.v[ 3ec0 5b 5b 2f 5c b8 d0 f6 49 dc eb 7a 2a b7 7f ff fe 9b 37 6f ee d9 b3 e7 dc b9 73 ef ba eb ae 7b ef [[/\...I..z*.....7o......s....{. 3ee0 bd b7 b0 b0 d0 f1 57 ff 5a da 5d f6 e2 c5 8b c2 0f 8f 11 77 2d fb c7 95 9d 9e ec 8c b8 b8 38 6f ......W.Z.]........w-.........8o 3f00 6f ef dc dc dc 81 03 07 0e 1f 3e 3c 37 37 57 a9 54 c6 c5 c5 e5 e5 e5 d9 1e 20 01 80 ee c0 2d 40 o.........><77W.T.............-@ 3f20 00 3a 48 a9 54 0a 82 70 db 6d b7 fd e1 0f 7f f8 db df fe 36 7e fc 78 e9 7d 53 77 77 77 95 4a 35 .:H.T..p.m.........6~.x.}Swww.J5 3f40 7e fc f8 d9 b3 67 b7 9d bf ed dd d8 0f 3d f4 d0 4b 2f bd f4 e1 87 1f 9e 38 71 22 24 24 64 d4 a8 ~....g.......=..K/......8q"$$d.. 3f60 51 6d e7 34 9b cd f5 f5 f5 b6 0c f0 fd f7 df fb f9 f9 49 c3 2a 95 4a 3a 0b 94 d4 d5 d5 59 2c 16 Qm.4..............I.*.J:.....Y,. 3f80 db d5 83 0f 3f fc 30 29 29 c9 76 7f f6 b5 9e 3d b5 9d a6 b7 9d 72 cb 2d b7 48 77 34 49 da 3e 3c ....?.0)).v....=.....r.-.Hw4I.>< 3fa0 da 49 ed 56 d5 d4 d4 d4 d8 d8 d8 b3 67 4f 69 b4 ba ba ba 5f bf 7e 0e b6 76 8c b4 bf df 7f ff bd .I.V........gOi...._.~..v....... 3fc0 6d 4a db 61 47 8c 1c 39 72 e4 c8 91 2d 2d 2d 07 0f 1e 9c 3f 7f fe 13 4f 3c 51 50 50 20 38 f6 ea mJ.aG..9r...---....?...O<QPP.8.. 3fe0 0b 3f 75 db 98 23 cb 4a a9 ac b2 b2 f2 7a e3 59 bb ec 1f 57 dd 24 26 26 e6 be fb ee cb cf cf 7f .?u..#.J.....z.Y...W.$&&........ 4000 fd f5 d7 df 78 e3 8d fc fc fc f7 de 7b 2f 26 26 66 df be 7d 32 7e 4a 12 00 57 c0 15 00 00 9d f5 ....x.......{/&&f..}2~J..W...... 4020 ec b3 cf 06 06 06 3e fb ec b3 b6 13 e8 31 63 c6 14 17 17 87 84 84 84 b5 31 70 e0 40 db 22 de de ......>......1c.........1p.@.".. 4040 de 13 26 4c d8 bc 79 f3 3f ff f9 cf df fe f6 b7 57 af 33 2f 2f 4f 1a 38 7a f4 e8 c5 8b 17 23 22 ..&L..y.?.......W.3//O.8z.....#" 4060 22 a4 d1 a8 a8 a8 82 82 82 c6 c6 46 69 34 27 27 47 9a 28 8d 36 34 34 b4 3d 35 dc b7 6f 9f e3 7b "..........Fi4''G.(.644.=5..o..{ 4080 71 db 6d b7 b5 bd e5 e3 eb af bf ee aa fe 71 a4 aa dc dc 5c 69 e0 d0 a1 43 75 75 75 57 7c a4 92 q.m...........q....\i...CuuuW|.. 40a0 fd d6 8e 89 8c 8c b4 75 b2 20 08 9f 7f fe 79 07 56 e2 ee ee 3e 6a d4 a8 a4 a4 a4 b3 67 cf da ae .......u......y.V...>j......g... 40c0 78 b4 fb ea 0b 82 20 bd dd de f6 9c db 91 65 47 8d 1a a5 50 28 de 7f ff fd b6 8b 5c f1 4e bc 4a x.............eG...P(......\.N.J 40e0 a5 b2 dd 7c e5 38 fb c7 55 37 31 99 4c 97 2f 5f 0e 0e 0e ae ad ad 3d 75 ea 54 70 70 b0 d9 6c 36 ...|.8..U71.L./_......=u.Tpp..l6 4100 99 4c 41 41 41 b6 c4 0b 00 dd 81 00 00 a0 b3 7a f6 ec 99 9e 9e 7e fa f4 69 db 27 e7 3c fb ec b3 .LAAA..........z.....~..i.'.<... 4120 15 15 15 8f 3e fa e8 ce 9d 3b 3f fb ec b3 4d 9b 36 cd 98 31 e3 e3 8f 3f 6e bb 54 72 72 72 59 59 ....>....;?...M.6..1...?n.TrrrYY 4140 59 6d 6d ed a4 49 93 ae 58 a1 bb bb fb 82 05 0b b6 6d db b6 6b d7 ae 39 73 e6 f8 fb fb 3f f4 d0 Ymm..I..X........m..k..9s....?.. 4160 43 52 d3 cc 99 33 eb eb eb a7 4f 9f be 6f df be 4d 9b 36 cd 9f 3f 3f 22 22 c2 f6 c0 40 5c 5c dc CR...3....O..o..M.6..??""...@\\. 4180 8e 1d 3b 0c 06 43 7d 7d fd a6 4d 9b 32 33 33 1d df 8b c7 1f 7f fc df ff fe f7 fa f5 eb 6b 6b 6b ..;..C}}..M.233..............kkk 41a0 3f f9 e4 93 ed db b7 77 55 ff b4 5b 95 28 8a 2f bc f0 c2 b6 6d db 32 33 33 9f 7c f2 c9 c0 c0 c0 ?......wU..[.(./....m.233.|..... 41c0 b6 4f 29 d8 69 bd 78 f1 e2 c9 93 27 4f 9e 3c 29 5d 25 90 86 db de b8 62 c7 9c 39 73 ca cb cb 9f .O).i.x....'O.<)]%.....b..9s.... 41e0 7a ea a9 cf 3f ff 7c e5 ca 95 07 0e 1c 70 7c 8f 3e f9 e4 93 99 33 67 7e f0 c1 07 5f 7c f1 c5 b6 z...?.|......p|.>....3g~..._|... 4200 6d db d6 ae 5d 1b 1b 1b eb ee ee ee f8 ab 3f 7c f8 70 51 14 5f 7e f9 e5 af be fa ea d0 a1 43 b6 m...].........?|.pQ._~........C. 4220 93 78 fb cb 06 06 06 ce 9c 39 f3 9d 77 de 99 3f 7f fe fe fd fb 3f fa e8 a3 c7 1f 7f fc 8a ca c3 .x.......9..w..?.....?.......... 4240 c3 c3 2b 2a 2a 36 6f de 5c 52 52 72 fc f8 71 db 27 f9 d8 ef 2b fb c7 55 37 c9 ca ca 0a 0b 0b 8b ..+**6o.\RRr..q.'...+..U7....... 4260 8f 8f 4f 48 48 58 bf 7e 7d 7c 7c fc a0 41 83 b2 b2 b2 c6 8f 1f 9f 9e 9e de ad 9b 06 e0 e2 b8 05 ..OHHX.~}||..A.................. 4280 08 40 17 88 8b 8b 7b f8 e1 87 37 6e dc 38 7e fc f8 51 a3 46 69 34 9a cc cc cc 65 cb 96 a5 a6 a6 .@....{...7n.8~..Q.Fi4....e..... 42a0 36 36 36 fa fb fb 8f 1e 3d fa b6 db 6e 6b bb 48 54 54 d4 80 01 03 86 0e 1d 7a f5 2d dd 9e 9e 9e 666.....=...nk.HTT.......z.-.... 42c0 0b 16 2c 78 f9 e5 97 cf 9e 3d 3b 6c d8 b0 bf fd ed 6f 1e 1e 1e b6 a5 36 6c d8 b0 6c d9 b2 c7 1e ..,x.....=;l.....o.....6l..l.... 42e0 7b cc cb cb 4b af d7 2f 58 b0 c0 76 ea f9 c2 0b 2f 98 cd e6 a4 a4 a4 e6 e6 e6 11 23 46 bc f6 da {...K../X..v..../..........#F... 4300 6b 3f f9 89 ef 3f e9 ee bb ef 5e b1 62 c5 1b 6f bc b1 60 c1 82 a1 43 87 3e f1 c4 13 cb 96 2d eb k?...?....^.b..o..`...C.>.....-. 4320 92 ce 69 b7 aa 9e 3d 7b a6 a5 a5 bd fa ea ab e7 ce 9d bb f3 ce 3b 17 2f 5e dc f6 a6 7f 3b ad af ..i...={.............;./^....;.. 4340 bc f2 ca 3f fe f1 0f db 9c 23 47 8e 14 04 e1 89 27 9e 78 fe f9 e7 db ad 6a d4 a8 51 af bf fe fa ...?.....#G.....'.x.....j..Q.... 4360 6b af bd 96 99 99 39 7c f8 f0 67 9f 7d 36 35 35 d5 c1 9b 5e d4 6a b5 42 a1 58 bc 78 f1 f9 f3 e7 k.....9|..g.}655...^.j.B.X.x.... 4380 fb f5 eb 37 76 ec d8 3f ff f9 cf b6 56 47 5e fd c0 c0 c0 95 2b 57 2e 5d ba 74 eb d6 ad 56 ab f5 ...7v..?....VG^.....+W.].t...V.. 43a0 f0 e1 c3 d2 45 92 76 97 5d b0 60 41 70 70 f0 c6 8d 1b b7 6c d9 e2 e3 e3 73 f7 dd 77 db 9e cd 95 ....E.v.].`App.....l....s..w.... 43c0 3c f2 c8 23 c5 c5 c5 4b 97 2e 3d 7f fe bc d5 6a 3d 76 ec 98 8f 8f 4f bb 7d 65 ff b8 ea 26 3a 9d <..#...K..=....j=v....O.}e...&:. 43e0 ee ab af be 2a 29 29 f1 f1 f1 91 9e 76 f0 f0 f0 d0 e9 74 a5 a5 a5 1a 8d a6 5b 37 0d c0 c5 b9 09 ....*)).....v.....t......[7..... 4400 42 b5 dc 35 00 5d e6 f4 e9 46 db 70 44 44 c4 92 25 4b c6 8d 1b 27 77 51 37 a9 80 80 9e b6 e1 23 B..5.]...F.pDD..%K...'wQ7......# 4420 47 9a 9e 7b ce 63 d7 ae 1f 4f 77 da f6 64 37 39 7c f8 70 42 42 c2 df ff fe f7 07 1e 78 40 ee ce G..{.c...Ow..d79|.pBB.......x@.. 4440 90 d3 92 25 4b d6 ad 5b 77 c5 97 9d 39 d8 da 85 56 ad 5a b5 7a f5 ea 2e f9 7e 2b 38 68 f0 e0 c1 ...%K..[w...9...V.Z.z....~+8h... 4460 93 26 4d 4a 4d 4d 9d 3b 77 6e 51 51 51 4e 4e ce e2 c5 8b b7 6c d9 72 f0 e0 c1 5e bd 7a d9 ff 26 .&MJMM.;wnQQQNN.....l.r...^.z..& 4480 60 d9 7f 7f 9d d2 d5 ff 35 02 02 02 ec 2e c1 19 14 7e 5e 7e bc 19 95 2b 00 00 6e b4 53 a7 4e 9d `.......5........~^~...+..n.S.N. 44a0 38 71 22 3d 3d 7d d0 a0 41 63 c7 8e 95 bb 1c 17 d5 d0 d0 b0 74 e9 d2 d8 d8 d8 01 03 06 18 0c 86 8q"==}..Ac..........t........... 44c0 bf fe f5 af 57 3c 7a 8b ee 66 b1 58 ac 56 ab 28 8a 56 ab d5 62 b1 48 17 76 a4 81 ab 9f 53 07 80 ....W<z..f.X.V.(.V..b.H.v....S.. 44e0 2e c4 9f 18 00 37 da 9a 35 6b 7e fb db df 36 35 35 bd f3 ce 3b dd 7d 97