The more I think about it the more it seems we should re-enter our
initramfs on shutdown.  Unfortunately, the Linux kernel frees it when
switching to the real root fs.  Not sure how to get Linux to find it again
(or how to get Linux to not free it in the first place) and to pivot_root
to it again (with some arg that we can use to indicate to us that we
are shutting the system down).

See also <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=778849>.

Regardless, my current non-initramfs attempt is:

(define %root-file-system-shepherd-service
  (shepherd-service
   (documentation "Take care of the root file system.")
   (provision '(root-file-system))
   (start #~(const #t))
   (stop #~(lambda _
             ;;; stop: To return #f if successfully stopped.

             (define (call-with-mounted-filesystem source mountpoint 
filesystem-type options proc)
               (mount source mountpoint file-system-type options #:update-mtab? 
#f)
               (catch #t proc
                 (lambda args
                   (umount mountpoint))))

             (sync)

             (let* ((null (%make-void-port "w"))
                    (call-with-io-file (lambda (file-name proc)
                                         (let ((port (open file-name O_RDWR)))
                                           (set-current-input-port port)
                                           (set-current-output-port port)
                                           (set-current-error-port port)
                                           (catch #t proc
                                            (lambda args
                                              (set-current-input-port null)
                                              (set-current-output-port null)
                                              (set-current-error-port  null)
                                              (close port))))))
                     (with-mounted-filesystem (syntax-rules ()
                                               ((source filesystem-type 
mountpoint options . exps)
                                                (call-with-mounted-filesystem 
source filesystem-type mountpoint options
                                                 (lambda () . exps))))))
               ;; Redirect the default output ports.
               (set-current-output-port null)
               (set-current-error-port null)

               ;; Close /dev/console.
               (for-each close-fdes '(0 1 2))

               ;; At this point, there should be no open files left so the
               ;; root file system can be re-mounted read-only.
               (let loop ((n 10))
                 (unless (catch 'system-error
                           (lambda ()
                             (mount #f "/" #f
                                    (logior MS_REMOUNT MS_RDONLY)
                                    #:update-mtab? #f)
                             #t)
                           (const #f))
                   ;;; Note: Alternatively, if we still had it, we could try 
pivot_root into the initrd.
                   ;;; (where we'd have a magic file to tell it that we are 
shutting down instead of booting).
                   ;;; Note: dracut can do it, see 
<https://www.kernel.org/pub/linux/utils/boot/dracut/dracut.html#_dracut_on_shutdown>.
                   ;;; See also 
<https://unix.stackexchange.com/questions/215169/get-back-to-initramfs-on-shutdown>.
                   (when (zero? n)
                     (with-mounted-filesystem "none" "/proc" "proc" 0
                       (with-mounted-filesystem "none" "/dev"  "devtmpfs" 0
                         (catch 'system-error
                           (mknod "/dev/tty" 'char-special #o600 (+ (* 5 256) 
0))
                           (const #f))
                         ;; TODO: Or make fuser (in package psmisc) static and 
don't put it in the store either.
                         ;; although we'd still need to resolve the symlink 
/run/booted-system and so on.
                         ;; We could also traverse /proc ourselves, 
reimplementing fuser.
                         (with-mounted-filesystem "/" #t "/gnu/store" (logior 
MS_BIND MS_READONLY)
                           (call-with-io-file "/dev/tty"
                             (lambda ()
                               (chvt 12) ; we don't have that (it would need to 
use ioctl VT_ACTIVATE int on /dev/tty)
                               ;; The "w" option only finds writers.
                               ;; The "i" option asks the user interactively 
what to kill.
                               (system* "/run/booted-system/profile/bin/fuser" 
"-vikwm" "/")))))))

                     ;;; If we had a rescue container, we'd have to had stopped 
it at this point.

                     (unless (catch 'system-error
                               (lambda ()
                                 (mount #f "/" #f
                                        (logior MS_REMOUNT MS_RDONLY)
                                        #:update-mtab? #f)
                                 #t)
                               (const #f))
                       (catch 'system-error
                         (mknod "/tty" 'char-special #o600 (+ (* 5 256) 0))
                         (const #f))
                       (with-io-file "/tty"
                         (display "umount / still not good\n")
                         (force-output)))
                     ((@ (fibers) sleep) 10))
                   (unless (zero? n)
                     ;; Yield to the other fibers.  That gives logging fibers
                     ;; an opportunity to close log files so the 'mount' call
                     ;; doesn't fail with EBUSY.
                     ((@ (fibers) sleep) 1)
                     (loop (- n 1)))))

               #f)))
   (respawn? #f)))

Reply via email to