Merge branch 'master' into core-updates
[jackhill/guix/guix.git] / gnu / system / grub.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2013, 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
3 ;;;
4 ;;; This file is part of GNU Guix.
5 ;;;
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
10 ;;;
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
15 ;;;
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19 (define-module (gnu system grub)
20 #:use-module (guix store)
21 #:use-module (guix packages)
22 #:use-module (guix derivations)
23 #:use-module (guix records)
24 #:use-module (guix monads)
25 #:use-module (guix gexp)
26 #:use-module (guix download)
27 #:use-module (gnu artwork)
28 #:use-module (gnu system file-systems)
29 #:autoload (gnu packages grub) (grub)
30 #:autoload (gnu packages compression) (gzip)
31 #:autoload (gnu packages gtk) (guile-cairo guile-rsvg)
32 #:use-module (ice-9 match)
33 #:use-module (ice-9 regex)
34 #:use-module (srfi srfi-1)
35 #:export (grub-image
36 grub-image?
37 grub-image-aspect-ratio
38 grub-image-file
39
40 grub-theme
41 grub-theme?
42 grub-theme-images
43 grub-theme-color-normal
44 grub-theme-color-highlight
45
46 %background-image
47 %default-theme
48
49 grub-configuration
50 grub-configuration?
51 grub-configuration-device
52
53 menu-entry
54 menu-entry?
55
56 grub-configuration-file))
57
58 ;;; Commentary:
59 ;;;
60 ;;; Configuration of GNU GRUB.
61 ;;;
62 ;;; Code:
63
64 (define (strip-mount-point fs file)
65 "Strip the mount point of FS from FILE, which is a gexp or other lowerable
66 object denoting a file name."
67 (let ((mount-point (file-system-mount-point fs)))
68 (if (string=? mount-point "/")
69 file
70 #~(let ((file #$file))
71 (if (string-prefix? #$mount-point file)
72 (substring #$file #$(string-length mount-point))
73 file)))))
74
75 (define-record-type* <grub-image>
76 grub-image make-grub-image
77 grub-image?
78 (aspect-ratio grub-image-aspect-ratio ;rational number
79 (default 4/3))
80 (file grub-image-file)) ;file-valued gexp (SVG)
81
82 (define-record-type* <grub-theme>
83 grub-theme make-grub-theme
84 grub-theme?
85 (images grub-theme-images
86 (default '())) ;list of <grub-image>
87 (color-normal grub-theme-color-normal
88 (default '((fg . cyan) (bg . blue))))
89 (color-highlight grub-theme-color-highlight
90 (default '((fg . white) (bg . blue)))))
91
92 (define %background-image
93 (grub-image
94 (aspect-ratio 4/3)
95 (file #~(string-append #$%artwork-repository
96 "/grub/GuixSD-fully-black-4-3.svg"))))
97
98 (define %default-theme
99 ;; Default theme contributed by Felipe López.
100 (grub-theme
101 (images (list %background-image))
102 (color-highlight '((fg . yellow) (bg . black)))
103 (color-normal '((fg . light-gray) (bg . black))))) ;XXX: #x303030
104
105 (define-record-type* <grub-configuration>
106 grub-configuration make-grub-configuration
107 grub-configuration?
108 (grub grub-configuration-grub ; package
109 (default (@ (gnu packages grub) grub)))
110 (device grub-configuration-device) ; string
111 (menu-entries grub-configuration-menu-entries ; list
112 (default '()))
113 (default-entry grub-configuration-default-entry ; integer
114 (default 0))
115 (timeout grub-configuration-timeout ; integer
116 (default 5))
117 (theme grub-configuration-theme ; <grub-theme>
118 (default %default-theme)))
119
120 (define-record-type* <menu-entry>
121 menu-entry make-menu-entry
122 menu-entry?
123 (label menu-entry-label)
124 (linux menu-entry-linux)
125 (linux-arguments menu-entry-linux-arguments
126 (default '())) ; list of string-valued gexps
127 (initrd menu-entry-initrd)) ; file name of the initrd as a gexp
128
129 \f
130 ;;;
131 ;;; Background image & themes.
132 ;;;
133
134 (define* (svg->png svg #:key width height)
135 "Build a PNG of HEIGHT x WIDTH from SVG."
136 (gexp->derivation "grub-image.png"
137 (with-imported-modules '((gnu build svg))
138 #~(begin
139 ;; We need these two libraries.
140 (add-to-load-path (string-append #$guile-rsvg
141 "/share/guile/site/"
142 (effective-version)))
143 (add-to-load-path (string-append #$guile-cairo
144 "/share/guile/site/"
145 (effective-version)))
146
147 (use-modules (gnu build svg))
148 (svg->png #$svg #$output
149 #:width #$width
150 #:height #$height)))))
151
152 (define* (grub-background-image config #:key (width 1024) (height 768))
153 "Return the GRUB background image defined in CONFIG with a ratio of
154 WIDTH/HEIGHT, or #f if none was found."
155 (let* ((ratio (/ width height))
156 (image (find (lambda (image)
157 (= (grub-image-aspect-ratio image) ratio))
158 (grub-theme-images (grub-configuration-theme config)))))
159 (if image
160 (svg->png (grub-image-file image)
161 #:width width #:height height)
162 (with-monad %store-monad
163 (return #f)))))
164
165 (define (eye-candy config root-fs system port)
166 "Return in %STORE-MONAD a gexp that writes to PORT (a port-valued gexp) the
167 'grub.cfg' part concerned with graphics mode, background images, colors, and
168 all that. ROOT-FS is a file-system object denoting the root file system where
169 the store is. SYSTEM must be the target system string---e.g.,
170 \"x86_64-linux\"."
171 (define setup-gfxterm-body
172 ;; Intel systems need to be switched into graphics mode, whereas most
173 ;; other modern architectures have no other mode and therefore don't need
174 ;; to be switched.
175 (if (string-match "^(x86_64|i[3-6]86)-" system)
176 "
177 # Leave 'gfxmode' to 'auto'.
178 insmod vbe
179 insmod vga
180 insmod video_bochs
181 insmod video_cirrus
182 insmod gfxterm
183 terminal_output gfxterm
184 "
185 ""))
186
187 (define (theme-colors type)
188 (let* ((theme (grub-configuration-theme config))
189 (colors (type theme)))
190 (string-append (symbol->string (assoc-ref colors 'fg)) "/"
191 (symbol->string (assoc-ref colors 'bg)))))
192
193 (define font-file
194 (strip-mount-point root-fs
195 (file-append grub "/share/grub/unicode.pf2")))
196
197 (mlet* %store-monad ((image (grub-background-image config)))
198 (return (and image
199 #~(format #$port "
200 function setup_gfxterm {~a}
201
202 # Set 'root' to the partition that contains /gnu/store.
203 ~a
204
205 if loadfont ~a; then
206 setup_gfxterm
207 fi
208
209 insmod png
210 if background_image ~a; then
211 set color_normal=~a
212 set color_highlight=~a
213 else
214 set menu_color_normal=cyan/blue
215 set menu_color_highlight=white/blue
216 fi~%"
217 #$setup-gfxterm-body
218 #$(grub-root-search root-fs font-file)
219 #$font-file
220
221 #$(strip-mount-point root-fs image)
222 #$(theme-colors grub-theme-color-normal)
223 #$(theme-colors grub-theme-color-highlight))))))
224
225 \f
226 ;;;
227 ;;; Configuration file.
228 ;;;
229
230 (define (grub-root-search root-fs file)
231 "Return the GRUB 'search' command to look for ROOT-FS, which contains FILE,
232 a gexp. The result is a gexp that can be inserted in the grub.cfg-generation
233 code."
234 ;; Usually FILE is a file name gexp like "/gnu/store/…-linux/vmlinuz", but
235 ;; it can also be something like "(hd0,msdos1)/vmlinuz" in the case of
236 ;; custom menu entries. In the latter case, don't emit a 'search' command.
237 (if (and (string? file) (not (string-prefix? "/" file)))
238 ""
239 (case (file-system-title root-fs)
240 ;; Preferably refer to ROOT-FS by its UUID or label. This is more
241 ;; efficient and less ambiguous, see <>.
242 ((uuid)
243 (format #f "search --fs-uuid --set ~a"
244 (uuid->string (file-system-device root-fs))))
245 ((label)
246 (format #f "search --label --set ~a"
247 (file-system-device root-fs)))
248 (else
249 ;; As a last resort, look for any device containing FILE.
250 #~(format #f "search --file --set ~a" #$file)))))
251
252 (define* (grub-configuration-file config store-fs entries
253 #:key
254 (system (%current-system))
255 (old-entries '()))
256 "Return the GRUB configuration file corresponding to CONFIG, a
257 <grub-configuration> object, and where the store is available at STORE-FS, a
258 <file-system> object. OLD-ENTRIES is taken to be a list of menu entries
259 corresponding to old generations of the system."
260 (define all-entries
261 (append entries (grub-configuration-menu-entries config)))
262
263 (define entry->gexp
264 (match-lambda
265 (($ <menu-entry> label linux arguments initrd)
266 ;; Use the right file names for LINUX and STORE-FS in case STORE-FS is
267 ;; not the "/" file system.
268 (let ((linux (strip-mount-point store-fs linux))
269 (initrd (strip-mount-point store-fs initrd)))
270 #~(format port "menuentry ~s {
271 ~a
272 linux ~a ~a
273 initrd ~a
274 }~%"
275 #$label
276 #$(grub-root-search store-fs linux)
277 #$linux (string-join (list #$@arguments))
278 #$initrd)))))
279
280 (mlet %store-monad ((sugar (eye-candy config store-fs system #~port)))
281 (define builder
282 #~(call-with-output-file #$output
283 (lambda (port)
284 (format port
285 "# This file was generated from your GuixSD configuration. Any changes
286 # will be lost upon reconfiguration.
287 ")
288 #$sugar
289 (format port "
290 set default=~a
291 set timeout=~a~%"
292 #$(grub-configuration-default-entry config)
293 #$(grub-configuration-timeout config))
294 #$@(map entry->gexp all-entries)
295
296 #$@(if (pair? old-entries)
297 #~((format port "
298 submenu \"GNU system, old configurations...\" {~%")
299 #$@(map entry->gexp old-entries)
300 (format port "}~%"))
301 #~()))))
302
303 (gexp->derivation "grub.cfg" builder)))
304
305 ;;; grub.scm ends here