1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2015 Sou Bunnbu <iyzsong@gmail.com>
4 ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
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 dbus)
22 #:use-module (gnu services)
23 #:use-module (gnu services shepherd)
24 #:use-module (gnu system shadow)
25 #:use-module (gnu system pam)
26 #:use-module ((gnu packages glib) #:select (dbus))
27 #:use-module (gnu packages polkit)
28 #:use-module (gnu packages admin)
29 #:use-module (guix gexp)
30 #:use-module ((guix packages) #:select (package-name))
31 #:use-module (guix records)
32 #:use-module (guix modules)
33 #:use-module (srfi srfi-1)
34 #:use-module (ice-9 match)
35 #:export (dbus-configuration
37 dbus-root-service-type
48 (define-record-type* <dbus-configuration>
49 dbus-configuration make-dbus-configuration
51 (dbus dbus-configuration-dbus ;<package>
53 (services dbus-configuration-services ;list of <package>
56 (define (system-service-directory services)
57 "Return the system service directory, containing @code{.service} files for
58 all the services that may be activated by the daemon."
59 (computed-file "dbus-system-services"
60 (with-imported-modules '((guix build utils))
62 (use-modules (guix build utils)
66 (append-map (lambda (service)
75 (for-each (lambda (file)
77 (string-append #$output "/"
82 (define (dbus-configuration-directory services)
83 "Return a directory contains the @code{system-local.conf} file for DBUS that
84 includes the @code{etc/dbus-1/system.d} directories of each package listed in
88 (use-modules (sxml simple)
91 (define-syntax directives
93 ;; Expand the given directives (SXML expressions) only if their
94 ;; key names a file that exists.
95 ((_ (name directory) rest ...)
96 (let ((dir directory))
97 (if (file-exists? dir)
99 ,@(directives rest ...))
100 (directives rest ...))))
104 (define (services->sxml services)
105 ;; Return the SXML 'includedir' clauses for DIRS.
107 (servicehelper "/run/setuid-programs/dbus-daemon-launch-helper")
109 ;; First, the '.service' files of services subject to activation.
110 ;; We use a fixed location under /etc because the setuid helper
111 ;; looks for them in that location and nowhere else. See
112 ;; <https://bugs.freedesktop.org/show_bug.cgi?id=92458>.
113 (servicedir "/etc/dbus-1/system-services")
115 ,@(append-map (lambda (dir)
118 (string-append dir "/etc/dbus-1/system.d"))
120 (string-append dir "/share/dbus-1/system.d"))
121 (servicedir ;for '.service' files
122 (string-append dir "/share/dbus-1/services"))))
127 ;; Provide /etc/dbus-1/system-services, which is where the setuid
128 ;; helper looks for system service files.
129 (symlink #$(system-service-directory services)
130 (string-append #$output "/system-services"))
132 ;; 'system-local.conf' is automatically included by the default
133 ;; 'system.conf', so this is where we stuff our own things.
134 (call-with-output-file (string-append #$output "/system-local.conf")
136 (sxml->xml (services->sxml (list #$@services))
139 (computed-file "dbus-configuration" build))
141 (define (dbus-etc-files config)
142 "Return a list of FILES for @var{etc-service-type} to build the
143 @code{/etc/dbus-1} directory."
144 (list `("dbus-1" ,(dbus-configuration-directory
145 (dbus-configuration-services config)))))
147 (define %dbus-accounts
148 ;; Accounts used by the system bus.
149 (list (user-group (name "messagebus") (system? #t))
154 (comment "D-Bus system bus user")
155 (home-directory "/var/run/dbus")
156 (shell (file-append shadow "/sbin/nologin")))))
158 (define dbus-setuid-programs
159 ;; Return the file name of the setuid program that we need.
161 (($ <dbus-configuration> dbus services)
162 (list (file-append dbus "/libexec/dbus-daemon-launch-helper")))))
164 (define (dbus-activation config)
165 "Return an activation gexp for D-Bus using @var{config}."
166 (with-imported-modules (source-module-closure
167 '((gnu build activation)
170 (use-modules (gnu build activation)
173 (let ((user (getpwnam "messagebus")))
174 ;; This directory contains the daemon's socket so it must be
176 (mkdir-p/perms "/var/run/dbus" user #o755))
178 (unless (file-exists? "/etc/machine-id")
179 (format #t "creating /etc/machine-id...~%")
180 (invoke (string-append #$(dbus-configuration-dbus config)
182 "--ensure=/etc/machine-id")))))
184 (define dbus-shepherd-service
186 (($ <dbus-configuration> dbus)
187 (list (shepherd-service
188 (documentation "Run the D-Bus system daemon.")
189 (provision '(dbus-system))
190 (requirement '(user-processes syslogd))
191 (start #~(make-forkexec-constructor
192 (list (string-append #$dbus "/bin/dbus-daemon")
193 "--nofork" "--system" "--syslog-only")
194 #:pid-file "/var/run/dbus/pid"))
195 (stop #~(make-kill-destructor)))))))
197 (define dbus-root-service-type
198 (service-type (name 'dbus)
200 (list (service-extension shepherd-root-service-type
201 dbus-shepherd-service)
202 (service-extension activation-service-type
204 (service-extension etc-service-type
206 (service-extension account-service-type
207 (const %dbus-accounts))
208 (service-extension setuid-program-service-type
209 dbus-setuid-programs)))
211 ;; Extensions consist of lists of packages (representing D-Bus
212 ;; services) that we just concatenate.
213 (compose concatenate)
215 ;; The service's parameters field is extended by augmenting
216 ;; its <dbus-configuration> 'services' field.
217 (extend (lambda (config services)
221 (append (dbus-configuration-services config)
224 (default-value (dbus-configuration))
225 (description "Run the system-wide D-Bus inter-process message
226 bus. It allows programs and daemons to communicate and is also responsible
227 for spawning (@dfn{activating}) D-Bus services on demand.")))
229 (define* (dbus-service #:key (dbus dbus) (services '()))
230 "Return a service that runs the \"system bus\", using @var{dbus}, with
231 support for @var{services}.
233 @uref{http://dbus.freedesktop.org/, D-Bus} is an inter-process communication
234 facility. Its system bus is used to allow system services to communicate and
235 be notified of system-wide events.
237 @var{services} must be a list of packages that provide an
238 @file{etc/dbus-1/system.d} directory containing additional D-Bus configuration
239 and policy files. For example, to allow avahi-daemon to use the system bus,
240 @var{services} must be equal to @code{(list avahi)}."
241 (service dbus-root-service-type
242 (dbus-configuration (dbus dbus)
243 (services services))))
245 (define (wrapped-dbus-service service program variables)
246 "Return a wrapper for @var{service}, a package containing a D-Bus service,
247 where @var{program} is wrapped such that @var{variables}, a list of name/value
248 tuples, are all set as environment variables when the bus daemon launches it."
250 (program-file (string-append (package-name service) "-program-wrapper")
252 (use-modules (ice-9 match))
254 (for-each (match-lambda
256 (setenv variable value)))
259 (apply execl (string-append #$service "/" #$program)
260 (string-append #$service "/" #$program)
261 (cdr (command-line))))))
264 (with-imported-modules '((guix build utils))
266 (use-modules (guix build utils))
268 (define service-directory
269 "/share/dbus-1/system-services")
271 (mkdir-p (dirname (string-append #$output
273 (copy-recursively (string-append #$service
275 (string-append #$output
277 (symlink (string-append #$service "/etc") ;for etc/dbus-1
278 (string-append #$output "/etc"))
280 (for-each (lambda (file)
282 (("Exec[[:blank:]]*=[[:blank:]]*([[:graph:]]+)(.*)$"
283 _ original-program arguments)
284 (string-append "Exec=" #$wrapper arguments
286 (find-files #$output "\\.service$")))))
288 (computed-file (string-append (package-name service) "-wrapper")
293 ;;; Polkit privilege management service.
296 (define-record-type* <polkit-configuration>
297 polkit-configuration make-polkit-configuration
298 polkit-configuration?
299 (polkit polkit-configuration-polkit ;<package>
301 (actions polkit-configuration-actions ;list of <package>
304 (define %polkit-accounts
305 (list (user-group (name "polkitd") (system? #t))
310 (comment "Polkit daemon user")
311 (home-directory "/var/empty")
312 (shell "/run/current-system/profile/sbin/nologin"))))
314 (define %polkit-pam-services
315 (list (unix-pam-service "polkit-1")))
317 (define (polkit-directory packages)
318 "Return a directory containing an @file{actions} and possibly a
319 @file{rules.d} sub-directory, for use as @file{/etc/polkit-1}."
320 (with-imported-modules '((guix build union))
321 (computed-file "etc-polkit-1"
323 (use-modules (guix build union) (srfi srfi-26))
325 (union-build #$output
326 (map (cut string-append <>
328 (list #$@packages)))))))
330 (define polkit-etc-files
332 (($ <polkit-configuration> polkit packages)
333 `(("polkit-1" ,(polkit-directory (cons polkit packages)))))))
335 (define polkit-setuid-programs
337 (($ <polkit-configuration> polkit)
338 (list (file-append polkit "/lib/polkit-1/polkit-agent-helper-1")
339 (file-append polkit "/bin/pkexec")))))
341 (define polkit-service-type
342 (service-type (name 'polkit)
344 (list (service-extension account-service-type
345 (const %polkit-accounts))
346 (service-extension pam-root-service-type
347 (const %polkit-pam-services))
348 (service-extension dbus-root-service-type
351 polkit-configuration-polkit))
352 (service-extension etc-service-type
354 (service-extension setuid-program-service-type
355 polkit-setuid-programs)))
357 ;; Extensions are lists of packages that provide polkit rules
358 ;; or actions under share/polkit-1/{actions,rules.d}.
359 (compose concatenate)
360 (extend (lambda (config actions)
361 (polkit-configuration
364 (append (polkit-configuration-actions config)
367 (default-value (polkit-configuration))
370 @uref{http://www.freedesktop.org/wiki/Software/polkit/, Polkit privilege
371 management service}, which allows system administrators to grant access to
372 privileged operations in a structured way. Polkit is a requirement for most
373 desktop environments, such as GNOME.")))
375 (define* (polkit-service #:key (polkit polkit))
376 "Return a service that runs the
377 @uref{http://www.freedesktop.org/wiki/Software/polkit/, Polkit privilege
378 management service}, which allows system administrators to grant access to
379 privileged operations in a structured way. By querying the Polkit service, a
380 privileged system component can know when it should grant additional
381 capabilities to ordinary users. For example, an ordinary user can be granted
382 the capability to suspend the system if the user is logged in locally."
383 (service polkit-service-type
384 (polkit-configuration (polkit polkit))))
386 ;;; dbus.scm ends here