summaryrefslogtreecommitdiff
path: root/gnu/build
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/build')
-rw-r--r--gnu/build/accounts.scm8
-rw-r--r--gnu/build/bootloader.scm1
-rw-r--r--gnu/build/chromium-extension.scm173
-rw-r--r--gnu/build/file-systems.scm36
-rw-r--r--gnu/build/hurd-boot.scm12
-rw-r--r--gnu/build/image.scm39
-rw-r--r--gnu/build/linux-container.scm3
-rw-r--r--gnu/build/linux-modules.scm10
-rw-r--r--gnu/build/marionette.scm84
-rw-r--r--gnu/build/secret-service.scm17
-rw-r--r--gnu/build/vm.scm500
11 files changed, 255 insertions, 628 deletions
diff --git a/gnu/build/accounts.scm b/gnu/build/accounts.scm
index f60d68d9b3..1247fc640c 100644
--- a/gnu/build/accounts.scm
+++ b/gnu/build/accounts.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2019, 2021 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -490,7 +490,11 @@ new UIDs."
(uid id)
(directory directory)
(gid (if (number? group) group (group-id group)))
- (real-name (if previous
+
+ ;; Users might change their name to something
+ ;; other than what the sysadmin chose, with
+ ;; 'chfn'. Thus consider it "stateful".
+ (real-name (if (and previous (not system?))
(password-entry-real-name previous)
real-name))
diff --git a/gnu/build/bootloader.scm b/gnu/build/bootloader.scm
index 3916930c89..9a89fe55cb 100644
--- a/gnu/build/bootloader.scm
+++ b/gnu/build/bootloader.scm
@@ -94,6 +94,7 @@ load the Grub bootloader located in the 'Guix_image' root partition."
;; (not eliminate it).
(format port
"insmod part_msdos~@
+ insmod part_gpt~@
search --set=root --label Guix_image~@
configfile /boot/grub/grub.cfg~%")))
(install-efi grub-efi grub-config esp)
diff --git a/gnu/build/chromium-extension.scm b/gnu/build/chromium-extension.scm
index d65df09f37..8ca5251957 100644
--- a/gnu/build/chromium-extension.scm
+++ b/gnu/build/chromium-extension.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2020 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2020, 2021 Marius Bakke <marius@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,13 +17,8 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu build chromium-extension)
- #:use-module (gcrypt base16)
- #:use-module ((gcrypt hash) #:prefix hash:)
- #:use-module (ice-9 iconv)
#:use-module (guix gexp)
#:use-module (guix packages)
- #:use-module (gnu packages base)
- #:use-module (gnu packages check)
#:use-module (gnu packages chromium)
#:use-module (gnu packages gnupg)
#:use-module (gnu packages tls)
@@ -39,28 +34,30 @@
(define (make-signing-key seed)
"Return a derivation for a deterministic PKCS #8 private key using SEED."
+ (computed-file
+ (string-append seed "-signing-key.pem")
+ (with-extensions (list guile-gcrypt)
+ #~(begin
+ (use-modules (gcrypt base16) (gcrypt hash) (ice-9 iconv))
+ (let* ((sha256sum (bytevector->base16-string
+ (sha256 (string->bytevector #$seed "UTF-8"))))
+ ;; certtool.c wants a 56 byte seed for a 2048 bit key.
+ (key-size 2048)
+ (normalized-seed (string-take sha256sum 56)))
- (define sha256sum
- (bytevector->base16-string (hash:sha256 (string->bytevector seed "UTF-8"))))
-
- ;; certtool.c wants a 56 byte seed for a 2048 bit key.
- (define size 2048)
- (define normalized-seed (string-take sha256sum 56))
-
- (computed-file (string-append seed "-signing-key.pem")
- #~(system* #$(file-append gnutls "/bin/certtool")
- "--generate-privkey"
- "--key-type=rsa"
- "--pkcs8"
- ;; Use the provable FIPS-PUB186-4 algorithm for
- ;; deterministic results.
- "--provable"
- "--password="
- "--no-text"
- (string-append "--bits=" #$(number->string size))
- (string-append "--seed=" #$normalized-seed)
- "--outfile" #$output)
- #:local-build? #t))
+ (system* #$(file-append gnutls "/bin/certtool")
+ "--generate-privkey"
+ "--key-type=rsa"
+ "--pkcs8"
+ ;; Use the provable FIPS-PUB186-4 algorithm for
+ ;; deterministic results.
+ "--provable"
+ "--password="
+ "--no-text"
+ (string-append "--bits=" (number->string key-size))
+ (string-append "--seed=" normalized-seed)
+ "--outfile" #$output))))
+ #:local-build? #t))
(define* (make-crx signing-key package #:optional (package-output "out"))
"Create a signed \".crx\" file from the unpacked Chromium extension residing
@@ -68,9 +65,9 @@ in PACKAGE-OUTPUT of PACKAGE. The extension will be signed with SIGNING-KEY."
(define name (package-name package))
(define version (package-version package))
- (with-imported-modules '((guix build utils))
- (computed-file
- (string-append name "-" version ".crx")
+ (computed-file
+ (string-append name "-" version ".crx")
+ (with-imported-modules '((guix build utils))
#~(begin
;; This is not great. We pull Xorg and Chromium just to Zip and
;; sign an extension. This should be implemented with something
@@ -78,27 +75,22 @@ in PACKAGE-OUTPUT of PACKAGE. The extension will be signed with SIGNING-KEY."
(use-modules (guix build utils))
(let ((chromium #$(file-append ungoogled-chromium "/bin/chromium"))
(xvfb #$(file-append xorg-server "/bin/Xvfb"))
- (packdir "/tmp/extension"))
- (mkdir-p (dirname packdir))
- (copy-recursively (ungexp package package-output) packdir)
+ (packdir (string-append (getcwd) "/extension")))
+ (mkdir packdir)
+ (copy-recursively (ungexp package package-output) packdir
+ ;; Ensure consistent file modification times.
+ #:keep-mtime? #t)
(system (string-append xvfb " :1 &"))
(setenv "DISPLAY" ":1")
(sleep 2) ;give Xorg some time to initialize...
- ;; Chromium stores the current time in the .crx Zip archive.
- ;; Use a fixed timestamp for deterministic behavior.
- ;; FIXME (core-updates): faketime is missing an absolute reference
- ;; to 'date', hence the need to set PATH.
- (setenv "PATH" #$(file-append coreutils "/bin"))
- (invoke #$(file-append libfaketime "/bin/faketime")
- "2000-01-01 00:00:00"
- chromium
- "--user-data-dir=/tmp/signing-profile"
+ (invoke chromium
+ "--user-data-dir=chromium-profile"
(string-append "--pack-extension=" packdir)
(string-append "--pack-extension-key=" #$signing-key))
- (copy-file (string-append packdir ".crx") #$output)))
- #:local-build? #t)))
+ (copy-file (string-append packdir ".crx") #$output))))
+ #:local-build? #t))
-(define* (crx->chromium-json crx version)
+(define (crx->chromium-json crx version)
"Return a derivation that creates a Chromium JSON settings file for the
extension given as CRX. VERSION is used to signify the CRX version, and
must match the version listed in the extension manifest.json."
@@ -127,66 +119,47 @@ format."
"--outder")
#:local-build? #t))
-(define (chromium-json->profile-object json signing-key)
- "Return a derivation that installs JSON to the directory searched by
-Chromium, using a file name (aka extension ID) derived from SIGNING-KEY."
- (define der (signing-key->public-der signing-key))
-
+(define (file-sha256sum file)
(with-extensions (list guile-gcrypt)
- (with-imported-modules '((guix build utils))
- (computed-file
- "chromium-extension"
- #~(begin
- (use-modules (guix build utils)
- (gcrypt base16)
- (gcrypt hash))
- (define (base16-string->chromium-base16 str)
- ;; Translate STR, a hexadecimal string, to a Chromium-style
- ;; representation using the letters a-p (where a=0, p=15).
- (define s1 "0123456789abcdef")
- (define s2 "abcdefghijklmnop")
- (let loop ((chars (string->list str))
- (converted '()))
- (if (null? chars)
- (list->string (reverse converted))
- (loop (cdr chars)
- (cons (string-ref s2 (string-index s1 (car chars)))
- converted)))))
-
- (let* ((checksum (bytevector->base16-string (file-sha256 #$der)))
- (file-name (base16-string->chromium-base16
- (string-take checksum 32)))
- (extension-directory (string-append #$output
- "/share/chromium/extensions")))
- (mkdir-p extension-directory)
- (symlink #$json (string-append extension-directory "/"
- file-name ".json"))))
- #:local-build? #t))))
+ #~(begin
+ (use-modules (gcrypt base16) (gcrypt hash))
+ (bytevector->base16-string (file-sha256 #$file)))))
-(define* (make-chromium-extension p #:optional (output "out"))
- "Create a Chromium extension from package P and return a package that,
-when installed, will make the extension contained in P available as a
-Chromium browser extension. OUTPUT specifies which output of P to use."
- (let* ((pname (package-name p))
- (version (package-version p))
- (signing-key (make-signing-key pname)))
+(define* (make-chromium-extension pkg #:optional (pkg-output "out"))
+ "Create a Chromium extension from package PKG and return a package that,
+when installed, will make the extension contained in PKG available as a
+Chromium browser extension. PKG-OUTPUT specifies which output of PKG to use."
+ (let* ((name (package-name pkg))
+ (version (package-version pkg))
+ (private-key (make-signing-key name))
+ (public-key (signing-key->public-der private-key))
+ (checksum (file-sha256sum public-key))
+ (crx (make-crx private-key pkg pkg-output))
+ (json (crx->chromium-json crx version)))
(package
- (inherit p)
- (name (string-append pname "-chromium"))
+ (inherit pkg)
+ (name (string-append name "-chromium"))
(source #f)
- (build-system trivial-build-system)
(native-inputs '())
- (inputs
- `(("extension" ,(chromium-json->profile-object
- (crx->chromium-json (make-crx signing-key p output)
- version)
- signing-key))))
+ (inputs '())
(propagated-inputs '())
(outputs '("out"))
+ (build-system trivial-build-system)
(arguments
- '(#:modules ((guix build utils))
- #:builder
- (begin
- (use-modules (guix build utils))
- (copy-recursively (assoc-ref %build-inputs "extension")
- (assoc-ref %outputs "out"))))))))
+ (list #:modules '((guix build utils))
+ #:builder
+ #~(begin
+ (use-modules (guix build utils))
+ (define (base16-char->chromium-base16 char)
+ ;; Translate CHAR, a hexadecimal character, to a Chromium-style
+ ;; representation using the letters a-p (where a=0, p=15).
+ (string-ref "abcdefghijklmnop"
+ (string-index "0123456789abcdef" char)))
+ (let ((file-name (string-map base16-char->chromium-base16
+ (string-take #$checksum 32)))
+ (extension-directory
+ (string-append #$output
+ "/share/chromium/extensions")))
+ (mkdir-p extension-directory)
+ (symlink #$json (string-append extension-directory "/"
+ file-name ".json")))))))))
diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm
index d8a5ddf1e5..d95340df83 100644
--- a/gnu/build/file-systems.scm
+++ b/gnu/build/file-systems.scm
@@ -29,6 +29,8 @@
#:use-module (guix build bournish)
#:use-module ((guix build syscalls)
#:hide (file-system-type))
+ #:use-module (guix diagnostics)
+ #:use-module (guix i18n)
#:use-module (rnrs io ports)
#:use-module (rnrs bytevectors)
#:use-module (ice-9 match)
@@ -54,7 +56,9 @@
mount-flags->bit-mask
check-file-system
- mount-file-system))
+ mount-file-system
+
+ swap-space->flags-bit-mask))
;;; Commentary:
;;;
@@ -227,6 +231,36 @@ if DEVICE does not contain an linux-swap file system."
"Return the label of Linux-swap superblock SBLOCK as a string."
(null-terminated-latin1->string
(sub-bytevector sblock (+ 1024 4 4 4 16) 16)))
+
+(define (swap-space->flags-bit-mask swap)
+ "Return the number suitable for the 'flags' argument of 'mount'
+that corresponds to the swap-space SWAP."
+ (define prio-flag
+ (let ((p (swap-space-priority swap))
+ (max (ash SWAP_FLAG_PRIO_MASK (- SWAP_FLAG_PRIO_SHIFT))))
+ (if p
+ (logior SWAP_FLAG_PREFER
+ (ash (cond
+ ((< p 0)
+ (begin (warning
+ (G_ "Given swap priority ~a is
+negative, defaulting to 0.~%") p)
+ 0))
+ ((> p max)
+ (begin (warning
+ (G_ "Limiting swap priority ~a to
+~a.~%")
+ p max)
+ max))
+ (else p))
+ SWAP_FLAG_PRIO_SHIFT))
+ 0)))
+ (define delayed-flag
+ (if (swap-space-discard? swap)
+ SWAP_FLAG_DISCARD
+ 0))
+ (logior prio-flag delayed-flag))
+
;;;
diff --git a/gnu/build/hurd-boot.scm b/gnu/build/hurd-boot.scm
index e66d4d1ba8..ac36bd17d4 100644
--- a/gnu/build/hurd-boot.scm
+++ b/gnu/build/hurd-boot.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2020, 2021 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
;;;
;;; This file is part of GNU Guix.
@@ -185,13 +185,9 @@ set."
("servers/crash-suspend" ("/hurd/crash" "--suspend"))
("servers/password" ("/hurd/password"))
("servers/socket/1" ("/hurd/pflocal"))
- ("servers/socket/2" ("/hurd/pfinet"
- "--interface" "eth0"
- "--address"
- "10.0.2.15" ;the default QEMU guest IP
- "--netmask" "255.255.255.0"
- "--gateway" "10.0.2.2"
- "--ipv6" "/servers/socket/16"))
+ ;; /servers/socket/2 and /26 are created by 'static-networking-service'.
+ ;; XXX: Spawn pfinet without arguments on these nodes so that a DHCP
+ ;; client has someone to talk to?
("proc" ("/hurd/procfs" "--stat-mode=444"))))
(define devices
diff --git a/gnu/build/image.scm b/gnu/build/image.scm
index 6eb0290256..bdd5ec25a9 100644
--- a/gnu/build/image.scm
+++ b/gnu/build/image.scm
@@ -166,6 +166,7 @@ produced by #:references-graphs. Pass WAL-MODE? to call-with-database."
bootcfg-location
bootloader-package
bootloader-installer
+ (copy-closures? #t)
(deduplicate? #t)
references-graphs
(register-closures? #t)
@@ -176,30 +177,50 @@ produced by #:references-graphs. Pass WAL-MODE? to call-with-database."
"Initialize the given ROOT directory. Use BOOTCFG and BOOTCFG-LOCATION to
install the bootloader configuration.
-If REGISTER-CLOSURES? is true, register REFERENCES-GRAPHS in the store. If
+If COPY-CLOSURES? is true, copy all of REFERENCES-GRAPHS to the partition. If
+REGISTER-CLOSURES? is true, register REFERENCES-GRAPHS in the store. If
DEDUPLICATE? is true, then also deduplicate files common to CLOSURES and the
rest of the store when registering the closures. SYSTEM-DIRECTORY is the name
of the directory of the 'system' derivation. Pass WAL-MODE? to
register-closure."
+ (define root-store
+ (string-append root (%store-directory)))
+
+ (define tmp-store ".tmp-store")
+
(populate-root-file-system system-directory root)
- (populate-store references-graphs root
- #:deduplicate? deduplicate?)
+
+ (when copy-closures?
+ (populate-store references-graphs root
+ #:deduplicate? deduplicate?))
;; Populate /dev.
(when make-device-nodes
(make-device-nodes root))
(when register-closures?
+ (unless copy-closures?
+ ;; XXX: 'register-closure' wants to palpate the things it registers, so
+ ;; create a symlink to the store.
+ (rename-file root-store tmp-store)
+ (symlink (%store-directory) root-store))
+
(for-each (lambda (closure)
(register-closure root closure
#:wal-mode? wal-mode?))
- references-graphs))
+ references-graphs)
+
+ (unless copy-closures?
+ (delete-file root-store)
+ (rename-file tmp-store root-store)))
- (when bootloader-installer
- (display "installing bootloader...\n")
- (bootloader-installer bootloader-package #f root))
- (when bootcfg
- (install-boot-config bootcfg bootcfg-location root)))
+ ;; There's no point installing a bootloader if we do not populate the store.
+ (when copy-closures?
+ (when bootloader-installer
+ (display "installing bootloader...\n")
+ (bootloader-installer bootloader-package #f root))
+ (when bootcfg
+ (install-boot-config bootcfg bootcfg-location root))))
(define* (make-iso9660-image xorriso grub-mkrescue-environment
grub bootcfg system-directory root target
diff --git a/gnu/build/linux-container.scm b/gnu/build/linux-container.scm
index 4a8bed5a9a..bdeca2cdb9 100644
--- a/gnu/build/linux-container.scm
+++ b/gnu/build/linux-container.scm
@@ -52,7 +52,7 @@ exists."
(file-exists? "/proc/self/setgroups"))
(define %namespaces
- '(mnt pid ipc uts user net))
+ '(cgroup mnt pid ipc uts user net))
(define (call-with-clean-exit thunk)
"Apply THUNK, but exit with a status code of 1 if it fails."
@@ -210,6 +210,7 @@ corresponds to the symbols in NAMESPACES."
;; Use the same flags as fork(3) in addition to the namespace flags.
(apply logior SIGCHLD
(map (match-lambda
+ ('cgroup CLONE_NEWCGROUP)
('mnt CLONE_NEWNS)
('uts CLONE_NEWUTS)
('ipc CLONE_NEWIPC)
diff --git a/gnu/build/linux-modules.scm b/gnu/build/linux-modules.scm
index 3a47322065..053720574b 100644
--- a/gnu/build/linux-modules.scm
+++ b/gnu/build/linux-modules.scm
@@ -354,11 +354,13 @@ appears in BLACK-LIST are not loaded."
(close-fdes fd)
#t)
(lambda args
- ;; If this module was already loaded and we're in modprobe style, ignore
- ;; the error.
(when fd (close-fdes fd))
- (or (and recursive? (= EEXIST (system-error-errno args)))
- (apply throw args)))))))
+ (let ((errno (system-error-errno args)))
+ (or (and recursive? ; we're operating in ‘modprobe’ style
+ (member errno
+ (list EEXIST ; already loaded
+ EINVAL))) ; unsupported by hardware
+ (apply throw args))))))))
(define (load-linux-modules-from-directory modules directory)
"Load MODULES and their dependencies from DIRECTORY, a directory containing
diff --git a/gnu/build/marionette.scm b/gnu/build/marionette.scm
index fe754cd147..b336024610 100644
--- a/gnu/build/marionette.scm
+++ b/gnu/build/marionette.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
;;;
;;; This file is part of GNU Guix.
@@ -20,9 +20,11 @@
(define-module (gnu build marionette)
#:use-module (srfi srfi-9)
#:use-module (srfi srfi-26)
+ #:use-module (srfi srfi-64)
#:use-module (rnrs io ports)
#:use-module (ice-9 match)
#:use-module (ice-9 popen)
+ #:use-module (ice-9 regex)
#:export (marionette?
make-marionette
marionette-eval
@@ -33,7 +35,10 @@
marionette-screen-text
wait-for-screen-text
%qwerty-us-keystrokes
- marionette-type))
+ marionette-type
+
+ system-test-runner
+ qemu-command))
;;; Commentary:
;;;
@@ -358,4 +363,79 @@ to actual keystrokes."
(for-each (cut marionette-control <> marionette)
(string->keystroke-commands str keystrokes)))
+
+;;;
+;;; Test helper.
+;;;
+
+(define* (system-test-runner #:optional log-directory)
+ "Return a SRFI-64 test runner that calls 'exit' upon 'test-end'. When
+LOG-DIRECTORY is specified, create log file within it."
+ (let ((runner (test-runner-simple)))
+ ;; Log to a file under LOG-DIRECTORY.
+ (test-runner-on-group-begin! runner
+ (let ((on-begin (test-runner-on-group-begin runner)))
+ (lambda (runner suite-name count)
+ (when log-directory
+ (catch 'system-error
+ (lambda ()
+ (mkdir log-directory))
+ (lambda args
+ (unless (= (system-error-errno args) EEXIST)
+ (apply throw args))))
+ (set! test-log-to-file
+ (string-append log-directory "/" suite-name ".log")))
+ (on-begin runner suite-name count))))
+
+ ;; The default behavior on 'test-end' is to only write a line if the test
+ ;; failed. Arrange to also write a line on success.
+ (test-runner-on-test-end! runner
+ (let ((on-end (test-runner-on-test-end runner)))
+ (lambda (runner)
+ (let* ((kind (test-result-ref runner 'result-kind))
+ (results (test-result-alist runner))
+ (test-name (assq-ref results 'test-name)))
+ (unless (memq kind '(fail xpass))
+ (format (current-output-port) "~a: ~a~%"
+ (string-upcase (symbol->string kind))
+ test-name)))
+
+ (on-end runner))))
+
+ ;; On 'test-end', display test results and exit with zero if and only if
+ ;; there were no test failures.
+ (test-runner-on-final! runner
+ (lambda (runner)
+ (let ((success? (= (test-runner-fail-count runner) 0)))
+ (test-on-final-simple runner)
+
+ (when (not success?)
+ (let* ((log-port (test-runner-aux-value runner))
+ (log-file (port-filename log-port)))
+ (format (current-error-port)
+ "\nTests failed, dumping log file '~a'.\n\n"
+ log-file)
+
+ ;; At this point LOG-PORT is not closed yet; flush it.
+ (force-output log-port)
+
+ ;; Brute force to avoid dependency on (guix build utils) for
+ ;; 'dump-port'.
+ (let ((content (call-with-input-file log-file
+ get-bytevector-all)))
+ (put-bytevector (current-error-port) content))))
+
+ (exit success?))))
+ runner))
+
+(define* (qemu-command #:optional (system %host-type))
+ "Return the default name of the QEMU command for SYSTEM."
+ (let ((cpu (substring system 0
+ (string-index system #\-))))
+ (string-append "qemu-system-"
+ (cond
+ ((string-match "^i[3456]86$" cpu) "i386")
+ ((string-match "armhf" cpu) "arm")
+ (else cpu)))))
+
;;; marionette.scm ends here
diff --git a/gnu/build/secret-service.scm b/gnu/build/secret-service.scm
index 46dcf1b9c3..4e183e11e8 100644
--- a/gnu/build/secret-service.scm
+++ b/gnu/build/secret-service.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2020, 2021 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
;;;
;;; This file is part of GNU Guix.
@@ -111,6 +111,15 @@ wait for at most HANDSHAKE-TIMEOUT seconds for handshake to complete. Return
(close-port sock)
#f))))
+(define (delete-file* file)
+ "Ensure FILE does not exist."
+ (catch 'system-error
+ (lambda ()
+ (delete-file file))
+ (lambda args
+ (unless (= ENOENT (system-error-errno args))
+ (apply throw args)))))
+
(define (secret-service-receive-secrets port)
"Listen to local PORT and wait for a secret service client to send secrets.
Write them to the file system. Return the list of files installed on success,
@@ -170,6 +179,12 @@ and #f otherwise."
(log "installing file '~a' (~a bytes)...~%"
file size)
(mkdir-p (dirname file))
+
+ ;; It could be that FILE already exists, for instance
+ ;; because it has been created by a service's activation
+ ;; snippet (e.g., SSH host keys). Delete it.
+ (delete-file* file)
+
(call-with-output-file file
(lambda (output)
(dump port output size)
diff --git a/gnu/build/vm.scm b/gnu/build/vm.scm
deleted file mode 100644
index 7b55127599..0000000000
--- a/gnu/build/vm.scm
+++ /dev/null
@@ -1,500 +0,0 @@
-;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2016 Christine Lemmer-Webber <cwebber@dustycloud.org>
-;;; Copyright © 2016, 2017 Leo Famulari <leo@famulari.name>
-;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
-;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com>
-;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
-;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
-;;;
-;;; This file is part of GNU Guix.
-;;;
-;;; GNU Guix is free software; you can redistribute it and/or modify it
-;;; under the terms of the GNU General Public License as published by
-;;; the Free Software Foundation; either version 3 of the License, or (at
-;;; your option) any later version.
-;;;
-;;; GNU Guix is distributed in the hope that it will be useful, but
-;;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;; GNU General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU General Public License
-;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
-
-(define-module (gnu build vm)
- #:use-module (guix build utils)
- #:use-module (guix build store-copy)
- #:use-module (guix build syscalls)
- #:use-module (guix store database)
- #:use-module (gnu build bootloader)
- #:use-module (gnu build linux-boot)
- #:use-module (gnu build install)
- #:use-module (gnu system uuid)
- #:use-module (guix records)
- #:use-module ((guix combinators) #:select (fold2))
- #:use-module (ice-9 format)
- #:use-module (ice-9 ftw)
- #:use-module (ice-9 match)
- #:use-module (ice-9 regex)
- #:use-module (ice-9 popen)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-19)
- #:use-module (srfi srfi-26)
- #:export (qemu-command
- load-in-linux-vm
- format-partition
-
- partition
- partition?
- partition-device
- partition-size
- partition-file-system
- partition-label
- partition-flags
- partition-initializer
-
- estimated-partition-size
- root-partition-initializer
- initialize-partition-table
- initialize-hard-disk))
-
-;;; Commentary:
-;;;
-;;; This module provides supporting code to run virtual machines and build
-;;; virtual machine images using QEMU.
-;;;
-;;; Code:
-
-(define* (qemu-command #:optional (system %host-type))
- "Return the default name of the QEMU command for SYSTEM."
- (let ((cpu (substring system 0
- (string-index system #\-))))
- (string-append "qemu-system-"
- (cond
- ((string-match "^i[3456]86$" cpu) "i386")
- ((string-match "armhf" cpu) "arm")
- (else cpu)))))
-
-(define* (load-in-linux-vm builder
- #:key
- output
- (qemu (qemu-command)) (memory-size 512)
- linux initrd
- make-disk-image?
- single-file-output?
- (disk-image-size (* 100 (expt 2 20)))
- (disk-image-format "qcow2")
- (references-graphs '()))
- "Run BUILDER, a Scheme file, into a VM running LINUX with INITRD, and copy
-the result to OUTPUT. If SINGLE-FILE-OUTPUT? is true, copy a single file from
-/xchg to OUTPUT. Otherwise, copy the contents of /xchg to a new directory
-OUTPUT.
-
-When MAKE-DISK-IMAGE? is true, OUTPUT will contain a VM image of
-DISK-IMAGE-SIZE bytes resulting from the execution of BUILDER, which may
-access it via /dev/hda.
-
-REFERENCES-GRAPHS can specify a list of reference-graph files as produced by
-the #:references-graphs parameter of 'derivation'."
-
- (define target-arm32?
- (string-prefix? "arm-" %host-type))
-
- (define target-aarch64?
- (string-prefix? "aarch64-" %host-type))
-
- (define target-arm?
- (or target-arm32? target-aarch64?))
-
- (define arch-specific-flags
- `(;; On ARM, a machine has to be specified. Use "virt" machine to avoid
- ;; hardware limits imposed by other machines.
- ,@(if target-arm?
- '("-M" "virt")
- '())
-
- ;; On ARM32, if the kernel is built without LPAE support, ECAM conflicts
- ;; with VIRT_PCIE_MMIO causing PCI devices not to show up. Disable
- ;; explicitely highmem to fix it.
- ;; See: https://bugs.launchpad.net/qemu/+bug/1790975.
- ,@(if target-arm32?
- '("-machine" "highmem=off")
- '())
-
- ;; Only enable kvm if we see /dev/kvm exists. This allows users without
- ;; hardware virtualization to still use these commands. KVM support is
- ;; still buggy on some ARM boards. Do not use it even if available.
- ,@(if (and (file-exists? "/dev/kvm")
- (not target-arm?))
- '("-enable-kvm")
- '())
-
- ;; Pass "panic=1" so that the guest dies upon error.
- "-append"
- ,(string-append "panic=1 --load=" builder
-
- ;; The serial port name differs between emulated
- ;; architectures/machines.
- " console="
- (if target-arm? "ttyAMA0" "ttyS0"))))
-
- (when make-disk-image?
- (format #t "creating ~a image of ~,2f MiB...~%"
- disk-image-format (/ disk-image-size (expt 2 20)))
- (force-output)
- (invoke "qemu-img" "create" "-f" disk-image-format output
- (number->string disk-image-size)))
-
- (mkdir "xchg")
- (mkdir "tmp")
-
- (match references-graphs
- ((graph-files ...)
- ;; Copy the reference-graph files under xchg/ so EXP can access it.
- (map (lambda (file)
- (copy-file file (string-append "xchg/" file)))
- graph-files))
- (_ #f))
-
- (apply invoke qemu "-nographic" "-no-reboot"
- ;; CPU "max" behaves as "host" when KVM is enabled, and like a system
- ;; CPU with the maximum possible feature set otherwise.
- "-cpu" "max"
- "-m" (number->string memory-size)
- "-nic" "user,model=virtio-net-pci"
- "-object" "rng-random,filename=/dev/urandom,id=guixsd-vm-rng"
- "-device" "virtio-rng-pci,rng=guixsd-vm-rng"
- "-virtfs"
- (string-append "local,id=store_dev,path="
- (%store-directory)
- ",security_model=none,mount_tag=store")
- "-virtfs"
- (string-append "local,id=xchg_dev,path=xchg"
- ",security_model=none,mount_tag=xchg")
- "-virtfs"
- ;; Some programs require more space in /tmp than is normally
- ;; available in the guest. Accommodate such programs by sharing a
- ;; temporary directory.
- (string-append "local,id=tmp_dev,path=tmp"
- ",security_model=none,mount_tag=tmp")
- "-kernel" linux
- "-initrd" initrd
- (append
- (if make-disk-image?
- `("-device" "virtio-blk,drive=myhd"
- "-drive" ,(string-append "if=none,file=" output
- ",format=" disk-image-format
- ",id=myhd"))
- '())
- arch-specific-flags))
-
- (unless (file-exists? "xchg/.exit-status")
- (error "VM did not produce an exit code"))
-
- (match (call-with-input-file "xchg/.exit-status" read)
- (0 #t)
- (status (error "guest VM code exited with a non-zero status" status)))
-
- (delete-file "xchg/.exit-status")
-
- ;; When MAKE-DISK-IMAGE? is true, the image is in OUTPUT already.
- (unless make-disk-image?
- (if single-file-output?
- (let ((graph? (lambda (name stat)
- (member (basename name) references-graphs))))
- (match (find-files "xchg" (negate graph?))
- ((result)
- (copy-file result output))
- (x
- (error "did not find a single result file" x))))
- (begin
- (mkdir output)
- (copy-recursively "xchg" output)))))
-
-(define* (register-closure prefix closure
- #:key
- (schema (sql-schema)))
- "Register CLOSURE in PREFIX, where PREFIX is the directory name of the
-target store and CLOSURE is the name of a file containing a reference graph as
-produced by #:references-graphs."
- (let ((items (call-with-input-file closure read-reference-graph)))
- (parameterize ((sql-schema schema))
- (with-database (store-database-file #:prefix prefix) db
- (register-items db items
- #:prefix prefix
- #:registration-time %epoch)))))
-
-
-;;;
-;;; Partitions.
-;;;
-
-(define-record-type* <partition> partition make-partition
- partition?
- (device partition-device (default #f))
- (size partition-size)
- (file-system partition-file-system (default "ext4"))
- (file-system-options partition-file-system-options ;passed to 'mkfs.FS'
- (default '()))
- (label partition-label (default #f))
- (uuid partition-uuid (default #f))
- (flags partition-flags (default '()))
- (initializer partition-initializer (default (const #t))))
-
-(define (estimated-partition-size graphs)
- "Return the estimated size of a partition that can store the store items
-given by GRAPHS, a list of file names produced by #:references-graphs."
- ;; Simply add a 25% overhead.
- (round (* 1.25 (closure-size graphs))))
-
-(define* (initialize-partition-table device partitions
- #:key
- (label-type "msdos")
- (offset (expt 2 20)))
- "Create on DEVICE a partition table of type LABEL-TYPE, containing the given
-PARTITIONS (a list of <partition> objects), starting at OFFSET bytes. On
-success, return PARTITIONS with their 'device' field changed to reflect their
-actual /dev name based on DEVICE."
- (define (partition-options part offset index)
- (cons* "mkpart" "primary" "ext2"
- (format #f "~aB" offset)
- (format #f "~aB" (+ offset (partition-size part)))
- (append-map (lambda (flag)
- (list "set" (number->string index)
- (symbol->string flag) "on"))
- (partition-flags part))))
-
- (define (options partitions offset)
- (let loop ((partitions partitions)
- (offset offset)
- (index 1)
- (result '()))
- (match partitions
- (()
- (concatenate (reverse result)))
- ((head tail ...)
- (loop tail
- ;; Leave one sector (512B) between partitions to placate
- ;; Parted.
- (+ offset 512 (partition-size head))
- (+ 1 index)
- (cons (partition-options head offset index)
- result))))))
-
- (format #t "creating partition table with ~a partitions (~a)...\n"
- (length partitions)
- (string-join (map (compose (cut string-append <> " MiB")
- number->string
- (lambda (size)
- (round (/ size (expt 2. 20))))
- partition-size)
- partitions)
- ", "))
- (apply invoke "parted" "--script"
- device "mklabel" label-type
- (options partitions offset))
-
- ;; Set the 'device' field of each partition.
- (reverse
- (fold2 (lambda (part result index)
- (values (cons (partition
- (inherit part)
- (device (string-append device
- (number->string index))))
- result)
- (+ 1 index)))
- '()
- 1
- partitions)))
-
-(define MS_BIND 4096) ; <sys/mounts.h> again!
-
-(define* (create-ext-file-system partition type
- #:key label uuid (options '()))
- "Create an ext-family file system of TYPE on PARTITION. If LABEL is true,
-use that as the volume name. If UUID is true, use it as the partition UUID."
- (format #t "creating ~a partition... ~@[label: ~s~] ~@[uuid: ~s~]\n"
- type label (and uuid (uuid->string uuid)))
- (apply invoke (string-append "mkfs." type)
- "-F" partition
- `(,@(if label
- `("-L" ,label)
- '())
- ,@(if uuid
- `("-U" ,(uuid->string uuid))
- '())
- ,@options)))
-
-(define* (create-fat-file-system partition
- #:key label uuid (options '()))
- "Create a FAT file system on PARTITION. The number of File Allocation Tables
-will be determined based on file system size. If LABEL is true, use that as the
-volume name."
- ;; FIXME: UUID is ignored!
- (format #t "creating FAT partition...\n")
- (apply invoke "mkfs.fat" partition
- (append (if label `("-n" ,label) '()) options)))
-
-(define* (format-partition partition type
- #:key label uuid (options '()))
- "Create a file system TYPE on PARTITION. If LABEL is true, use that as the
-volume name. Options is a list of command-line options passed to 'mkfs.FS'."
- (cond ((string-prefix? "ext" type)
- (create-ext-file-system partition type #:label label #:uuid uuid
- #:options options))
- ((or (string-prefix? "fat" type) (string= "vfat" type))
- (create-fat-file-system partition #:label label #:uuid uuid
- #:options options))
- (else (error "Unsupported file system."))))
-
-(define (initialize-partition partition)
- "Format PARTITION, a <partition> object with a non-#f 'device' field, mount
-it, run its initializer, and unmount it."
- (let ((target "/fs"))
- (format-partition (partition-device partition)
- (partition-file-system partition)
- #:label (partition-label partition)
- #:uuid (partition-uuid partition)
- #:options (partition-file-system-options partition))
- (mkdir-p target)
- (mount (partition-device partition) target
- (partition-file-system partition))
-
- ((partition-initializer partition) target)
-
- (umount target)
- partition))
-
-(define* (root-partition-initializer #:key (closures '())
- copy-closures?
- (register-closures? #t)
- system-directory
- (deduplicate? #t)
- (make-device-nodes
- make-essential-device-nodes)
- (extra-directives '()))
- "Return a procedure to initialize a root partition.
-
-If REGISTER-CLOSURES? is true, register all of CLOSURES in the partition's
-store. If DEDUPLICATE? is true, then also deduplicate files common to
-CLOSURES and the rest of the store when registering the closures. If
-COPY-CLOSURES? is true, copy all of CLOSURES to the partition.
-SYSTEM-DIRECTORY is the name of the directory of the 'system' derivation.
-
-EXTRA-DIRECTIVES is an optional list of directives to populate the root file
-system that is passed to 'populate-root-file-system'."
- (lambda (target)
- (define target-store
- (string-append target (%store-directory)))
-
- (when copy-closures?
- ;; Populate the store.
- (populate-store (map (cut string-append "/xchg/" <>) closures)
- target
- #:deduplicate? deduplicate?))
-
- ;; Populate /dev.
- (make-device-nodes target)
-
- ;; Optionally, register the inputs in the image's store.
- (when register-closures?
- (unless copy-closures?
- ;; XXX: 'register-closure' wants to palpate the things it registers, so
- ;; bind-mount the store on the target.
- (mkdir-p target-store)
- (mount (%store-directory) target-store "" MS_BIND))
-
- (display "registering closures...\n")
- (for-each (lambda (closure)
- (register-closure target
- (string-append "/xchg/" closure)))
- closures)
- (unless copy-closures?
- (umount target-store)))
-
- ;; Add the non-store directories and files.
- (display "populating...\n")
- (populate-root-file-system system-directory target
- #:extras extra-directives)
-
- ;; 'register-closure' resets timestamps and everything, so no need to do it
- ;; once more in that case.
- (unless register-closures?
- ;; 'reset-timestamps' also resets file permissions; do that everywhere
- ;; except on /dev so that /dev/null remains writable, etc.
- (for-each (lambda (directory)
- (reset-timestamps (string-append target "/" directory)))
- (scandir target
- (match-lambda
- ((or "." ".." "dev") #f)
- (_ #t))))
- (reset-timestamps (string-append target "/dev")
- #:preserve-permissions? #t))))
-
-(define (register-bootcfg-root target bootcfg)
- "On file system TARGET, register BOOTCFG as a GC root."
- (let ((directory (string-append target "/var/guix/gcroots")))
- (mkdir-p directory)
- (symlink bootcfg (string-append directory "/bootcfg"))))
-
-(define* (initialize-hard-disk device
- #:key
- bootloader-package
- bootcfg
- bootcfg-location
- bootloader-installer
- (grub-efi #f)
- (partitions '()))
- "Initialize DEVICE as a disk containing all the <partition> objects listed
-in PARTITIONS, and using BOOTCFG as its bootloader configuration file.
-
-Each partition is initialized by calling its 'initializer' procedure,
-passing it a directory name where it is mounted."
-
- (define (partition-bootable? partition)
- "Return the first partition found with the boot flag set."
- (member 'boot (partition-flags partition)))
-
- (define (partition-esp? partition)
- "Return the first EFI System Partition."
- (member 'esp (partition-flags partition)))
-
- (let* ((partitions (initialize-partition-table device partitions))
- (root (find partition-bootable? partitions))
- (esp (find partition-esp? partitions))
- (target "/fs"))
- (unless root
- (error "no bootable partition specified" partitions))
-
- (for-each initialize-partition partitions)
-
- (display "mounting root partition...\n")
- (mkdir-p target)
- (mount (partition-device root) target (partition-file-system root))
- (install-boot-config bootcfg bootcfg-location target)
- (when bootloader-installer
- (display "installing bootloader...\n")
- (bootloader-installer bootloader-package device target))
-
- (when esp
- ;; Mount the ESP somewhere and install GRUB UEFI image.
- (let ((mount-point (string-append target "/boot/efi")))
- (display "mounting EFI system partition...\n")
- (mkdir-p mount-point)
- (mount (partition-device esp) mount-point
- (partition-file-system esp))
-
- (display "creating EFI firmware image...")
- (install-efi-loader grub-efi mount-point)
- (display "done.\n")
-
- (umount mount-point)))
-
- ;; Register BOOTCFG as a GC root.
- (register-bootcfg-root target bootcfg)
-
- (umount target)))
-
-;;; vm.scm ends here