gnu: imapfilter: Use G-expressions.
[jackhill/guix/guix.git] / gnu / system.scm
index 58b5946..b7f19d7 100644 (file)
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2013-2022 Ludovic Courtès <ludo@gnu.org>
 ;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
 ;;; Copyright © 2015, 2016 Alex Kost <alezost@gmail.com>
 ;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com>
@@ -9,9 +9,9 @@
 ;;; Copyright © 2020 Danny Milosavljevic <dannym@scratchpost.org>
 ;;; Copyright © 2020, 2021 Brice Waegeneire <brice@waegenei.re>
 ;;; Copyright © 2020 Florian Pelz <pelzflorian@pelzflorian.de>
-;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;; Copyright © 2020, 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
 ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <jannek@gnu.org>
-;;; Copyright © 2020 Efraim Flashner <efraim@flashner.co.il>
+;;; Copyright © 2020, 2022 Efraim Flashner <efraim@flashner.co.il>
 ;;; Copyright © 2021 Maxime Devos <maximedevos@telenet.be>
 ;;; Copyright © 2021 raid5atemyhomework <raid5atemyhomework@protonmail.com>
 ;;;
@@ -33,6 +33,7 @@
 (define-module (gnu system)
   #:use-module (guix inferior)
   #:use-module (guix store)
+  #:use-module (guix memoization)
   #:use-module (guix monads)
   #:use-module (guix gexp)
   #:use-module (guix records)
@@ -42,6 +43,7 @@
   #:use-module ((guix utils) #:select (substitute-keyword-arguments))
   #:use-module (guix i18n)
   #:use-module (guix diagnostics)
+  #:use-module (guix ui)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages base)
   #:use-module (gnu packages bash)
   #:use-module (gnu system uuid)
   #:use-module (gnu system file-systems)
   #:use-module (gnu system mapped-devices)
+  #:use-module (ice-9 format)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-34)
   #:use-module (srfi srfi-35)
+  #:use-module (srfi srfi-71)
   #:use-module (rnrs bytevectors)
   #:export (operating-system
             operating-system?
             boot-parameters-kernel-arguments
             boot-parameters-initrd
             boot-parameters-multiboot-modules
+            boot-parameters-version
+            %boot-parameters-version
             read-boot-parameters
             read-boot-parameters-file
             boot-parameters->menu-entry
 ;;;
 ;;; Code:
 
-(define (bootable-kernel-arguments system root-device)
-  "Return a list of kernel arguments (gexps) to boot SYSTEM from ROOT-DEVICE."
-  (list (string-append "--root="
+(define* (bootable-kernel-arguments system root-device version)
+  "Return a list of kernel arguments (gexps) to boot SYSTEM from ROOT-DEVICE.
+VERSION is the target version of the boot-parameters record."
+  ;; If the version is newer than 0, we use the new style initrd parameter
+  ;; names, otherwise we use the legacy ones.  This is to maintain backward
+  ;; compatibility when producing bootloader configurations for older
+  ;; generations.
+  (define version>0? (> version 0))
+  (list (string-append (if version>0? "root=" "--root=")
                        ;; Note: Always use the DCE format because that's what
-                       ;; (gnu build linux-boot) expects for the '--root'
+                       ;; (gnu build linux-boot) expects for the 'root'
                        ;; kernel command-line option.
                        (file-system-device->string root-device
                                                    #:uuid-type 'dce))
-        #~(string-append "--system=" #$system)
-        #~(string-append "--load=" #$system "/boot")))
+        #~(string-append (if #$version>0? "gnu.system=" "--system=") #$system)
+        #~(string-append (if #$version>0? "gnu.load=" "--load=")
+                         #$system "/boot")))
 
 ;; System-wide configuration.
 ;; TODO: Add per-field docstrings/stexi.
   (mapped-devices operating-system-mapped-devices ; list of <mapped-device>
                   (default '()))
   (file-systems operating-system-file-systems)    ; list of fs
-  (swap-devices operating-system-swap-devices     ; list of strings
-                (default '()))
+  (swap-devices operating-system-swap-devices     ; list of string | <swap-space>
+                (default '())
+                (delayed)
+                (sanitize warn-swap-devices-change))
 
   (users operating-system-users                   ; list of user accounts
          (default %base-user-accounts))
   (packages operating-system-packages             ; list of (PACKAGE OUTPUT...)
             (default %base-packages))             ; or just PACKAGE
 
-  (timezone operating-system-timezone)            ; string
+  (timezone operating-system-timezone
+            (default "Etc/UTC"))                  ; string
   (locale   operating-system-locale               ; string
             (default "en_US.utf8"))
   (locale-definitions operating-system-locale-definitions ; list of <locale-definition>
                       (default (operating-system-default-essential-services
                                 this-operating-system)))
   (services operating-system-user-services        ; list of services
+            (thunked)                     ;allow for system-dependent services
             (default %base-services))
 
   (pam-services operating-system-pam-services     ; list of PAM services
                             source-properties->location))
             (innate)))
 
-(define (operating-system-kernel-arguments os root-device)
-  "Return all the kernel arguments, including the ones not specified
-directly by the user."
-  (append (bootable-kernel-arguments os root-device)
+(define* (operating-system-kernel-arguments
+          os root-device #:key (version %boot-parameters-version))
+  "Return all the kernel arguments, including the ones not specified directly
+by the user.  VERSION should match that of the target <boot-parameters> record
+object that will contain the kernel parameters."
+  (append (bootable-kernel-arguments os root-device version)
           (operating-system-user-kernel-arguments os)))
 
 \f
@@ -292,6 +311,13 @@ directly by the user."
 ;;; Boot parameters
 ;;;
 
+;;; Version 1 was introduced early 2022 to mark the departure from long option
+;;; names such as '--load' to the more conventional initrd option names like
+;;; 'gnu.load'.
+;;;
+;;; When bumping the boot-parameters version, increment it by one (1).
+(define %boot-parameters-version 1)
+
 (define-record-type* <boot-parameters>
   boot-parameters make-boot-parameters boot-parameters?
   (label            boot-parameters-label)
@@ -319,7 +345,9 @@ directly by the user."
   (kernel           boot-parameters-kernel)
   (kernel-arguments boot-parameters-kernel-arguments)
   (initrd           boot-parameters-initrd)
-  (multiboot-modules boot-parameters-multiboot-modules))
+  (multiboot-modules boot-parameters-multiboot-modules)
+  (version          boot-parameters-version  ;positive integer
+                    (default %boot-parameters-version)))
 
 (define (ensure-not-/dev device)
   "If DEVICE starts with a slash, return #f.  This is meant to filter out
@@ -331,7 +359,7 @@ file system labels."
 
 (define (read-boot-parameters port)
   "Read boot parameters from PORT and return the corresponding
-<boot-parameters> object or #f if the format is unrecognized."
+<boot-parameters> object.  Raise an error if the format is unrecognized."
   (define device-sexp->device
     (match-lambda
       (('uuid (? symbol? type) (? bytevector? bv))
@@ -356,12 +384,18 @@ file system labels."
        (warning (G_ "unrecognized uuid ~a at '~a'~%") x (port-filename port))
        #f)))
 
+  ;; New versions are not backward-compatible, so only accept past and current
+  ;; versions, not future ones.
+  (define (version? n)
+    (member n (iota (1+ %boot-parameters-version))))
+
   (match (read port)
-    (('boot-parameters ('version 0)
+    (('boot-parameters ('version (? version? version))
                        ('label label) ('root-device root)
                        ('kernel kernel)
                        rest ...)
      (boot-parameters
+      (version version)
       (label label)
       (root-device (device-sexp->device root))
 
@@ -452,9 +486,20 @@ file system labels."
          (_                                       ;the old format
           "/")))))
     (x                                            ;unsupported format
-     (warning (G_ "unrecognized boot parameters at '~a'~%")
-              (port-filename port))
-     #f)))
+     (raise
+      (make-compound-condition
+       (formatted-message
+        (G_ "unrecognized boot parameters at '~a'~%")
+        (port-filename port))
+       (condition
+        (&fix-hint (hint (format #f (G_ "This probably means that this version
+of Guix is older than the one that created @file{~a}.  To address this, you
+need to update Guix:
+
+@example
+guix pull
+@end example")
+                                 (port-filename port))))))))))
 
 (define (read-boot-parameters-file system)
   "Read boot parameters from SYSTEM's (system or generation) \"parameters\"
@@ -463,13 +508,15 @@ format is unrecognized.
 The object has its kernel-arguments extended in order to make it bootable."
   (let* ((file (string-append system "/parameters"))
          (params (call-with-input-file file read-boot-parameters))
-         (root (boot-parameters-root-device params)))
+         (root (boot-parameters-root-device params))
+         (version (boot-parameters-version params)))
     (boot-parameters
      (inherit params)
-     (kernel-arguments (append (bootable-kernel-arguments system root)
+     (kernel-arguments (append (bootable-kernel-arguments system root version)
                                (boot-parameters-kernel-arguments params))))))
 
 (define (boot-parameters->menu-entry conf)
+  "Return a <menu-entry> instance given CONF, a <boot-parameters> instance."
   (let* ((kernel (boot-parameters-kernel conf))
          (multiboot-modules (boot-parameters-multiboot-modules conf))
          (multiboot? (pair? multiboot-modules)))
@@ -529,6 +576,14 @@ marked as 'needed-for-boot'."
   (service file-system-service-type
            (map add-dependencies file-systems)))
 
+(define (boot-file-system-service os)
+  "Return a service which adds, to the system profile, packages providing the
+utilites for the file systems marked as 'needed-for-boot' in OS."
+  (let ((file-systems (filter file-system-needed-for-boot?
+                              (operating-system-file-systems os))))
+    (simple-service 'boot-file-system-utilities profile-service-type
+                    (file-system-utilities file-systems))))
+
 (define (mapped-device-users device file-systems)
   "Return the subset of FILE-SYSTEMS that use DEVICE."
   (let ((targets (map (cut string-append "/dev/mapper/" <>)
@@ -559,34 +614,67 @@ from the initrd."
                (any file-system-needed-for-boot? users)))
            devices)))
 
-(define (operating-system-bootloader-crypto-devices os)
-  "Return the subset of mapped devices that the bootloader must open.
-Only devices specified by uuid are supported."
-  (define (valid-crypto-device? dev)
-    (or (uuid? dev)
-        (begin
-          (warning (G_ "\
-mapped-device '~a' may not be mounted by the bootloader.~%")
-                   dev)
-          #f)))
-  (filter-map (match-lambda
-                ((and (= mapped-device-type type)
-                      (= mapped-device-source source))
-                 (and (eq? luks-device-mapping type)
-                      (valid-crypto-device? source)
-                      source))
-                (_ #f))
-              ;; XXX: Ordering is important, we trust the returned one.
-              (operating-system-boot-mapped-devices os)))
+(define operating-system-bootloader-crypto-devices
+  (mlambdaq (os)                        ;to avoid duplicated output
+    "Return the sources of the LUKS mapped devices specified by UUID."
+    ;; XXX: Device ordering is important, we trust the returned one.
+    (let* ((luks-devices (filter (lambda (m)
+                                   (eq? luks-device-mapping
+                                        (mapped-device-type m)))
+                                 (operating-system-boot-mapped-devices os)))
+           (uuid-crypto-devices non-uuid-crypto-devices
+                                (partition (compose uuid? mapped-device-source)
+                                           luks-devices)))
+      (when (not (null? non-uuid-crypto-devices))
+        (for-each (lambda (dev)
+                    (warning
+                     (source-properties->location (mapped-device-location dev))
+                     (G_ "mapped device '~a' may be ignored by bootloader~%")
+                     (mapped-device-source dev)))
+                  non-uuid-crypto-devices)
+        (display-hint "Specify mapped device sources via their LUKS UUID."))
+      (map mapped-device-source uuid-crypto-devices))))
 
 (define (device-mapping-services os)
   "Return the list of device-mapping services for OS as a list."
   (map device-mapping-service
        (operating-system-user-mapped-devices os)))
 
+(define-syntax-rule (warn-swap-devices-change value)
+  (%warn-swap-devices-change value (current-source-location)))
+
+(define (%warn-swap-devices-change value location)
+  (map (lambda (x)
+         (unless (swap-space? x)
+           (warning
+            (source-properties->location
+             location)
+            (G_ "List elements of the field 'swap-devices' should \
+now use the <swap-space> record, as the old method is deprecated. \
+See \"(guix) operating-system Reference\" for more details.~%")))
+         x) value))
+
 (define (swap-services os)
   "Return the list of swap services for OS."
-  (map swap-service (operating-system-swap-devices os)))
+  (define early-userspace-file-systems
+    (filter file-system-needed-for-boot?
+            (operating-system-file-systems os)))
+
+  (define early-userspace-mapped-devices
+    (operating-system-boot-mapped-devices os))
+
+  (define (filter-deps swap)
+    (if (swap-space? swap)
+        (swap-space
+         (inherit swap)
+         (dependencies (remove (lambda (dep)
+                                 (or (member dep early-userspace-mapped-devices)
+                                     (member dep early-userspace-file-systems)))
+                               (swap-space-dependencies swap))))
+        swap))
+
+  (map (compose swap-service filter-deps)
+       (operating-system-swap-devices os)))
 
 (define* (system-linux-image-file-name #:optional
                                        (target (or (%current-target-system)
@@ -596,6 +684,7 @@ mapped-device '~a' may not be mounted by the bootloader.~%")
    ((string-prefix? "arm" target) "zImage")
    ((string-prefix? "mips" target) "vmlinuz")
    ((string-prefix? "aarch64" target) "Image")
+   ((string-prefix? "riscv64" target) "Image")
    (else "bzImage")))
 
 (define (operating-system-kernel-file os)
@@ -640,13 +729,14 @@ bookkeeping."
   (define known-fs
     (map file-system-mount-point (operating-system-file-systems os)))
 
-  (let* ((mappings  (device-mapping-services os))
-         (root-fs   (root-file-system-service))
-         (other-fs  (non-boot-file-system-service os))
-         (swaps     (swap-services os))
-         (procs     (service user-processes-service-type))
-         (host-name (host-name-service (operating-system-host-name os)))
-         (entries   (operating-system-directory-base-entries os)))
+  (let* ((mappings     (device-mapping-services os))
+         (root-fs      (root-file-system-service))
+         (boot-fs      (boot-file-system-service os))
+         (non-boot-fs  (non-boot-file-system-service os))
+         (swaps        (swap-services os))
+         (procs        (service user-processes-service-type))
+         (host-name    (host-name-service (operating-system-host-name os)))
+         (entries      (operating-system-directory-base-entries os)))
     (cons* (service system-service-type entries)
            (service linux-builder-service-type
                     (linux-builder-configuration
@@ -677,7 +767,7 @@ bookkeeping."
                     (operating-system-setuid-programs os))
            (service profile-service-type
                     (operating-system-packages os))
-           other-fs
+           boot-fs non-boot-fs
            (append mappings swaps
 
                    ;; Add the firmware service.
@@ -691,7 +781,10 @@ bookkeeping."
           %boot-service
           %hurd-startup-service
           %activation-service
-          %shepherd-root-service
+          (service shepherd-root-service-type
+                   (shepherd-configuration
+                    (shepherd shepherd-0.8)))     ;no Fibers
+
           (service user-processes-service-type)
           (account-service (append (operating-system-accounts os)
                                    (operating-system-groups os))
@@ -763,7 +856,7 @@ of PROVENANCE-SERVICE-TYPE to its services."
 (define %base-packages-utils
   ;; Default set of  utilities packages.
  (cons* procps psmisc which
-        (@ (gnu packages admin) shadow) ;for 'passwd'
+        (@ (gnu packages admin) shadow-with-man-pages) ;for 'passwd'
 
         guile-3.0-latest
 
@@ -804,8 +897,9 @@ of PROVENANCE-SERVICE-TYPE to its services."
         iw wireless-tools))
 
 (define %base-packages-disk-utilities
-  ;; A well-rounded set of packages for interacting with disks, partitions
-  ;; and filesystems.
+  ;; A well-rounded set of packages for interacting with disks,
+  ;; partitions and filesystems, included with the Guix installation
+  ;; image.
   (list parted gptfdisk ddrescue
         ;; We used to provide fdisk from GNU fdisk, but as of version 2.0.0a
         ;; it pulls Guile 1.8, which takes unreasonable space; furthermore
@@ -820,8 +914,7 @@ of PROVENANCE-SERVICE-TYPE to its services."
 (define %base-packages
   ;; Default set of packages globally visible.  It should include anything
   ;; required for basic administrator tasks.
-  (append (list e2fsprogs)
-          %base-packages-artwork
+  (append %base-packages-artwork
           %base-packages-interactive
           %base-packages-linux
           %base-packages-networking
@@ -878,7 +971,12 @@ the /etc directory."
                         "/run/current-system/profile/sbin\n"
                         "ENV_SUPATH  /run/setuid-programs:"
                         "/run/current-system/profile/bin:"
-                        "/run/current-system/profile/sbin\n")))
+                        "/run/current-system/profile/sbin\n"
+
+                        "\n"
+                        "# Allow 'chfn' to change the full name,\n"
+                        "# room number, and so on.\n"
+                        "CHFN_RESTRICT   frwh\n")))
 
          (hurd       (operating-system-hurd os))
          (issue      (plain-file "issue" (operating-system-issue os)))
@@ -1093,16 +1191,17 @@ use 'plain-file' instead~%")
     ;; TODO: Remove when glibc@2.23 is long gone.
     ("GUIX_LOCPATH" . "/run/current-system/locale")))
 
-(define-syntax-rule (ensure-setuid-program-list lst)
-  "Ensure LST is a list of <setuid-program> records and warn otherwise."
-  (%ensure-setuid-program-list lst (current-source-location)))
+;; Ensure LST is a list of <setuid-program> records and warn otherwise.
+(define-with-syntax-properties (ensure-setuid-program-list (lst properties))
+  (%ensure-setuid-program-list lst properties))
 
-(define (%ensure-setuid-program-list lst location)
+;; We want to be able to use defines, so define a procedure.
+(define (%ensure-setuid-program-list lst properties)
   (define warned? #f)
 
   (define (warn-once)
     (unless warned?
-      (warning (source-properties->location location)
+      (warning (source-properties->location properties)
                (G_ "representing setuid programs with file-like objects is \
 deprecated; use 'setuid-program' instead~%"))
       (set! warned? #t)))
@@ -1122,6 +1221,7 @@ deprecated; use 'setuid-program' instead~%"))
   (let ((shadow (@ (gnu packages admin) shadow)))
     (map file-like->setuid-program
          (list (file-append shadow "/bin/passwd")
+               (file-append shadow "/bin/chfn")
                (file-append shadow "/bin/sg")
                (file-append shadow "/bin/su")
                (file-append shadow "/bin/newgrp")
@@ -1132,6 +1232,7 @@ deprecated; use 'setuid-program' instead~%"))
                (file-append sudo "/bin/sudo")
                (file-append sudo "/bin/sudoedit")
                (file-append fuse "/bin/fusermount")
+               (file-append fuse-3 "/bin/fusermount3")
 
                ;; To allow mounts with the "user" option, "mount" and "umount" must
                ;; be setuid-root.
@@ -1398,8 +1499,11 @@ a list of <menu-entry>, to populate the \"old entries\" menu."
 (define* (operating-system-boot-parameters os root-device
                                            #:key system-kernel-arguments?)
   "Return a monadic <boot-parameters> record that describes the boot
-parameters of OS.  When SYSTEM-KERNEL-ARGUMENTS? is true, add kernel arguments
-such as '--root' and '--load' to <boot-parameters>."
+parameters of OS.  When SYSTEM-KERNEL-ARGUMENTS? is true, add the kernel
+arguments 'root', 'gnu.load' and 'gnu.system' to <boot-parameters>.  The
+SYSTEM-KERNEL-ARGUMENTS? should only be used in necessity, as the 'gnu.load'
+and 'gnu.system' values are self-referential (they refer to the system), thus
+susceptible to introduce a cyclic dependency."
   (let* ((initrd          (and (not (operating-system-hurd os))
                                (operating-system-initrd-file os)))
          (store           (operating-system-store-file-system os))
@@ -1440,25 +1544,16 @@ such as '--root' and '--load' to <boot-parameters>."
     (_
      device)))
 
-(define* (operating-system-boot-parameters-file os
-                                                #:key system-kernel-arguments?)
-   "Return a file that describes the boot parameters of OS.  The primary use of
-this file is the reconstruction of GRUB menu entries for old configurations.
-
-When SYSTEM-KERNEL-ARGUMENTS? is true, add kernel arguments such as '--root'
-and '--load' to the returned file (since the returned file is then usually
-stored into the content-addressed \"system\" directory, it's usually not a
-good idea to give it because the content hash would change by the content hash
-being stored into the \"parameters\" file)."
+(define* (operating-system-boot-parameters-file os)
+   "Return a file that describes the boot parameters of OS.  The primary use
+of this file is the reconstruction of GRUB menu entries for old
+configurations."
    (let* ((root   (operating-system-root-file-system os))
           (device (file-system-device root))
-          (params (operating-system-boot-parameters
-                   os device
-                   #:system-kernel-arguments?
-                   system-kernel-arguments?)))
+          (params (operating-system-boot-parameters os device)))
      (scheme-file "parameters"
                   #~(boot-parameters
-                     (version 0)
+                     (version #$(boot-parameters-version params))
                      (label #$(boot-parameters-label params))
                      (root-device
                       #$(device->sexp
@@ -1498,8 +1593,13 @@ being stored into the \"parameters\" file)."
     (lambda (store)
       ;; XXX: This is not super elegant but we can't pass SYSTEM and TARGET to
       ;; 'operating-system-derivation'.
-      (run-with-store store (operating-system-derivation os)
-                      #:system system
-                      #:target target)))))
+      (parameterize ((%current-system system)
+                     (%current-target-system target))
+        (run-with-store store
+          (mbegin %store-monad
+            (set-guile-for-build (default-guile))
+            (operating-system-derivation os))
+          #:system system
+          #:target target))))))
 
 ;;; system.scm ends here