services: hostapd: Use package from configuration.
[jackhill/guix/guix.git] / gnu / services / shepherd.scm
index e99458d..7277273 100644 (file)
@@ -1,7 +1,9 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2016, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2013, 2014, 2015, 2016, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
 ;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org>
 ;;; Copyright © 2018 Carlo Zancanaro <carlo@zancanaro.id.au>
+;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
+;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -25,6 +27,7 @@
   #:use-module (guix store)
   #:use-module (guix records)
   #:use-module (guix derivations)                 ;imported-modules, etc.
+  #:use-module (guix utils)
   #:use-module (gnu services)
   #:use-module (gnu services herd)
   #:use-module (gnu packages admin)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-34)
   #:use-module (srfi srfi-35)
-  #:export (shepherd-root-service-type
+  #:export (shepherd-configuration
+            shepherd-configuration?
+            shepherd-configuration-shepherd
+            shepherd-configuration-services
+
+            shepherd-root-service-type
             %shepherd-root-service
             shepherd-service-type
 
@@ -65,7 +73,9 @@
             shepherd-service-back-edges
             shepherd-service-upgrade
 
-            user-processes-service-type))
+            user-processes-service-type
+
+            assert-valid-graph))
 
 ;;; Commentary:
 ;;;
 ;;; Code:
 
 
-(define (shepherd-boot-gexp services)
+(define-record-type* <shepherd-configuration>
+  shepherd-configuration make-shepherd-configuration
+  shepherd-configuration?
+  (shepherd shepherd-configuration-shepherd
+            (default shepherd)) ; package
+  (services shepherd-configuration-services
+            (default '()))) ; list of <shepherd-service>
+
+(define (shepherd-boot-gexp config)
+  "Return a gexp starting the shepherd service."
+  (let ((shepherd (shepherd-configuration-shepherd config))
+        (services (shepherd-configuration-services config)))
   #~(begin
       ;; Keep track of the booted system.
       (false-if-exception (delete-file "/run/booted-system"))
-      (symlink (readlink "/run/current-system")
+
+      ;; Make /run/booted-system, an indirect GC root, point to the store item
+      ;; /run/current-system points to.  Use 'canonicalize-path' rather than
+      ;; 'readlink' to make sure we get the store item.
+      (symlink (canonicalize-path "/run/current-system")
                "/run/booted-system")
 
       ;; Close any remaining open file descriptors to be on the safe
       ;; Start shepherd.
       (execl #$(file-append shepherd "/bin/shepherd")
              "shepherd" "--config"
-             #$(shepherd-configuration-file services))))
+             #$(shepherd-configuration-file services shepherd)))))
+
+(define shepherd-packages
+  (compose list shepherd-configuration-shepherd))
 
 (define shepherd-root-service-type
   (service-type
    ;; Extending the root shepherd service (aka. PID 1) happens by
    ;; concatenating the list of services provided by the extensions.
    (compose concatenate)
-   (extend append)
+   (extend (lambda (config extra-services)
+             (shepherd-configuration
+               (inherit config)
+               (services (append (shepherd-configuration-services config)
+                                 extra-services)))))
    (extensions (list (service-extension boot-service-type
                                         shepherd-boot-gexp)
                      (service-extension profile-service-type
-                                        (const (list shepherd)))))))
+                                        shepherd-packages)))
+   (default-value (shepherd-configuration))
+   (description
+    "Run the GNU Shepherd as PID 1---i.e., the operating system's first
+process.  The Shepherd takes care of managing services such as daemons by
+ensuring they are started and stopped in the right order.")))
 
 (define %shepherd-root-service
-  ;; The root shepherd service, aka. PID 1.  Its parameter is a list of
-  ;; <shepherd-service> objects.
-  (service shepherd-root-service-type '()))
+  ;; The root shepherd service, aka. PID 1.  Its parameter is a
+  ;; <shepherd-configuration>.
+  (service shepherd-root-service-type))
 
 (define-syntax shepherd-service-type
-  (syntax-rules ()
+  (syntax-rules (description)
     "Return a <service-type> denoting a simple shepherd service--i.e., the type
 for a service that extends SHEPHERD-ROOT-SERVICE-TYPE and nothing else.  When
 DEFAULT is given, use it as the service's default value."
-    ((_ service-name proc default)
+    ((_ service-name proc default (description text))
      (service-type
       (name service-name)
       (extensions
        (list (service-extension shepherd-root-service-type
                                 (compose list proc))))
-      (default-value default)))
-    ((_ service-name proc)
+      (default-value default)
+      (description text)))
+    ((_ service-name proc (description text))
      (service-type
       (name service-name)
       (extensions
        (list (service-extension shepherd-root-service-type
-                                (compose list proc))))))))
+                                (compose list proc))))
+      (description text)))))
 
 (define %default-imported-modules
   ;; Default set of modules imported for a service's consumption.
@@ -140,7 +179,7 @@ DEFAULT is given, use it as the service's default value."
   ;; Default set of modules visible in a service's file.
   `((shepherd service)
     (oop goops)
-    (guix build utils)
+    ((guix build utils) #:hide (delete))
     (guix build syscalls)))
 
 (define-record-type* <shepherd-service>
@@ -218,16 +257,21 @@ which is not provided by any service")
 
   (for-each assert-satisfied-requirements services))
 
+(define %store-characters
+  ;; Valid store characters; see 'checkStoreName' in the daemon.
+  (string->char-set
+   "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-._?="))
+
 (define (shepherd-service-file-name service)
   "Return the file name where the initialization code for SERVICE is to be
 stored."
   (let ((provisions (string-join (map symbol->string
                                       (shepherd-service-provision service)))))
     (string-append "shepherd-"
-                   (string-map (match-lambda
-                                 (#\/ #\-)
-                                 (#\  #\-)
-                                 (chr chr))
+                   (string-map (lambda (chr)
+                                 (if (char-set-contains? %store-characters chr)
+                                     chr
+                                     #\-))
                                provisions)
                    ".scm")))
 
@@ -257,31 +301,37 @@ stored."
                                    #~(#$name #$doc #$proc)))
                                 (shepherd-service-actions service))))))))
 
-(define (scm->go file)
+(define (scm->go file shepherd)
   "Compile FILE, which contains code to be loaded by shepherd's config file,
-and return the resulting '.go' file."
-  (with-extensions (list shepherd)
-    (computed-file (string-append (basename (scheme-file-name file) ".scm")
-                                  ".go")
-                   #~(begin
-                       (use-modules (system base compile))
-
-                       ;; Do the same as the Shepherd's 'load-in-user-module'.
-                       (let ((env (make-fresh-user-module)))
-                         (module-use! env (resolve-interface '(oop goops)))
-                         (module-use! env (resolve-interface '(shepherd service)))
-                         (compile-file #$file #:output-file #$output
-                                       #:env env)))
-
-                   ;; It's faster to build locally than to download.
-                   #:options '(#:local-build? #t
-                               #:substitutable? #f))))
-
-(define (shepherd-configuration-file services)
-  "Return the shepherd configuration file for SERVICES."
+and return the resulting '.go' file. SHEPHERD is used as shepherd package."
+  (let-system (system target)
+    (with-extensions (list shepherd)
+      (computed-file (string-append (basename (scheme-file-name file) ".scm")
+                                    ".go")
+                     #~(begin
+                         (use-modules (system base compile)
+                                      (system base target))
+
+                         ;; Do the same as the Shepherd's 'load-in-user-module'.
+                         (let ((env (make-fresh-user-module)))
+                           (module-use! env (resolve-interface '(oop goops)))
+                           (module-use! env (resolve-interface '(shepherd service)))
+                           (with-target #$(or target #~%host-type)
+                             (lambda _
+                               (compile-file #$file #:output-file #$output
+                                             #:env env)))))
+
+                     ;; It's faster to build locally than to download.
+                     #:options '(#:local-build? #t
+                                 #:substitutable? #f)))))
+
+(define (shepherd-configuration-file services shepherd)
+  "Return the shepherd configuration file for SERVICES.  SHEPHERD is used
+as shepherd package."
   (assert-valid-graph services)
 
-  (let ((files (map shepherd-service-file services)))
+  (let ((files (map shepherd-service-file services))
+        (scm->go (cute scm->go <> shepherd)))
     (define config
       #~(begin
           (use-modules (srfi srfi-34)
@@ -297,15 +347,16 @@ and return the resulting '.go' file."
           ;; everything slow.  Thus, increase the timeout compared to the
           ;; default 5s in the Shepherd 0.7.0.  See
           ;; <https://bugs.gnu.org/40572>.
-          ;; XXX: Use something better when the next Shepherd is out.
-          (set! (@@ (shepherd service) %pid-file-timeout) 30)
+          (default-pid-file-timeout 30)
 
           ;; Arrange to spawn a REPL if something goes wrong.  This is better
           ;; than a kernel panic.
           (call-with-error-handling
             (lambda ()
               (apply register-services
-                     (map load-compiled '#$(map scm->go files)))))
+                     (parameterize ((current-warning-port
+                                     (%make-void-port "w")))
+                       (map load-compiled '#$(map scm->go files))))))
 
           (format #t "starting services...~%")
           (for-each (lambda (service)