diff options
| author | Mathieu Othacehe <othacehe@gnu.org> | 2020-08-13 14:16:12 +0200 |
|---|---|---|
| committer | Mathieu Othacehe <othacehe@gnu.org> | 2020-09-02 17:05:23 +0200 |
| commit | 8ce6f4dc2879919c12bc76a2f4b01200af97e019 (patch) | |
| tree | bcdfea85d25af8ae24622310a035688ac8257dcc | |
| parent | 5316dfc0f125b658e4a2acf7f00f49501663d943 (diff) | |
installer: Run the installation inside a container.
When the store overlay is mounted, other processes such as kmscon, udev
and guix-daemon may open files from the store, preventing the
underlying install support from being umounted. See:
https://lists.gnu.org/archive/html/guix-devel/2018-12/msg00161.html.
To avoid this situation, mount the store overlay inside a container,
and run the installation from within that container.
* gnu/build/shepherd.scm (fork+exec-command/container): New procedure.
* gnu/services/base.scm (guix-shepherd-service): Support an optional PID
argument passed to the "start" method. If that argument is passed, ensure that
guix-daemon enters the given PID MNT namespace by using
fork+exec-command/container procedure.
* gnu/installer/final.scm (umount-cow-store): Remove it,
(install-system): run the installation from within a container.
* gnu/installer/newt/final.scm (run-install-shell): Remove the display hack.
| -rw-r--r-- | gnu/build/shepherd.scm | 18 | ||||
| -rw-r--r-- | gnu/installer/final.scm | 124 | ||||
| -rw-r--r-- | gnu/installer/newt/final.scm | 7 | ||||
| -rw-r--r-- | gnu/services/base.scm | 115 |
4 files changed, 142 insertions, 122 deletions
diff --git a/gnu/build/shepherd.scm b/gnu/build/shepherd.scm index fd93e7f3f43..65141bd60f8 100644 --- a/gnu/build/shepherd.scm +++ b/gnu/build/shepherd.scm | |||
| @@ -20,10 +20,12 @@ | |||
| 20 | #:use-module (gnu system file-systems) | 20 | #:use-module (gnu system file-systems) |
| 21 | #:use-module (gnu build linux-container) | 21 | #:use-module (gnu build linux-container) |
| 22 | #:use-module (guix build utils) | 22 | #:use-module (guix build utils) |
| 23 | #:use-module (guix utils) | ||
| 23 | #:use-module (srfi srfi-1) | 24 | #:use-module (srfi srfi-1) |
| 24 | #:use-module (srfi srfi-26) | 25 | #:use-module (srfi srfi-26) |
| 25 | #:use-module (ice-9 match) | 26 | #:use-module (ice-9 match) |
| 26 | #:export (make-forkexec-constructor/container)) | 27 | #:export (make-forkexec-constructor/container |
| 28 | fork+exec-command/container)) | ||
| 27 | 29 | ||
| 28 | ;;; Commentary: | 30 | ;;; Commentary: |
| 29 | ;;; | 31 | ;;; |
| @@ -93,7 +95,8 @@ | |||
| 93 | ;; XXX: Lazy-bind the Shepherd to avoid a compile-time dependency. | 95 | ;; XXX: Lazy-bind the Shepherd to avoid a compile-time dependency. |
| 94 | (module-autoload! (current-module) | 96 | (module-autoload! (current-module) |
| 95 | '(shepherd service) | 97 | '(shepherd service) |
| 96 | '(read-pid-file exec-command %precious-signals)) | 98 | '(fork+exec-command read-pid-file exec-command |
| 99 | %precious-signals)) | ||
| 97 | (module-autoload! (current-module) | 100 | (module-autoload! (current-module) |
| 98 | '(shepherd system) '(unblock-signals)) | 101 | '(shepherd system) '(unblock-signals)) |
| 99 | 102 | ||
| @@ -188,6 +191,17 @@ namespace, in addition to essential bind-mounts such /proc." | |||
| 188 | (read-pid-file pid-file #:max-delay pid-file-timeout)) | 191 | (read-pid-file pid-file #:max-delay pid-file-timeout)) |
| 189 | pid)))) | 192 | pid)))) |
| 190 | 193 | ||
| 194 | (define* (fork+exec-command/container command | ||
| 195 | #:key pid | ||
| 196 | #:allow-other-keys | ||
| 197 | #:rest args) | ||
| 198 | "This is a variant of 'fork+exec-command' procedure, that joins the | ||
| 199 | namespaces of process PID beforehand." | ||
| 200 | (container-excursion* pid | ||
| 201 | (lambda () | ||
| 202 | (apply fork+exec-command command | ||
| 203 | (strip-keyword-arguments '(#:pid) args))))) | ||
| 204 | |||
| 191 | ;; Local Variables: | 205 | ;; Local Variables: |
| 192 | ;; eval: (put 'container-excursion* 'scheme-indent-function 1) | 206 | ;; eval: (put 'container-excursion* 'scheme-indent-function 1) |
| 193 | ;; End: | 207 | ;; End: |
diff --git a/gnu/installer/final.scm b/gnu/installer/final.scm index 685aa81d89b..11143b2adbe 100644 --- a/gnu/installer/final.scm +++ b/gnu/installer/final.scm | |||
| @@ -26,6 +26,8 @@ | |||
| 26 | #:use-module (guix build syscalls) | 26 | #:use-module (guix build syscalls) |
| 27 | #:use-module (guix build utils) | 27 | #:use-module (guix build utils) |
| 28 | #:use-module (gnu build accounts) | 28 | #:use-module (gnu build accounts) |
| 29 | #:use-module (gnu build install) | ||
| 30 | #:use-module (gnu build linux-container) | ||
| 29 | #:use-module ((gnu system shadow) #:prefix sys:) | 31 | #:use-module ((gnu system shadow) #:prefix sys:) |
| 30 | #:use-module (rnrs io ports) | 32 | #:use-module (rnrs io ports) |
| 31 | #:use-module (srfi srfi-1) | 33 | #:use-module (srfi srfi-1) |
| @@ -133,49 +135,18 @@ USERS." | |||
| 133 | (_ #f)))))) | 135 | (_ #f)))))) |
| 134 | pids))) | 136 | pids))) |
| 135 | 137 | ||
| 136 | (define (umount-cow-store) | ||
| 137 | "Remove the store overlay and the bind-mount on /tmp created by the | ||
| 138 | cow-store service. This procedure is very fragile and a better approach would | ||
| 139 | be much appreciated." | ||
| 140 | (catch #t | ||
| 141 | (lambda () | ||
| 142 | (let ((tmp-dir "/remove")) | ||
| 143 | (syslog "Unmounting cow-store.~%") | ||
| 144 | |||
| 145 | (mkdir-p tmp-dir) | ||
| 146 | (mount (%store-directory) tmp-dir "" MS_MOVE) | ||
| 147 | |||
| 148 | ;; The guix-daemon has possibly opened files from the cow-store, | ||
| 149 | ;; restart it. | ||
| 150 | (restart-service 'guix-daemon) | ||
| 151 | |||
| 152 | (syslog "Killing cow users.") | ||
| 153 | |||
| 154 | ;; Kill all processes started while the cow-store was active (logins | ||
| 155 | ;; on other TTYs for instance). | ||
| 156 | (kill-cow-users tmp-dir) | ||
| 157 | |||
| 158 | ;; Try to umount the store overlay. Some process such as udevd | ||
| 159 | ;; workers might still be active, so do some retries. | ||
| 160 | (let loop ((try 5)) | ||
| 161 | (syslog "Umount try ~a~%" (- 5 try)) | ||
| 162 | (sleep 1) | ||
| 163 | (let ((umounted? (false-if-exception (umount tmp-dir)))) | ||
| 164 | (if (and (not umounted?) (> try 0)) | ||
| 165 | (loop (- try 1)) | ||
| 166 | (if umounted? | ||
| 167 | (syslog "Umounted ~a successfully.~%" tmp-dir) | ||
| 168 | (syslog "Failed to umount ~a.~%" tmp-dir))))) | ||
| 169 | |||
| 170 | (umount "/tmp"))) | ||
| 171 | (lambda args | ||
| 172 | (syslog "~a~%" args)))) | ||
| 173 | |||
| 174 | (define* (install-system locale #:key (users '())) | 138 | (define* (install-system locale #:key (users '())) |
| 175 | "Create /etc/shadow and /etc/passwd on the installation target for USERS. | 139 | "Create /etc/shadow and /etc/passwd on the installation target for USERS. |
| 176 | Start COW-STORE service on target directory and launch guix install command in | 140 | Start COW-STORE service on target directory and launch guix install command in |
| 177 | a subshell. LOCALE must be the locale name under which that command will run, | 141 | a subshell. LOCALE must be the locale name under which that command will run, |
| 178 | or #f. Return #t on success and #f on failure." | 142 | or #f. Return #t on success and #f on failure." |
| 143 | (define backing-directory | ||
| 144 | ;; Sub-directory used as the backing store for copy-on-write. | ||
| 145 | "/tmp/guix-inst") | ||
| 146 | |||
| 147 | (define (assert-exit x) | ||
| 148 | (primitive-exit (if x 0 1))) | ||
| 149 | |||
| 179 | (let* ((options (catch 'system-error | 150 | (let* ((options (catch 'system-error |
| 180 | (lambda () | 151 | (lambda () |
| 181 | ;; If this file exists, it can provide | 152 | ;; If this file exists, it can provide |
| @@ -188,7 +159,11 @@ or #f. Return #t on success and #f on failure." | |||
| 188 | "--fallback") | 159 | "--fallback") |
| 189 | options | 160 | options |
| 190 | (list (%installer-configuration-file) | 161 | (list (%installer-configuration-file) |
| 191 | (%installer-target-dir))))) | 162 | (%installer-target-dir)))) |
| 163 | (database-dir "/var/guix/db") | ||
| 164 | (database-file (string-append database-dir "/db.sqlite")) | ||
| 165 | (saved-database (string-append database-dir "/db.save")) | ||
| 166 | (ret #f)) | ||
| 192 | (mkdir-p (%installer-target-dir)) | 167 | (mkdir-p (%installer-target-dir)) |
| 193 | 168 | ||
| 194 | ;; We want to initialize user passwords but we don't want to store them in | 169 | ;; We want to initialize user passwords but we don't want to store them in |
| @@ -198,27 +173,50 @@ or #f. Return #t on success and #f on failure." | |||
| 198 | ;; passwords that we've put in there. | 173 | ;; passwords that we've put in there. |
| 199 | (create-user-database users (%installer-target-dir)) | 174 | (create-user-database users (%installer-target-dir)) |
| 200 | 175 | ||
| 201 | (dynamic-wind | 176 | ;; When the store overlay is mounted, other processes such as kmscon, udev |
| 202 | (lambda () | 177 | ;; and guix-daemon may open files from the store, preventing the |
| 203 | (start-service 'cow-store (list (%installer-target-dir)))) | 178 | ;; underlying install support from being umounted. See: |
| 204 | (lambda () | 179 | ;; https://lists.gnu.org/archive/html/guix-devel/2018-12/msg00161.html. |
| 205 | ;; If there are any connected clients, assume that we are running | 180 | ;; |
| 206 | ;; installation tests. In that case, dump the standard and error | 181 | ;; To avoid this situation, mount the store overlay inside a container, |
| 207 | ;; outputs to syslog. | 182 | ;; and run the installation from within that container. |
| 208 | (if (not (null? (current-clients))) | 183 | (zero? |
| 209 | (with-output-to-file "/dev/console" | 184 | (call-with-container '() |
| 210 | (lambda () | 185 | (lambda () |
| 211 | (with-error-to-file "/dev/console" | 186 | (dynamic-wind |
| 212 | (lambda () | 187 | (lambda () |
| 213 | (setvbuf (current-output-port) 'none) | 188 | ;; Save the database, so that it can be restored once the |
| 214 | (setvbuf (current-error-port) 'none) | 189 | ;; cow-store is umounted. |
| 215 | (run-command install-command #:locale locale))))) | 190 | (copy-file database-file saved-database) |
| 216 | (run-command install-command #:locale locale))) | 191 | (mount-cow-store (%installer-target-dir) backing-directory)) |
| 217 | (lambda () | 192 | (lambda () |
| 218 | (stop-service 'cow-store) | 193 | ;; We need to drag the guix-daemon to the container MNT |
| 219 | ;; Remove the store overlay created at cow-store service start. | 194 | ;; namespace, so that it can operate on the cow-store. |
| 220 | ;; Failing to do that will result in further umount calls to fail | 195 | (stop-service 'guix-daemon) |
| 221 | ;; because the target device is seen as busy. See: | 196 | (start-service 'guix-daemon (list (number->string (getpid)))) |
| 222 | ;; https://lists.gnu.org/archive/html/guix-devel/2018-12/msg00161.html. | 197 | |
| 223 | (umount-cow-store) | 198 | (setvbuf (current-output-port) 'none) |
| 224 | #f)))) | 199 | (setvbuf (current-error-port) 'none) |
| 200 | |||
| 201 | ;; If there are any connected clients, assume that we are running | ||
| 202 | ;; installation tests. In that case, dump the standard and error | ||
| 203 | ;; outputs to syslog. | ||
| 204 | (set! ret | ||
| 205 | (if (not (null? (current-clients))) | ||
| 206 | (with-output-to-file "/dev/console" | ||
| 207 | (lambda () | ||
| 208 | (with-error-to-file "/dev/console" | ||
| 209 | (lambda () | ||
| 210 | (run-command install-command | ||
| 211 | #:locale locale))))) | ||
| 212 | (run-command install-command #:locale locale)))) | ||
| 213 | (lambda () | ||
| 214 | ;; Restart guix-daemon so that it does no keep the MNT namespace | ||
| 215 | ;; alive. | ||
| 216 | (restart-service 'guix-daemon) | ||
| 217 | (copy-file saved-database database-file) | ||
| 218 | |||
| 219 | ;; Finally umount the cow-store and exit the container. | ||
| 220 | (unmount-cow-store (%installer-target-dir) backing-directory) | ||
| 221 | (assert-exit ret)))) | ||
| 222 | #:namespaces '(mnt))))) | ||
diff --git a/gnu/installer/newt/final.scm b/gnu/installer/newt/final.scm index fa8d6fea718..89684c4d8ad 100644 --- a/gnu/installer/newt/final.scm +++ b/gnu/installer/newt/final.scm | |||
| @@ -102,13 +102,6 @@ a specific step, or restart the installer.")) | |||
| 102 | #:key (users '())) | 102 | #:key (users '())) |
| 103 | (clear-screen) | 103 | (clear-screen) |
| 104 | (newt-suspend) | 104 | (newt-suspend) |
| 105 | ;; XXX: Force loading 'bold' font files before mouting the | ||
| 106 | ;; cow-store. Otherwise, if the file is loaded by kmscon after the cow-store | ||
| 107 | ;; in mounted, it will be necessary to kill kmscon to umount to cow-store. | ||
| 108 | (display | ||
| 109 | (colorize-string | ||
| 110 | (format #f (G_ "Installing Guix System ...~%")) | ||
| 111 | (color BOLD))) | ||
| 112 | (let ((install-ok? (install-system locale #:users users))) | 105 | (let ((install-ok? (install-system locale #:users users))) |
| 113 | (newt-resume) | 106 | (newt-resume) |
| 114 | install-ok?)) | 107 | install-ok?)) |
diff --git a/gnu/services/base.scm b/gnu/services/base.scm index 491f35702a0..d560ad5a13b 100644 --- a/gnu/services/base.scm +++ b/gnu/services/base.scm | |||
| @@ -1558,57 +1558,72 @@ proxy of 'guix-daemon'...~%") | |||
| 1558 | (provision '(guix-daemon)) | 1558 | (provision '(guix-daemon)) |
| 1559 | (requirement '(user-processes)) | 1559 | (requirement '(user-processes)) |
| 1560 | (actions (list shepherd-set-http-proxy-action)) | 1560 | (actions (list shepherd-set-http-proxy-action)) |
| 1561 | (modules '((srfi srfi-1))) | 1561 | (modules '((srfi srfi-1) |
| 1562 | (ice-9 match) | ||
| 1563 | (gnu build shepherd))) | ||
| 1562 | (start | 1564 | (start |
| 1563 | #~(lambda _ | 1565 | (with-imported-modules (source-module-closure |
| 1564 | (define proxy | 1566 | '((gnu build shepherd))) |
| 1565 | ;; HTTP/HTTPS proxy. The 'http_proxy' variable is set by | 1567 | #~(lambda args |
| 1566 | ;; the 'set-http-proxy' action. | 1568 | (define proxy |
| 1567 | (or (getenv "http_proxy") #$http-proxy)) | 1569 | ;; HTTP/HTTPS proxy. The 'http_proxy' variable is set by |
| 1568 | 1570 | ;; the 'set-http-proxy' action. | |
| 1569 | (fork+exec-command | 1571 | (or (getenv "http_proxy") #$http-proxy)) |
| 1570 | (cons* #$(file-append guix "/bin/guix-daemon") | 1572 | |
| 1571 | "--build-users-group" #$build-group | 1573 | (fork+exec-command/container |
| 1572 | "--max-silent-time" #$(number->string max-silent-time) | 1574 | (cons* #$(file-append guix "/bin/guix-daemon") |
| 1573 | "--timeout" #$(number->string timeout) | 1575 | "--build-users-group" #$build-group |
| 1574 | "--log-compression" #$(symbol->string log-compression) | 1576 | "--max-silent-time" |
| 1575 | #$@(if use-substitutes? | 1577 | #$(number->string max-silent-time) |
| 1576 | '() | 1578 | "--timeout" #$(number->string timeout) |
| 1577 | '("--no-substitutes")) | 1579 | "--log-compression" |
| 1578 | "--substitute-urls" #$(string-join substitute-urls) | 1580 | #$(symbol->string log-compression) |
| 1579 | #$@extra-options | 1581 | #$@(if use-substitutes? |
| 1580 | 1582 | '() | |
| 1581 | ;; Add CHROOT-DIRECTORIES and all their dependencies | 1583 | '("--no-substitutes")) |
| 1582 | ;; (if these are store items) to the chroot. | 1584 | "--substitute-urls" #$(string-join substitute-urls) |
| 1583 | (append-map (lambda (file) | 1585 | #$@extra-options |
| 1584 | (append-map (lambda (directory) | 1586 | |
| 1585 | (list "--chroot-directory" | 1587 | ;; Add CHROOT-DIRECTORIES and all their dependencies |
| 1586 | directory)) | 1588 | ;; (if these are store items) to the chroot. |
| 1587 | (call-with-input-file file | 1589 | (append-map |
| 1588 | read))) | 1590 | (lambda (file) |
| 1589 | '#$(map references-file | 1591 | (append-map (lambda (directory) |
| 1590 | chroot-directories))) | 1592 | (list "--chroot-directory" |
| 1591 | 1593 | directory)) | |
| 1592 | #:environment-variables | 1594 | (call-with-input-file file |
| 1593 | (append (list #$@(if tmpdir | 1595 | read))) |
| 1594 | (list (string-append "TMPDIR=" tmpdir)) | 1596 | '#$(map references-file |
| 1595 | '()) | 1597 | chroot-directories))) |
| 1596 | 1598 | ||
| 1597 | ;; Make sure we run in a UTF-8 locale so that | 1599 | ;; When running the installer, we need guix-daemon to |
| 1598 | ;; 'guix offload' correctly restores nars that | 1600 | ;; operate from within the same MNT namespace as the |
| 1599 | ;; contain UTF-8 file names such as | 1601 | ;; installation container. In that case only, enter the |
| 1600 | ;; 'nss-certs'. See | 1602 | ;; namespace of the process PID passed as start argument. |
| 1601 | ;; <https://bugs.gnu.org/32942>. | 1603 | #:pid (match args |
| 1602 | (string-append "GUIX_LOCPATH=" | 1604 | ((pid) (string->number pid)) |
| 1603 | #$glibc-utf8-locales | 1605 | (else (getpid))) |
| 1604 | "/lib/locale") | 1606 | |
| 1605 | "LC_ALL=en_US.utf8") | 1607 | #:environment-variables |
| 1606 | (if proxy | 1608 | (append (list #$@(if tmpdir |
| 1607 | (list (string-append "http_proxy=" proxy) | 1609 | (list (string-append "TMPDIR=" tmpdir)) |
| 1608 | (string-append "https_proxy=" proxy)) | 1610 | '()) |
| 1609 | '())) | 1611 | |
| 1610 | 1612 | ;; Make sure we run in a UTF-8 locale so that | |
| 1611 | #:log-file #$log-file))) | 1613 | ;; 'guix offload' correctly restores nars |
| 1614 | ;; that contain UTF-8 file names such as | ||
| 1615 | ;; 'nss-certs'. See | ||
| 1616 | ;; <https://bugs.gnu.org/32942>. | ||
| 1617 | (string-append "GUIX_LOCPATH=" | ||
| 1618 | #$glibc-utf8-locales | ||
| 1619 | "/lib/locale") | ||
| 1620 | "LC_ALL=en_US.utf8") | ||
| 1621 | (if proxy | ||
| 1622 | (list (string-append "http_proxy=" proxy) | ||
| 1623 | (string-append "https_proxy=" proxy)) | ||
| 1624 | '())) | ||
| 1625 | |||
| 1626 | #:log-file #$log-file)))) | ||
| 1612 | (stop #~(make-kill-destructor)))))) | 1627 | (stop #~(make-kill-destructor)))))) |
| 1613 | 1628 | ||
| 1614 | (define (guix-accounts config) | 1629 | (define (guix-accounts config) |
