gnu: Add cxxopts.
[jackhill/guix/guix.git] / gnu / services / shepherd.scm
CommitLineData
db4fdc04 1;;; GNU Guix --- Functional package management for GNU
8b9cad01 2;;; Copyright © 2013, 2014, 2015, 2016, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
750a4239 3;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org>
4245ddcb 4;;; Copyright © 2018 Carlo Zancanaro <carlo@zancanaro.id.au>
d2fc7646 5;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
db4fdc04
LC
6;;;
7;;; This file is part of GNU Guix.
8;;;
9;;; GNU Guix is free software; you can redistribute it and/or modify it
10;;; under the terms of the GNU General Public License as published by
11;;; the Free Software Foundation; either version 3 of the License, or (at
12;;; your option) any later version.
13;;;
14;;; GNU Guix is distributed in the hope that it will be useful, but
15;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;;; GNU General Public License for more details.
18;;;
19;;; You should have received a copy of the GNU General Public License
20;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21
0190c1c0 22(define-module (gnu services shepherd)
116244df
LC
23 #:use-module (guix ui)
24 #:use-module (guix sets)
b5f4e686 25 #:use-module (guix gexp)
e87f0591 26 #:use-module (guix store)
0adfe95a 27 #:use-module (guix records)
e87f0591 28 #:use-module (guix derivations) ;imported-modules, etc.
d2fc7646 29 #:use-module (guix utils)
db4fdc04 30 #:use-module (gnu services)
7b44cae5 31 #:use-module (gnu services herd)
0adfe95a 32 #:use-module (gnu packages admin)
db4fdc04 33 #:use-module (ice-9 match)
80a67734 34 #:use-module (ice-9 vlist)
db4fdc04 35 #:use-module (srfi srfi-1)
80a67734 36 #:use-module (srfi srfi-26)
116244df
LC
37 #:use-module (srfi srfi-34)
38 #:use-module (srfi srfi-35)
d4053c71
AK
39 #:export (shepherd-root-service-type
40 %shepherd-root-service
41 shepherd-service-type
42
43 shepherd-service
44 shepherd-service?
45 shepherd-service-documentation
46 shepherd-service-provision
240b57f0 47 shepherd-service-canonical-name
d4053c71 48 shepherd-service-requirement
95ef8b85 49 shepherd-service-one-shot?
d4053c71
AK
50 shepherd-service-respawn?
51 shepherd-service-start
52 shepherd-service-stop
53 shepherd-service-auto-start?
54 shepherd-service-modules
fae685b9 55
70138308
LC
56 shepherd-action
57 shepherd-action?
58 shepherd-action-name
59 shepherd-action-documentation
60 shepherd-action-procedure
61
fae685b9 62 %default-modules
80a67734 63
240b57f0
LC
64 shepherd-service-file
65
a5d78eb6 66 shepherd-service-lookup-procedure
7b44cae5 67 shepherd-service-back-edges
10c41368
LC
68 shepherd-service-upgrade
69
70 user-processes-service-type))
db4fdc04
LC
71
72;;; Commentary:
73;;;
fe1ad5f5 74;;; Instantiating system services as a shepherd configuration file.
db4fdc04
LC
75;;;
76;;; Code:
77
0adfe95a 78
d4053c71 79(define (shepherd-boot-gexp services)
378daa8c
LC
80 #~(begin
81 ;; Keep track of the booted system.
82 (false-if-exception (delete-file "/run/booted-system"))
83 (symlink (readlink "/run/current-system")
84 "/run/booted-system")
0adfe95a 85
378daa8c
LC
86 ;; Close any remaining open file descriptors to be on the safe
87 ;; side. This must be the very last thing we do, because
88 ;; Guile has internal FDs such as 'sleep_pipe' that need to be
89 ;; alive.
90 (let loop ((fd 3))
91 (when (< fd 1024)
92 (false-if-exception (close-fdes fd))
93 (loop (+ 1 fd))))
0adfe95a 94
378daa8c
LC
95 ;; Start shepherd.
96 (execl #$(file-append shepherd "/bin/shepherd")
97 "shepherd" "--config"
98 #$(shepherd-configuration-file services))))
0adfe95a 99
d4053c71 100(define shepherd-root-service-type
0adfe95a 101 (service-type
d4053c71
AK
102 (name 'shepherd-root)
103 ;; Extending the root shepherd service (aka. PID 1) happens by
104 ;; concatenating the list of services provided by the extensions.
0adfe95a
LC
105 (compose concatenate)
106 (extend append)
d4053c71
AK
107 (extensions (list (service-extension boot-service-type
108 shepherd-boot-gexp)
c273d81b 109 (service-extension profile-service-type
dd0804c6
LC
110 (const (list shepherd)))))
111 (description
112 "Run the GNU Shepherd as PID 1---i.e., the operating system's first
113process. The Shepherd takes care of managing services such as daemons by
114ensuring they are started and stopped in the right order.")))
0adfe95a 115
d4053c71
AK
116(define %shepherd-root-service
117 ;; The root shepherd service, aka. PID 1. Its parameter is a list of
118 ;; <shepherd-service> objects.
119 (service shepherd-root-service-type '()))
0adfe95a 120
88cd7bbd
LC
121(define-syntax shepherd-service-type
122 (syntax-rules ()
123 "Return a <service-type> denoting a simple shepherd service--i.e., the type
124for a service that extends SHEPHERD-ROOT-SERVICE-TYPE and nothing else. When
125DEFAULT is given, use it as the service's default value."
126 ((_ service-name proc default)
127 (service-type
128 (name service-name)
129 (extensions
130 (list (service-extension shepherd-root-service-type
131 (compose list proc))))
132 (default-value default)))
133 ((_ service-name proc)
134 (service-type
135 (name service-name)
136 (extensions
137 (list (service-extension shepherd-root-service-type
138 (compose list proc))))))))
0adfe95a 139
fae685b9
LC
140(define %default-imported-modules
141 ;; Default set of modules imported for a service's consumption.
142 '((guix build utils)
479b417b 143 (guix build syscalls)))
fae685b9
LC
144
145(define %default-modules
146 ;; Default set of modules visible in a service's file.
34044d55 147 `((shepherd service)
fae685b9 148 (oop goops)
408ae72c 149 ((guix build utils) #:hide (delete))
479b417b 150 (guix build syscalls)))
fae685b9 151
d4053c71
AK
152(define-record-type* <shepherd-service>
153 shepherd-service make-shepherd-service
154 shepherd-service?
155 (documentation shepherd-service-documentation ;string
0adfe95a 156 (default "[No documentation.]"))
d4053c71
AK
157 (provision shepherd-service-provision) ;list of symbols
158 (requirement shepherd-service-requirement ;list of symbols
0adfe95a 159 (default '()))
95ef8b85
LC
160 (one-shot? shepherd-service-one-shot? ;Boolean
161 (default #f))
d4053c71 162 (respawn? shepherd-service-respawn? ;Boolean
0adfe95a 163 (default #t))
d4053c71
AK
164 (start shepherd-service-start) ;g-expression (procedure)
165 (stop shepherd-service-stop ;g-expression (procedure)
0adfe95a 166 (default #~(const #f)))
70138308
LC
167 (actions shepherd-service-actions ;list of <shepherd-action>
168 (default '()))
d4053c71 169 (auto-start? shepherd-service-auto-start? ;Boolean
fae685b9 170 (default #t))
d4053c71 171 (modules shepherd-service-modules ;list of module names
a91c3fc7 172 (default %default-modules)))
0adfe95a 173
70138308
LC
174(define-record-type* <shepherd-action>
175 shepherd-action make-shepherd-action
176 shepherd-action?
177 (name shepherd-action-name) ;symbol
178 (procedure shepherd-action-procedure) ;gexp
179 (documentation shepherd-action-documentation)) ;string
180
240b57f0
LC
181(define (shepherd-service-canonical-name service)
182 "Return the 'canonical name' of SERVICE."
183 (first (shepherd-service-provision service)))
0adfe95a 184
2d2651e7 185(define (assert-valid-graph services)
d4053c71
AK
186 "Raise an error if SERVICES does not define a valid shepherd service graph,
187for instance if a service requires a nonexistent service, or if more than one
2d2651e7 188service uses a given name.
116244df 189
d4053c71
AK
190These are constraints that shepherd's 'register-service' verifies but we'd
191better verify them here statically than wait until PID 1 halts with an
192assertion failure."
2d2651e7
LC
193 (define provisions
194 ;; The set of provisions (symbols). Bail out if a symbol is given more
195 ;; than once.
196 (fold (lambda (service set)
197 (define (assert-unique symbol)
198 (when (set-contains? set symbol)
199 (raise (condition
200 (&message
201 (message
69daee23 202 (format #f (G_ "service '~a' provided more than once")
2d2651e7
LC
203 symbol)))))))
204
d4053c71
AK
205 (for-each assert-unique (shepherd-service-provision service))
206 (fold set-insert set (shepherd-service-provision service)))
207 (setq 'shepherd)
2d2651e7
LC
208 services))
209
210 (define (assert-satisfied-requirements service)
211 ;; Bail out if the requirements of SERVICE aren't satisfied.
212 (for-each (lambda (requirement)
213 (unless (set-contains? provisions requirement)
214 (raise (condition
215 (&message
216 (message
69daee23 217 (format #f (G_ "service '~a' requires '~a', \
2c2ec261 218which is not provided by any service")
d4053c71 219 (match (shepherd-service-provision service)
2d2651e7
LC
220 ((head . _) head)
221 (_ service))
222 requirement)))))))
d4053c71 223 (shepherd-service-requirement service)))
2d2651e7
LC
224
225 (for-each assert-satisfied-requirements services))
116244df 226
d4053c71 227(define (shepherd-service-file-name service)
fae685b9
LC
228 "Return the file name where the initialization code for SERVICE is to be
229stored."
230 (let ((provisions (string-join (map symbol->string
d4053c71
AK
231 (shepherd-service-provision service)))))
232 (string-append "shepherd-"
fae685b9
LC
233 (string-map (match-lambda
234 (#\/ #\-)
750a4239 235 (#\ #\-)
fae685b9
LC
236 (chr chr))
237 provisions)
238 ".scm")))
239
d4053c71 240(define (shepherd-service-file service)
fae685b9 241 "Return a file defining SERVICE."
33033a62
LC
242 (scheme-file (shepherd-service-file-name service)
243 (with-imported-modules %default-imported-modules
244 #~(begin
245 (use-modules #$@(shepherd-service-modules service))
246
247 (make <service>
248 #:docstring '#$(shepherd-service-documentation service)
249 #:provides '#$(shepherd-service-provision service)
250 #:requires '#$(shepherd-service-requirement service)
95ef8b85
LC
251
252 ;; The 'one-shot?' slot is new in Shepherd 0.6.0.
253 ;; Older versions ignore it.
254 #:one-shot? '#$(shepherd-service-one-shot? service)
255
33033a62
LC
256 #:respawn? '#$(shepherd-service-respawn? service)
257 #:start #$(shepherd-service-start service)
70138308
LC
258 #:stop #$(shepherd-service-stop service)
259 #:actions
260 (make-actions
261 #$@(map (match-lambda
262 (($ <shepherd-action> name proc doc)
263 #~(#$name #$doc #$proc)))
264 (shepherd-service-actions service))))))))
fae685b9 265
63b0ce39
LC
266(define (scm->go file)
267 "Compile FILE, which contains code to be loaded by shepherd's config file,
268and return the resulting '.go' file."
5e9cf933 269 (let-system (system target)
d2fc7646
JN
270 (with-extensions (list shepherd)
271 (computed-file (string-append (basename (scheme-file-name file) ".scm")
272 ".go")
273 #~(begin
274 (use-modules (system base compile)
275 (system base target))
276
277 ;; Do the same as the Shepherd's 'load-in-user-module'.
278 (let ((env (make-fresh-user-module)))
279 (module-use! env (resolve-interface '(oop goops)))
280 (module-use! env (resolve-interface '(shepherd service)))
281 (with-target #$(or target #~%host-type)
282 (lambda _
283 (compile-file #$file #:output-file #$output
284 #:env env)))))
285
286 ;; It's faster to build locally than to download.
287 #:options '(#:local-build? #t
288 #:substitutable? #f)))))
63b0ce39 289
fe1ad5f5
AK
290(define (shepherd-configuration-file services)
291 "Return the shepherd configuration file for SERVICES."
2d2651e7 292 (assert-valid-graph services)
116244df 293
33033a62 294 (let ((files (map shepherd-service-file services)))
23ed63a1
LC
295 (define config
296 #~(begin
081bd3bd
LC
297 (use-modules (srfi srfi-34)
298 (system repl error-handling))
b9c7ed71 299
8b9cad01
LC
300 ;; Specify the default environment visible to all the services.
301 ;; Without this statement, all the environment variables of PID 1
302 ;; are inherited by child services.
303 (default-environment-variables
304 '("PATH=/run/current-system/profile/bin"))
305
8aa752ba
LC
306 ;; Booting off a DVD, especially on a slow machine, can make
307 ;; everything slow. Thus, increase the timeout compared to the
308 ;; default 5s in the Shepherd 0.7.0. See
309 ;; <https://bugs.gnu.org/40572>.
e3358a83 310 (default-pid-file-timeout 30)
8aa752ba 311
234ea8a7
LC
312 ;; Arrange to spawn a REPL if something goes wrong. This is better
313 ;; than a kernel panic.
b9c7ed71
LC
314 (call-with-error-handling
315 (lambda ()
63b0ce39 316 (apply register-services
408ae72c
LC
317 (parameterize ((current-warning-port
318 (%make-void-port "w")))
319 (map load-compiled '#$(map scm->go files))))))
63b0ce39 320
63b0ce39
LC
321 (format #t "starting services...~%")
322 (for-each (lambda (service)
323 ;; In the Shepherd 0.3 the 'start' method can raise
324 ;; '&action-runtime-error' if it fails, so protect
325 ;; against it. (XXX: 'action-runtime-error?' is not
326 ;; exported is 0.3, hence 'service-error?'.)
327 (guard (c ((service-error? c)
328 (format (current-error-port)
329 "failed to start service '~a'~%"
330 service)))
331 (start service)))
332 '#$(append-map shepherd-service-provision
333 (filter shepherd-service-auto-start?
334 services)))
335
336 ;; Hang up stdin. At this point, we assume that 'start' methods
337 ;; that required user interaction on the console (e.g.,
338 ;; 'cryptsetup open' invocations, post-fsck emergency REPL) have
339 ;; completed. User interaction becomes impossible after this
340 ;; call; this avoids situations where services wrongfully lead
341 ;; PID 1 to read from stdin (the console), which users may not
342 ;; have access to (see <https://bugs.gnu.org/23697>).
343 (redirect-port (open-input-file "/dev/null")
344 (current-input-port))))
23ed63a1 345
33033a62 346 (scheme-file "shepherd.conf" config)))
db4fdc04 347
a5d78eb6
LC
348(define* (shepherd-service-lookup-procedure services
349 #:optional
350 (provision
351 shepherd-service-provision))
352 "Return a procedure that, when passed a symbol, return the item among
353SERVICES that provides this symbol. PROVISION must be a one-argument
354procedure that takes a service and returns the list of symbols it provides."
355 (let ((services (fold (lambda (service result)
356 (fold (cut vhash-consq <> service <>)
357 result
358 (provision service)))
359 vlist-null
360 services)))
361 (lambda (name)
362 (match (vhash-assq name services)
363 ((_ . service) service)
364 (#f #f)))))
365
6673bddc
LC
366(define* (shepherd-service-back-edges services
367 #:key
368 (provision shepherd-service-provision)
369 (requirement shepherd-service-requirement))
d4053c71 370 "Return a procedure that, when given a <shepherd-service> from SERVICES,
6673bddc
LC
371returns the list of <shepherd-service> that depend on it.
372
373Use PROVISION and REQUIREMENT as one-argument procedures that return the
374symbols provided/required by a service."
80a67734 375 (define provision->service
6673bddc 376 (shepherd-service-lookup-procedure services provision))
80a67734
LC
377
378 (define edges
379 (fold (lambda (service edges)
380 (fold (lambda (requirement edges)
381 (vhash-consq (provision->service requirement) service
382 edges))
383 edges
6673bddc 384 (requirement service)))
80a67734
LC
385 vlist-null
386 services))
387
388 (lambda (service)
389 (vhash-foldq* cons '() service edges)))
390
7b44cae5
LC
391(define (shepherd-service-upgrade live target)
392 "Return two values: the subset of LIVE (a list of <live-service>) that needs
393to be unloaded, and the subset of TARGET (a list of <shepherd-service>) that
4245ddcb 394need to be restarted to complete their upgrade."
7b44cae5
LC
395 (define (essential? service)
396 (memq (first (live-service-provision service))
397 '(root shepherd)))
398
399 (define lookup-target
400 (shepherd-service-lookup-procedure target
401 shepherd-service-provision))
402
403 (define lookup-live
404 (shepherd-service-lookup-procedure live
405 live-service-provision))
406
407 (define (running? service)
408 (and=> (lookup-live (shepherd-service-canonical-name service))
409 live-service-running))
410
7b44cae5
LC
411 (define live-service-dependents
412 (shepherd-service-back-edges live
413 #:provision live-service-provision
414 #:requirement live-service-requirement))
415
416 (define (obsolete? service)
417 (match (lookup-target (first (live-service-provision service)))
418 (#f (every obsolete? (live-service-dependents service)))
419 (_ #f)))
420
4245ddcb
CZ
421 (define to-restart
422 ;; Restart services that are currently running.
423 (filter running? target))
7b44cae5
LC
424
425 (define to-unload
4245ddcb
CZ
426 ;; Unload services that are no longer required.
427 (remove essential? (filter obsolete? live)))
7b44cae5 428
4245ddcb 429 (values to-unload to-restart))
7b44cae5 430
10c41368
LC
431\f
432;;;
433;;; User processes.
434;;;
435
436(define %do-not-kill-file
437 ;; Name of the file listing PIDs of processes that must survive when halting
438 ;; the system. Typical example is user-space file systems.
439 "/etc/shepherd/do-not-kill")
440
441(define (user-processes-shepherd-service requirements)
442 "Return the 'user-processes' Shepherd service with dependencies on
443REQUIREMENTS (a list of service names).
444
445This is a synchronization point used to make sure user processes and daemons
446get started only after crucial initial services have been started---file
447system mounts, etc. This is similar to the 'sysvinit' target in systemd."
448 (define grace-delay
449 ;; Delay after sending SIGTERM and before sending SIGKILL.
450 4)
451
452 (list (shepherd-service
453 (documentation "When stopped, terminate all user processes.")
454 (provision '(user-processes))
455 (requirement requirements)
456 (start #~(const #t))
457 (stop #~(lambda _
458 (define (kill-except omit signal)
459 ;; Kill all the processes with SIGNAL except those listed
460 ;; in OMIT and the current process.
461 (let ((omit (cons (getpid) omit)))
462 (for-each (lambda (pid)
463 (unless (memv pid omit)
464 (false-if-exception
465 (kill pid signal))))
466 (processes))))
467
468 (define omitted-pids
469 ;; List of PIDs that must not be killed.
470 (if (file-exists? #$%do-not-kill-file)
471 (map string->number
472 (call-with-input-file #$%do-not-kill-file
473 (compose string-tokenize
474 (@ (ice-9 rdelim) read-string))))
475 '()))
476
477 (define (now)
478 (car (gettimeofday)))
479
480 (define (sleep* n)
481 ;; Really sleep N seconds.
482 ;; Work around <http://bugs.gnu.org/19581>.
483 (define start (now))
484 (let loop ((elapsed 0))
485 (when (> n elapsed)
486 (sleep (- n elapsed))
487 (loop (- (now) start)))))
488
489 (define lset= (@ (srfi srfi-1) lset=))
490
491 (display "sending all processes the TERM signal\n")
492
493 (if (null? omitted-pids)
494 (begin
495 ;; Easy: terminate all of them.
496 (kill -1 SIGTERM)
497 (sleep* #$grace-delay)
498 (kill -1 SIGKILL))
499 (begin
500 ;; Kill them all except OMITTED-PIDS. XXX: We would
501 ;; like to (kill -1 SIGSTOP) to get a fixed list of
502 ;; processes, like 'killall5' does, but that seems
503 ;; unreliable.
504 (kill-except omitted-pids SIGTERM)
505 (sleep* #$grace-delay)
506 (kill-except omitted-pids SIGKILL)
507 (delete-file #$%do-not-kill-file)))
508
509 (let wait ()
510 ;; Reap children, if any, so that we don't end up with
511 ;; zombies and enter an infinite loop.
512 (let reap-children ()
513 (define result
514 (false-if-exception
515 (waitpid WAIT_ANY (if (null? omitted-pids)
516 0
517 WNOHANG))))
518
519 (when (and (pair? result)
520 (not (zero? (car result))))
521 (reap-children)))
522
523 (let ((pids (processes)))
524 (unless (lset= = pids (cons 1 omitted-pids))
525 (format #t "waiting for process termination\
526 (processes left: ~s)~%"
527 pids)
528 (sleep* 2)
529 (wait))))
530
531 (display "all processes have been terminated\n")
532 #f))
533 (respawn? #f))))
534
535(define user-processes-service-type
536 (service-type
537 (name 'user-processes)
538 (extensions (list (service-extension shepherd-root-service-type
539 user-processes-shepherd-service)))
540 (compose concatenate)
541 (extend append)
542
543 ;; The value is the list of Shepherd services 'user-processes' depends on.
544 ;; Extensions can add new services to this list.
545 (default-value '())
546
547 (description "The @code{user-processes} service is responsible for
548terminating all the processes so that the root file system can be re-mounted
549read-only, just before rebooting/halting. Processes still running after a few
550seconds after @code{SIGTERM} has been sent are terminated with
551@code{SIGKILL}.")))
552
0190c1c0 553;;; shepherd.scm ends here