summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathieu Othacehe <othacehe@gnu.org>2020-08-13 14:16:12 +0200
committerMathieu Othacehe <othacehe@gnu.org>2020-09-02 17:05:23 +0200
commit8ce6f4dc2879919c12bc76a2f4b01200af97e019 (patch)
treebcdfea85d25af8ae24622310a035688ac8257dcc
parent5316dfc0f125b658e4a2acf7f00f49501663d943 (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.scm18
-rw-r--r--gnu/installer/final.scm124
-rw-r--r--gnu/installer/newt/final.scm7
-rw-r--r--gnu/services/base.scm115
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
199namespaces 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
138cow-store service. This procedure is very fragile and a better approach would
139be 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.
176Start COW-STORE service on target directory and launch guix install command in 140Start COW-STORE service on target directory and launch guix install command in
177a subshell. LOCALE must be the locale name under which that command will run, 141a subshell. LOCALE must be the locale name under which that command will run,
178or #f. Return #t on success and #f on failure." 142or #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)