guix-install.sh: Add the build users to the 'kvm' group.
[jackhill/guix/guix.git] / gnu / installer / steps.scm
CommitLineData
d0f3a672 1;;; GNU Guix --- Functional package management for GNU
33023baa 2;;; Copyright © 2018, 2019 Mathieu Othacehe <m.othacehe@gmail.com>
63b8c089 3;;; Copyright © 2020 Ludovic Courtès <ludo@gnu.org>
d0f3a672
MO
4;;;
5;;; This file is part of GNU Guix.
6;;;
7;;; GNU Guix is free software; you can redistribute it and/or modify it
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
12;;; GNU Guix is distributed in the hope that it will be useful, but
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20(define-module (gnu installer steps)
21 #:use-module (guix records)
dc5f3275 22 #:use-module (guix build utils)
63b8c089 23 #:use-module (gnu installer utils)
d0f3a672 24 #:use-module (ice-9 match)
dc5f3275 25 #:use-module (ice-9 pretty-print)
d0f3a672
MO
26 #:use-module (srfi srfi-1)
27 #:use-module (srfi srfi-34)
28 #:use-module (srfi srfi-35)
dc5f3275 29 #:use-module (rnrs io ports)
d0f3a672
MO
30 #:export (&installer-step-abort
31 installer-step-abort?
32
33 &installer-step-break
34 installer-step-break?
35
36 <installer-step>
37 installer-step
38 make-installer-step
39 installer-step?
40 installer-step-id
41 installer-step-description
42 installer-step-compute
dc5f3275 43 installer-step-configuration-formatter
d0f3a672
MO
44
45 run-installer-steps
46 find-step-by-id
47 result->step-ids
48 result-step
dc5f3275
MO
49 result-step-done?
50
51 %installer-configuration-file
52 %installer-target-dir
dc5f3275
MO
53 format-configuration
54 configuration->file))
d0f3a672
MO
55
56;; This condition may be raised to abort the current step.
57(define-condition-type &installer-step-abort &condition
58 installer-step-abort?)
59
60;; This condition may be raised to break out from the steps execution.
61(define-condition-type &installer-step-break &condition
62 installer-step-break?)
63
64;; An installer-step record is basically an id associated to a compute
65;; procedure. The COMPUTE procedure takes exactly one argument, an association
66;; list containing the results of previously executed installer-steps (see
67;; RUN-INSTALLER-STEPS description). The value returned by the COMPUTE
68;; procedure will be stored in the results list passed to the next
69;; installer-step and so on.
70(define-record-type* <installer-step>
71 installer-step make-installer-step
72 installer-step?
dc5f3275
MO
73 (id installer-step-id) ;symbol
74 (description installer-step-description ;string
7ae9979c
LC
75 (default #f)
76
77 ;; Make it thunked so that 'G_' is called at the
78 ;; right time, as opposed to being called once
79 ;; when the installer starts.
80 (thunked))
dc5f3275
MO
81 (compute installer-step-compute) ;procedure
82 (configuration-formatter installer-step-configuration-formatter ;procedure
83 (default #f)))
d0f3a672
MO
84
85(define* (run-installer-steps #:key
86 steps
87 (rewind-strategy 'previous)
88 (menu-proc (const #f)))
89 "Run the COMPUTE procedure of all <installer-step> records in STEPS
e1f37889 90sequentially. If the &installer-step-abort condition is raised, fallback to a
d0f3a672
MO
91previous install-step, accordingly to the specified REWIND-STRATEGY.
92
93REWIND-STRATEGY possible values are 'previous, 'menu and 'start. If 'previous
94is selected, the execution will resume at the previous installer-step. If
95'menu is selected, the MENU-PROC procedure will be called. Its return value
96has to be an installer-step ID to jump to. The ID has to be the one of a
97previously executed step. It is impossible to jump forward. Finally if 'start
98is selected, the execution will resume at the first installer-step.
99
100The result of every COMPUTE procedures is stored in an association list, under
101the form:
102
103 '((STEP-ID . COMPUTE-RESULT) ...)
104
105where STEP-ID is the ID field of the installer-step and COMPUTE-RESULT the
106result of the associated COMPUTE procedure. This result association list is
107passed as argument of every COMPUTE procedure. It is finally returned when the
108computation is over.
109
110If the &installer-step-break condition is raised, stop the computation and
111return the accumalated result so far."
112 (define (pop-result list)
113 (cdr list))
114
115 (define (first-step? steps step)
116 (match steps
117 ((first-step . rest-steps)
118 (equal? first-step step))))
119
120 (define* (skip-to-step step result
121 #:key todo-steps done-steps)
33023baa
MO
122 (match todo-steps
123 ((todo . rest-todo)
124 (let ((found? (eq? (installer-step-id todo)
125 (installer-step-id step))))
126 (cond
127 (found?
d0f3a672
MO
128 (run result
129 #:todo-steps todo-steps
33023baa
MO
130 #:done-steps done-steps))
131 ((and (not found?)
132 (null? done-steps))
133 (error (format #f "Step ~a not found" (installer-step-id step))))
134 (else
135 (match done-steps
136 ((prev-done ... last-done)
137 (skip-to-step step (pop-result result)
138 #:todo-steps (cons last-done todo-steps)
139 #:done-steps prev-done)))))))))
d0f3a672
MO
140
141 (define* (run result #:key todo-steps done-steps)
142 (match todo-steps
143 (() (reverse result))
144 ((step . rest-steps)
145 (guard (c ((installer-step-abort? c)
146 (case rewind-strategy
147 ((previous)
148 (match done-steps
149 (()
150 ;; We cannot go previous the first step. So re-raise
151 ;; the exception. It might be useful in the case of
152 ;; nested run-installer-steps. Abort to 'raise-above
153 ;; prompt to prevent the condition from being catched
154 ;; by one of the previously installed guard.
155 (abort-to-prompt 'raise-above c))
156 ((prev-done ... last-done)
157 (run (pop-result result)
158 #:todo-steps (cons last-done todo-steps)
159 #:done-steps prev-done))))
160 ((menu)
161 (let ((goto-step (menu-proc
162 (append done-steps (list step)))))
163 (if (eq? goto-step step)
164 (run result
165 #:todo-steps todo-steps
166 #:done-steps done-steps)
167 (skip-to-step goto-step result
168 #:todo-steps todo-steps
169 #:done-steps done-steps))))
170 ((start)
171 (if (null? done-steps)
172 ;; Same as above, it makes no sense to jump to start
173 ;; when we are at the first installer-step. Abort to
174 ;; 'raise-above prompt to re-raise the condition.
175 (abort-to-prompt 'raise-above c)
176 (run '()
177 #:todo-steps steps
178 #:done-steps '())))))
179 ((installer-step-break? c)
180 (reverse result)))
5c04b00c 181 (syslog "running step '~a'~%" (installer-step-id step))
d0f3a672
MO
182 (let* ((id (installer-step-id step))
183 (compute (installer-step-compute step))
dc5f3275 184 (res (compute result done-steps)))
d0f3a672
MO
185 (run (alist-cons id res result)
186 #:todo-steps rest-steps
187 #:done-steps (append done-steps (list step))))))))
188
63b8c089
LC
189 ;; Ignore SIGPIPE so that we don't die if a client closes the connection
190 ;; prematurely.
191 (sigaction SIGPIPE SIG_IGN)
192
193 (with-server-socket
194 (call-with-prompt 'raise-above
195 (lambda ()
196 (run '()
197 #:todo-steps steps
198 #:done-steps '()))
199 (lambda (k condition)
200 (raise condition)))))
d0f3a672
MO
201
202(define (find-step-by-id steps id)
203 "Find and return the step in STEPS whose id is equal to ID."
204 (find (lambda (step)
205 (eq? (installer-step-id step) id))
206 steps))
207
208(define (result-step results step-id)
209 "Return the result of the installer-step specified by STEP-ID in
210RESULTS."
211 (assoc-ref results step-id))
212
213(define (result-step-done? results step-id)
214 "Return #t if the installer-step specified by STEP-ID has a COMPUTE value
215stored in RESULTS. Return #f otherwise."
216 (and (assoc step-id results) #t))
dc5f3275
MO
217
218(define %installer-configuration-file (make-parameter "/mnt/etc/config.scm"))
219(define %installer-target-dir (make-parameter "/mnt"))
dc5f3275
MO
220
221(define (format-configuration steps results)
222 "Return the list resulting from the application of the procedure defined in
223CONFIGURATION-FORMATTER field of <installer-step> on the associated result
224found in RESULTS."
225 (let ((configuration
226 (append-map
227 (lambda (step)
228 (let* ((step-id (installer-step-id step))
229 (conf-formatter
230 (installer-step-configuration-formatter step))
231 (result-step (result-step results step-id)))
232 (if (and result-step conf-formatter)
233 (conf-formatter result-step)
234 '())))
235 steps))
236 (modules '((use-modules (gnu))
469e56b4 237 (use-service-modules desktop networking ssh xorg))))
dc5f3275
MO
238 `(,@modules
239 ()
240 (operating-system ,@configuration))))
241
242(define* (configuration->file configuration
243 #:key (filename (%installer-configuration-file)))
244 "Write the given CONFIGURATION to FILENAME."
245 (mkdir-p (dirname filename))
246 (call-with-output-file filename
247 (lambda (port)
248 (format port ";; This is an operating system configuration generated~%")
249 (format port ";; by the graphical installer.~%")
250 (newline port)
251 (for-each (lambda (part)
252 (if (null? part)
253 (newline port)
254 (pretty-print part port)))
255 configuration)
256 (flush-output-port port))))
63b8c089
LC
257
258;;; Local Variables:
259;;; eval: (put 'with-server-socket 'scheme-indent-function 0)
260;;; End: