(define-module (ruther bootloader grub)
#:use-module (guix gexp)
#:use-module (gnu bootloader)
#:use-module (gnu bootloader grub)
#:use-module (gnu packages bootloaders)
#:use-module (guix utils)
#:use-module (gnu system locale))
(define install-grub-efi-copy
(with-imported-modules '((guix build utils))
#~(lambda (bootloader efi-dir mount-point)
(use-modules (guix build utils)
(ice-9 textual-ports)
(srfi srfi-1))
;; There is nothing useful to do when called in the context of a disk
;; image generation.
(when efi-dir
;; Install GRUB onto the EFI partition mounted at EFI-DIR, for the
;; system whose root is mounted at MOUNT-POINT.
(let* ((grub-install (string-append bootloader "/sbin/grub-install"))
(install-dir (string-append mount-point "/boot"))
;; When installing Guix, it's common to mount EFI-DIR below
;; MOUNT-POINT rather than /boot/efi on the live image.
(target-esp (if (file-exists? (string-append mount-point efi-dir))
(string-append mount-point efi-dir)
efi-dir))
(grub-cfg #$(@@ (gnu bootloader grub) grub-cfg))
(grub-cfg-lines (string-split (call-with-input-file grub-cfg get-string-all) #\newline)))
;; Tell 'grub-install' that there might be a LUKS-encrypted /boot or
;; root partition.
(setenv "GRUB_ENABLE_CRYPTODISK" "y")
(invoke/quiet grub-install "--boot-directory" install-dir
"--bootloader-id=Guix"
"--efi-directory" target-esp)
;; After the grub is copied, we need to copy the gnu store.
;; To get the entries to copy we look into the grub.cfg file,
;; and obtain all lines starting with # NEEDED FILE:,
;; after : there is likely /gnu/store path that shall be copied to
;; /boot/gnu/store
;; for each line starting with NEEDED FILE:
;; Get the string after :, trim it, call it needed-file
;; (mkdir-p (dirname (string-append install-dir needed-file)))
;; (copy-file needed-file (string-append install-dir needed-file))
(for-each
(lambda (file-line)
(let* ((source-file (string-trim-both (substring file-line (string-length "# NEEDED FILE:"))))
(dest-file (string-append install-dir source-file)))
(mkdir-p (dirname dest-file))
(copy-recursively source-file dest-file)))
(delete-duplicates (filter
(lambda (line) (string-prefix? "# NEEDED FILE:" line))
grub-cfg-lines)))
;; TODO: remove files in install-dir under /boot/gnu/store that
;; are not alive anymore.
)))))
(define* (grub-copy-configuration-file config entries
#:key
(locale #f)
(system (%current-system))
(old-entries '())
(store-crypto-devices '())
store-directory-prefix)
;; To get all the needed files we need to get:
;; First from each entry linux and vmlinuz.
;; Then the image
;; Then
(let* ((menu-entry-needed-files
(lambda (menu-entry)
#~(list
#$(menu-entry-linux menu-entry)
#$(menu-entry-initrd menu-entry))))
(locales (and locale
((@@ (gnu bootloader grub) grub-locale-directory) grub-efi)))
(image ((@@ (gnu bootloader grub) grub-background-image) config))
(layout (bootloader-configuration-keyboard-layout config))
(keymap* (and layout
((@@ (gnu bootloader grub) keyboard-layout-file) layout #:grub grub-efi)))
(needed-files-gexps
#~(cons*
#$image
#$locales
#$keymap*
(apply append
(list #$@(map menu-entry-needed-files (append entries old-entries))))))
(modify-devices
(lambda (orig-menu-entry)
(menu-entry
(inherit orig-menu-entry)
(device #f)))) ;; This will force grub.scm to use search --file. Which will work, since the file is on one of the disks already!
(modified-entries (map modify-devices entries))
(modified-old-entries (map modify-devices old-entries))
(original-grub-cfg
((@@ (gnu bootloader grub) make-grub-configuration)
grub-efi config modified-entries
#:locale locale
#:system system
#:old-entries modified-old-entries
#:store-crypto-devices '()
#:store-directory-prefix #f))
(builder
#~(call-with-output-file #$output
(lambda (port)
;; First copy the original file.
(use-modules (ice-9 textual-ports))
(display (call-with-input-file #$original-grub-cfg get-string-all) port)
(display "\n\n" port)
(for-each
(lambda (file)
(display (string-append "# NEEDED FILE: " file "\n") port))
#$needed-files-gexps)))))
(computed-file "grub.cfg" builder
#:options '(#:local-build? #t
#:substitutable? #f))))
(define-public grub-efi-copy-bootloader
(bootloader
(name 'grub-copy-efi)
(package grub-efi)
(installer install-grub-efi-copy)
(disk-image-installer #f)
(configuration-file (@@ (gnu bootloader grub) grub-cfg))
(configuration-file-generator grub-copy-configuration-file)))
(define* (grub-lvm-configuration-file config entries
#:key
(locale #f)
(system (%current-system))
(old-entries '())
(store-crypto-devices '())
store-directory-prefix)
(let* ((original-grub-cfg
((@@ (gnu bootloader grub) make-grub-configuration)
grub-efi config entries
#:locale locale
#:system system
#:old-entries old-entries
#:store-crypto-devices store-crypto-devices
#:store-directory-prefix store-directory-prefix))
(builder
#~(call-with-output-file #$output
(lambda (port)
(use-modules (ice-9 textual-ports))
;; Sneek in insmod lvm at beginning of the file
(display "insmod lvm\n" port)
;; After, copy the original file.
(display (call-with-input-file #$original-grub-cfg get-string-all) port)))))
(computed-file "grub.cfg" builder
#:options '(#:local-build? #t
#:substitutable? #f))))
(define-public grub-lvm-bootloader
(bootloader
(inherit grub-efi-removable-bootloader)
(configuration-file-generator grub-lvm-configuration-file)))