gnu: Add java-jetbrains-annotations.
[jackhill/guix/guix.git] / gnu / services.scm
CommitLineData
db4fdc04 1;;; GNU Guix --- Functional package management for GNU
223ede4e 2;;; Copyright © 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
4d343a14 3;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com>
59bcffa3 4;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
db4fdc04
LC
5;;;
6;;; This file is part of GNU Guix.
7;;;
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.
12;;;
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.
17;;;
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/>.
20
21(define-module (gnu services)
bebc8681 22 #:use-module (guix gexp)
0adfe95a
LC
23 #:use-module (guix monads)
24 #:use-module (guix store)
db4fdc04 25 #:use-module (guix records)
af4c3fd5 26 #:use-module (guix profiles)
0c0c1b21 27 #:use-module (guix discovery)
d466b1fc 28 #:use-module (guix combinators)
33b7cb7a
LC
29 #:use-module (guix channels)
30 #:use-module (guix describe)
0adfe95a
LC
31 #:use-module (guix sets)
32 #:use-module (guix ui)
1bb895ea 33 #:use-module ((guix utils) #:select (source-properties->location))
eb5cf39e 34 #:autoload (guix openpgp) (openpgp-format-fingerprint)
232ccbef 35 #:use-module (guix modules)
0adfe95a
LC
36 #:use-module (gnu packages base)
37 #:use-module (gnu packages bash)
59bcffa3 38 #:use-module (gnu packages hurd)
0adfe95a
LC
39 #:use-module (srfi srfi-1)
40 #:use-module (srfi srfi-9)
41 #:use-module (srfi srfi-9 gnu)
42 #:use-module (srfi srfi-26)
43 #:use-module (srfi srfi-34)
44 #:use-module (srfi srfi-35)
45 #:use-module (ice-9 vlist)
46 #:use-module (ice-9 match)
33b7cb7a 47 #:autoload (ice-9 pretty-print) (pretty-print)
0adfe95a
LC
48 #:export (service-extension
49 service-extension?
7d8b5913
CB
50 service-extension-target
51 service-extension-compute
0adfe95a
LC
52
53 service-type
54 service-type?
5152d13b
LC
55 service-type-name
56 service-type-extensions
57 service-type-compose
58 service-type-extend
1bb895ea 59 service-type-default-value
b714395a
LC
60 service-type-description
61 service-type-location
62
0c0c1b21
LC
63 %service-type-path
64 fold-service-types
49483f71 65 lookup-service-types
0adfe95a 66
db4fdc04 67 service
0adfe95a
LC
68 service?
69 service-kind
efe7d19a
LC
70 service-value
71 service-parameters ;deprecated
0adfe95a 72
71654dfd 73 simple-service
cd6f6c22 74 modify-services
5152d13b 75 service-back-edges
d466b1fc 76 instantiate-missing-services
0adfe95a
LC
77 fold-services
78
79 service-error?
1bb895ea
LC
80 missing-value-service-error?
81 missing-value-service-error-type
82 missing-value-service-error-location
0adfe95a
LC
83 missing-target-service-error?
84 missing-target-service-error-service
85 missing-target-service-error-target-type
86 ambiguous-target-service-error?
87 ambiguous-target-service-error-service
88 ambiguous-target-service-error-target-type
89
d62e201c 90 system-service-type
33b7cb7a 91 provenance-service-type
0adfe95a 92 boot-service-type
be7be9e8 93 cleanup-service-type
0adfe95a
LC
94 activation-service-type
95 activation-service->script
a241a7ac 96 %linux-bare-metal-service
68d8c094
JN
97 %hurd-rc-script
98 %hurd-startup-service
387e1754
LC
99 special-files-service-type
100 extra-special-file
0adfe95a
LC
101 etc-service-type
102 etc-directory
103 setuid-program-service-type
af4c3fd5 104 profile-service-type
0adfe95a 105 firmware-service-type
e0b47290 106 gc-root-service-type
0adfe95a
LC
107
108 %boot-service
109 %activation-service
d298c815 110 etc-service))
0adfe95a
LC
111
112;;; Comment:
113;;;
114;;; This module defines a broad notion of "service types" and "services."
db4fdc04 115;;;
0adfe95a
LC
116;;; A service type describe how its instances extend instances of other
117;;; service types. For instance, some services extend the instance of
118;;; ACCOUNT-SERVICE-TYPE by providing it with accounts and groups to create;
d4053c71
AK
119;;; others extend SHEPHERD-ROOT-SERVICE-TYPE by passing it instances of
120;;; <shepherd-service>.
0adfe95a
LC
121;;;
122;;; When applicable, the service type defines how it can itself be extended,
123;;; by providing one procedure to compose extensions, and one procedure to
124;;; extend itself.
125;;;
d62e201c
LC
126;;; A notable service type is SYSTEM-SERVICE-TYPE, which has a single
127;;; instance, which is the root of the service DAG. Its value is the
128;;; derivation that produces the 'system' directory as returned by
129;;; 'operating-system-derivation'.
0adfe95a
LC
130;;;
131;;; The 'fold-services' procedure can be passed a list of procedures, which it
132;;; "folds" by propagating extensions down the graph; it returns the root
133;;; service after the applying all its extensions.
db4fdc04
LC
134;;;
135;;; Code:
136
0adfe95a
LC
137(define-record-type <service-extension>
138 (service-extension target compute)
139 service-extension?
140 (target service-extension-target) ;<service-type>
141 (compute service-extension-compute)) ;params -> params
142
1bb895ea
LC
143(define &no-default-value
144 ;; Value used to denote service types that have no associated default value.
145 '(no default value))
146
0adfe95a
LC
147(define-record-type* <service-type> service-type make-service-type
148 service-type?
149 (name service-type-name) ;symbol (for debugging)
150
151 ;; Things extended by services of this type.
152 (extensions service-type-extensions) ;list of <service-extensions>
153
154 ;; Given a list of extensions, "compose" them.
155 (compose service-type-compose ;list of Any -> Any
156 (default #f))
157
158 ;; Extend the services' own parameters with the extension composition.
159 (extend service-type-extend ;list of Any -> parameters
1bb895ea
LC
160 (default #f))
161
162 ;; Optional default value for instances of this type.
163 (default-value service-type-default-value ;Any
b714395a
LC
164 (default &no-default-value))
165
166 ;; Meta-data.
167 (description service-type-description ;string
168 (default #f))
169 (location service-type-location ;<location>
170 (default (and=> (current-source-location)
171 source-properties->location))
172 (innate)))
0adfe95a
LC
173
174(define (write-service-type type port)
175 (format port "#<service-type ~a ~a>"
176 (service-type-name type)
177 (number->string (object-address type) 16)))
178
179(set-record-type-printer! <service-type> write-service-type)
180
0c0c1b21
LC
181(define %distro-root-directory
182 ;; Absolute file name of the module hierarchy.
183 (dirname (search-path %load-path "guix.scm")))
184
185(define %service-type-path
186 ;; Search path for service types.
187 (make-parameter `((,%distro-root-directory . "gnu/services")
188 (,%distro-root-directory . "gnu/system"))))
189
3943913f
LC
190(define (all-service-modules)
191 "Return the default set of service modules."
192 (cons (resolve-interface '(gnu services))
3c0128b0
LC
193 (all-modules (%service-type-path)
194 #:warn warn-about-load-error)))
3943913f 195
0c0c1b21
LC
196(define* (fold-service-types proc seed
197 #:optional
3943913f 198 (modules (all-service-modules)))
0c0c1b21
LC
199 "For each service type exported by one of MODULES, call (PROC RESULT). SEED
200is used as the initial value of RESULT."
201 (fold-module-public-variables (lambda (object result)
202 (if (service-type? object)
203 (proc object result)
204 result))
a3d37f3a 205 seed
0c0c1b21
LC
206 modules))
207
49483f71
LC
208(define lookup-service-types
209 (let ((table
210 (delay (fold-service-types (lambda (type result)
211 (vhash-consq (service-type-name type)
212 type result))
213 vlist-null))))
214 (lambda (name)
215 "Return the list of services with the given NAME (a symbol)."
216 (vhash-foldq* cons '() name (force table)))))
217
0adfe95a
LC
218;; Services of a given type.
219(define-record-type <service>
1bb895ea 220 (make-service type value)
db4fdc04 221 service?
0adfe95a 222 (type service-kind)
efe7d19a
LC
223 (value service-value))
224
1bb895ea
LC
225(define-syntax service
226 (syntax-rules ()
227 "Return a service instance of TYPE. The service value is VALUE or, if
228omitted, TYPE's default value."
229 ((_ type value)
230 (make-service type value))
231 ((_ type)
232 (%service-with-default-value (current-source-location)
233 type))))
234
235(define (%service-with-default-value location type)
236 "Return a instance of service type TYPE with its default value, if any. If
237TYPE does not have a default value, an error is raised."
238 ;; TODO: Currently this is a run-time error but with a little bit macrology
239 ;; we could turn it into an expansion-time error.
240 (let ((default (service-type-default-value type)))
241 (if (eq? default &no-default-value)
242 (let ((location (source-properties->location location)))
243 (raise
244 (condition
245 (&missing-value-service-error (type type) (location location))
246 (&message
69daee23 247 (message (format #f (G_ "~a: no value specified \
1bb895ea
LC
248for service of type '~a'")
249 (location->string location)
250 (service-type-name type)))))))
251 (service type default))))
252
253(define-condition-type &service-error &error
254 service-error?)
255
256(define-condition-type &missing-value-service-error &service-error
257 missing-value-service-error?
258 (type missing-value-service-error-type)
259 (location missing-value-service-error-location))
260
261
262\f
263;;;
264;;; Helpers.
265;;;
266
efe7d19a
LC
267(define service-parameters
268 ;; Deprecated alias.
269 service-value)
0adfe95a 270
71654dfd
LC
271(define (simple-service name target value)
272 "Return a service that extends TARGET with VALUE. This works by creating a
273singleton service type NAME, of which the returned service is an instance."
274 (let* ((extension (service-extension target identity))
275 (type (service-type (name name)
276 (extensions (list extension)))))
277 (service type value)))
0adfe95a 278
cd6f6c22
LC
279(define-syntax %modify-service
280 (syntax-rules (=>)
281 ((_ service)
282 service)
283 ((_ svc (kind param => exp ...) clauses ...)
284 (if (eq? (service-kind svc) kind)
efe7d19a 285 (let ((param (service-value svc)))
cd6f6c22
LC
286 (service (service-kind svc)
287 (begin exp ...)))
288 (%modify-service svc clauses ...)))))
289
290(define-syntax modify-services
291 (syntax-rules ()
4d343a14
CM
292 "Modify the services listed in SERVICES according to CLAUSES and return
293the resulting list of services. Each clause must have the form:
cd6f6c22
LC
294
295 (TYPE VARIABLE => BODY)
296
297where TYPE is a service type, such as 'guix-service-type', and VARIABLE is an
298identifier that is bound within BODY to the value of the service of that
299TYPE. Consider this example:
300
301 (modify-services %base-services
302 (guix-service-type config =>
303 (guix-configuration
304 (inherit config)
305 (use-substitutes? #f)
306 (extra-options '(\"--gc-keep-derivations\"))))
307 (mingetty-service-type config =>
308 (mingetty-configuration
309 (inherit config)
310 (motd (plain-file \"motd\" \"Hi there!\")))))
311
312It changes the configuration of the GUIX-SERVICE-TYPE instance, and that of
313all the MINGETTY-SERVICE-TYPE instances.
314
315This is a shorthand for (map (lambda (svc) ...) %base-services)."
316 ((_ services clauses ...)
317 (map (lambda (service)
318 (%modify-service service clauses ...))
319 services))))
0adfe95a
LC
320
321\f
322;;;
323;;; Core services.
324;;;
325
0e5c2d5e 326(define (system-derivation entries mextensions)
d62e201c
LC
327 "Return as a monadic value the derivation of the 'system' directory
328containing the given entries."
0e5c2d5e 329 (mlet %store-monad ((extensions (mapm/accumulate-builds identity
223ede4e 330 mextensions)))
d62e201c
LC
331 (lower-object
332 (file-union "system"
333 (append entries (concatenate extensions))))))
334
335(define system-service-type
336 ;; This is the ultimate service type, the root of the service DAG. The
337 ;; service of this type is extended by monadic name/item pairs. These items
338 ;; end up in the "system directory" as returned by
339 ;; 'operating-system-derivation'.
340 (service-type (name 'system)
341 (extensions '())
342 (compose identity)
636bb2b5
LC
343 (extend system-derivation)
344 (description
345 "Build the operating system top-level directory, which in
346turn refers to everything the operating system needs: its kernel, initrd,
347system profile, boot script, and so on.")))
d62e201c 348
378daa8c
LC
349(define (compute-boot-script _ gexps)
350 ;; Reverse GEXPS so that extensions appear in the boot script in the right
661c237b
LC
351 ;; order. That is, user extensions would come first, and extensions added
352 ;; by 'essential-services' (e.g., running shepherd) are guaranteed to come
353 ;; last.
378daa8c
LC
354 (gexp->file "boot"
355 ;; Clean up and activate the system, then spawn shepherd.
356 #~(begin #$@(reverse gexps))))
0adfe95a 357
d62e201c
LC
358(define (boot-script-entry mboot)
359 "Return, as a monadic value, an entry for the boot script in the system
360directory."
361 (mlet %store-monad ((boot mboot))
362 (return `(("boot" ,boot)))))
363
0adfe95a 364(define boot-service-type
378daa8c
LC
365 ;; The service of this type is extended by being passed gexps. It
366 ;; aggregates them in a single script, as a monadic value, which becomes its
367 ;; value.
0adfe95a 368 (service-type (name 'boot)
d62e201c
LC
369 (extensions
370 (list (service-extension system-service-type
371 boot-script-entry)))
7874e9e0 372 (compose identity)
636bb2b5
LC
373 (extend compute-boot-script)
374 (description
375 "Produce the operating system's boot script, which is spawned
376by the initrd once the root file system is mounted.")))
0adfe95a
LC
377
378(define %boot-service
d62e201c 379 ;; The service that produces the boot script.
0adfe95a 380 (service boot-service-type #t))
be7be9e8 381
33b7cb7a
LC
382\f
383;;;
384;;; Provenance tracking.
385;;;
386
387(define (object->pretty-string obj)
388 "Like 'object->string', but using 'pretty-print'."
389 (call-with-output-string
390 (lambda (port)
391 (pretty-print obj port))))
392
393(define (channel->code channel)
394 "Return code to build CHANNEL, ready to be dropped in a 'channels.scm'
395file."
eb5cf39e
LC
396 ;; Since the 'introduction' field is backward-incompatible, and since it's
397 ;; optional when using the "official" 'guix channel, include it if and only
398 ;; if we're referring to a different channel.
399 (let ((intro (and (not (equal? (list channel) %default-channels))
400 (channel-introduction channel))))
401 `(channel (name ',(channel-name channel))
402 (url ,(channel-url channel))
403 (branch ,(channel-branch channel))
404 (commit ,(channel-commit channel))
405 ,@(if intro
406 `((introduction
407 (make-channel-introduction
408 ,(channel-introduction-first-signed-commit intro)
409 (openpgp-fingerprint
410 ,(openpgp-format-fingerprint
411 (channel-introduction-first-commit-signer
412 intro))))))
413 '()))))
33b7cb7a
LC
414
415(define (channel->sexp channel)
416 "Return an sexp describing CHANNEL. The sexp is _not_ code and is meant to
417be parsed by tools; it's potentially more future-proof than code."
eb5cf39e
LC
418 ;; TODO: Add CHANNEL's introduction. Currently we can't do that because
419 ;; older 'guix system describe' expect exactly name/url/branch/commit
420 ;; without any additional fields.
33b7cb7a
LC
421 `(channel (name ,(channel-name channel))
422 (url ,(channel-url channel))
423 (branch ,(channel-branch channel))
424 (commit ,(channel-commit channel))))
425
426(define (provenance-file channels config-file)
427 "Return a 'provenance' file describing CHANNELS, a list of channels, and
428CONFIG-FILE, which can be either #f or a <local-file> containing the OS
429configuration being used."
430 (scheme-file "provenance"
431 #~(provenance
432 (version 0)
433 (channels #+@(if channels
434 (map channel->sexp channels)
435 '()))
436 (configuration-file #+config-file))))
437
438(define (provenance-entry config-file)
439 "Return system entries describing the operating system provenance: the
440channels in use and CONFIG-FILE, if it is true."
441 (define profile
442 (current-profile))
443
444 (define channels
445 (and=> profile profile-channels))
446
447 (mbegin %store-monad
448 (let ((config-file (cond ((string? config-file)
449 (local-file config-file "configuration.scm"))
450 ((not config-file)
451 #f)
452 (else
453 config-file))))
454 (return `(("provenance" ,(provenance-file channels config-file))
455 ,@(if channels
456 `(("channels.scm"
457 ,(plain-file "channels.scm"
458 (object->pretty-string
459 `(list
460 ,@(map channel->code channels))))))
461 '())
462 ,@(if config-file
463 `(("configuration.scm" ,config-file))
464 '()))))))
465
466(define provenance-service-type
467 (service-type (name 'provenance)
468 (extensions
469 (list (service-extension system-service-type
470 provenance-entry)))
471 (default-value #f) ;the OS config file
472 (description
473 "Store provenance information about the system in the system
474itself: the channels used when building the system, and its configuration
475file, when available.")))
476
477\f
478;;;
479;;; Cleanup.
480;;;
481
be7be9e8 482(define (cleanup-gexp _)
378daa8c
LC
483 "Return a gexp to clean up /tmp and similar places upon boot."
484 (with-imported-modules '((guix build utils))
485 #~(begin
486 (use-modules (guix build utils))
487
488 ;; Clean out /tmp and /var/run.
489 ;;
490 ;; XXX This needs to happen before service activations, so it
491 ;; has to be here, but this also implicitly assumes that /tmp
492 ;; and /var/run are on the root partition.
493 (letrec-syntax ((fail-safe (syntax-rules ()
494 ((_ exp rest ...)
495 (begin
496 (catch 'system-error
497 (lambda () exp)
498 (const #f))
499 (fail-safe rest ...)))
500 ((_)
501 #t))))
502 ;; Ignore I/O errors so the system can boot.
503 (fail-safe
504 ;; Remove stale Shadow lock files as they would lead to
505 ;; failures of 'useradd' & co.
506 (delete-file "/etc/group.lock")
507 (delete-file "/etc/passwd.lock")
508 (delete-file "/etc/.pwd.lock") ;from 'lckpwdf'
509
510 ;; Force file names to be decoded as UTF-8. See
511 ;; <https://bugs.gnu.org/26353>.
512 (setenv "GUIX_LOCPATH"
513 #+(file-append glibc-utf8-locales "/lib/locale"))
514 (setlocale LC_CTYPE "en_US.utf8")
515 (delete-file-recursively "/tmp")
516 (delete-file-recursively "/var/run")
517
518 (mkdir "/tmp")
519 (chmod "/tmp" #o1777)
520 (mkdir "/var/run")
521 (chmod "/var/run" #o755)
522 (delete-file-recursively "/run/udev/watch.old"))))))
be7be9e8
LC
523
524(define cleanup-service-type
525 ;; Service that cleans things up in /tmp and similar.
526 (service-type (name 'cleanup)
527 (extensions
528 (list (service-extension boot-service-type
636bb2b5
LC
529 cleanup-gexp)))
530 (description
531 "Delete files from @file{/tmp}, @file{/var/run}, and other
532temporary locations at boot time.")))
0adfe95a 533
0adfe95a
LC
534(define* (activation-service->script service)
535 "Return as a monadic value the activation script for SERVICE, a service of
536ACTIVATION-SCRIPT-TYPE."
efe7d19a 537 (activation-script (service-value service)))
0adfe95a
LC
538
539(define (activation-script gexps)
540 "Return the system's activation script, which evaluates GEXPS."
378daa8c 541 (define actions
03cbd94d
JK
542 (map (cut program-file "activate-service.scm" <>) gexps))
543
544 (program-file "activate.scm"
545 (with-imported-modules (source-module-closure
546 '((gnu build activation)
547 (guix build utils)))
548 #~(begin
549 (use-modules (gnu build activation)
550 (guix build utils))
551
552 ;; Make sure the user accounting database exists. If it
553 ;; does not exist, 'setutxent' does not create it and
554 ;; thus there is no accounting at all.
555 (close-port (open-file "/var/run/utmpx" "a0"))
556
557 ;; Same for 'wtmp', which is populated by mingetty et
558 ;; al.
559 (mkdir-p "/var/log")
560 (close-port (open-file "/var/log/wtmp" "a0"))
561
562 ;; Set up /run/current-system. Among other things this
563 ;; sets up locales, which the activation snippets
564 ;; executed below may expect.
565 (activate-current-system)
566
567 ;; Run the services' activation snippets.
568 ;; TODO: Use 'load-compiled'.
569 (for-each primitive-load '#$actions)))))
0adfe95a
LC
570
571(define (gexps->activation-gexp gexps)
572 "Return a gexp that runs the activation script containing GEXPS."
378daa8c 573 #~(primitive-load #$(activation-script gexps)))
0adfe95a 574
3a391e68
LC
575(define (second-argument a b) b)
576
0adfe95a
LC
577(define activation-service-type
578 (service-type (name 'activate)
579 (extensions
580 (list (service-extension boot-service-type
581 gexps->activation-gexp)))
7874e9e0 582 (compose identity)
636bb2b5
LC
583 (extend second-argument)
584 (description
585 "Run @dfn{activation} code at boot time and upon
586@command{guix system reconfigure} completion.")))
0adfe95a
LC
587
588(define %activation-service
589 ;; The activation service produces the activation script from the gexps it
590 ;; receives.
591 (service activation-service-type #t))
592
a241a7ac
LC
593(define %modprobe-wrapper
594 ;; Wrapper for the 'modprobe' command that knows where modules live.
595 ;;
596 ;; This wrapper is typically invoked by the Linux kernel ('call_modprobe',
597 ;; in kernel/kmod.c), a situation where the 'LINUX_MODULE_DIRECTORY'
598 ;; environment variable is not set---hence the need for this wrapper.
599 (let ((modprobe "/run/current-system/profile/bin/modprobe"))
600 (program-file "modprobe"
601 #~(begin
602 (setenv "LINUX_MODULE_DIRECTORY"
603 "/run/booted-system/kernel/lib/modules")
8c88e242
BW
604 ;; FIXME: Remove this crutch when the patch #40422,
605 ;; updating to kmod 27 is merged.
606 (setenv "MODPROBE_OPTIONS"
607 "-C /etc/modprobe.d")
a241a7ac
LC
608 (apply execl #$modprobe
609 (cons #$modprobe (cdr (command-line))))))))
610
611(define %linux-kernel-activation
612 ;; Activation of the Linux kernel running on the bare metal (as opposed to
613 ;; running in a container.)
614 #~(begin
615 ;; Tell the kernel to use our 'modprobe' command.
616 (activate-modprobe #$%modprobe-wrapper)
617
618 ;; Let users debug their own processes!
619 (activate-ptrace-attach)))
620
a241a7ac
LC
621(define %linux-bare-metal-service
622 ;; The service that does things that are needed on the "bare metal", but not
623 ;; necessary or impossible in a container.
6ddf4fcf
LC
624 (simple-service 'linux-bare-metal
625 activation-service-type
626 %linux-kernel-activation))
627
68d8c094
JN
628(define %hurd-rc-script
629 ;; The RC script to be started upon boot.
630 (program-file "rc"
631 (with-imported-modules (source-module-closure
632 '((guix build utils)
633 (gnu build hurd-boot)
634 (guix build syscalls)))
635 #~(begin
636 (use-modules (guix build utils)
637 (gnu build hurd-boot)
638 (guix build syscalls)
639 (ice-9 match)
640 (system repl repl)
641 (srfi srfi-1)
642 (srfi srfi-26))
643 (boot-hurd-system)))))
644
645(define (hurd-rc-entry rc)
646 "Return, as a monadic value, an entry for the RC script in the system
647directory."
648 (mlet %store-monad ((rc (lower-object rc)))
649 (return `(("rc" ,rc)))))
650
651(define hurd-startup-service-type
652 ;; The service that creates the initial SYSTEM/rc startup file.
653 (service-type (name 'startup)
654 (extensions
655 (list (service-extension system-service-type hurd-rc-entry)))
656 (default-value %hurd-rc-script)))
657
658(define %hurd-startup-service
659 ;; The service that produces the RC script.
660 (service hurd-startup-service-type %hurd-rc-script))
a241a7ac 661
387e1754
LC
662(define special-files-service-type
663 ;; Service to install "special files" such as /bin/sh and /usr/bin/env.
664 (service-type
665 (name 'special-files)
666 (extensions
667 (list (service-extension activation-service-type
668 (lambda (files)
669 #~(activate-special-files '#$files)))))
670 (compose concatenate)
636bb2b5
LC
671 (extend append)
672 (description
673 "Add special files to the root file system---e.g.,
674@file{/usr/bin/env}.")))
387e1754
LC
675
676(define (extra-special-file file target)
677 "Use TARGET as the \"special file\" FILE. For example, TARGET might be
678 (file-append coreutils \"/bin/env\")
679and FILE could be \"/usr/bin/env\"."
680 (simple-service (string->symbol (string-append "special-file-" file))
681 special-files-service-type
682 `((,file ,target))))
683
0adfe95a
LC
684(define (etc-directory service)
685 "Return the directory for SERVICE, a service of type ETC-SERVICE-TYPE."
efe7d19a 686 (files->etc-directory (service-value service)))
0adfe95a
LC
687
688(define (files->etc-directory files)
a322e9d1
LC
689 (define (assert-no-duplicates files)
690 (let loop ((files files)
691 (seen (set)))
692 (match files
693 (() #t)
694 (((file _) rest ...)
695 (when (set-contains? seen file)
696 (raise (condition
697 (&message
698 (message (format #f (G_ "duplicate '~a' entry for /etc")
699 file))))))
700 (loop rest (set-insert file seen))))))
701
702 ;; Detect duplicates early instead of letting them through, eventually
703 ;; leading to a build failure of "etc.drv".
704 (assert-no-duplicates files)
705
0adfe95a
LC
706 (file-union "etc" files))
707
d62e201c
LC
708(define (etc-entry files)
709 "Return an entry for the /etc directory consisting of FILES in the system
710directory."
711 (with-monad %store-monad
712 (return `(("etc" ,(files->etc-directory files))))))
713
0adfe95a
LC
714(define etc-service-type
715 (service-type (name 'etc)
716 (extensions
717 (list
718 (service-extension activation-service-type
719 (lambda (files)
720 (let ((etc
721 (files->etc-directory files)))
d62e201c
LC
722 #~(activate-etc #$etc))))
723 (service-extension system-service-type etc-entry)))
0adfe95a 724 (compose concatenate)
636bb2b5
LC
725 (extend append)
726 (description "Populate the @file{/etc} directory.")))
0adfe95a
LC
727
728(define (etc-service files)
729 "Return a new service of ETC-SERVICE-TYPE that populates /etc with FILES.
730FILES must be a list of name/file-like object pairs."
731 (service etc-service-type files))
732
733(define setuid-program-service-type
734 (service-type (name 'setuid-program)
735 (extensions
736 (list (service-extension activation-service-type
737 (lambda (programs)
738 #~(activate-setuid-programs
739 (list #$@programs))))))
740 (compose concatenate)
636bb2b5
LC
741 (extend append)
742 (description
743 "Populate @file{/run/setuid-programs} with the specified
744executables, making them setuid-root.")))
0adfe95a 745
af4c3fd5
LC
746(define (packages->profile-entry packages)
747 "Return a system entry for the profile containing PACKAGES."
45bd9133
LC
748 (with-monad %store-monad
749 (return `(("profile" ,(profile
750 (content (packages->manifest
751 (delete-duplicates packages eq?)))))))))
af4c3fd5
LC
752
753(define profile-service-type
754 ;; The service that populates the system's profile---i.e.,
755 ;; /run/current-system/profile. It is extended by package lists.
756 (service-type (name 'profile)
757 (extensions
758 (list (service-extension system-service-type
759 packages->profile-entry)))
760 (compose concatenate)
636bb2b5
LC
761 (extend append)
762 (description
763 "This is the @dfn{system profile}, available as
764@file{/run/current-system/profile}. It contains packages that the sysadmin
765wants to be globally available to all the system users.")))
af4c3fd5 766
0adfe95a
LC
767(define (firmware->activation-gexp firmware)
768 "Return a gexp to make the packages listed in FIRMWARE loadable by the
769kernel."
770 (let ((directory (directory-union "firmware" firmware)))
771 ;; Tell the kernel where firmware is.
772 #~(activate-firmware (string-append #$directory "/lib/firmware"))))
773
774(define firmware-service-type
775 ;; The service that collects firmware.
776 (service-type (name 'firmware)
777 (extensions
778 (list (service-extension activation-service-type
779 firmware->activation-gexp)))
780 (compose concatenate)
636bb2b5
LC
781 (extend append)
782 (description
783 "Make ``firmware'' files loadable by the operating system
784kernel. Firmware may then be uploaded to some of the machine's devices, such
785as Wifi cards.")))
0adfe95a 786
e0b47290
LC
787(define (gc-roots->system-entry roots)
788 "Return an entry in the system's output containing symlinks to ROOTS."
789 (mlet %store-monad ((entry (gexp->derivation
790 "gc-roots"
791 #~(let ((roots '#$roots))
792 (mkdir #$output)
793 (chdir #$output)
794 (for-each symlink
795 roots
796 (map number->string
797 (iota (length roots))))))))
798 (return (if (null? roots)
799 '()
800 `(("gc-roots" ,entry))))))
801
802(define gc-root-service-type
803 ;; A service to associate extra garbage-collector roots to the system. This
804 ;; is a simple hack that guarantees that the system retains references to
805 ;; the given list of roots. Roots must be "lowerable" objects like
806 ;; packages, or derivations.
807 (service-type (name 'gc-roots)
808 (extensions
809 (list (service-extension system-service-type
810 gc-roots->system-entry)))
811 (compose concatenate)
636bb2b5
LC
812 (extend append)
813 (description
814 "Register garbage-collector roots---i.e., store items that
dc5729a8
LC
815will not be reclaimed by the garbage collector.")
816 (default-value '())))
e0b47290 817
0adfe95a
LC
818\f
819;;;
820;;; Service folding.
821;;;
822
0adfe95a
LC
823(define-condition-type &missing-target-service-error &service-error
824 missing-target-service-error?
825 (service missing-target-service-error-service)
826 (target-type missing-target-service-error-target-type))
827
828(define-condition-type &ambiguous-target-service-error &service-error
829 ambiguous-target-service-error?
830 (service ambiguous-target-service-error-service)
831 (target-type ambiguous-target-service-error-target-type))
832
d466b1fc
LC
833(define (missing-target-error service target-type)
834 (raise
835 (condition (&missing-target-service-error
836 (service service)
837 (target-type target-type))
838 (&message
839 (message
840 (format #f (G_ "no target of type '~a' for service '~a'")
841 (service-type-name target-type)
842 (service-type-name
843 (service-kind service))))))))
844
0adfe95a
LC
845(define (service-back-edges services)
846 "Return a procedure that, when passed a <service>, returns the list of
847<service> objects that depend on it."
848 (define (add-edges service edges)
849 (define (add-edge extension edges)
850 (let ((target-type (service-extension-target extension)))
851 (match (filter (lambda (service)
852 (eq? (service-kind service) target-type))
853 services)
854 ((target)
855 (vhash-consq target service edges))
856 (()
d466b1fc 857 (missing-target-error service target-type))
0adfe95a
LC
858 (x
859 (raise
860 (condition (&ambiguous-target-service-error
861 (service service)
862 (target-type target-type))
863 (&message
864 (message
865 (format #f
69daee23 866 (G_ "more than one target service of type '~a'")
0adfe95a
LC
867 (service-type-name target-type))))))))))
868
869 (fold add-edge edges (service-type-extensions (service-kind service))))
870
871 (let ((edges (fold add-edges vlist-null services)))
872 (lambda (node)
873 (reverse (vhash-foldq* cons '() node edges)))))
874
d466b1fc
LC
875(define (instantiate-missing-services services)
876 "Return SERVICES, a list, augmented with any services targeted by extensions
877and missing from SERVICES. Only service types with a default value can be
878instantiated; other missing services lead to a
879'&missing-target-service-error'."
880 (define (adjust-service-list svc result instances)
881 (fold2 (lambda (extension result instances)
882 (define target-type
883 (service-extension-target extension))
884
885 (match (vhash-assq target-type instances)
886 (#f
887 (let ((default (service-type-default-value target-type)))
888 (if (eq? &no-default-value default)
889 (missing-target-error svc target-type)
890 (let ((new (service target-type)))
891 (values (cons new result)
892 (vhash-consq target-type new instances))))))
893 (_
894 (values result instances))))
895 result
896 instances
897 (service-type-extensions (service-kind svc))))
898
9b6c4355
LC
899 (let loop ((services services))
900 (define instances
901 (fold (lambda (service result)
902 (vhash-consq (service-kind service) service
903 result))
904 vlist-null services))
905
906 (define adjusted
907 (fold2 adjust-service-list
908 services instances
909 services))
910
911 ;; If we instantiated services, they might in turn depend on missing
912 ;; services. Loop until we've reached fixed point.
913 (if (= (length adjusted) (vlist-length instances))
914 adjusted
915 (loop adjusted))))
d466b1fc 916
d62e201c
LC
917(define* (fold-services services
918 #:key (target-type system-service-type))
0adfe95a
LC
919 "Fold SERVICES by propagating their extensions down to the root of type
920TARGET-TYPE; return the root service adjusted accordingly."
921 (define dependents
922 (service-back-edges services))
923
924 (define (matching-extension target)
925 (let ((target (service-kind target)))
926 (match-lambda
927 (($ <service-extension> type)
928 (eq? type target)))))
929
930 (define (apply-extension target)
931 (lambda (service)
932 (match (find (matching-extension target)
933 (service-type-extensions (service-kind service)))
934 (($ <service-extension> _ compute)
efe7d19a 935 (compute (service-value service))))))
0adfe95a
LC
936
937 (match (filter (lambda (service)
938 (eq? (service-kind service) target-type))
939 services)
940 ((sink)
2a4309de
LC
941 ;; Use the state monad to keep track of already-visited services in the
942 ;; graph and to memoize their value once folded.
943 (run-with-state
944 (let loop ((sink sink))
945 (mlet %state-monad ((visited (current-state)))
946 (match (vhash-assq sink visited)
947 (#f
948 (mlet* %state-monad
949 ((dependents (mapm %state-monad loop (dependents sink)))
950 (visited (current-state))
951 (extensions -> (map (apply-extension sink) dependents))
952 (extend -> (service-type-extend (service-kind sink)))
953 (compose -> (service-type-compose (service-kind sink)))
954 (params -> (service-value sink))
955 (service
956 ->
957 ;; Distinguish COMPOSE and EXTEND because PARAMS typically
958 ;; has a different type than the elements of EXTENSIONS.
959 (if extend
960 (service (service-kind sink)
961 (extend params (compose extensions)))
962 sink)))
963 (mbegin %state-monad
964 (set-current-state (vhash-consq sink service visited))
965 (return service))))
966 ((_ . service) ;SINK was already visited
967 (return service)))))
968 vlist-null))
0adfe95a
LC
969 (()
970 (raise
971 (condition (&missing-target-service-error
972 (service #f)
973 (target-type target-type))
974 (&message
69daee23 975 (message (format #f (G_ "service of type '~a' not found")
0adfe95a
LC
976 (service-type-name target-type)))))))
977 (x
978 (raise
979 (condition (&ambiguous-target-service-error
980 (service #f)
981 (target-type target-type))
982 (&message
983 (message
984 (format #f
69daee23 985 (G_ "more than one target service of type '~a'")
0adfe95a 986 (service-type-name target-type)))))))))
db4fdc04
LC
987
988;;; services.scm ends here.