1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2016 Alex Kost <alezost@gmail.com>
4 ;;; Copyright © 2016, 2017, 2018 Chris Marusich <cmmarusich@gmail.com>
5 ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
6 ;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
7 ;;; Copyright © 2019 Christopher Baines <mail@cbaines.net>
9 ;;; This file is part of GNU Guix.
11 ;;; GNU Guix is free software; you can redistribute it and/or modify it
12 ;;; under the terms of the GNU General Public License as published by
13 ;;; the Free Software Foundation; either version 3 of the License, or (at
14 ;;; your option) any later version.
16 ;;; GNU Guix is distributed in the hope that it will be useful, but
17 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;;; GNU General Public License for more details.
21 ;;; You should have received a copy of the GNU General Public License
22 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
24 (define-module (guix scripts system)
25 #:use-module (guix config)
26 #:use-module (guix ui)
27 #:use-module ((guix status) #:select (with-status-verbosity))
28 #:use-module (guix store)
29 #:autoload (guix store database) (register-path)
30 #:use-module (guix grafts)
31 #:use-module (guix gexp)
32 #:use-module (guix derivations)
33 #:use-module (guix packages)
34 #:use-module (guix utils)
35 #:use-module (guix monads)
36 #:use-module (guix records)
37 #:use-module (guix profiles)
38 #:use-module (guix scripts)
39 #:use-module (guix scripts build)
40 #:autoload (guix scripts package) (delete-generations
41 delete-matching-generations)
42 #:use-module (guix graph)
43 #:use-module (guix scripts graph)
44 #:use-module (guix build utils)
45 #:use-module (guix progress)
46 #:use-module ((guix build syscalls) #:select (terminal-columns))
47 #:use-module (gnu build install)
48 #:autoload (gnu build file-systems)
49 (find-partition-by-label find-partition-by-uuid)
50 #:autoload (gnu build linux-modules)
51 (device-module-aliases matching-modules)
52 #:use-module (gnu system linux-initrd)
53 #:use-module (gnu system)
54 #:use-module (gnu bootloader)
55 #:use-module (gnu system file-systems)
56 #:use-module (gnu system mapped-devices)
57 #:use-module (gnu system linux-container)
58 #:use-module (gnu system uuid)
59 #:use-module (gnu system vm)
60 #:use-module (gnu services)
61 #:use-module (gnu services shepherd)
62 #:use-module (gnu services herd)
63 #:use-module (srfi srfi-1)
64 #:use-module (srfi srfi-11)
65 #:use-module (srfi srfi-19)
66 #:use-module (srfi srfi-26)
67 #:use-module (srfi srfi-34)
68 #:use-module (srfi srfi-35)
69 #:use-module (srfi srfi-37)
70 #:use-module (ice-9 match)
71 #:use-module (rnrs bytevectors)
73 read-operating-system))
77 ;;; Operating system declaration.
81 ;; Module in which the machine description file is loaded.
82 (make-user-module '((gnu system)
84 (gnu system shadow))))
86 (define (read-operating-system file)
87 "Read the operating-system declaration from FILE and return it."
88 (load* file %user-module))
95 (define-syntax-rule (save-load-path-excursion body ...)
96 "Save the current values of '%load-path' and '%load-compiled-path', run
97 BODY..., and restore them."
98 (let ((path %load-path)
99 (cpath %load-compiled-path))
105 (set! %load-path path)
106 (set! %load-compiled-path cpath)))))
108 (define-syntax-rule (save-environment-excursion body ...)
109 "Save the current environment variables, run BODY..., and restore them."
110 (let ((env (environ)))
118 (define topologically-sorted*
119 (store-lift topologically-sorted))
122 (define* (copy-item item references target
123 #:key (log-port (current-error-port)))
124 "Copy ITEM to the store under root directory TARGET and register it with
125 REFERENCES as its set of references."
126 (let ((dest (string-append target item))
127 (state (string-append target "/var/guix")))
128 (format log-port "copying '~a'...~%" item)
130 ;; Remove DEST if it exists to make sure that (1) we do not fail badly
131 ;; while trying to overwrite it (see <http://bugs.gnu.org/20722>), and
132 ;; (2) we end up with the right contents.
133 (when (false-if-exception (lstat dest))
134 (for-each make-file-writable
135 (find-files dest (lambda (file stat)
136 (eq? 'directory (stat:type stat)))
138 (delete-file-recursively dest))
140 (copy-recursively item dest
141 #:log (%make-void-port "w"))
143 ;; Register ITEM; as a side-effect, it resets timestamps, etc.
144 ;; Explicitly use "TARGET/var/guix" as the state directory, to avoid
145 ;; reproducing the user's current settings; see
146 ;; <http://bugs.gnu.org/18049>.
147 (unless (register-path item
149 #:state-directory state
150 #:references references)
151 (leave (G_ "failed to register '~a' under '~a'~%")
154 (define* (copy-closure item target
155 #:key (log-port (current-error-port)))
156 "Copy ITEM and all its dependencies to the store under root directory
157 TARGET, and register them."
158 (mlet* %store-monad ((to-copy (topologically-sorted* (list item)))
159 (refs (mapm %store-monad references* to-copy))
160 (info (mapm %store-monad query-path-info*
162 (append to-copy (concatenate refs)))))
163 (size -> (reduce + 0 (map path-info-nar-size info))))
165 (progress-reporter/bar (length to-copy)
166 (format #f (G_ "copying to '~a'...")
169 (check-available-space size target)
171 (call-with-progress-reporter progress-bar
173 (let ((void (%make-void-port "w")))
174 (for-each (lambda (item refs)
175 (copy-item item refs target #:log-port void)
179 (return *unspecified*)))
181 (define* (install-bootloader installer
185 "Run INSTALLER, a bootloader installation script, with error handling, in
187 (mlet %store-monad ((installer-drv (if installer
188 (lower-object installer)
190 (bootcfg (lower-object bootcfg)))
191 (let* ((gc-root (string-append target %gc-roots-directory
193 (temp-gc-root (string-append gc-root ".new"))
194 (install (and installer-drv
195 (derivation->output-path installer-drv)))
196 (bootcfg (derivation->output-path bootcfg)))
197 ;; Prepare the symlink to bootloader config file to make sure that it's
198 ;; a GC root when 'installer-drv' completes (being a bit paranoid.)
199 (switch-symlinks temp-gc-root bootcfg)
201 (unless (false-if-exception
203 (install-boot-config bootcfg bootcfg-file target)
205 (save-load-path-excursion (primitive-load install)))))
206 (delete-file temp-gc-root)
207 (leave (G_ "failed to install bootloader ~a~%") install))
209 ;; Register bootloader config file as a GC root so that its dependencies
210 ;; (background image, font, etc.) are not reclaimed.
211 (rename-file temp-gc-root gc-root)
214 (define* (install os-drv target
215 #:key (log-port (current-output-port))
216 bootloader-installer install-bootloader?
217 bootcfg bootcfg-file)
218 "Copy the closure of BOOTCFG, which includes the output of OS-DRV, to
219 directory TARGET. TARGET must be an absolute directory name since that's what
220 'register-path' expects.
222 When INSTALL-BOOTLOADER? is true, install bootloader using BOOTCFG."
223 (define (maybe-copy to-copy)
224 (with-monad %store-monad
225 (if (string=? target "/")
227 (warning (G_ "initializing the current root file system~%"))
230 ;; Make sure the target store exists.
231 (mkdir-p (string-append target (%store-prefix)))
233 ;; Copy items to the new store.
234 (copy-closure to-copy target #:log-port log-port)))))
236 ;; Make sure TARGET is root-owned when running as root, but still allow
237 ;; non-root uses (useful for testing.) See
238 ;; <http://lists.gnu.org/archive/html/guix-devel/2015-05/msg00452.html>.
239 (if (zero? (geteuid))
241 (warning (G_ "not running as 'root', so \
242 the ownership of '~a' may be incorrect!~%")
245 ;; If a previous installation was attempted, make sure we start anew; in
246 ;; particular, we don't want to keep a store database that might not
247 ;; correspond to what we're actually putting in the store.
248 (let ((state (string-append target "/var/guix")))
249 (when (file-exists? state)
250 (delete-file-recursively state)))
253 (let ((os-dir (derivation->output-path os-drv))
254 (format (lift format %store-monad))
255 (populate (lift2 populate-root-file-system %store-monad)))
257 (mlet %store-monad ((bootcfg (lower-object bootcfg)))
259 ;; Copy the closure of BOOTCFG, which includes OS-DIR,
260 ;; eventual background image and so on.
261 (maybe-copy (derivation->output-path bootcfg))
263 ;; Create a bunch of additional files.
264 (format log-port "populating '~a'...~%" target)
265 (populate os-dir target)
267 (mwhen install-bootloader?
268 (install-bootloader bootloader-installer
270 #:bootcfg-file bootcfg-file
271 #:target target))))))
278 (define %system-profile
279 ;; The system profile.
280 (string-append %state-directory "/profiles/system"))
282 (define-syntax-rule (with-shepherd-error-handling mbody ...)
283 "Catch and report Shepherd errors that arise when binding MBODY, a monadic
284 expression in %STORE-MONAD."
288 (guard (c ((shepherd-error? c)
289 (values (report-shepherd-error c) store)))
290 (values (run-with-store store (begin mbody ...))
292 (lambda (key proc format-string format-args errno . rest)
293 (warning (G_ "while talking to shepherd: ~a~%")
294 (apply format #f format-string format-args))
295 (values #f store)))))
297 (define (report-shepherd-error error)
298 "Report ERROR, a '&shepherd-error' error condition object."
299 (cond ((service-not-found-error? error)
300 (report-error (G_ "service '~a' could not be found~%")
301 (service-not-found-error-service error)))
302 ((action-not-found-error? error)
303 (report-error (G_ "service '~a' does not have an action '~a'~%")
304 (action-not-found-error-service error)
305 (action-not-found-error-action error)))
306 ((action-exception-error? error)
307 (report-error (G_ "exception caught while executing '~a' \
309 (action-exception-error-action error)
310 (action-exception-error-service error))
311 (print-exception (current-error-port) #f
312 (action-exception-error-key error)
313 (action-exception-error-arguments error)))
314 ((unknown-shepherd-error? error)
315 (report-error (G_ "something went wrong: ~s~%")
316 (unknown-shepherd-error-sexp error)))
317 ((shepherd-error? error)
318 (report-error (G_ "shepherd error~%")))
319 ((not error) ;not an error
322 (define (call-with-service-upgrade-info new-services mproc)
323 "Call MPROC, a monadic procedure in %STORE-MONAD, passing it the list of
324 names of services to load (upgrade), and the list of names of services to
326 (match (current-services)
328 (let-values (((to-unload to-restart)
329 (shepherd-service-upgrade services new-services)))
331 (map (compose first live-service-provision)
334 (with-monad %store-monad
335 (warning (G_ "failed to obtain list of shepherd services~%"))
338 (define (upgrade-shepherd-services os)
339 "Upgrade the Shepherd (PID 1) by unloading obsolete services and loading new
340 services specified in OS and not currently running.
342 This is currently very conservative in that it does not stop or unload any
343 running service. Unloading or stopping the wrong service ('udev', say) could
344 bring the system down."
347 (fold-services (operating-system-services os)
348 #:target-type shepherd-root-service-type)))
350 ;; Arrange to simply emit a warning if the service upgrade fails.
351 (with-shepherd-error-handling
352 (call-with-service-upgrade-info new-services
353 (lambda (to-restart to-unload)
354 (for-each (lambda (unload)
355 (info (G_ "unloading service '~a'...~%") unload)
356 (unload-service unload))
359 (with-monad %store-monad
360 (munless (null? new-services)
361 (let ((new-service-names (map shepherd-service-canonical-name new-services))
362 (to-restart-names (map shepherd-service-canonical-name to-restart))
363 (to-start (filter shepherd-service-auto-start? new-services)))
364 (info (G_ "loading new services:~{ ~a~}...~%") new-service-names)
365 (unless (null? to-restart-names)
366 ;; Listing TO-RESTART-NAMES in the message below wouldn't help
367 ;; because many essential services cannot be meaningfully
368 ;; restarted. See <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=22039#30>.
369 (format #t (G_ "To complete the upgrade, run 'herd restart SERVICE' to stop,
370 upgrade, and restart each service that was not automatically restarted.\n")))
371 (mlet %store-monad ((files (mapm %store-monad
372 (compose lower-object
373 shepherd-service-file)
375 ;; Here we assume that FILES are exactly those that were computed
376 ;; as part of the derivation that built OS, which is normally the
378 (load-services/safe (map derivation->output-path files))
380 (for-each start-service
381 (map shepherd-service-canonical-name to-start))
384 (define* (switch-to-system os
385 #:optional (profile %system-profile))
386 "Make a new generation of PROFILE pointing to the directory of OS, switch to
387 it atomically, and then run OS's activation script."
388 (mlet* %store-monad ((drv (operating-system-derivation os))
389 (script (lower-object (operating-system-activation-script os))))
390 (let* ((system (derivation->output-path drv))
391 (number (+ 1 (generation-number profile)))
392 (generation (generation-file-name profile number)))
393 (switch-symlinks generation system)
394 (switch-symlinks profile generation)
396 (format #t (G_ "activating system...~%"))
398 ;; The activation script may change $PATH, among others, so protect
400 (save-environment-excursion
401 ;; Tell 'activate-current-system' what the new system is.
402 (setenv "GUIX_NEW_SYSTEM" system)
404 ;; The activation script may modify '%load-path' & co., so protect
405 ;; against that. This is necessary to ensure that
406 ;; 'upgrade-shepherd-services' gets to see the right modules when it
407 ;; computes derivations with 'gexp->derivation'.
408 (save-load-path-excursion
409 (primitive-load (derivation->output-path script))))
411 ;; Finally, try to update system services.
412 (upgrade-shepherd-services os))))
414 (define-syntax-rule (unless-file-not-found exp)
419 (if (= ENOENT (system-error-errno args))
421 (apply throw args)))))
423 (define (seconds->string seconds)
424 "Return a string representing the date for SECONDS."
425 (let ((time (make-time time-utc 0 seconds)))
426 (date->string (time-utc->date time)
429 (define* (profile-boot-parameters #:optional (profile %system-profile)
431 (reverse (generation-numbers profile))))
432 "Return a list of 'boot-parameters' for the generations of PROFILE specified
433 by NUMBERS, which is a list of generation numbers. The list is ordered from
434 the most recent to the oldest profiles."
435 (define (system->boot-parameters system number time)
436 (unless-file-not-found
437 (let* ((params (read-boot-parameters-file system))
438 (label (boot-parameters-label params)))
441 (label (string-append label " (#"
442 (number->string number) ", "
443 (seconds->string time) ")"))))))
444 (let* ((systems (map (cut generation-file-name profile <>)
446 (times (map (lambda (system)
447 (unless-file-not-found
448 (stat:mtime (lstat system))))
450 (filter-map system->boot-parameters systems numbers times)))
456 (define (roll-back-system store)
457 "Roll back the system profile to its previous generation. STORE is an open
458 connection to the store."
459 (switch-to-system-generation store "-1"))
463 ;;; Switch generations.
465 (define (switch-to-system-generation store spec)
466 "Switch the system profile to the generation specified by SPEC, and
467 re-install bootloader with a configuration file that uses the specified system
468 generation as its default entry. STORE is an open connection to the store."
469 (let ((number (relative-generation-spec->number %system-profile spec)))
472 (reinstall-bootloader store number)
473 (switch-to-generation* %system-profile number))
474 (leave (G_ "cannot switch to system generation '~a'~%") spec))))
476 (define* (system-bootloader-name #:optional (system %system-profile))
477 "Return the bootloader name stored in SYSTEM's \"parameters\" file."
478 (let ((params (unless-file-not-found
479 (read-boot-parameters-file system))))
480 (boot-parameters-bootloader-name params)))
482 (define (reinstall-bootloader store number)
483 "Re-install bootloader for existing system profile generation NUMBER.
484 STORE is an open connection to the store."
485 (let* ((generation (generation-file-name %system-profile number))
486 ;; Detect the bootloader used in %system-profile.
487 (bootloader (lookup-bootloader-by-name (system-bootloader-name)))
489 ;; Use the detected bootloader with default configuration.
490 ;; It will be enough to allow the system to boot.
491 (bootloader-config (bootloader-configuration
492 (bootloader bootloader)))
494 ;; Make the specified system generation the default entry.
495 (params (profile-boot-parameters %system-profile (list number)))
497 (delv number (reverse (generation-numbers %system-profile))))
498 (old-params (profile-boot-parameters
499 %system-profile old-generations))
500 (entries (map boot-parameters->menu-entry params))
501 (old-entries (map boot-parameters->menu-entry old-params)))
502 (run-with-store store
504 ((bootcfg (lower-object
505 ((bootloader-configuration-file-generator bootloader)
506 bootloader-config entries
507 #:old-entries old-entries)))
508 (bootcfg-file -> (bootloader-configuration-file bootloader))
510 (drvs -> (list bootcfg)))
512 (show-what-to-build* drvs)
513 (built-derivations drvs)
514 ;; Only install bootloader configuration file. Thus, no installer is
516 (install-bootloader #f
518 #:bootcfg-file bootcfg-file
519 #:target target))))))
526 (define (service-node-label service)
527 "Return a label to represent SERVICE."
528 (let ((type (service-kind service))
529 (value (service-value service)))
530 (string-append (symbol->string (service-type-name type))
531 (cond ((or (number? value) (symbol? value))
532 (string-append " " (object->string value)))
534 (string-append " " value))
535 ((file-system? value)
536 (string-append " " (file-system-mount-point value)))
540 (define (service-node-type services)
541 "Return a node type for SERVICES. Since <service> instances are not
542 self-contained (they express dependencies on service types, not on services),
543 we have to create the 'edges' procedure dynamically as a function of the full
547 (description "the DAG of services")
548 (identifier (lift1 object-address %store-monad))
549 (label service-node-label)
550 (edges (lift1 (service-back-edges services) %store-monad))))
552 (define (shepherd-service-node-label service)
553 "Return a label for a node representing a <shepherd-service>."
554 (string-join (map symbol->string (shepherd-service-provision service))))
556 (define (shepherd-service-node-type services)
557 "Return a node type for SERVICES, a list of <shepherd-service>."
559 (name "shepherd-service")
560 (description "the dependency graph of shepherd services")
561 (identifier (lift1 shepherd-service-node-label %store-monad))
562 (label shepherd-service-node-label)
563 (edges (lift1 (shepherd-service-back-edges services) %store-monad))))
570 (define* (display-system-generation number
571 #:optional (profile %system-profile))
572 "Display a summary of system generation NUMBER in a human-readable format."
573 (unless (zero? number)
574 (let* ((generation (generation-file-name profile number))
575 (params (read-boot-parameters-file generation))
576 (label (boot-parameters-label params))
577 (bootloader-name (boot-parameters-bootloader-name params))
578 (root (boot-parameters-root-device params))
579 (root-device (if (bytevector? root)
582 (kernel (boot-parameters-kernel params)))
583 (display-generation profile number)
584 (format #t (G_ " file name: ~a~%") generation)
585 (format #t (G_ " canonical file name: ~a~%") (readlink* generation))
586 ;; TRANSLATORS: Please preserve the two-space indentation.
587 (format #t (G_ " label: ~a~%") label)
588 (format #t (G_ " bootloader: ~a~%") bootloader-name)
590 ;; TRANSLATORS: The '~[', '~;', and '~]' sequences in this string must
591 ;; be preserved. They denote conditionals, such that the result will
593 ;; root device: UUID: 12345-678
595 ;; root device: label: "my-root"
597 ;; root device: /dev/sda3
598 (format #t (G_ " root device: ~[UUID: ~a~;label: ~s~;~a~]~%")
599 (cond ((uuid? root-device) 0)
600 ((file-system-label? root-device) 1)
602 (cond ((uuid? root-device)
603 (uuid->string root-device))
604 ((file-system-label? root-device)
605 (file-system-label->string root-device))
609 (format #t (G_ " kernel: ~a~%") kernel))))
611 (define* (list-generations pattern #:optional (profile %system-profile))
612 "Display in a human-readable format all the system generations matching
613 PATTERN, a string. When PATTERN is #f, display all the system generations."
614 (cond ((not (file-exists? profile)) ; XXX: race condition
615 (raise (condition (&profile-not-found-error
616 (profile profile)))))
617 ((string-null? pattern)
618 (for-each display-system-generation (profile-generations profile)))
619 ((matching-generations pattern profile)
622 (if (null-list? numbers)
625 (for-each display-system-generation numbers)))))
627 (leave (G_ "invalid syntax: ~a~%") pattern))))
631 ;;; File system declaration checks.
634 (define (check-file-system-availability file-systems)
635 "Check whether the UUIDs or partition labels that FILE-SYSTEMS refer to, if
636 any, are available. Raise an error if they're not."
639 (and (file-system-mount? fs)
640 (not (member (file-system-type fs)
641 %pseudo-file-system-types))
642 (not (memq 'bind-mount (file-system-flags fs)))))
647 (file-system-label? (file-system-device fs)))
652 (string? (file-system-device fs)))
657 (uuid? (file-system-device fs)))
662 (define (file-system-location* fs)
664 (source-properties->location
665 (file-system-location fs))))
667 (let-syntax ((error (syntax-rules ()
671 (format (current-error-port)
673 (for-each (lambda (fs)
676 (stat (file-system-device fs)))
678 (let ((errno (system-error-errno args))
679 (device (file-system-device fs)))
680 (error (G_ "~a: error: device '~a' not found: ~a~%")
681 (file-system-location* fs) device
683 (unless (string-prefix? "/" device)
684 (display-hint (format #f (G_ "If '~a' is a file system
685 label, write @code{(file-system-label ~s)} in your @code{device} field.")
688 (for-each (lambda (fs)
689 (let ((label (file-system-label->string
690 (file-system-device fs))))
691 (unless (find-partition-by-label label)
692 (error (G_ "~a: error: file system with label '~a' not found~%")
693 (file-system-location* fs) label))))
695 (for-each (lambda (fs)
696 (unless (find-partition-by-uuid (file-system-device fs))
697 (error (G_ "~a: error: file system with UUID '~a' not found~%")
698 (file-system-location* fs)
699 (uuid->string (file-system-device fs)))))
703 ;; Better be safe than sorry.
706 (define (check-mapped-devices os)
707 "Check that each of MAPPED-DEVICES is valid according to the 'check'
708 procedure of its type."
709 (define boot-mapped-devices
710 (operating-system-boot-mapped-devices os))
712 (define (needed-for-boot? md)
713 (memq md boot-mapped-devices))
715 (define initrd-modules
716 (operating-system-initrd-modules os))
718 (for-each (lambda (md)
719 (let ((check (mapped-device-kind-check
720 (mapped-device-type md))))
721 ;; We expect CHECK to raise an exception with a detailed
722 ;; '&message' if something goes wrong.
724 #:needed-for-boot? (needed-for-boot? md)
725 #:initrd-modules initrd-modules)))
726 (operating-system-mapped-devices os)))
728 (define (check-initrd-modules os)
729 "Check that modules needed by 'needed-for-boot' file systems in OS are
730 available in the initrd. Note that mapped devices are responsible for
731 checking this by themselves in their 'check' procedure."
732 (define (file-system-/dev fs)
733 (let ((device (file-system-device fs)))
738 (find-partition-by-uuid device))
739 ((? file-system-label?)
740 (find-partition-by-label (file-system-label->string device))))))
743 (filter file-system-needed-for-boot?
744 (operating-system-file-systems os)))
746 (for-each (lambda (fs)
747 (check-device-initrd-modules (file-system-/dev fs)
748 (operating-system-initrd-modules os)
749 (source-properties->location
750 (file-system-location fs))))
758 (define* (system-derivation-for-action os action
759 #:key image-size file-system-type
760 full-boot? container-shared-network?
762 "Return as a monadic value the derivation for OS according to ACTION."
764 ((build init reconfigure)
765 (operating-system-derivation os))
770 #:shared-network? container-shared-network?))
772 (system-qemu-image os #:disk-image-size image-size))
774 (system-qemu-image/shared-store-script os
775 #:full-boot? full-boot?
780 #:mappings mappings))
782 (system-disk-image os
783 #:name (match file-system-type
784 ("iso9660" "image.iso")
786 #:disk-image-size image-size
787 #:file-system-type file-system-type))
789 (system-docker-image os))))
791 (define (maybe-suggest-running-guix-pull)
792 "Suggest running 'guix pull' if this has never been done before."
793 ;; The reason for this is that the 'guix' binding that we see here comes
794 ;; from either ~/.config/latest or, if it's missing, from the
795 ;; globally-installed Guix, which is necessarily older. See
796 ;; <http://lists.gnu.org/archive/html/guix-devel/2014-08/msg00057.html> for
799 (string-append (config-directory) "/current"))
801 (unless (file-exists? latest)
802 (warning (G_ "~a not found: 'guix pull' was never run~%") latest)
803 (warning (G_ "Consider running 'guix pull' before 'reconfigure'.~%"))
804 (warning (G_ "Failing to do that may downgrade your system!~%"))))
806 (define (bootloader-installer-script installer
807 bootloader device target)
808 "Return a file calling INSTALLER gexp with given BOOTLOADER, DEVICE
809 and TARGET arguments."
810 (scheme-file "bootloader-installer"
811 (with-imported-modules '((gnu build bootloader)
814 (use-modules (gnu build bootloader)
820 (guard (c ((message-condition? c) ;XXX: i18n
821 (format (current-error-port) "error: ~a~%"
822 (condition-message c))
824 (#$installer #$bootloader #$device #$target)
825 (format #t "bootloader successfully installed on '~a'~%"
828 (define* (perform-action action os
829 #:key skip-safety-checks?
831 dry-run? derivations-only?
832 use-substitutes? bootloader-target target
833 image-size file-system-type full-boot?
834 container-shared-network?
837 "Perform ACTION for OS. INSTALL-BOOTLOADER? specifies whether to install
838 bootloader; BOOTLOADER-TAGET is the target for the bootloader; TARGET is the
839 target root directory; IMAGE-SIZE is the size of the image to be built, for
840 the 'vm-image' and 'disk-image' actions. The root file system is created as a
841 FILE-SYSTEM-TYPE file system. FULL-BOOT? is used for the 'vm' action; it
842 determines whether to boot directly to the kernel or to the bootloader.
843 CONTAINER-SHARED-NETWORK? determines if the container will use a separate
846 When DERIVATIONS-ONLY? is true, print the derivation file name(s) without
849 When GC-ROOT is a path, also make that path an indirect root of the build
850 output when building a system derivation, such as a disk image.
852 When SKIP-SAFETY-CHECKS? is true, skip the file system and initrd module
855 (cut format #t "~a~%" <>))
858 (if (eq? 'init action)
860 (map boot-parameters->menu-entry (profile-boot-parameters))))
863 (bootloader-configuration-bootloader (operating-system-bootloader os)))
866 (and (memq action '(init reconfigure))
867 (operating-system-bootcfg os menu-entries)))
869 (define bootloader-script
870 (let ((installer (bootloader-installer bootloader))
871 (target (or target "/")))
872 (bootloader-installer-script installer
873 (bootloader-package bootloader)
874 bootloader-target target)))
876 (when (eq? action 'reconfigure)
877 (maybe-suggest-running-guix-pull))
879 ;; Check whether the declared file systems exist. This is better than
880 ;; instantiating a broken configuration. Assume that we can only check if
882 (when (and (not skip-safety-checks?)
883 (memq action '(init reconfigure)))
884 (check-mapped-devices os)
885 (when (zero? (getuid))
886 (check-file-system-availability (operating-system-file-systems os))
887 (check-initrd-modules os)))
890 ((sys (system-derivation-for-action os action
891 #:file-system-type file-system-type
892 #:image-size image-size
893 #:full-boot? full-boot?
894 #:container-shared-network? container-shared-network?
895 #:mappings mappings))
897 ;; For 'init' and 'reconfigure', always build BOOTCFG, even if
898 ;; --no-bootloader is passed, because we then use it as a GC root.
899 ;; See <http://bugs.gnu.org/21068>.
900 (drvs (mapm %store-monad lower-object
901 (if (memq action '(init reconfigure))
902 (if install-bootloader?
903 (list sys bootcfg bootloader-script)
906 (% (if derivations-only?
907 (return (for-each (compose println derivation-file-name)
909 (maybe-build drvs #:dry-run? dry-run?
910 #:use-substitutes? use-substitutes?))))
912 (if (or dry-run? derivations-only?)
914 (let ((bootcfg-file (bootloader-configuration-file bootloader)))
915 (for-each (compose println derivation->output-path)
921 (switch-to-system os)
922 (mwhen install-bootloader?
923 (install-bootloader bootloader-script
925 #:bootcfg-file bootcfg-file
929 (format #t (G_ "initializing operating system under '~a'...~%")
931 (install sys (canonicalize-path target)
932 #:install-bootloader? install-bootloader?
934 #:bootcfg-file bootcfg-file
935 #:bootloader-installer bootloader-script))
937 ;; All we had to do was to build SYS and maybe register an
939 (let ((output (derivation->output-path sys)))
942 (register-root* (list output) gc-root))
943 (return output)))))))))
945 (define (export-extension-graph os port)
946 "Export the service extension graph of OS to PORT."
947 (let* ((services (operating-system-services os))
948 (system (find (lambda (service)
949 (eq? (service-kind service) system-service-type))
951 (export-graph (list system) (current-output-port)
952 #:node-type (service-node-type services)
953 #:reverse-edges? #t)))
955 (define (export-shepherd-graph os port)
956 "Export the graph of shepherd services of OS to PORT."
957 (let* ((services (operating-system-services os))
958 (pid1 (fold-services services
959 #:target-type shepherd-root-service-type))
960 (shepherds (service-value pid1)) ;list of <shepherd-service>
961 (sinks (filter (lambda (service)
962 (null? (shepherd-service-requirement service)))
964 (export-graph sinks (current-output-port)
965 #:node-type (shepherd-service-node-type shepherds)
966 #:reverse-edges? #t)))
974 (display (G_ "Usage: guix system [OPTION ...] ACTION [ARG ...] [FILE]
975 Build the operating system declared in FILE according to ACTION.
976 Some ACTIONS support additional ARGS.\n"))
978 (display (G_ "The valid values for ACTION are:\n"))
981 search search for existing service types\n"))
983 reconfigure switch to a new operating system configuration\n"))
985 roll-back switch to the previous operating system configuration\n"))
987 list-generations list the system generations\n"))
989 switch-generation switch to an existing operating system configuration\n"))
991 delete-generations delete old system generations\n"))
993 build build the operating system without installing anything\n"))
995 container build a container that shares the host's store\n"))
997 vm build a virtual machine image that shares the host's store\n"))
999 vm-image build a freestanding virtual machine image\n"))
1001 disk-image build a disk image, suitable for a USB stick\n"))
1003 docker-image build a Docker image\n"))
1005 init initialize a root file system to run GNU\n"))
1007 extension-graph emit the service extension graph in Dot format\n"))
1009 shepherd-graph emit the graph of shepherd services in Dot format\n"))
1011 (show-build-options-help)
1013 -d, --derivation return the derivation of the given system"))
1015 -e, --expression=EXPR consider the operating-system EXPR evaluates to
1016 instead of reading FILE, when applicable"))
1019 apply STRATEGY (one of nothing-special, backtrace,
1020 or debug) when an error occurs while reading FILE"))
1022 --file-system-type=TYPE
1023 for 'disk-image', produce a root file system of TYPE
1024 (one of 'ext4', 'iso9660')"))
1026 --image-size=SIZE for 'vm-image', produce an image of SIZE"))
1028 --no-bootloader for 'init', do not install a bootloader"))
1030 --share=SPEC for 'vm', share host file system according to SPEC"))
1032 -N, --network for 'container', allow containers to access the network"))
1034 -r, --root=FILE for 'vm', 'vm-image', 'disk-image', 'container',
1035 and 'build', make FILE a symlink to the result, and
1036 register it as a garbage collector root"))
1038 --expose=SPEC for 'vm', expose host file system according to SPEC"))
1040 --full-boot for 'vm', make a full boot sequence"))
1042 --skip-checks skip file system and initrd module safety checks"))
1044 -v, --verbosity=LEVEL use the given verbosity LEVEL"))
1047 -h, --help display this help and exit"))
1049 -V, --version display version information and exit"))
1051 (show-bug-report-information))
1054 ;; Specifications of the command-line options.
1055 (cons* (option '(#\h "help") #f #f
1059 (option '(#\V "version") #f #f
1061 (show-version-and-exit "guix system")))
1062 (option '(#\e "expression") #t #f
1063 (lambda (opt name arg result)
1064 (alist-cons 'expression arg result)))
1065 (option '(#\d "derivation") #f #f
1066 (lambda (opt name arg result)
1067 (alist-cons 'derivations-only? #t result)))
1068 (option '("on-error") #t #f
1069 (lambda (opt name arg result)
1070 (alist-cons 'on-error (string->symbol arg)
1072 (option '(#\t "file-system-type") #t #f
1073 (lambda (opt name arg result)
1074 (alist-cons 'file-system-type arg
1076 (option '("image-size") #t #f
1077 (lambda (opt name arg result)
1078 (alist-cons 'image-size (size->number arg)
1080 (option '(#\N "network") #f #f
1081 (lambda (opt name arg result)
1082 (alist-cons 'container-shared-network? #t result)))
1083 (option '("no-bootloader" "no-grub") #f #f
1084 (lambda (opt name arg result)
1085 (alist-cons 'install-bootloader? #f result)))
1086 (option '("full-boot") #f #f
1087 (lambda (opt name arg result)
1088 (alist-cons 'full-boot? #t result)))
1089 (option '("skip-checks") #f #f
1090 (lambda (opt name arg result)
1091 (alist-cons 'skip-safety-checks? #t result)))
1093 (option '("share") #t #f
1094 (lambda (opt name arg result)
1095 (alist-cons 'file-system-mapping
1096 (specification->file-system-mapping arg #t)
1098 (option '("expose") #t #f
1099 (lambda (opt name arg result)
1100 (alist-cons 'file-system-mapping
1101 (specification->file-system-mapping arg #f)
1104 (option '(#\n "dry-run") #f #f
1105 (lambda (opt name arg result)
1106 (alist-cons 'dry-run? #t (alist-cons 'graft? #f result))))
1107 (option '(#\v "verbosity") #t #f
1108 (lambda (opt name arg result)
1109 (let ((level (string->number* arg)))
1110 (alist-cons 'verbosity level
1111 (alist-delete 'verbosity result)))))
1112 (option '(#\s "system") #t #f
1113 (lambda (opt name arg result)
1114 (alist-cons 'system arg
1115 (alist-delete 'system result eq?))))
1116 (option '(#\r "root") #t #f
1117 (lambda (opt name arg result)
1118 (alist-cons 'gc-root arg result)))
1119 %standard-build-options))
1121 (define %default-options
1122 ;; Alist of default option values.
1123 `((system . ,(%current-system))
1126 (print-build-trace? . #t)
1127 (print-extended-build-trace? . #t)
1128 (multiplexed-build-output? . #t)
1131 (verbosity . #f) ;default
1132 (file-system-type . "ext4")
1133 (image-size . guess)
1134 (install-bootloader? . #t)))
1141 (define (process-action action args opts)
1142 "Process ACTION, a sub-command, with the arguments are listed in ARGS.
1143 ACTION must be one of the sub-commands that takes an operating system
1144 declaration as an argument (a file name.) OPTS is the raw alist of options
1145 resulting from command-line parsing."
1146 (let* ((file (match args
1149 (expr (assoc-ref opts 'expression))
1150 (system (assoc-ref opts 'system))
1154 (G_ "both file and expression cannot be specified~%")))
1158 (load* file %user-module
1159 #:on-error (assoc-ref opts 'on-error)))
1161 (leave (G_ "no configuration specified~%")))))
1163 (dry? (assoc-ref opts 'dry-run?))
1164 (bootloader? (assoc-ref opts 'install-bootloader?))
1166 ((first second) second)
1170 (bootloader-configuration-target
1171 (operating-system-bootloader os)))))
1174 (set-build-options-from-command-line store opts)
1176 (run-with-store store
1177 (mbegin %store-monad
1178 (set-guile-for-build (default-guile))
1181 (export-extension-graph os (current-output-port)))
1183 (export-shepherd-graph os (current-output-port)))
1185 (unless (memq action '(build init))
1186 (warn-about-old-distro #:suggested-command
1187 "guix system reconfigure"))
1189 (perform-action action os
1191 #:derivations-only? (assoc-ref opts
1193 #:use-substitutes? (assoc-ref opts 'substitutes?)
1194 #:skip-safety-checks?
1195 (assoc-ref opts 'skip-safety-checks?)
1196 #:file-system-type (assoc-ref opts 'file-system-type)
1197 #:image-size (assoc-ref opts 'image-size)
1198 #:full-boot? (assoc-ref opts 'full-boot?)
1199 #:container-shared-network?
1200 (assoc-ref opts 'container-shared-network?)
1201 #:mappings (filter-map (match-lambda
1202 (('file-system-mapping . m)
1206 #:install-bootloader? bootloader?
1208 #:bootloader-target bootloader-target
1209 #:gc-root (assoc-ref opts 'gc-root)))))
1211 (warn-about-disk-space)))
1213 (define (resolve-subcommand name)
1214 (let ((module (resolve-interface
1215 `(guix scripts system ,(string->symbol name))))
1216 (proc (string->symbol (string-append "guix-system-" name))))
1217 (module-ref module proc)))
1219 (define (process-command command args opts)
1220 "Process COMMAND, one of the 'guix system' sub-commands. ARGS is its
1221 argument list and OPTS is the option alist."
1223 ;; The following commands do not need to use the store, and they do not need
1224 ;; an operating system configuration file.
1226 (let ((pattern (match args
1229 (x (leave (G_ "wrong number of arguments~%"))))))
1230 (list-generations pattern)))
1232 (apply (resolve-subcommand "search") args))
1233 ;; The following commands need to use the store, but they do not need an
1234 ;; operating system configuration file.
1235 ((delete-generations)
1236 (let ((pattern (match args
1239 (x (leave (G_ "wrong number of arguments~%"))))))
1241 (delete-matching-generations store %system-profile pattern)
1242 (reinstall-bootloader store (generation-number %system-profile)))))
1243 ((switch-generation)
1244 (let ((pattern (match args
1246 (x (leave (G_ "wrong number of arguments~%"))))))
1248 (set-build-options-from-command-line store opts)
1249 (switch-to-system-generation store pattern))))
1251 (let ((pattern (match args
1253 (x (leave (G_ "wrong number of arguments~%"))))))
1255 (set-build-options-from-command-line store opts)
1256 (roll-back-system store))))
1257 ;; The following commands need to use the store, and they also
1258 ;; need an operating system configuration file.
1259 (else (process-action command args opts))))
1261 (define (guix-system . args)
1262 (define (parse-sub-command arg result)
1263 ;; Parse sub-command ARG and augment RESULT accordingly.
1264 (if (assoc-ref result 'action)
1265 (alist-cons 'argument arg result)
1266 (let ((action (string->symbol arg)))
1268 ((build container vm vm-image disk-image reconfigure init
1269 extension-graph shepherd-graph
1270 list-generations delete-generations roll-back
1271 switch-generation search docker-image)
1272 (alist-cons 'action action result))
1273 (else (leave (G_ "~a: unknown action~%") action))))))
1275 (define (match-pair car)
1276 ;; Return a procedure that matches a pair with CAR.
1279 (and (eq? car head) tail))
1282 (define (option-arguments opts)
1283 ;; Extract the plain arguments from OPTS.
1284 (let* ((args (reverse (filter-map (match-pair 'argument) opts)))
1285 (count (length args))
1286 (action (assoc-ref opts 'action))
1287 (expr (assoc-ref opts 'expression)))
1289 (leave (G_ "wrong number of arguments for action '~a'~%")
1293 (format (current-error-port)
1294 (G_ "guix system: missing command name~%"))
1295 (format (current-error-port)
1296 (G_ "Try 'guix system --help' for more information.~%"))
1300 ((build container vm vm-image disk-image docker-image reconfigure)
1301 (unless (or (= count 1)
1302 (and expr (= count 0)))
1309 (with-error-handling
1310 (let* ((opts (parse-command-line args %options
1311 (list %default-options)
1314 (args (option-arguments opts))
1315 (command (assoc-ref opts 'action)))
1316 (parameterize ((%graft? (assoc-ref opts 'graft?)))
1317 (with-status-verbosity (or (assoc-ref opts 'verbosity)
1318 (if (eq? command 'build) 2 1))
1319 (process-command command args opts))))))
1321 ;;; Local Variables:
1322 ;;; eval: (put 'call-with-service-upgrade-info 'scheme-indent-function 1)
1325 ;;; system.scm ends here