Merge branch 'staging'
[jackhill/guix/guix.git] / gnu / services.scm
CommitLineData
db4fdc04 1;;; GNU Guix --- Functional package management for GNU
d466b1fc 2;;; Copyright © 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
4d343a14 3;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com>
db4fdc04
LC
4;;;
5;;; This file is part of GNU Guix.
6;;;
7;;; GNU Guix is free software; you can redistribute it and/or modify it
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
12;;; GNU Guix is distributed in the hope that it will be useful, but
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20(define-module (gnu services)
bebc8681 21 #:use-module (guix gexp)
0adfe95a
LC
22 #:use-module (guix monads)
23 #:use-module (guix store)
db4fdc04 24 #:use-module (guix records)
af4c3fd5 25 #:use-module (guix profiles)
0c0c1b21 26 #:use-module (guix discovery)
d466b1fc 27 #:use-module (guix combinators)
0adfe95a
LC
28 #:use-module (guix sets)
29 #:use-module (guix ui)
1bb895ea 30 #:use-module ((guix utils) #:select (source-properties->location))
232ccbef 31 #:use-module (guix modules)
0adfe95a
LC
32 #:use-module (gnu packages base)
33 #:use-module (gnu packages bash)
34 #:use-module (srfi srfi-1)
35 #:use-module (srfi srfi-9)
36 #:use-module (srfi srfi-9 gnu)
37 #:use-module (srfi srfi-26)
38 #:use-module (srfi srfi-34)
39 #:use-module (srfi srfi-35)
40 #:use-module (ice-9 vlist)
41 #:use-module (ice-9 match)
42 #:export (service-extension
43 service-extension?
7d8b5913
CB
44 service-extension-target
45 service-extension-compute
0adfe95a
LC
46
47 service-type
48 service-type?
5152d13b
LC
49 service-type-name
50 service-type-extensions
51 service-type-compose
52 service-type-extend
1bb895ea 53 service-type-default-value
b714395a
LC
54 service-type-description
55 service-type-location
56
0c0c1b21
LC
57 %service-type-path
58 fold-service-types
49483f71 59 lookup-service-types
0adfe95a 60
db4fdc04 61 service
0adfe95a
LC
62 service?
63 service-kind
efe7d19a
LC
64 service-value
65 service-parameters ;deprecated
0adfe95a 66
71654dfd 67 simple-service
cd6f6c22 68 modify-services
5152d13b 69 service-back-edges
d466b1fc 70 instantiate-missing-services
0adfe95a
LC
71 fold-services
72
73 service-error?
1bb895ea
LC
74 missing-value-service-error?
75 missing-value-service-error-type
76 missing-value-service-error-location
0adfe95a
LC
77 missing-target-service-error?
78 missing-target-service-error-service
79 missing-target-service-error-target-type
80 ambiguous-target-service-error?
81 ambiguous-target-service-error-service
82 ambiguous-target-service-error-target-type
83
d62e201c 84 system-service-type
0adfe95a 85 boot-service-type
be7be9e8 86 cleanup-service-type
0adfe95a
LC
87 activation-service-type
88 activation-service->script
a241a7ac 89 %linux-bare-metal-service
387e1754
LC
90 special-files-service-type
91 extra-special-file
0adfe95a
LC
92 etc-service-type
93 etc-directory
94 setuid-program-service-type
af4c3fd5 95 profile-service-type
0adfe95a 96 firmware-service-type
e0b47290 97 gc-root-service-type
0adfe95a
LC
98
99 %boot-service
100 %activation-service
d298c815 101 etc-service))
0adfe95a
LC
102
103;;; Comment:
104;;;
105;;; This module defines a broad notion of "service types" and "services."
db4fdc04 106;;;
0adfe95a
LC
107;;; A service type describe how its instances extend instances of other
108;;; service types. For instance, some services extend the instance of
109;;; ACCOUNT-SERVICE-TYPE by providing it with accounts and groups to create;
d4053c71
AK
110;;; others extend SHEPHERD-ROOT-SERVICE-TYPE by passing it instances of
111;;; <shepherd-service>.
0adfe95a
LC
112;;;
113;;; When applicable, the service type defines how it can itself be extended,
114;;; by providing one procedure to compose extensions, and one procedure to
115;;; extend itself.
116;;;
d62e201c
LC
117;;; A notable service type is SYSTEM-SERVICE-TYPE, which has a single
118;;; instance, which is the root of the service DAG. Its value is the
119;;; derivation that produces the 'system' directory as returned by
120;;; 'operating-system-derivation'.
0adfe95a
LC
121;;;
122;;; The 'fold-services' procedure can be passed a list of procedures, which it
123;;; "folds" by propagating extensions down the graph; it returns the root
124;;; service after the applying all its extensions.
db4fdc04
LC
125;;;
126;;; Code:
127
0adfe95a
LC
128(define-record-type <service-extension>
129 (service-extension target compute)
130 service-extension?
131 (target service-extension-target) ;<service-type>
132 (compute service-extension-compute)) ;params -> params
133
1bb895ea
LC
134(define &no-default-value
135 ;; Value used to denote service types that have no associated default value.
136 '(no default value))
137
0adfe95a
LC
138(define-record-type* <service-type> service-type make-service-type
139 service-type?
140 (name service-type-name) ;symbol (for debugging)
141
142 ;; Things extended by services of this type.
143 (extensions service-type-extensions) ;list of <service-extensions>
144
145 ;; Given a list of extensions, "compose" them.
146 (compose service-type-compose ;list of Any -> Any
147 (default #f))
148
149 ;; Extend the services' own parameters with the extension composition.
150 (extend service-type-extend ;list of Any -> parameters
1bb895ea
LC
151 (default #f))
152
153 ;; Optional default value for instances of this type.
154 (default-value service-type-default-value ;Any
b714395a
LC
155 (default &no-default-value))
156
157 ;; Meta-data.
158 (description service-type-description ;string
159 (default #f))
160 (location service-type-location ;<location>
161 (default (and=> (current-source-location)
162 source-properties->location))
163 (innate)))
0adfe95a
LC
164
165(define (write-service-type type port)
166 (format port "#<service-type ~a ~a>"
167 (service-type-name type)
168 (number->string (object-address type) 16)))
169
170(set-record-type-printer! <service-type> write-service-type)
171
0c0c1b21
LC
172(define %distro-root-directory
173 ;; Absolute file name of the module hierarchy.
174 (dirname (search-path %load-path "guix.scm")))
175
176(define %service-type-path
177 ;; Search path for service types.
178 (make-parameter `((,%distro-root-directory . "gnu/services")
179 (,%distro-root-directory . "gnu/system"))))
180
3943913f
LC
181(define (all-service-modules)
182 "Return the default set of service modules."
183 (cons (resolve-interface '(gnu services))
184 (all-modules (%service-type-path))))
185
0c0c1b21
LC
186(define* (fold-service-types proc seed
187 #:optional
3943913f 188 (modules (all-service-modules)))
0c0c1b21
LC
189 "For each service type exported by one of MODULES, call (PROC RESULT). SEED
190is used as the initial value of RESULT."
191 (fold-module-public-variables (lambda (object result)
192 (if (service-type? object)
193 (proc object result)
194 result))
a3d37f3a 195 seed
0c0c1b21
LC
196 modules))
197
49483f71
LC
198(define lookup-service-types
199 (let ((table
200 (delay (fold-service-types (lambda (type result)
201 (vhash-consq (service-type-name type)
202 type result))
203 vlist-null))))
204 (lambda (name)
205 "Return the list of services with the given NAME (a symbol)."
206 (vhash-foldq* cons '() name (force table)))))
207
0adfe95a
LC
208;; Services of a given type.
209(define-record-type <service>
1bb895ea 210 (make-service type value)
db4fdc04 211 service?
0adfe95a 212 (type service-kind)
efe7d19a
LC
213 (value service-value))
214
1bb895ea
LC
215(define-syntax service
216 (syntax-rules ()
217 "Return a service instance of TYPE. The service value is VALUE or, if
218omitted, TYPE's default value."
219 ((_ type value)
220 (make-service type value))
221 ((_ type)
222 (%service-with-default-value (current-source-location)
223 type))))
224
225(define (%service-with-default-value location type)
226 "Return a instance of service type TYPE with its default value, if any. If
227TYPE does not have a default value, an error is raised."
228 ;; TODO: Currently this is a run-time error but with a little bit macrology
229 ;; we could turn it into an expansion-time error.
230 (let ((default (service-type-default-value type)))
231 (if (eq? default &no-default-value)
232 (let ((location (source-properties->location location)))
233 (raise
234 (condition
235 (&missing-value-service-error (type type) (location location))
236 (&message
69daee23 237 (message (format #f (G_ "~a: no value specified \
1bb895ea
LC
238for service of type '~a'")
239 (location->string location)
240 (service-type-name type)))))))
241 (service type default))))
242
243(define-condition-type &service-error &error
244 service-error?)
245
246(define-condition-type &missing-value-service-error &service-error
247 missing-value-service-error?
248 (type missing-value-service-error-type)
249 (location missing-value-service-error-location))
250
251
252\f
253;;;
254;;; Helpers.
255;;;
256
efe7d19a
LC
257(define service-parameters
258 ;; Deprecated alias.
259 service-value)
0adfe95a 260
71654dfd
LC
261(define (simple-service name target value)
262 "Return a service that extends TARGET with VALUE. This works by creating a
263singleton service type NAME, of which the returned service is an instance."
264 (let* ((extension (service-extension target identity))
265 (type (service-type (name name)
266 (extensions (list extension)))))
267 (service type value)))
0adfe95a 268
cd6f6c22
LC
269(define-syntax %modify-service
270 (syntax-rules (=>)
271 ((_ service)
272 service)
273 ((_ svc (kind param => exp ...) clauses ...)
274 (if (eq? (service-kind svc) kind)
efe7d19a 275 (let ((param (service-value svc)))
cd6f6c22
LC
276 (service (service-kind svc)
277 (begin exp ...)))
278 (%modify-service svc clauses ...)))))
279
280(define-syntax modify-services
281 (syntax-rules ()
4d343a14
CM
282 "Modify the services listed in SERVICES according to CLAUSES and return
283the resulting list of services. Each clause must have the form:
cd6f6c22
LC
284
285 (TYPE VARIABLE => BODY)
286
287where TYPE is a service type, such as 'guix-service-type', and VARIABLE is an
288identifier that is bound within BODY to the value of the service of that
289TYPE. Consider this example:
290
291 (modify-services %base-services
292 (guix-service-type config =>
293 (guix-configuration
294 (inherit config)
295 (use-substitutes? #f)
296 (extra-options '(\"--gc-keep-derivations\"))))
297 (mingetty-service-type config =>
298 (mingetty-configuration
299 (inherit config)
300 (motd (plain-file \"motd\" \"Hi there!\")))))
301
302It changes the configuration of the GUIX-SERVICE-TYPE instance, and that of
303all the MINGETTY-SERVICE-TYPE instances.
304
305This is a shorthand for (map (lambda (svc) ...) %base-services)."
306 ((_ services clauses ...)
307 (map (lambda (service)
308 (%modify-service service clauses ...))
309 services))))
0adfe95a
LC
310
311\f
312;;;
313;;; Core services.
314;;;
315
d62e201c
LC
316(define (system-derivation mentries mextensions)
317 "Return as a monadic value the derivation of the 'system' directory
318containing the given entries."
319 (mlet %store-monad ((entries mentries)
320 (extensions (sequence %store-monad mextensions)))
321 (lower-object
322 (file-union "system"
323 (append entries (concatenate extensions))))))
324
325(define system-service-type
326 ;; This is the ultimate service type, the root of the service DAG. The
327 ;; service of this type is extended by monadic name/item pairs. These items
328 ;; end up in the "system directory" as returned by
329 ;; 'operating-system-derivation'.
330 (service-type (name 'system)
331 (extensions '())
332 (compose identity)
333 (extend system-derivation)))
334
3a391e68 335(define (compute-boot-script _ mexps)
be7be9e8 336 (mlet %store-monad ((gexps (sequence %store-monad mexps)))
0adfe95a 337 (gexp->file "boot"
d4053c71 338 ;; Clean up and activate the system, then spawn shepherd.
be7be9e8 339 #~(begin #$@gexps))))
0adfe95a 340
d62e201c
LC
341(define (boot-script-entry mboot)
342 "Return, as a monadic value, an entry for the boot script in the system
343directory."
344 (mlet %store-monad ((boot mboot))
345 (return `(("boot" ,boot)))))
346
0adfe95a
LC
347(define boot-service-type
348 ;; The service of this type is extended by being passed gexps as monadic
349 ;; values. It aggregates them in a single script, as a monadic value, which
350 ;; becomes its 'parameters'. It is the only service that extends nothing.
351 (service-type (name 'boot)
d62e201c
LC
352 (extensions
353 (list (service-extension system-service-type
354 boot-script-entry)))
3a391e68
LC
355 (compose append)
356 (extend compute-boot-script)))
0adfe95a
LC
357
358(define %boot-service
d62e201c 359 ;; The service that produces the boot script.
0adfe95a 360 (service boot-service-type #t))
be7be9e8
LC
361
362(define (cleanup-gexp _)
363 "Return as a monadic value a gexp to clean up /tmp and similar places upon
364boot."
fd129893
LC
365 (with-monad %store-monad
366 (with-imported-modules '((guix build utils))
367 (return #~(begin
368 (use-modules (guix build utils))
369
370 ;; Clean out /tmp and /var/run.
371 ;;
372 ;; XXX This needs to happen before service activations, so it
373 ;; has to be here, but this also implicitly assumes that /tmp
374 ;; and /var/run are on the root partition.
375 (letrec-syntax ((fail-safe (syntax-rules ()
376 ((_ exp rest ...)
377 (begin
378 (catch 'system-error
379 (lambda () exp)
380 (const #f))
381 (fail-safe rest ...)))
382 ((_)
383 #t))))
384 ;; Ignore I/O errors so the system can boot.
385 (fail-safe
aad8a143
LC
386 ;; Remove stale Shadow lock files as they would lead to
387 ;; failures of 'useradd' & co.
388 (delete-file "/etc/group.lock")
389 (delete-file "/etc/passwd.lock")
390 (delete-file "/etc/.pwd.lock") ;from 'lckpwdf'
391
fd129893
LC
392 (delete-file-recursively "/tmp")
393 (delete-file-recursively "/var/run")
394 (mkdir "/tmp")
395 (chmod "/tmp" #o1777)
396 (mkdir "/var/run")
e25ca462
DM
397 (chmod "/var/run" #o755)
398 (delete-file-recursively "/run/udev/watch.old"))))))))
be7be9e8
LC
399
400(define cleanup-service-type
401 ;; Service that cleans things up in /tmp and similar.
402 (service-type (name 'cleanup)
403 (extensions
404 (list (service-extension boot-service-type
405 cleanup-gexp)))))
0adfe95a 406
0adfe95a
LC
407(define* (activation-service->script service)
408 "Return as a monadic value the activation script for SERVICE, a service of
409ACTIVATION-SCRIPT-TYPE."
efe7d19a 410 (activation-script (service-value service)))
0adfe95a
LC
411
412(define (activation-script gexps)
413 "Return the system's activation script, which evaluates GEXPS."
0adfe95a
LC
414 (define (service-activations)
415 ;; Return the activation scripts for SERVICES.
416 (mapm %store-monad
417 (cut gexp->file "activate-service" <>)
418 gexps))
419
fd129893 420 (mlet* %store-monad ((actions (service-activations)))
0adfe95a 421 (gexp->file "activate"
232ccbef 422 (with-imported-modules (source-module-closure
fe2b6434
CB
423 '((gnu build activation)
424 (guix build utils)))
fd129893 425 #~(begin
fe2b6434
CB
426 (use-modules (gnu build activation)
427 (guix build utils))
0adfe95a 428
caa78166
LC
429 ;; Make sure the user accounting database exists. If it
430 ;; does not exist, 'setutxent' does not create it and
431 ;; thus there is no accounting at all.
432 (close-port (open-file "/var/run/utmpx" "a0"))
433
2986995b
LC
434 ;; Same for 'wtmp', which is populated by mingetty et
435 ;; al.
fe2b6434 436 (mkdir-p "/var/log")
2986995b
LC
437 (close-port (open-file "/var/log/wtmp" "a0"))
438
97bb1ab6
CB
439 ;; Set up /run/current-system. Among other things this
440 ;; sets up locales, which the activation snippets
441 ;; executed below may expect.
442 (activate-current-system)
443
fd129893
LC
444 ;; Run the services' activation snippets.
445 ;; TODO: Use 'load-compiled'.
97bb1ab6 446 (for-each primitive-load '#$actions))))))
0adfe95a
LC
447
448(define (gexps->activation-gexp gexps)
449 "Return a gexp that runs the activation script containing GEXPS."
450 (mlet %store-monad ((script (activation-script gexps)))
451 (return #~(primitive-load #$script))))
452
3a391e68
LC
453(define (second-argument a b) b)
454
0adfe95a
LC
455(define activation-service-type
456 (service-type (name 'activate)
457 (extensions
458 (list (service-extension boot-service-type
459 gexps->activation-gexp)))
460 (compose append)
461 (extend second-argument)))
462
463(define %activation-service
464 ;; The activation service produces the activation script from the gexps it
465 ;; receives.
466 (service activation-service-type #t))
467
a241a7ac
LC
468(define %modprobe-wrapper
469 ;; Wrapper for the 'modprobe' command that knows where modules live.
470 ;;
471 ;; This wrapper is typically invoked by the Linux kernel ('call_modprobe',
472 ;; in kernel/kmod.c), a situation where the 'LINUX_MODULE_DIRECTORY'
473 ;; environment variable is not set---hence the need for this wrapper.
474 (let ((modprobe "/run/current-system/profile/bin/modprobe"))
475 (program-file "modprobe"
476 #~(begin
477 (setenv "LINUX_MODULE_DIRECTORY"
478 "/run/booted-system/kernel/lib/modules")
479 (apply execl #$modprobe
480 (cons #$modprobe (cdr (command-line))))))))
481
482(define %linux-kernel-activation
483 ;; Activation of the Linux kernel running on the bare metal (as opposed to
484 ;; running in a container.)
485 #~(begin
486 ;; Tell the kernel to use our 'modprobe' command.
487 (activate-modprobe #$%modprobe-wrapper)
488
489 ;; Let users debug their own processes!
490 (activate-ptrace-attach)))
491
a241a7ac
LC
492(define %linux-bare-metal-service
493 ;; The service that does things that are needed on the "bare metal", but not
494 ;; necessary or impossible in a container.
6ddf4fcf
LC
495 (simple-service 'linux-bare-metal
496 activation-service-type
497 %linux-kernel-activation))
498
a241a7ac 499
387e1754
LC
500(define special-files-service-type
501 ;; Service to install "special files" such as /bin/sh and /usr/bin/env.
502 (service-type
503 (name 'special-files)
504 (extensions
505 (list (service-extension activation-service-type
506 (lambda (files)
507 #~(activate-special-files '#$files)))))
508 (compose concatenate)
509 (extend append)))
510
511(define (extra-special-file file target)
512 "Use TARGET as the \"special file\" FILE. For example, TARGET might be
513 (file-append coreutils \"/bin/env\")
514and FILE could be \"/usr/bin/env\"."
515 (simple-service (string->symbol (string-append "special-file-" file))
516 special-files-service-type
517 `((,file ,target))))
518
0adfe95a
LC
519(define (etc-directory service)
520 "Return the directory for SERVICE, a service of type ETC-SERVICE-TYPE."
efe7d19a 521 (files->etc-directory (service-value service)))
0adfe95a
LC
522
523(define (files->etc-directory files)
524 (file-union "etc" files))
525
d62e201c
LC
526(define (etc-entry files)
527 "Return an entry for the /etc directory consisting of FILES in the system
528directory."
529 (with-monad %store-monad
530 (return `(("etc" ,(files->etc-directory files))))))
531
0adfe95a
LC
532(define etc-service-type
533 (service-type (name 'etc)
534 (extensions
535 (list
536 (service-extension activation-service-type
537 (lambda (files)
538 (let ((etc
539 (files->etc-directory files)))
d62e201c
LC
540 #~(activate-etc #$etc))))
541 (service-extension system-service-type etc-entry)))
0adfe95a
LC
542 (compose concatenate)
543 (extend append)))
544
545(define (etc-service files)
546 "Return a new service of ETC-SERVICE-TYPE that populates /etc with FILES.
547FILES must be a list of name/file-like object pairs."
548 (service etc-service-type files))
549
550(define setuid-program-service-type
551 (service-type (name 'setuid-program)
552 (extensions
553 (list (service-extension activation-service-type
554 (lambda (programs)
555 #~(activate-setuid-programs
556 (list #$@programs))))))
557 (compose concatenate)
558 (extend append)))
559
af4c3fd5
LC
560(define (packages->profile-entry packages)
561 "Return a system entry for the profile containing PACKAGES."
562 (mlet %store-monad ((profile (profile-derivation
60a0886d
SB
563 (packages->manifest
564 (delete-duplicates packages eq?)))))
af4c3fd5
LC
565 (return `(("profile" ,profile)))))
566
567(define profile-service-type
568 ;; The service that populates the system's profile---i.e.,
569 ;; /run/current-system/profile. It is extended by package lists.
570 (service-type (name 'profile)
571 (extensions
572 (list (service-extension system-service-type
573 packages->profile-entry)))
574 (compose concatenate)
575 (extend append)))
576
0adfe95a
LC
577(define (firmware->activation-gexp firmware)
578 "Return a gexp to make the packages listed in FIRMWARE loadable by the
579kernel."
580 (let ((directory (directory-union "firmware" firmware)))
581 ;; Tell the kernel where firmware is.
582 #~(activate-firmware (string-append #$directory "/lib/firmware"))))
583
584(define firmware-service-type
585 ;; The service that collects firmware.
586 (service-type (name 'firmware)
587 (extensions
588 (list (service-extension activation-service-type
589 firmware->activation-gexp)))
590 (compose concatenate)
591 (extend append)))
592
e0b47290
LC
593(define (gc-roots->system-entry roots)
594 "Return an entry in the system's output containing symlinks to ROOTS."
595 (mlet %store-monad ((entry (gexp->derivation
596 "gc-roots"
597 #~(let ((roots '#$roots))
598 (mkdir #$output)
599 (chdir #$output)
600 (for-each symlink
601 roots
602 (map number->string
603 (iota (length roots))))))))
604 (return (if (null? roots)
605 '()
606 `(("gc-roots" ,entry))))))
607
608(define gc-root-service-type
609 ;; A service to associate extra garbage-collector roots to the system. This
610 ;; is a simple hack that guarantees that the system retains references to
611 ;; the given list of roots. Roots must be "lowerable" objects like
612 ;; packages, or derivations.
613 (service-type (name 'gc-roots)
614 (extensions
615 (list (service-extension system-service-type
616 gc-roots->system-entry)))
617 (compose concatenate)
618 (extend append)))
619
0adfe95a
LC
620\f
621;;;
622;;; Service folding.
623;;;
624
0adfe95a
LC
625(define-condition-type &missing-target-service-error &service-error
626 missing-target-service-error?
627 (service missing-target-service-error-service)
628 (target-type missing-target-service-error-target-type))
629
630(define-condition-type &ambiguous-target-service-error &service-error
631 ambiguous-target-service-error?
632 (service ambiguous-target-service-error-service)
633 (target-type ambiguous-target-service-error-target-type))
634
d466b1fc
LC
635(define (missing-target-error service target-type)
636 (raise
637 (condition (&missing-target-service-error
638 (service service)
639 (target-type target-type))
640 (&message
641 (message
642 (format #f (G_ "no target of type '~a' for service '~a'")
643 (service-type-name target-type)
644 (service-type-name
645 (service-kind service))))))))
646
0adfe95a
LC
647(define (service-back-edges services)
648 "Return a procedure that, when passed a <service>, returns the list of
649<service> objects that depend on it."
650 (define (add-edges service edges)
651 (define (add-edge extension edges)
652 (let ((target-type (service-extension-target extension)))
653 (match (filter (lambda (service)
654 (eq? (service-kind service) target-type))
655 services)
656 ((target)
657 (vhash-consq target service edges))
658 (()
d466b1fc 659 (missing-target-error service target-type))
0adfe95a
LC
660 (x
661 (raise
662 (condition (&ambiguous-target-service-error
663 (service service)
664 (target-type target-type))
665 (&message
666 (message
667 (format #f
69daee23 668 (G_ "more than one target service of type '~a'")
0adfe95a
LC
669 (service-type-name target-type))))))))))
670
671 (fold add-edge edges (service-type-extensions (service-kind service))))
672
673 (let ((edges (fold add-edges vlist-null services)))
674 (lambda (node)
675 (reverse (vhash-foldq* cons '() node edges)))))
676
d466b1fc
LC
677(define (instantiate-missing-services services)
678 "Return SERVICES, a list, augmented with any services targeted by extensions
679and missing from SERVICES. Only service types with a default value can be
680instantiated; other missing services lead to a
681'&missing-target-service-error'."
682 (define (adjust-service-list svc result instances)
683 (fold2 (lambda (extension result instances)
684 (define target-type
685 (service-extension-target extension))
686
687 (match (vhash-assq target-type instances)
688 (#f
689 (let ((default (service-type-default-value target-type)))
690 (if (eq? &no-default-value default)
691 (missing-target-error svc target-type)
692 (let ((new (service target-type)))
693 (values (cons new result)
694 (vhash-consq target-type new instances))))))
695 (_
696 (values result instances))))
697 result
698 instances
699 (service-type-extensions (service-kind svc))))
700
701 (let ((instances (fold (lambda (service result)
702 (vhash-consq (service-kind service) service
703 result))
704 vlist-null services)))
705 (fold2 adjust-service-list
706 services instances
707 services)))
708
d62e201c
LC
709(define* (fold-services services
710 #:key (target-type system-service-type))
0adfe95a
LC
711 "Fold SERVICES by propagating their extensions down to the root of type
712TARGET-TYPE; return the root service adjusted accordingly."
713 (define dependents
714 (service-back-edges services))
715
716 (define (matching-extension target)
717 (let ((target (service-kind target)))
718 (match-lambda
719 (($ <service-extension> type)
720 (eq? type target)))))
721
722 (define (apply-extension target)
723 (lambda (service)
724 (match (find (matching-extension target)
725 (service-type-extensions (service-kind service)))
726 (($ <service-extension> _ compute)
efe7d19a 727 (compute (service-value service))))))
0adfe95a
LC
728
729 (match (filter (lambda (service)
730 (eq? (service-kind service) target-type))
731 services)
732 ((sink)
733 (let loop ((sink sink))
734 (let* ((dependents (map loop (dependents sink)))
735 (extensions (map (apply-extension sink) dependents))
736 (extend (service-type-extend (service-kind sink)))
737 (compose (service-type-compose (service-kind sink)))
efe7d19a 738 (params (service-value sink)))
0adfe95a
LC
739 ;; We distinguish COMPOSE and EXTEND because PARAMS typically has a
740 ;; different type than the elements of EXTENSIONS.
741 (if extend
742 (service (service-kind sink)
743 (extend params (compose extensions)))
744 sink))))
745 (()
746 (raise
747 (condition (&missing-target-service-error
748 (service #f)
749 (target-type target-type))
750 (&message
69daee23 751 (message (format #f (G_ "service of type '~a' not found")
0adfe95a
LC
752 (service-type-name target-type)))))))
753 (x
754 (raise
755 (condition (&ambiguous-target-service-error
756 (service #f)
757 (target-type target-type))
758 (&message
759 (message
760 (format #f
69daee23 761 (G_ "more than one target service of type '~a'")
0adfe95a 762 (service-type-name target-type)))))))))
db4fdc04
LC
763
764;;; services.scm ends here.