From mboxrd@z Thu Jan 1 00:00:00 1970 From: Chris Marusich Subject: Patches to implement system roll-back and switch-generation Date: Thu, 29 Sep 2016 23:21:59 -0700 Message-ID: <87a8ep6h6g.fsf@gmail.com> Mime-Version: 1.0 Content-Type: multipart/signed; boundary="==-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:32952) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bprD8-0001mi-3y for guix-devel@gnu.org; Fri, 30 Sep 2016 02:22:21 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bprD5-0002Kc-1R for guix-devel@gnu.org; Fri, 30 Sep 2016 02:22:18 -0400 List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org Sender: "Guix-devel" To: guix-devel@gnu.org --==-=-= Content-Type: multipart/mixed; boundary="=-=-=" --=-=-= Content-Type: text/plain Content-Transfer-Encoding: quoted-printable Hi, Here are some patches which, when applied to 1df00601b280db1cdfe0fc8d539ee6c6c726c355, make it possible to switch system generations using two new "guix system" subcommands: "roll-back" and "switch-generation". These commands currently just rebuild the grub.cfg file with a new default entry. As a result, if you want to roll back or switch to another system generation, you don't have to manually select a previous version every single time you boot, which is nice. I believe my patch does NOT yet make the regenerated grub.cfg a GC root, so it is possible that after rolling back or switching generations, invoking GC might clean up some things you'd rather keep around (like the grub background image file). Please be careful not to try this patch on a machine you care about; please use a VM instead. I've verified that it works in a VM. I'm hoping for some constructive feedback. I've had a rough time getting this to work on my own because this is the first real work I've tried doing with Guile or a free software project, and because a lot of the machinery that already exists (e.g., in (guix scripts system)) assumes that we are going to get the info we need for building the grub config from an operating system configuration file, which is not true for this use case. I've also had quite a hard time understanding the relationship between monadic values in the store monad, gexps, derivations, and how to use them effectively. If you notice I am doing something strange or outright incorrect, please let me know so I can fix it and do better. Unfortunately, these patches do not apply cleanly to the current master branch because of commit 0f65f54ebd76324653fd5506a7dab42ee44d9255. This commit has thrown a wrench in my plans. When I try to rebase onto master, there are multiple conflicts, and I am not sure yet what the best way to resolve them will be. In any case, I think it will be more productive to ask for feedback now, rather than to continue on my own down uncertain paths. I look forward to your feedback. =2D-=20 Chris --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-Improve-docstring-switch-to-generation.patch Content-Transfer-Encoding: quoted-printable From=201741e8a66be3d7e5f647796f434689b0a61e1122 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Tue, 12 Jul 2016 22:58:03 -0700 Subject: [PATCH 1/9] Improve docstring: switch-to-generation =2D-- guix/profiles.scm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/guix/profiles.scm b/guix/profiles.scm index 4a2ba1c..38f69dd 100644 =2D-- a/guix/profiles.scm +++ b/guix/profiles.scm @@ -1011,7 +1011,9 @@ that fails." =20 (define (switch-to-generation profile number) "Atomically switch PROFILE to the generation NUMBER. Return the number = of =2Dthe generation that was current before switching." +the generation that was current before switching. Raise a +&profile-not-found-error when the profile does not exist. Raise a +&missing-generation-error when the generation does not exist." (let ((current (generation-number profile)) (generation (generation-file-name profile number))) (cond ((not (file-exists? profile)) =2D-=20 2.9.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-Refactor-extract-procedure-relative-generation-spec-.patch Content-Transfer-Encoding: quoted-printable From=209f554133be83f06d5a3d0bfc713331e59eb2116c Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Tue, 12 Jul 2016 22:53:10 -0700 Subject: [PATCH 2/9] Refactor: extract procedure: relative-generation-spec->number =2D-- guix/profiles.scm | 16 ++++++++++++++++ guix/scripts/package.scm | 7 +------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/guix/profiles.scm b/guix/profiles.scm index 38f69dd..3828d44 100644 =2D-- a/guix/profiles.scm +++ b/guix/profiles.scm @@ -96,6 +96,7 @@ generation-number generation-numbers profile-generations + relative-generation-spec->number relative-generation previous-generation-number generation-time @@ -968,6 +969,21 @@ former profiles were found." '() generations))) =20 +(define (relative-generation-spec->number profile spec) + "Return PROFILE's generation specified by SPEC, which is a string. The = SPEC +may be a N, -N, or +N, where N is a number. If the spec is N, then the nu= mber +returned is N. If it is -N, then the number returned is the profile's cur= rent +generation number minus N. If it is +N, then the number returned is the +profile's current generation number plus N. Return #f if there is no such +generation." + (let ((number (string->number spec))) + (and number + (case (string-ref spec 0) + ((#\+ #\-) + (relative-generation profile number)) + (else number))))) + + (define* (relative-generation profile shift #:optional (current (generation-number profile))) "Return PROFILE's generation shifted from the CURRENT generation by SHIF= T. diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm index b87aee0..1d8972c 100644 =2D-- a/guix/scripts/package.scm +++ b/guix/scripts/package.scm @@ -774,12 +774,7 @@ processed, #f otherwise." #:key dry-run?) "Switch PROFILE to the generation specified by SPEC." (unless dry-run? =2D (let* ((number (string->number spec)) =2D (number (and number =2D (case (string-ref spec 0) =2D ((#\+ #\-) =2D (relative-generation profile number)) =2D (else number))))) + (let ((number (relative-generation-spec->number profile spec))) (if number (switch-to-generation* profile number) (leave (_ "cannot switch to generation '~a'~%") spec))))) =2D-=20 2.9.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0003-Rename-previous-grub-entries-to-grub-entries.patch Content-Transfer-Encoding: quoted-printable From=2042b1f00ce802745fbdc3b9bc5be38ccd47c0af33 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Sat, 16 Jul 2016 15:53:22 -0700 Subject: [PATCH 3/9] Rename previous-grub-entries to grub-entries This procedure actually returns an entry for every generation of the profil= e, so its name is confusing if it suggests that it only returns "previous" entries. =2D-- guix/scripts/system.scm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm index 953c624..25a7743 100644 =2D-- a/guix/scripts/system.scm +++ b/guix/scripts/system.scm @@ -370,7 +370,7 @@ it atomically, and then run OS's activation script." (date->string (time-utc->date time) "~Y-~m-~d ~H:~M"))) =20 =2D(define* (previous-grub-entries #:optional (profile %system-profile)) +(define* (grub-entries #:optional (profile %system-profile)) "Return a list of 'menu-entry' for the generations of PROFILE." (define (system->grub-entry system number time) (unless-file-not-found @@ -564,7 +564,7 @@ building anything." (operating-system-grub.cfg os (if (eq? 'init action) '() =2D (previous-grub-entr= ies))))) + (grub-entries))))) =20 ;; For 'init' and 'reconfigure', always build GRUB.CFG, even if ;; --no-grub is passed, because GRUB.CFG because we then use it as = a GC =2D-=20 2.9.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0004-Fix-docstrings-these-procedures-return-derivations.patch Content-Transfer-Encoding: quoted-printable From=20a440eb18eaa6c2fe12d91db1c9d62e79823e7ad0 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Mon, 1 Aug 2016 07:51:38 -0700 Subject: [PATCH 4/9] Fix docstrings: these procedures return derivations =2D-- gnu/system.scm | 4 ++-- gnu/system/grub.scm | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gnu/system.scm b/gnu/system.scm index 7edb018..1d1ed5e 100644 =2D-- a/gnu/system.scm +++ b/gnu/system.scm @@ -718,8 +718,8 @@ listed in OS. The C library expects to find it under (store-file-system (operating-system-file-systems os))) =20 (define* (operating-system-grub.cfg os #:optional (old-entries '())) =2D "Return the GRUB configuration file for OS. Use OLD-ENTRIES to popula= te the =2D\"old entries\" menu." + "Return a derivation which builds the GRUB configuration file for OS. U= se +OLD-ENTRIES to populate the \"old entries\" menu." (mlet* %store-monad ((system (operating-system-derivation os)) (root-fs -> (operating-system-root-file-system os)) diff --git a/gnu/system/grub.scm b/gnu/system/grub.scm index 4592747..c426d5f 100644 =2D-- a/gnu/system/grub.scm +++ b/gnu/system/grub.scm @@ -239,10 +239,10 @@ code." #:key (system (%current-system)) (old-entries '())) =2D "Return the GRUB configuration file corresponding to CONFIG, a =2D object, and where the store is available at STORE-F= S, a =2D object. OLD-ENTRIES is taken to be a list of menu entries =2Dcorresponding to old generations of the system." + "Return a derivation which builds the GRUB configuration file correspond= ing +to CONFIG, a object, and where the store is available= at +STORE-FS, a object. OLD-ENTRIES is taken to be a list of me= nu +entries corresponding to old generations of the system." (define all-entries (append entries (grub-configuration-menu-entries config))) =20 =2D-=20 2.9.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0005-Factor-out-procedure-device-title.patch Content-Transfer-Encoding: quoted-printable From=20227ffc6c34c7bef29a39b2745865ac25c28a7e74 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Mon, 1 Aug 2016 08:46:48 -0700 Subject: [PATCH 5/9] Factor out procedure: device->title =2D-- gnu/build/file-systems.scm | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm index f1fccbd..4a8acd5 100644 =2D-- a/gnu/build/file-systems.scm +++ b/gnu/build/file-systems.scm @@ -38,6 +38,7 @@ find-partition-by-uuid find-partition-by-luks-uuid canonicalize-device-spec + device->title =20 uuid->string string->uuid @@ -364,19 +365,6 @@ the following: ;; this long. 20) =20 =2D (define canonical-title =2D ;; The realm of canonicalization. =2D (if (eq? title 'any) =2D (if (string? spec) =2D ;; The "--root=3DSPEC" kernel command-line option always pro= vides a =2D ;; string, but the string can represent a device, a UUID, or= a =2D ;; label. So check for all three. =2D (cond ((string-prefix? "/" spec) 'device) =2D ((string->uuid spec) 'uuid) =2D (else 'label)) =2D 'uuid) =2D title)) =2D (define (resolve find-partition spec fmt) (let loop ((count 0)) (let ((device (find-partition spec))) @@ -391,6 +379,10 @@ the following: (sleep 1) (loop (+ 1 count)))))))) =20 + (define canonical-title (if (eq? title 'any) + (device->title spec) + title)) + (case canonical-title ((device) ;; Nothing to do. @@ -407,6 +399,19 @@ the following: (else (error "unknown device title" title)))) =20 +(define (device->title device) + "Guess the title for the given DEVICE, which must be a device parameter = from +a object. As a special case, when the DEVICE is a UUID, it = may +be specified as a string." + (if (string? device) + ;; The "--root=3DSPEC" kernel command-line option always provides a + ;; string, but the string can represent a device, a UUID, or a + ;; label. So check for all three. + (cond ((string-prefix? "/" device) 'device) + ((string->uuid device) 'uuid) + (else 'label)) + 'uuid)) + (define (check-file-system device type) "Run a file system check of TYPE on DEVICE." (define fsck =2D-=20 2.9.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0006-gnu-system-and-gnu-system-grub-use-root-fs-device-no.patch Content-Transfer-Encoding: quoted-printable From=201ade872ffae08ded1b8dae2fca05fee33ac0c69f Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Mon, 1 Aug 2016 08:57:08 -0700 Subject: [PATCH 6/9] gnu/system and gnu/system/grub: use root-fs-device, not root-fs =2D-- gnu/system.scm | 3 ++- gnu/system/grub.scm | 37 ++++++++++++++++++++----------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/gnu/system.scm b/gnu/system.scm index 1d1ed5e..3750094 100644 =2D-- a/gnu/system.scm +++ b/gnu/system.scm @@ -740,7 +740,8 @@ OLD-ENTRIES to populate the \"old entries\" menu." (operating-system-kernel-arguments os))) (initrd (file-append system "/initrd")))))) (grub-configuration-file (operating-system-bootloader os) =2D store-fs entries + (file-system-device store-fs) + entries #:old-entries old-entries))) =20 (define (operating-system-parameters-file os) diff --git a/gnu/system/grub.scm b/gnu/system/grub.scm index c426d5f..02f8e68 100644 =2D-- a/gnu/system/grub.scm +++ b/gnu/system/grub.scm @@ -26,6 +26,7 @@ #:use-module (guix download) #:use-module (gnu artwork) #:use-module (gnu system file-systems) + #:use-module (gnu build file-systems) #:autoload (gnu packages grub) (grub) #:autoload (gnu packages inkscape) (inkscape) #:autoload (gnu packages imagemagick) (imagemagick) @@ -154,12 +155,12 @@ WIDTH/HEIGHT, or #f if none was found." (with-monad %store-monad (return #f))))) =20 =2D(define (eye-candy config root-fs system port) +(define (eye-candy config root-fs-device system port) "Return in %STORE-MONAD a gexp that writes to PORT (a port-valued gexp) = the 'grub.cfg' part concerned with graphics mode, background images, colors, a= nd =2Dall that. ROOT-FS is a file-system object denoting the root file system= where =2Dthe store is. SYSTEM must be the target system string---e.g., =2D\"x86_64-linux\"." +all that. ROOT-FS-DEVICE is the device parameter from a obj= ect +denoting the root file system where the store is. SYSTEM must be the targ= et +system string---e.g., \"x86_64-linux\"." (define setup-gfxterm-body ;; Intel systems need to be switched into graphics mode, whereas most ;; other modern architectures have no other mode and therefore don't n= eed @@ -206,7 +207,7 @@ else set menu_color_highlight=3Dwhite/blue fi~%" #$setup-gfxterm-body =2D #$(grub-root-search root-fs font-file) + #$(grub-root-search root-fs-device font-file) #$font-file =20 #$image @@ -218,31 +219,32 @@ fi~%" ;;; Configuration file. ;;; =20 =2D(define (grub-root-search root-fs file) =2D "Return the GRUB 'search' command to look for ROOT-FS, which contains = FILE, +(define (grub-root-search root-fs-device file) + "Return the GRUB 'search' command to look for ROOT-FS-DEVICE, which cont= ains FILE, a gexp. The result is a gexp that can be inserted in the grub.cfg-generat= ion code." =2D (case (file-system-title root-fs) =2D ;; Preferably refer to ROOT-FS by its UUID or label. This is more =2D ;; efficient and less ambiguous, see <>. + (case (device->title root-fs-device) + ;; Preferably refer to ROOT-FS-DEVICE by its UUID or label. This is m= ore + ;; efficient and less ambiguous. ((uuid) (format #f "search --fs-uuid --set ~a" =2D (uuid->string (file-system-device root-fs)))) + (uuid->string root-fs-device))) ((label) (format #f "search --label --set ~a" =2D (file-system-device root-fs))) + root-fs-device)) (else ;; As a last resort, look for any device containing FILE. #~(format #f "search --file --set ~a" #$file)))) =20 =2D(define* (grub-configuration-file config store-fs entries +(define* (grub-configuration-file config store-fs-device entries #:key (system (%current-system)) (old-entries '())) "Return a derivation which builds the GRUB configuration file correspond= ing to CONFIG, a object, and where the store is available= at =2DSTORE-FS, a object. OLD-ENTRIES is taken to be a list of = menu =2Dentries corresponding to old generations of the system." +STORE-FS-DEVICE, the device parameter from a object. +OLD-ENTRIES is taken to be a list of menu entries corresponding to old +generations of the system." (define all-entries (append entries (grub-configuration-menu-entries config))) =20 @@ -255,11 +257,12 @@ entries corresponding to old generations of the syste= m." initrd ~a }~%" #$label =2D #$(grub-root-search store-fs linux) + #$(grub-root-search store-fs-device linux) #$linux (string-join (list #$@arguments)) #$initrd)))) =20 =2D (mlet %store-monad ((sugar (eye-candy config store-fs system #~port))) + (mlet %store-monad + ((sugar (eye-candy config store-fs-device system #~port))) (define builder #~(call-with-output-file #$output (lambda (port) =2D-=20 2.9.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0007-gnu-build-install-Factor-out-procedure-install-grub-.patch Content-Transfer-Encoding: quoted-printable From=2055cf900abf6dba10ed640d24433cc1ca23e4acf2 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Tue, 2 Aug 2016 21:28:21 -0700 Subject: [PATCH 7/9] gnu/build/install: Factor out procedure: install-grub-config =2D-- gnu/build/install.scm | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/gnu/build/install.scm b/gnu/build/install.scm index 7431a09..ce4f06e 100644 =2D-- a/gnu/build/install.scm +++ b/gnu/build/install.scm @@ -22,6 +22,7 @@ #:use-module (srfi srfi-26) #:use-module (ice-9 match) #:export (install-grub + install-grub-config populate-root-file-system reset-timestamps register-closure @@ -36,13 +37,24 @@ ;;; ;;; Code: =20 =2D(define* (install-grub grub.cfg device mount-point) +(define (install-grub grub.cfg device mount-point) "Install GRUB with GRUB.CFG on DEVICE, which is assumed to be mounted on MOUNT-POINT. =20 Note that the caller must make sure that GRUB.CFG is registered as a GC ro= ot so that the fonts, background images, etc. referred to by GRUB.CFG are not GC'd." + (install-grub-config grub.cfg mount-point) + (unless (zero? (system* "grub-install" "--no-floppy" + "--boot-directory" + (string-append mount-point "/boot") + device)) + (error "failed to install GRUB"))) + +(define (install-grub-config grub.cfg mount-point) + "Atomically copy GRUB.CFG into boot/grub/grub.cfg on the MOUNT-POINT. N= ote +that the caller must make sure that GRUB.CFG is registered as a GC root so +that the fonts, background images, etc. referred to by GRUB.CFG are not GC= 'd." (let* ((target (string-append mount-point "/boot/grub/grub.cfg")) (pivot (string-append target ".new"))) (mkdir-p (dirname target)) @@ -50,13 +62,7 @@ GC'd." ;; Copy GRUB.CFG instead of just symlinking it, because symlinks won't ;; work when /boot is on a separate partition. Do that atomically. (copy-file grub.cfg pivot) =2D (rename-file pivot target) =2D =2D (unless (zero? (system* "grub-install" "--no-floppy" =2D "--boot-directory" =2D (string-append mount-point "/boot") =2D device)) =2D (error "failed to install GRUB")))) + (rename-file pivot target))) =20 (define (evaluate-populate-directive directive target) "Evaluate DIRECTIVE, an sexp describing a file or directory to create un= der =2D-=20 2.9.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0008-grub-entries-take-a-list-of-numbers-on-input.patch Content-Transfer-Encoding: quoted-printable From=20059e79ab26f5e8ee60b60f5df01907300346c8e2 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Thu, 28 Jul 2016 02:31:38 -0700 Subject: [PATCH 8/9] grub-entries: take a list of numbers on input =2D-- guix/scripts/system.scm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm index 25a7743..f450c9a 100644 =2D-- a/guix/scripts/system.scm +++ b/guix/scripts/system.scm @@ -370,8 +370,10 @@ it atomically, and then run OS's activation script." (date->string (time-utc->date time) "~Y-~m-~d ~H:~M"))) =20 =2D(define* (grub-entries #:optional (profile %system-profile)) =2D "Return a list of 'menu-entry' for the generations of PROFILE." +(define* (grub-entries #:optional (profile %system-profile) + (numbers (generation-numbers profile))) + "Return a list of 'menu-entry' for the generations of PROFILE specified = by +NUMBERS, which is a list of generation numbers." (define (system->grub-entry system number time) (unless-file-not-found (let* ((file (string-append system "/parameters")) @@ -396,8 +398,7 @@ it atomically, and then run OS's activation script." kernel-arguments)) (initrd #~(string-append #$system "/initrd")))))) =20 =2D (let* ((numbers (generation-numbers profile)) =2D (systems (map (cut generation-file-name profile <>) + (let* ((systems (map (cut generation-file-name profile <>) numbers)) (times (map (lambda (system) (unless-file-not-found =2D-=20 2.9.2 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0009-Implement-switch-generation-and-roll-back.patch Content-Transfer-Encoding: quoted-printable From=20b5816897c6db7984f678963dabe8ae58c6947677 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Wed, 3 Aug 2016 00:41:01 -0700 Subject: [PATCH 9/9] Implement switch-generation and roll-back =2D-- guix/scripts/system.scm | 87 +++++++++++++++++++++++++++++++++++++++++++++= +--- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm index f450c9a..5c72808 100644 =2D-- a/guix/scripts/system.scm +++ b/guix/scripts/system.scm @@ -408,6 +408,57 @@ NUMBERS, which is a list of generation numbers." =20 ;;; +;;; Roll-back. +;;; +(define (roll-back-system store) + "Roll back the system profile to its previous generation." + (switch-to-system-generation store "-1")) + +;;; +;;; Switch generations. +;;; +(define (switch-to-system-generation store spec) + "Switch the system profile to the generation specified by SPEC, and +re-install grub with a grub configuration file that uses the specified sys= tem +generation as its default entry." + (let ((number (relative-generation-spec->number %system-profile spec))) + (if number + (begin + (reinstall-grub store number) + (switch-to-generation* %system-profile number)) + (leave (_ "cannot switch to system generation '~a'~%") spec)))) + +(define (reinstall-grub store number) + "Re-install grub for existing system profile generation NUMBER." + (unless-file-not-found + (let* + ((generation (generation-file-name %system-profile number)) + (file (string-append generation "/parameters")) + (params (call-with-input-file file read-boot-parameters)) + ;; We assume that the root file system contains the store. If this + ;; assumption is ever false, then problems might occur. + (root-device (boot-parameters-root-device params)) + ;; Generate a new grub configuration which uses default values for + ;; just about everything. If the specified system generation was + ;; built from an operating system configuration file that contained + ;; non-default values in its grub-configuration, then the grub + ;; configuration we generate here will be different. However, even + ;; if that is the case, this default configuration will be good + ;; enough to enable the user to boot into the specified generation. + (grub-config (grub-configuration (device root-device))) + ;; Make the specified system generation the default entry. + (entries (grub-entries %system-profile (list number))) + (old-entries (grub-entries)) + (grub.cfg-derivation (run-with-store store + (grub-configuration-file grub-config + root-device + entries + #:old-entries old-entries)))) + (build-derivations store (list grub.cfg-derivation)) + (install-grub-config (derivation->output-path grub.cfg-derivation) "/= ")))) + + +;;; ;;; Graphs. ;;; =20 @@ -642,14 +693,19 @@ building anything." ;;; =20 (define (show-help) =2D (display (_ "Usage: guix system [OPTION] ACTION [FILE] =2DBuild the operating system declared in FILE according to ACTION.\n")) + (display (_ "Usage: guix system [OPTION ...] ACTION [ARG ...] [FILE] +Build the operating system declared in FILE according to ACTION. +Some ACTIONS support additional ARGS.\n")) (newline) (display (_ "The valid values for ACTION are:\n")) (newline) (display (_ "\ reconfigure switch to a new operating system configuration\n")) (display (_ "\ + roll-back switch to the previous operating system configuration\= n")) + (display (_ "\ + switch-generation switch to an existing operating system configuration\= n")) + (display (_ "\ list-generations list the system generations\n")) (display (_ "\ build build the operating system without installing anything= \n")) @@ -809,6 +865,8 @@ resulting from command-line parsing." (define (process-command command args opts) "Process COMMAND, one of the 'guix system' sub-commands. ARGS is its argument list and OPTS is the option alist." + ;; The following commands do not need to use the store, and they do not = need + ;; an operating system configuration file. (case command ((list-generations) ;; List generations. No need to connect to the daemon, etc. @@ -818,7 +876,27 @@ argument list and OPTS is the option alist." (x (leave (_ "wrong number of arguments~%")))))) (list-generations pattern))) (else =2D (process-action command args opts)))) + ;; The following commands need to use the store, but they do not need= an + ;; operating system configuration file. + (case command + ((switch-generation) + (with-store store + (set-build-options-from-command-line store opts) + (let ((pattern (match args + ((pattern) pattern) + (x (leave (_ "wrong number of arguments~%")))))) + (switch-to-system-generation store pattern)))) + ((roll-back) + (with-store store + (set-build-options-from-command-line store opts) + (let ((pattern (match args + (() "") + (x (leave (_ "wrong number of arguments~%")))))) + (roll-back-system store)))) + (else + ;; The following commands need to use the store, and they also + ;; need an operating system configuration file. + (process-action command args opts)))))) =20 (define (guix-system . args) (define (parse-sub-command arg result) @@ -828,7 +906,8 @@ argument list and OPTS is the option alist." (let ((action (string->symbol arg))) (case action ((build container vm vm-image disk-image reconfigure init =2D extension-graph shepherd-graph list-generations) + extension-graph shepherd-graph list-generations roll-back + switch-generation) (alist-cons 'action action result)) (else (leave (_ "~a: unknown action~%") action)))))) =20 =2D-=20 2.9.2 --=-=-=-- --==-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAEBCAAGBQJX7gSJAAoJEN1AmhXYIkadgy0QAJ/7Qf2tvNEVSGynHGAQ5FvR 5iVsxd29NcADHd1u5WFrpjmETuOjgW021mzOpAMsA1jGnNIdWkoLlV+ix7ROTzyR Pfp8WQIaBsmKzpaZFlxc4yaAZh+rwLfRw9HaJ/dYPZlVP7rzbhyLGbQQN7Bp+AdD /zGuLio6jnoXp6nGTL/t9UmWKKdF43K7pGm1Y7mZ/7DXDmJ+bWhmGWhR8+6zRCZ9 yU4uRGdPK/Z5FLi7XhzmrW8T98psbXE3DFeWZpKPPEOd4LyPgYXXTkH0onw6Oubv p/GJaAYY2kCDR40oMK5gLrmCsuOyVzBLLTMSCT2vdGjZhgTvxCg+Qad7YloISkIe 1EkCZDTvQMhfV5c9r80ZL1aDYio7vXxixsNM7xMAw7C41FOJjLu2gN+cLA3P+BxA YQKJQk1hmfOlfSXxtXteRciJ8PFfhq0dmRHA11vdq9hhTr3f5toErJgrFpwHBIcL drftqS2sYuyimWHf7am4BWG1ROmQacWOxE2GNsIgqpsmoe19b1jkU034+ZREiOe7 u7qlYRboXnnimc7cXV5mxQETQkFWisPcfclkbUhFIM6s4DImY7T5oxJcZQ1zpRro N9h+oUmoPPTRpjz5zTQUQyHgeurbDedqgx4+nNqVfyY8lfNwYnJQStIwzuZbtgdr 53k5sjNzSuwqorVCdV4/ =1RBJ -----END PGP SIGNATURE----- --==-=-=--