system: When unionfs-fuse is used for /, don't kill it when halting.

* guix/build/linux-initrd.scm (pidof): New procedure.
  (mount-root-file-system)[mark-as-not-killable]: New procedure.
  Use it for unionfs when VOLATILE-ROOT?.
* gnu/services/base.scm (%do-not-kill-file): New variable.
  (user-processes-service)[stop]: Honor it.
master
Ludovic Courtès 2014-05-14 16:38:21 +02:00
parent 17a4d34489
commit 7d57cfd3b6
2 changed files with 64 additions and 4 deletions

View File

@ -110,6 +110,11 @@ OPTIONS. When CHECK? is true, check the file system before mounting it."
(umount #$target) (umount #$target)
#f)))))) #f))))))
(define %do-not-kill-file
;; Name of the file listing PIDs of processes that must survive when halting
;; the system. Typical example is user-space file systems.
"/etc/dmd/do-not-kill")
(define* (user-processes-service requirements #:key (grace-delay 2)) (define* (user-processes-service requirements #:key (grace-delay 2))
"Return the service that is responsible for terminating all the processes so "Return the service that is responsible for terminating all the processes so
that the root file system can be re-mounted read-only, just before that the root file system can be re-mounted read-only, just before
@ -128,6 +133,25 @@ stopped before 'kill' is called."
(requirement (cons 'root-file-system requirements)) (requirement (cons 'root-file-system requirements))
(start #~(const #t)) (start #~(const #t))
(stop #~(lambda _ (stop #~(lambda _
(define (kill-except omit signal)
;; Kill all the processes with SIGNAL except those
;; listed in OMIT and the current process.
(let ((omit (cons (getpid) omit)))
(for-each (lambda (pid)
(unless (memv pid omit)
(false-if-exception
(kill pid signal))))
(processes))))
(define omitted-pids
;; List of PIDs that must not be killed.
(if (file-exists? #$%do-not-kill-file)
(map string->number
(call-with-input-file #$%do-not-kill-file
(compose string-tokenize
(@ (ice-9 rdelim) read-string))))
'()))
;; When this happens, all the processes have been ;; When this happens, all the processes have been
;; killed, including 'deco', so DMD-OUTPUT-PORT and ;; killed, including 'deco', so DMD-OUTPUT-PORT and
;; thus CURRENT-OUTPUT-PORT are dangling. ;; thus CURRENT-OUTPUT-PORT are dangling.
@ -136,9 +160,21 @@ stopped before 'kill' is called."
(display "sending all processes the TERM signal\n" (display "sending all processes the TERM signal\n"
port))) port)))
(kill -1 SIGTERM) (if (null? omitted-pids)
(sleep #$grace-delay) (begin
(kill -1 SIGKILL) ;; Easy: terminate all of them.
(kill -1 SIGTERM)
(sleep #$grace-delay)
(kill -1 SIGKILL))
(begin
;; Kill them all except OMITTED-PIDS. XXX: We
;; would like to (kill -1 SIGSTOP) to get a fixed
;; list of processes, like 'killall5' does, but
;; that seems unreliable.
(kill-except omitted-pids SIGTERM)
(sleep #$grace-delay)
(kill-except omitted-pids SIGKILL)
(delete-file #$%do-not-kill-file)))
(display "all processes have been terminated\n") (display "all processes have been terminated\n")
#f)) #f))

View File

@ -200,11 +200,30 @@ networking values.) Return #t if INTERFACE is up, #f otherwise."
the last argument of `mknod'." the last argument of `mknod'."
(+ (* major 256) minor)) (+ (* major 256) minor))
(define (pidof program)
"Return the PID of the first presumed instance of PROGRAM."
(let ((program (basename program)))
(find (lambda (pid)
(let ((exe (format #f "/proc/~a/exe" pid)))
(and=> (false-if-exception (readlink exe))
(compose (cut string=? program <>) basename))))
(filter-map string->number (scandir "/proc")))))
(define* (mount-root-file-system root type (define* (mount-root-file-system root type
#:key volatile-root? (unionfs "unionfs")) #:key volatile-root? (unionfs "unionfs"))
"Mount the root file system of type TYPE at device ROOT. If VOLATILE-ROOT? "Mount the root file system of type TYPE at device ROOT. If VOLATILE-ROOT?
is true, mount ROOT read-only and make it a union with a writable tmpfs using is true, mount ROOT read-only and make it a union with a writable tmpfs using
UNIONFS." UNIONFS."
(define (mark-as-not-killable pid)
;; Tell the 'user-processes' dmd service that PID must be kept alive when
;; shutting down.
(mkdir-p "/root/etc/dmd")
(let ((port (open-file "/root/etc/dmd/do-not-kill" "a")))
(chmod port #o600)
(write pid port)
(newline port)
(close-port port)))
(catch #t (catch #t
(lambda () (lambda ()
(if volatile-root? (if volatile-root?
@ -222,7 +241,12 @@ UNIONFS."
"cow,allow_other,use_ino,suid,dev" "cow,allow_other,use_ino,suid,dev"
"/rw-root=RW:/real-root=RO" "/rw-root=RW:/real-root=RO"
"/root")) "/root"))
(error "unionfs failed"))) (error "unionfs failed"))
;; Make sure unionfs remains alive till the end. Because
;; 'fuse_daemonize' doesn't tell the PID of the forked daemon, we
;; have to resort to 'pidof' here.
(mark-as-not-killable (pidof unionfs)))
(begin (begin
(check-file-system root type) (check-file-system root type)
(mount root "/root" type)))) (mount root "/root" type))))