[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libguestfs] [PATCH] sparsify: Let --in-place capture ^C and shut down gracefully (RHBZ#1277122).



When virt-sparsify receives a user quit signal (eg from ^C) it
currently kills virt-sparsify and qemu instantly, meaning any mount +
fstrim in progress is uncleanly stopped.  The (minor) side effect of
this is that the guest filesystem may require a journal replay or fsck
on boot.

Let virt-sparsify capture the user quit signal and shut down
gracefully.

It is not thought that the previous behaviour could cause guest
corruption; see
https://lists.nongnu.org/archive/html/qemu-devel/2015-11/threads.html#00402
for discussion.
---
 sparsify/in_place.ml | 143 ++++++++++++++++++++++++++++++---------------------
 1 file changed, 84 insertions(+), 59 deletions(-)

diff --git a/sparsify/in_place.ml b/sparsify/in_place.ml
index 36100de..3a2b32c 100644
--- a/sparsify/in_place.ml
+++ b/sparsify/in_place.ml
@@ -35,8 +35,15 @@ let rec run disk format ignores machine_readable zeroes =
   if trace () then g#set_trace true;
   if verbose () then g#set_verbose true;
 
+  (* Capture ^C and clean up gracefully. *)
+  let quit = ref false in
+  let set_quit _ = quit := true in
+  Sys.set_signal Sys.sigint (Sys.Signal_handle set_quit);
+  Sys.set_signal Sys.sigquit (Sys.Signal_handle set_quit);
+  g#set_pgroup true;
+
   try
-    perform g disk format ignores machine_readable zeroes
+    perform g disk format ignores machine_readable zeroes quit
   with
     G.Error msg as exn ->
       if g#last_errno () = G.Errno.errno_ENOTSUP then (
@@ -45,7 +52,7 @@ let rec run disk format ignores machine_readable zeroes =
       )
       else raise exn
 
-and perform g disk format ignores machine_readable zeroes =
+and perform g disk format ignores machine_readable zeroes quit =
   (* XXX Current limitation of the API.  Can remove this hunk in future. *)
   let format =
     match format with
@@ -71,80 +78,98 @@ and perform g disk format ignores machine_readable zeroes =
 
   let is_read_only_lv = is_read_only_lv g in
 
-  List.iter (
-    fun fs ->
-      if not (is_ignored fs) && not (is_read_only_lv fs) then (
-        if List.mem fs zeroes then (
-          message (f_"Zeroing %s") fs;
+  let tasks =
+    List.map (
+      fun fs () ->
+        if not (is_ignored fs) && not (is_read_only_lv fs) then (
+          if List.mem fs zeroes then (
+            message (f_"Zeroing %s") fs;
 
-          if not (g#blkdiscardzeroes fs) then
-            g#zero_device fs;
-          g#blkdiscard fs
-        ) else (
-          let mounted =
-            try g#mount_options "discard" fs "/"; true
-            with _ -> false in
-
-          if mounted then (
-            message (f_"Trimming %s") fs;
-
-            g#fstrim "/"
+            if not (g#blkdiscardzeroes fs) then
+              g#zero_device fs;
+            g#blkdiscard fs
           ) else (
-            let is_linux_x86_swap =
-              (* Look for the signature for Linux swap on i386.
-               * Location depends on page size, so it definitely won't
-               * work on non-x86 architectures (eg. on PPC, page size is
-               * 64K).  Also this avoids hibernated swap space: in those,
-               * the signature is moved to a different location.
-               *)
-              try g#pread_device fs 10 4086L = "SWAPSPACE2"
+            let mounted =
+              try g#mount_options "discard" fs "/"; true
               with _ -> false in
 
-            if is_linux_x86_swap then (
-              message (f_"Clearing Linux swap on %s") fs;
+            if mounted then (
+              message (f_"Trimming %s") fs;
 
-              (* Don't use mkswap.  Just preserve the header containing
-               * the label, UUID and swap format version (libguestfs
-               * mkswap may differ from guest's own).
-               *)
-              let header = g#pread_device fs 4096 0L in
-              g#blkdiscard fs;
-              if g#pwrite_device fs header 0L <> 4096 then
-                error (f_"pwrite: short write restoring swap partition header")
+              g#fstrim "/"
+            ) else (
+              let is_linux_x86_swap =
+                (* Look for the signature for Linux swap on i386.
+                 * Location depends on page size, so it definitely won't
+                 * work on non-x86 architectures (eg. on PPC, page size is
+                 * 64K).  Also this avoids hibernated swap space: in those,
+                 * the signature is moved to a different location.
+                 *)
+                try g#pread_device fs 10 4086L = "SWAPSPACE2"
+                with _ -> false in
+
+              if is_linux_x86_swap then (
+                message (f_"Clearing Linux swap on %s") fs;
+
+                (* Don't use mkswap.  Just preserve the header containing
+                 * the label, UUID and swap format version (libguestfs
+                 * mkswap may differ from guest's own).
+                 *)
+                let header = g#pread_device fs 4096 0L in
+                g#blkdiscard fs;
+                if g#pwrite_device fs header 0L <> 4096 then
+                  error (f_"pwrite: short write restoring swap partition header")
+              )
             )
-          )
-        );
+          );
 
-        g#umount_all ()
-      )
-  ) filesystems;
+          g#umount_all ()
+        )
+    ) filesystems in
 
   (* Discard unused space in volume groups. *)
   let vgs = g#vgs () in
   let vgs = Array.to_list vgs in
   let vgs = List.sort compare vgs in
-  List.iter (
-    fun vg ->
-      if not (List.mem vg ignores) then (
-        let lvname = String.random8 () in
-        let lvdev = "/dev/" ^ vg ^ "/" ^ lvname in
 
-        let created =
-          try g#lvcreate_free lvname vg 100; true
-          with _ -> false in
+  let tasks = tasks @
+    List.map (
+      fun vg () ->
+        if not (List.mem vg ignores) then (
+          let lvname = String.random8 () in
+          let lvdev = "/dev/" ^ vg ^ "/" ^ lvname in
 
-        if created then (
-          message (f_"Discard space in volgroup %s") vg;
+          let created =
+            try g#lvcreate_free lvname vg 100; true
+            with _ -> false in
 
-          g#blkdiscard lvdev;
-          g#sync ();
-          g#lvremove lvdev
+          if created then (
+            message (f_"Discard space in volgroup %s") vg;
+
+            g#blkdiscard lvdev;
+            g#sync ();
+            g#lvremove lvdev
+          )
         )
-      )
-  ) vgs;
+    ) vgs in
+
+  (* The above calls to List.map just created a list of tasks (thunks)
+   * to run.  Now we actually run that code, keeping an eye on the
+   * state of the 'quit' flag.
+   *)
+  List.iter (
+    fun task ->
+      if not !quit then task ();
+  ) tasks;
 
   g#shutdown ();
   g#close ();
 
-  (* Finished. *)
-  message (f_"Sparsify in-place operation completed with no errors")
+  if not !quit then (
+    (* Finished. *)
+    message (f_"Sparsify in-place operation completed with no errors")
+  )
+  else (
+    (* User quit. *)
+    error (f_"quit (^C) at user request")
+  )
-- 
2.5.0


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]