1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2014, 2015 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2015 Andy Wingo <wingo@igalia.com>
4 ;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
6 ;;; This file is part of GNU Guix.
8 ;;; GNU Guix is free software; you can redistribute it and/or modify it
9 ;;; under the terms of the GNU General Public License as published by
10 ;;; the Free Software Foundation; either version 3 of the License, or (at
11 ;;; your option) any later version.
13 ;;; GNU Guix is distributed in the hope that it will be useful, but
14 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;;; GNU General Public License for more details.
18 ;;; You should have received a copy of the GNU General Public License
19 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21 (define-module (gnu services desktop)
22 #:use-module (gnu services)
23 #:use-module (gnu services base)
24 #:use-module (gnu services avahi)
25 #:use-module (gnu services xorg)
26 #:use-module (gnu services networking)
27 #:use-module (gnu system shadow)
28 #:use-module (gnu system linux) ; unix-pam-service
29 #:use-module (gnu packages glib)
30 #:use-module (gnu packages admin)
31 #:use-module (gnu packages freedesktop)
32 #:use-module (gnu packages gnome)
33 #:use-module (gnu packages avahi)
34 #:use-module (gnu packages wicd)
35 #:use-module (gnu packages polkit)
36 #:use-module ((gnu packages linux)
37 #:select (lvm2 fuse alsa-utils crda))
38 #:use-module (guix monads)
39 #:use-module (guix records)
40 #:use-module (guix store)
41 #:use-module (guix gexp)
42 #:use-module (ice-9 match)
43 #:export (dbus-service
47 %standard-geoclue-applications
56 ;;; This module contains service definitions for a "desktop" environment.
66 (if value "true\n" "false\n"))
73 (define (dbus-configuration-directory dbus services)
74 "Return a configuration directory for @var{dbus} that includes the
75 @code{etc/dbus-1/system.d} directories of each package listed in
79 (use-modules (sxml simple)
82 (define (services->sxml services)
83 ;; Return the SXML 'includedir' clauses for DIRS.
85 ,@(append-map (lambda (dir)
87 ,(string-append dir "/etc/dbus-1/system.d"))
88 (servicedir ;for '.service' files
89 ,(string-append dir "/share/dbus-1/services"))))
93 (copy-file (string-append #$dbus "/etc/dbus-1/system.conf")
94 (string-append #$output "/system.conf"))
96 ;; The default 'system.conf' has an <includedir> clause for
97 ;; 'system.d', so create it.
98 (mkdir (string-append #$output "/system.d"))
100 ;; 'system-local.conf' is automatically included by the default
101 ;; 'system.conf', so this is where we stuff our own things.
102 (call-with-output-file (string-append #$output "/system-local.conf")
104 (sxml->xml (services->sxml (list #$@services))
107 (gexp->derivation "dbus-configuration" build))
109 (define* (dbus-service services #:key (dbus dbus))
110 "Return a service that runs the \"system bus\", using @var{dbus}, with
111 support for @var{services}.
113 @uref{http://dbus.freedesktop.org/, D-Bus} is an inter-process communication
114 facility. Its system bus is used to allow system services to communicate and
115 be notified of system-wide events.
117 @var{services} must be a list of packages that provide an
118 @file{etc/dbus-1/system.d} directory containing additional D-Bus configuration
119 and policy files. For example, to allow avahi-daemon to use the system bus,
120 @var{services} must be equal to @code{(list avahi)}."
121 (mlet %store-monad ((conf (dbus-configuration-directory dbus services)))
124 (documentation "Run the D-Bus system daemon.")
125 (provision '(dbus-system))
126 (requirement '(user-processes))
127 (start #~(make-forkexec-constructor
128 (list (string-append #$dbus "/bin/dbus-daemon")
130 (string-append "--config-file=" #$conf "/system.conf"))))
131 (stop #~(make-kill-destructor))
132 (user-groups (list (user-group
135 (user-accounts (list (user-account
139 (comment "D-Bus system bus user")
140 (home-directory "/var/run/dbus")
142 #~(string-append #$shadow "/sbin/nologin")))))
144 (use-modules (guix build utils))
146 (mkdir-p "/var/run/dbus")
148 (let ((user (getpwnam "messagebus")))
149 (chown "/var/run/dbus"
150 (passwd:uid user) (passwd:gid user)))
152 (unless (file-exists? "/etc/machine-id")
153 (format #t "creating /etc/machine-id...~%")
154 (let ((prog (string-append #$dbus "/bin/dbus-uuidgen")))
155 ;; XXX: We can't use 'system' because the initrd's
156 ;; guile system(3) only works when 'sh' is in $PATH.
157 (let ((pid (primitive-fork)))
159 (call-with-output-file "/etc/machine-id"
162 (dup2 (port->fdes port) 1)
164 (waitpid pid)))))))))))
168 ;;; Upower D-Bus service.
171 (define* (upower-configuration-file #:key watts-up-pro? poll-batteries?
172 ignore-lid? use-percentage-for-policy?
173 percentage-low percentage-critical
174 percentage-action time-low
175 time-critical time-action
176 critical-power-action)
177 "Return an upower-daemon configuration file."
178 (text-file "UPower.conf"
181 "EnableWattsUpPro=" (bool watts-up-pro?)
182 "NoPollBatteries=" (bool (not poll-batteries?))
183 "IgnoreLid=" (bool ignore-lid?)
184 "UsePercentageForPolicy=" (bool use-percentage-for-policy?)
185 "PercentageLow=" (number->string percentage-low) "\n"
186 "PercentageCritical=" (number->string percentage-critical) "\n"
187 "PercentageAction=" (number->string percentage-action) "\n"
188 "TimeLow=" (number->string time-low) "\n"
189 "TimeCritical=" (number->string time-critical) "\n"
190 "TimeAction=" (number->string time-action) "\n"
191 "CriticalPowerAction=" (match critical-power-action
192 ('hybrid-sleep "HybridSleep")
193 ('hibernate "Hibernate")
194 ('power-off "PowerOff"))
197 (define* (upower-service #:key (upower upower)
201 (use-percentage-for-policy? #f)
203 (percentage-critical 3)
204 (percentage-action 2)
208 (critical-power-action 'hybrid-sleep))
209 "Return a service that runs @uref{http://upower.freedesktop.org/,
210 @command{upowerd}}, a system-wide monitor for power consumption and battery
211 levels, with the given configuration settings. It implements the
212 @code{org.freedesktop.UPower} D-Bus interface, and is notably used by GNOME."
213 (mlet %store-monad ((config (upower-configuration-file
214 #:watts-up-pro? watts-up-pro?
215 #:poll-batteries? poll-batteries?
216 #:ignore-lid? ignore-lid?
217 #:use-percentage-for-policy? use-percentage-for-policy?
218 #:percentage-low percentage-low
219 #:percentage-critical percentage-critical
220 #:percentage-action percentage-action
222 #:time-critical time-critical
223 #:time-action time-action
224 #:critical-power-action critical-power-action)))
227 (documentation "Run the UPower power and battery monitor.")
228 (provision '(upower-daemon))
229 (requirement '(dbus-system udev))
231 (start #~(make-forkexec-constructor
232 (list (string-append #$upower "/libexec/upowerd"))
233 #:environment-variables
234 (list (string-append "UPOWER_CONF_FILE_NAME=" #$config))))
235 (stop #~(make-kill-destructor))
237 (use-modules (guix build utils))
238 (mkdir-p "/var/lib/upower")
239 (let ((user (getpwnam "upower")))
240 (chown "/var/lib/upower"
241 (passwd:uid user) (passwd:gid user)))))
243 (user-groups (list (user-group
246 (user-accounts (list (user-account
250 (comment "UPower daemon user")
251 (home-directory "/var/empty")
253 #~(string-append #$shadow "/sbin/nologin")))))))))
257 ;;; Colord D-Bus service.
260 (define* (colord-service #:key (colord colord))
261 "Return a service that runs @command{colord}, a system service with a D-Bus
262 interface to manage the color profiles of input and output devices such as
263 screens and scanners. It is notably used by the GNOME Color Manager graphical
264 tool. See @uref{http://www.freedesktop.org/software/colord/, the colord web
265 site} for more information."
266 (with-monad %store-monad
269 (documentation "Run the colord color management service.")
270 (provision '(colord-daemon))
271 (requirement '(dbus-system udev))
273 (start #~(make-forkexec-constructor
274 (list (string-append #$colord "/libexec/colord"))))
275 (stop #~(make-kill-destructor))
277 (use-modules (guix build utils))
278 (mkdir-p "/var/lib/colord")
279 (let ((user (getpwnam "colord")))
280 (chown "/var/lib/colord"
281 (passwd:uid user) (passwd:gid user)))))
283 (user-groups (list (user-group
286 (user-accounts (list (user-account
290 (comment "colord daemon user")
291 (home-directory "/var/empty")
293 #~(string-append #$shadow "/sbin/nologin")))))))))
297 ;;; GeoClue D-Bus service.
300 (define* (geoclue-application name #:key (allowed? #t) system? (users '()))
301 "Configure default GeoClue access permissions for an application. NAME is
302 the Desktop ID of the application, without the .desktop part. If ALLOWED? is
303 true, the application will have access to location information by default.
304 The boolean SYSTEM? value indicates that an application is a system component
305 or not. Finally USERS is a list of UIDs of all users for which this
306 application is allowed location info access. An empty users list means all
310 "allowed=" (bool allowed?)
311 "system=" (bool system?)
312 "users=" (string-join users ";") "\n"))
314 (define %standard-geoclue-applications
315 (list (geoclue-application "gnome-datetime-panel" #:system? #t)
316 (geoclue-application "epiphany" #:system? #f)
317 (geoclue-application "firefox" #:system? #f)))
319 (define* (geoclue-configuration-file #:key whitelist wifi-geolocation-url
321 wifi-submission-url submission-nick
323 "Return a geoclue configuration file."
324 (text-file "geoclue.conf"
327 "whitelist=" (string-join whitelist ";") "\n"
329 "url=" wifi-geolocation-url "\n"
330 "submit-data=" (bool submit-data?)
331 "submission-url=" wifi-submission-url "\n"
332 "submission-nick=" submission-nick "\n"
333 (string-join applications "\n"))))
335 (define* (geoclue-service #:key (geoclue geoclue)
337 (wifi-geolocation-url
338 ;; Mozilla geolocation service:
339 "https://location.services.mozilla.com/v1/geolocate?key=geoclue")
342 "https://location.services.mozilla.com/v1/submit?key=geoclue")
343 (submission-nick "geoclue")
344 (applications %standard-geoclue-applications))
345 "Return a service that runs the @command{geoclue} location service. This
346 service provides a D-Bus interface to allow applications to request access to
347 a user's physical location, and optionally to add information to online
348 location databases. By default, only the GNOME date-time panel and the Icecat
349 and Epiphany web browsers are able to ask for the user's location, and in the
350 case of Icecat and Epiphany, both will ask the user for permission first. See
351 @uref{https://wiki.freedesktop.org/www/Software/GeoClue/, the geoclue web
352 site} for more information."
353 (mlet %store-monad ((config (geoclue-configuration-file
354 #:whitelist whitelist
355 #:wifi-geolocation-url wifi-geolocation-url
356 #:submit-data? submit-data?
357 #:wifi-submission-url wifi-submission-url
358 #:submission-nick submission-nick
359 #:applications applications)))
362 (documentation "Run the GeoClue location service.")
363 (provision '(geoclue-daemon))
364 (requirement '(dbus-system))
366 (start #~(make-forkexec-constructor
367 (list (string-append #$geoclue "/libexec/geoclue"))
369 #:environment-variables
370 (list (string-append "GEOCLUE_CONFIG_FILE=" #$config))))
371 (stop #~(make-kill-destructor))
373 (user-groups (list (user-group
376 (user-accounts (list (user-account
380 (comment "GeoClue daemon user")
381 (home-directory "/var/empty")
383 "/run/current-system/profile/sbin/nologin"))))))))
387 ;;; Polkit privilege management service.
390 (define* (polkit-service #:key (polkit polkit))
391 "Return a service that runs the @command{polkit} privilege management
392 service. By querying the @command{polkit} service, a privileged system
393 component can know when it should grant additional capabilities to ordinary
394 users. For example, an ordinary user can be granted the capability to suspend
395 the system if the user is logged in locally."
396 (with-monad %store-monad
399 (documentation "Run the polkit privilege management service.")
400 (provision '(polkit-daemon))
401 (requirement '(dbus-system))
403 (start #~(make-forkexec-constructor
404 (list (string-append #$polkit "/lib/polkit-1/polkitd"))))
405 (stop #~(make-kill-destructor))
407 (user-groups (list (user-group
410 (user-accounts (list (user-account
414 (comment "Polkit daemon user")
415 (home-directory "/var/empty")
417 "/run/current-system/profile/sbin/nologin"))))
419 (pam-services (list (unix-pam-service "polkit-1")))))))
423 ;;; Elogind login and seat management service.
426 (define-record-type* <elogind-configuration> elogind-configuration
427 make-elogind-configuration
428 elogind-configuration
429 (kill-user-processes? elogind-kill-user-processes?
431 (kill-only-users elogind-kill-only-users
433 (kill-exclude-users elogind-kill-exclude-users
435 (inhibit-delay-max-seconds elogind-inhibit-delay-max-seconds
437 (handle-power-key elogind-handle-power-key
439 (handle-suspend-key elogind-handle-suspend-key
441 (handle-hibernate-key elogind-handle-hibernate-key
442 ;; (default 'hibernate)
443 ;; XXX Ignore it for now, since we don't
444 ;; yet handle resume-from-hibernation in
447 (handle-lid-switch elogind-handle-lid-switch
449 (handle-lid-switch-docked elogind-handle-lid-switch-docked
451 (power-key-ignore-inhibited? elogind-power-key-ignore-inhibited?
453 (suspend-key-ignore-inhibited? elogind-suspend-key-ignore-inhibited?
455 (hibernate-key-ignore-inhibited? elogind-hibernate-key-ignore-inhibited?
457 (lid-switch-ignore-inhibited? elogind-lid-switch-ignore-inhibited?
459 (holdoff-timeout-seconds elogind-holdoff-timeout-seconds
461 (idle-action elogind-idle-action
463 (idle-action-seconds elogind-idle-action-seconds
465 (runtime-directory-size-percent elogind-runtime-directory-size-percent
467 (runtime-directory-size elogind-runtime-directory-size
469 (remove-ipc? elogind-remove-ipc?
472 (suspend-state elogind-suspend-state
473 (default '("mem" "standby" "freeze")))
474 (suspend-mode elogind-suspend-mode
476 (hibernate-state elogind-hibernate-state
478 (hibernate-mode elogind-hibernate-mode
479 (default '("platform" "shutdown")))
480 (hybrid-sleep-state elogind-hybrid-sleep-state
482 (hybrid-sleep-mode elogind-hybrid-sleep-mode
484 '("suspend" "platform" "shutdown"))))
486 (define (elogind-configuration-file config)
491 (_ (error "expected #t or #f, instead got:" x))))
492 (define char-set:user-name
493 (string->char-set "abcdefghijklmnopqrstuvwxyz0123456789_-"))
494 (define (valid-list? l pred)
495 (and-map (lambda (x) (string-every pred x)) l))
496 (define (user-name-list users)
497 (unless (valid-list? users char-set:user-name)
498 (error "invalid user list" users))
499 (string-join users " "))
500 (define (enum val allowed)
501 (unless (memq val allowed)
502 (error "invalid value" val allowed))
503 (symbol->string val))
504 (define (non-negative-integer x)
505 (unless (exact-integer? x) (error "not an integer" x))
506 (when (negative? x) (error "negative number not allowed" x))
508 (define handle-actions
509 '(ignore poweroff reboot halt kexec suspend hibernate hybrid-sleep lock))
510 (define (handle-action x)
511 (enum x handle-actions))
512 (define (sleep-list tokens)
513 (unless (valid-list? tokens char-set:user-name)
514 (error "invalid sleep list" tokens))
515 (string-join tokens " "))
516 (define-syntax ini-file-clause
518 ((_ config (prop (parser getter)))
519 (string-append prop "=" (parser (getter config)) "\n"))
521 (string-append str "\n"))))
522 (define-syntax-rule (ini-file config file clause ...)
523 (text-file file (string-append (ini-file-clause config clause) ...)))
527 ("KillUserProcesses" (yesno elogind-kill-user-processes?))
528 ("KillOnlyUsers" (user-name-list elogind-kill-only-users))
529 ("KillExcludeUsers" (user-name-list elogind-kill-exclude-users))
530 ("InhibitDelayMaxSecs" (non-negative-integer elogind-inhibit-delay-max-seconds))
531 ("HandlePowerKey" (handle-action elogind-handle-power-key))
532 ("HandleSuspendKey" (handle-action elogind-handle-suspend-key))
533 ("HandleHibernateKey" (handle-action elogind-handle-hibernate-key))
534 ("HandleLidSwitch" (handle-action elogind-handle-lid-switch))
535 ("HandleLidSwitchDocked" (handle-action elogind-handle-lid-switch-docked))
536 ("PowerKeyIgnoreInhibited" (yesno elogind-power-key-ignore-inhibited?))
537 ("SuspendKeyIgnoreInhibited" (yesno elogind-suspend-key-ignore-inhibited?))
538 ("HibernateKeyIgnoreInhibited" (yesno elogind-hibernate-key-ignore-inhibited?))
539 ("LidSwitchIgnoreInhibited" (yesno elogind-lid-switch-ignore-inhibited?))
540 ("HoldoffTimeoutSecs" (non-negative-integer elogind-holdoff-timeout-seconds))
541 ("IdleAction" (handle-action elogind-idle-action))
542 ("IdleActionSeconds" (non-negative-integer elogind-idle-action-seconds))
543 ("RuntimeDirectorySize"
546 (match (elogind-runtime-directory-size-percent config)
547 (#f (non-negative-integer (elogind-runtime-directory-size config)))
548 (percent (string-append (non-negative-integer percent) "%"))))))
549 ("RemoveIpc" (yesno elogind-remove-ipc?))
551 ("SuspendState" (sleep-list elogind-suspend-state))
552 ("SuspendMode" (sleep-list elogind-suspend-mode))
553 ("HibernateState" (sleep-list elogind-hibernate-state))
554 ("HibernateMode" (sleep-list elogind-hibernate-mode))
555 ("HybridSleepState" (sleep-list elogind-hybrid-sleep-state))
556 ("HybridSleepMode" (sleep-list elogind-hybrid-sleep-mode))))
558 (define* (elogind-service #:key (elogind elogind)
559 (config (elogind-configuration)))
560 "Return a service that runs the @command{elogind} login and seat management
561 service. The @command{elogind} service integrates with PAM to allow other
562 system components to know the set of logged-in users as well as their session
563 types (graphical, console, remote, etc.). It can also clean up after users
565 (mlet %store-monad ((config-file (elogind-configuration-file config)))
568 (documentation "Run the elogind login and seat management service.")
569 (provision '(elogind))
570 (requirement '(dbus-system))
572 (start #~(make-forkexec-constructor
573 (list (string-append #$elogind "/libexec/elogind/elogind"))
574 #:environment-variables
575 (list (string-append "ELOGIND_CONF_FILE=" #$config-file))))
576 (stop #~(make-kill-destructor))))))
580 ;;; The default set of desktop services.
582 (define %desktop-services
583 ;; List of services typically useful for a "desktop" use case.
584 (cons* (slim-service)
589 ;; FIXME: The colord, geoclue, and polkit services could all be
590 ;; bus-activated by default, so they don't run at program startup.
591 ;; However, user creation and /var/lib/colord creation happen at
592 ;; service activation time, so we currently add them to the set of
598 (dbus-service (list avahi wicd upower colord geoclue polkit elogind))
602 (map (lambda (mservice)
603 (mlet %store-monad ((service mservice))
605 ;; Provide an nscd ready to use nss-mdns.
606 ((memq 'nscd (service-provision service))
607 (nscd-service (nscd-configuration)
608 #:name-services (list nss-mdns)))
610 ;; Add more rules to udev-service.
612 ;; XXX Keep this in sync with the 'udev-service' call in
613 ;; %base-services. Here we intend only to add 'upower',
614 ;; 'colord', and 'elogind'.
615 ((memq 'udev (service-provision service))
616 (udev-service #:rules
617 (list lvm2 fuse alsa-utils crda
618 upower colord elogind)))
623 ;;; desktop.scm ends here