installer: Fix cow-store umount.

This fixes <https://bugs.gnu.org/39712>.

The guix-daemon was preventing the cow-store umount, so restart it.  Some
udevd workers, using cow-store files might also still be around, so have some
umount retries.

* gnu/installer/final.scm (kill-cow-users): New procedure,
(umount-cow-store): restart guix-daemon and kill all processes started from
within the cow-store before trying to umount the store overlay. Also try 5
times to umount the overlay in case it is still busy.
This commit is contained in:
Mathieu Othacehe 2020-02-27 16:22:28 +01:00 committed by Ludovic Courtès
parent ccb1a8c437
commit 64704be417
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5

View file

@ -28,6 +28,12 @@ (define-module (gnu installer final)
#:use-module (gnu build accounts)
#:use-module ((gnu system shadow) #:prefix sys:)
#:use-module (rnrs io ports)
#:use-module (srfi srfi-1)
#:use-module (ice-9 ftw)
#:use-module (ice-9 popen)
#:use-module (ice-9 match)
#:use-module (ice-9 format)
#:use-module (ice-9 rdelim)
#:export (install-system))
(define %seed
@ -97,14 +103,73 @@ (define-values (group password shadow)
(write-passwd password (string-append etc "/passwd"))
(write-shadow shadow (string-append etc "/shadow")))
(define* (kill-cow-users cow-path #:key (spare '("udevd")))
"Kill all processes that have references to the given COW-PATH in their
'maps' file. The process whose names are in SPARE list are spared."
(define %not-nul
(char-set-complement (char-set #\nul)))
(let ((pids
(filter-map (lambda (pid)
(call-with-input-file
(string-append "/proc/" pid "/maps")
(lambda (port)
(and (string-contains (get-string-all port)
cow-path)
(string->number pid)))))
(scandir "/proc" string->number))))
(for-each (lambda (pid)
;; cmdline does not always exist.
(false-if-exception
(call-with-input-file
(string-append "/proc/" (number->string pid) "/cmdline")
(lambda (port)
(match (string-tokenize (read-string port) %not-nul)
((argv0 _ ...)
(unless (member (pk (basename argv0)) spare)
(syslog "Killing process ~a~%" pid)
(kill pid SIGKILL)))
(_ #f))))))
pids)))
(define (umount-cow-store)
"Remove the store overlay and the bind-mount on /tmp created by the
cow-store service."
(let ((tmp-dir "/remove"))
(mkdir-p tmp-dir)
(mount (%store-directory) tmp-dir "" MS_MOVE)
(umount tmp-dir)
(umount "/tmp")))
cow-store service. This procedure is very fragile and a better approach would
be much appreciated."
;; Remove when integrated in (gnu services herd).
(define (restart-service name)
(with-shepherd-action name ('restart) result
result))
(catch #t
(lambda ()
(let ((tmp-dir "/remove"))
(mkdir-p tmp-dir)
(mount (%store-directory) tmp-dir "" MS_MOVE)
;; The guix-daemon has possibly opened files from the cow-store,
;; restart it.
(restart-service 'guix-daemon)
;; Kill all processes started while the cow-store was active (logins
;; on other TTYs for instance).
(kill-cow-users tmp-dir)
;; Try to umount the store overlay. Some process such as udevd
;; workers might still be active, so do some retries.
(let loop ((try 5))
(sleep 1)
(let ((umounted? (false-if-exception (umount tmp-dir))))
(if (and (not umounted?) (> try 0))
(loop (- try 1))
(if umounted?
(syslog "Umounted ~a successfully.~%" tmp-dir)
(syslog "Failed to umount ~a.~%" tmp-dir)))))
(umount "/tmp")))
(lambda args
(syslog "~a~%" args))))
(define* (install-system locale #:key (users '()))
"Create /etc/shadow and /etc/passwd on the installation target for USERS.