[Libguestfs] [PATCH 6/7] New API: guestfs_shutdown: Cleanly shutdown the backend.

Richard W.M. Jones rjones at redhat.com
Tue Jul 3 18:03:22 UTC 2012


From: "Richard W.M. Jones" <rjones at redhat.com>

The new API splits orderly close into a two-step process:

  if (guestfs_shutdown (g) == -1) {
    /* handle the error, eg. qemu error */
  }
  guestfs_close (g);

Note that the explicit shutdown step is only necessary in the case
where you have made changes to the disk image and want to handle write
errors.  Read the documentation for further information.

This change also:

 - deprecates guestfs_kill_subprocess

 - turns guestfs_kill_subprocess into the same as guestfs_shutdown

 - changes guestfish and other tools to call shutdown + close
   where necessary (not for read-only tools)

 - updates documentation

 - updates examples
---
 edit/virt-edit.c                            |    2 +-
 erlang/examples/create_disk.erl             |    5 ++
 examples/copy_over.c                        |    6 +-
 examples/create_disk.c                      |    7 ++
 examples/mount_local.c                      |    4 +-
 fish/fish.c                                 |    3 +
 fish/reopen.c                               |    3 +
 fish/test-events.sh                         |    3 +
 format/format.c                             |    4 ++
 fuse/guestmount.c                           |    2 +
 generator/generator_actions.ml              |   26 ++++++-
 java/examples/CreateDisk.java               |    5 ++
 java/t/GuestFS010Basic.java                 |    1 +
 ocaml/examples/create_disk.ml               |    6 ++
 ocaml/t/guestfs_010_basic.ml                |    1 +
 ocaml/t/guestfs_070_threads.ml              |    3 +-
 ocaml/t/guestfs_500_parallel_mount_local.ml |    1 +
 perl/examples/create_disk.pl                |    5 ++
 python/examples/create_disk.py              |    5 ++
 python/t/010-basic.py                       |    1 +
 rescue/virt-rescue.c                        |    4 +-
 resize/resize.ml                            |    8 +--
 ruby/examples/create_disk.rb                |    5 ++
 sparsify/sparsify.ml                        |    1 +
 src/guestfs.c                               |  103 +++++++++++++++++----------
 src/guestfs.pod                             |   25 ++++---
 src/launch.c                                |   12 +---
 sysprep/main.ml                             |    1 +
 test-tool/test-tool.c                       |    5 ++
 tests/btrfs/test-btrfs-subvolume-default.pl |    1 +
 tests/disks/test-max-disks.pl               |    2 +-
 tests/guests/guest-aux/make-fedora-img.pl   |    3 +
 tests/selinux/run-test.pl                   |    1 +
 tools/virt-make-fs                          |    5 +-
 tools/virt-tar                              |    6 +-
 tools/virt-win-reg                          |    6 +-
 36 files changed, 193 insertions(+), 88 deletions(-)

diff --git a/edit/virt-edit.c b/edit/virt-edit.c
index c042745..3d23da8 100644
--- a/edit/virt-edit.c
+++ b/edit/virt-edit.c
@@ -298,7 +298,7 @@ main (int argc, char *argv[])
   free (root);
 
   /* Cleanly unmount the disks after editing. */
-  if (guestfs_umount_all (g) == -1 || guestfs_sync (g) == -1)
+  if (guestfs_shutdown (g) == -1)
     exit (EXIT_FAILURE);
 
   guestfs_close (g);
diff --git a/erlang/examples/create_disk.erl b/erlang/examples/create_disk.erl
index a0c9044..d192435 100755
--- a/erlang/examples/create_disk.erl
+++ b/erlang/examples/create_disk.erl
@@ -51,6 +51,11 @@ main(_) ->
     % the disk image.
     ok = guestfs:upload(G, "/etc/resolv.conf", "/foo/resolv.conf"),
 
+    % Because we wrote to the disk and we want to detect write
+    % errors, call guestfs:shutdown.  You don't need to do this:
+    % guestfs:close will do it implicitly.
+    ok = guestfs:shutdown(G),
+
     % Note also that handles are automatically closed if they are
     % reaped by the garbage collector.  You only need to call close
     % if you want to close the handle right away.
diff --git a/examples/copy_over.c b/examples/copy_over.c
index 9cff00b..45b8fa6 100644
--- a/examples/copy_over.c
+++ b/examples/copy_over.c
@@ -145,7 +145,7 @@ main (int argc, char *argv[])
   }
 
   /* Clean up. */
-  if (guestfs_umount_all (destg) == -1)
+  if (guestfs_shutdown (destg) == -1)
     exit (EXIT_FAILURE);
   guestfs_close (destg);
 
@@ -193,10 +193,6 @@ start_srcthread (void *arg)
   }
 
   /* Clean up. */
-  if (guestfs_umount_all (srcg) == -1) {
-    pthread_cancel (threaddata->mainthread);
-    exit (EXIT_FAILURE);
-  }
   guestfs_close (srcg);
 
   return NULL;
diff --git a/examples/create_disk.c b/examples/create_disk.c
index 5380888..e314328 100644
--- a/examples/create_disk.c
+++ b/examples/create_disk.c
@@ -99,6 +99,13 @@ main (int argc, char *argv[])
   if (guestfs_upload (g, "/etc/resolv.conf", "/foo/resolv.conf") == -1)
     exit (EXIT_FAILURE);
 
+  /* Because we wrote to the disk and we want to detect write
+   * errors, call guestfs_shutdown.  You don't need to do this:
+   * guestfs_close will do it implicitly.
+   */
+  if (guestfs_shutdown (g) == -1)
+    exit (EXIT_FAILURE);
+
   guestfs_close (g);
 
   /* Free up the lists. */
diff --git a/examples/mount_local.c b/examples/mount_local.c
index 240e31f..5c24cd1 100644
--- a/examples/mount_local.c
+++ b/examples/mount_local.c
@@ -186,8 +186,8 @@ main (int argc, char *argv[])
 
   waitpid (pid, NULL, 0);
 
-  /* Unmount and close. */
-  if (guestfs_umount (g, "/") == -1)
+  /* Shutdown the handle explicitly so write errors can be detected. */
+  if (guestfs_shutdown (g) == -1)
     exit (EXIT_FAILURE);
 
   guestfs_close (g);
diff --git a/fish/fish.c b/fish/fish.c
index 80b3364..ded80ec 100644
--- a/fish/fish.c
+++ b/fish/fish.c
@@ -553,6 +553,9 @@ main (int argc, char *argv[])
   else
     cmdline (argv, optind, argc);
 
+  if (guestfs_shutdown (g) == -1)
+    exit (EXIT_FAILURE);
+
   guestfs_close (g);
 
  out_after_handle_close:
diff --git a/fish/reopen.c b/fish/reopen.c
index 3fc9510..6b3707e 100644
--- a/fish/reopen.c
+++ b/fish/reopen.c
@@ -40,6 +40,9 @@ run_reopen (const char *cmd, size_t argc, char *argv[])
     return -1;
   }
 
+  if (guestfs_shutdown (g) == -1)
+    return -1;
+
   /* Open the new handle first, so we can copy the settings from the
    * old one to the new one, and also so if it fails we still have an
    * open handle.
diff --git a/fish/test-events.sh b/fish/test-events.sh
index 76d3823..b3684a8 100755
--- a/fish/test-events.sh
+++ b/fish/test-events.sh
@@ -53,6 +53,9 @@ if [ "$(cat test.out)" != '"ev1" (0): *: echo $EVENT $@
 "ev1" (1): *: echo $EVENT $@
 "ev2" (2): *: echo $EVENT $@
 "ev2" (2): *: echo $EVENT $@
+enter shutdown
+trace shutdown
+trace shutdown = 0
 enter get_autosync
 trace get_autosync
 trace get_autosync = 1
diff --git a/format/format.c b/format/format.c
index b9bab4d..bbe7f5c 100644
--- a/format/format.c
+++ b/format/format.c
@@ -260,6 +260,8 @@ main (int argc, char *argv[])
       guestfs_set_verbose (g2, guestfs_get_verbose (g));
       guestfs_set_trace (g2, guestfs_get_trace (g));
 
+      if (guestfs_shutdown (g) == -1)
+        exit (EXIT_FAILURE);
       guestfs_close (g);
       g = g2;
     }
@@ -278,6 +280,8 @@ main (int argc, char *argv[])
   /* Free up data structures. */
   free_drives (drvs);
 
+  if (guestfs_shutdown (g) == -1)
+    exit (EXIT_FAILURE);
   guestfs_close (g);
 
   exit (EXIT_SUCCESS);
diff --git a/fuse/guestmount.c b/fuse/guestmount.c
index b6c6467..0c2e2dc 100644
--- a/fuse/guestmount.c
+++ b/fuse/guestmount.c
@@ -431,6 +431,8 @@ main (int argc, char *argv[])
   r = guestfs_mount_local_run (g);
 
   /* Cleanup. */
+  if (guestfs_shutdown (g) == -1)
+    r = -1;
   guestfs_close (g);
 
   exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml
index e6219d1..5baa9b2 100644
--- a/generator/generator_actions.ml
+++ b/generator/generator_actions.ml
@@ -137,11 +137,13 @@ If you see any calls to this function in code then you can just
 remove them, unless you want to retain compatibility with older
 versions of the API.");
 
-  ("kill_subprocess", (RErr, [], []), -1, [],
+  ("kill_subprocess", (RErr, [], []), -1, [DeprecatedBy "shutdown"],
    [],
    "kill the qemu subprocess",
    "\
-This kills the qemu subprocess.  You should never need to call this.");
+This kills the qemu subprocess.
+
+Do not call this.  See: C<guestfs_shutdown> instead.");
 
   ("add_drive", (RErr, [String "filename"], []), -1, [ConfigOnly],
    [],
@@ -1766,6 +1768,26 @@ Converted to C</dev/VG/LV> form using C<guestfs_lvm_canonical_lvm_name>.
 
 Other strings are returned unmodified.");
 
+  ("shutdown", (RErr, [], []), -1, [],
+   [],
+   "shutdown the qemu subprocess",
+   "\
+This is the opposite of C<guestfs_launch>.  It performs an orderly
+shutdown of the backend process(es).  If the autosync flag is set
+(which is the default) then the disk image is synchronized.
+
+If the subprocess exits with an error then this function will return
+an error, which should I<not> be ignored (it may indicate that the
+disk image could not be written out properly).
+
+It is safe to call this multiple times.  Extra calls are ignored.
+
+This call does I<not> close or free up the handle.  You still
+need to call C<guestfs_close> afterwards.
+
+C<guestfs_close> will call this if you don't do it explicitly,
+but note that any errors are ignored in that case.");
+
 ]
 
 (* daemon_functions are any functions which cause some action
diff --git a/java/examples/CreateDisk.java b/java/examples/CreateDisk.java
index 41f8739..a6fcfed 100644
--- a/java/examples/CreateDisk.java
+++ b/java/examples/CreateDisk.java
@@ -66,6 +66,11 @@ public class CreateDisk
             // the disk image.
             g.upload ("/etc/resolv.conf", "/foo/resolv.conf");
 
+            // Because we wrote to the disk and we want to detect write
+            // errors, call g.shutdown.  You don't need to do this:
+            //g.close will do it implicitly.
+            g.shutdown ();
+
             // Note also that handles are automatically closed if they are
             // reaped by the garbage collector.  You only need to call close
             // if you want to close the handle right away.
diff --git a/java/t/GuestFS010Basic.java b/java/t/GuestFS010Basic.java
index 21f88dd..a44fde1 100644
--- a/java/t/GuestFS010Basic.java
+++ b/java/t/GuestFS010Basic.java
@@ -55,6 +55,7 @@ public class GuestFS010Basic
             assert m.get ("/dev/VG/LV1").equals ("ext2");
             assert m.get ("/dev/VG/LV2").equals ("unknown");
 
+            g.shutdown ();
             g.close ();
 
             File f2 = new File ("test.img");
diff --git a/ocaml/examples/create_disk.ml b/ocaml/examples/create_disk.ml
index 75dbdc3..4437b1c 100644
--- a/ocaml/examples/create_disk.ml
+++ b/ocaml/examples/create_disk.ml
@@ -57,6 +57,12 @@ let () =
    *)
   g#upload "/etc/resolv.conf" "/foo/resolv.conf";
 
+  (* Because we wrote to the disk and we want to detect write
+   * errors, call g#shutdown.  You don't need to do this:
+   * g#close will do it implicitly.
+   *)
+  g#shutdown ();
+
   (* Note also that handles are automatically closed if they are
    * reaped by the garbage collector.  You only need to call close
    * if you want to close the handle right away.
diff --git a/ocaml/t/guestfs_010_basic.ml b/ocaml/t/guestfs_010_basic.ml
index b6750a1..bb96737 100644
--- a/ocaml/t/guestfs_010_basic.ml
+++ b/ocaml/t/guestfs_010_basic.ml
@@ -61,5 +61,6 @@ let () =
                "q", 'r' ] then
     failwith "Guestfs.readdir returned incorrect result";
 
+  g#shutdown ();
   g#close ();
   unlink "test.img"
diff --git a/ocaml/t/guestfs_070_threads.ml b/ocaml/t/guestfs_070_threads.ml
index 8b22f93..419f853 100644
--- a/ocaml/t/guestfs_070_threads.ml
+++ b/ocaml/t/guestfs_070_threads.ml
@@ -65,8 +65,7 @@ let () =
   let s = String.copy "/test" in
   Guestfs.touch g s;
 
-  Guestfs.umount_all g;
-  Guestfs.sync g;
+  Guestfs.shutdown g;
   Guestfs.close g;
   unlink "test.img";
   Gc.compact ();
diff --git a/ocaml/t/guestfs_500_parallel_mount_local.ml b/ocaml/t/guestfs_500_parallel_mount_local.ml
index 5a432f0..a26ff9d 100644
--- a/ocaml/t/guestfs_500_parallel_mount_local.ml
+++ b/ocaml/t/guestfs_500_parallel_mount_local.ml
@@ -145,6 +145,7 @@ and start_thread (filename, mp) =
   in
   loop ();
 
+  g#shutdown ();
   g#close ()
 
 (* This is run in a child program. *)
diff --git a/perl/examples/create_disk.pl b/perl/examples/create_disk.pl
index fa15bc7..11b9146 100755
--- a/perl/examples/create_disk.pl
+++ b/perl/examples/create_disk.pl
@@ -57,6 +57,11 @@ $g->mkdir ("/foo");
 # the disk image.
 $g->upload ("/etc/resolv.conf", "/foo/resolv.conf");
 
+# Because we wrote to the disk and we want to detect write
+# errors, call $g->shutdown.  You don't need to do this:
+# $g->close will do it implicitly.
+$g->shutdown ();
+
 # Note also that handles are automatically closed if they are
 # reaped by reference counting.  You only need to call close
 # if you want to close the handle right away.
diff --git a/python/examples/create_disk.py b/python/examples/create_disk.py
index 0885383..86f5042 100644
--- a/python/examples/create_disk.py
+++ b/python/examples/create_disk.py
@@ -51,6 +51,11 @@ g.mkdir ("/foo")
 # the disk image.
 g.upload ("/etc/resolv.conf", "/foo/resolv.conf")
 
+# Because we wrote to the disk and we want to detect write
+# errors, call g.shutdown.  You don't need to do this:
+# g.close will do it implicitly.
+g.shutdown ()
+
 # Note also that handles are automatically closed if they are
 # reaped by reference counting.  You only need to call close
 # if you want to close the handle right away.
diff --git a/python/t/010-basic.py b/python/t/010-basic.py
index a97f3d2..779af48 100644
--- a/python/t/010-basic.py
+++ b/python/t/010-basic.py
@@ -31,6 +31,7 @@ g.lvcreate ("LV1", "VG", 200)
 g.lvcreate ("LV2", "VG", 200)
 if (g.lvs () != ["/dev/VG/LV1", "/dev/VG/LV2"]):
     raise "Error: g.lvs() returned incorrect result"
+g.shutdown ()
 g.close ()
 
 os.unlink ("test.img")
diff --git a/rescue/virt-rescue.c b/rescue/virt-rescue.c
index 684baeb..f1f8d85 100644
--- a/rescue/virt-rescue.c
+++ b/rescue/virt-rescue.c
@@ -341,7 +341,9 @@ main (int argc, char *argv[])
    */
   guestfs_set_error_handler (g, NULL, NULL);
 
-  /* We expect launch to fail, so ignore the return value. */
+  /* We expect launch to fail, so ignore the return value, and don't
+   * bother with explicit guestfs_shutdown either.
+   */
   ignore_value (guestfs_launch (g));
 
   guestfs_close (g);
diff --git a/resize/resize.ml b/resize/resize.ml
index 5e323ba..cd4c9d6 100644
--- a/resize/resize.ml
+++ b/resize/resize.ml
@@ -836,7 +836,7 @@ let g =
     if ok then g, true
     else if attempts > 0 then (
       g#zero "/dev/sdb";
-      g#sync ();
+      g#shutdown ();
       g#close ();
 
       let g = connect_both_disks () in
@@ -1120,8 +1120,7 @@ let to_be_expanded =
 
 let g =
   if to_be_expanded then (
-    g#umount_all ();
-    g#sync ();
+    g#shutdown ();
     g#close ();
 
     let g = new G.guestfs () in
@@ -1192,8 +1191,7 @@ let () =
 
 (* Finished.  Unmount disks and exit. *)
 let () =
-  g#umount_all ();
-  g#sync ();
+  g#shutdown ();
   g#close ();
 
   if not quiet then (
diff --git a/ruby/examples/create_disk.rb b/ruby/examples/create_disk.rb
index 026ea2e..32fb117 100644
--- a/ruby/examples/create_disk.rb
+++ b/ruby/examples/create_disk.rb
@@ -54,6 +54,11 @@ g.mkdir("/foo")
 # the disk image.
 g.upload("/etc/resolv.conf", "/foo/resolv.conf")
 
+# Because we wrote to the disk and we want to detect write
+# errors, call g.shutdown.  You don't need to do this:
+# g.close will do it implicitly.
+g.shutdown()
+
 # Note also that handles are automatically closed if they are
 # reaped by the garbage collector.  You only need to call close
 # if you want to close the handle right away.
diff --git a/sparsify/sparsify.ml b/sparsify/sparsify.ml
index ec3512a..6f1635b 100644
--- a/sparsify/sparsify.ml
+++ b/sparsify/sparsify.ml
@@ -295,6 +295,7 @@ let () =
 
 (* Don't need libguestfs now. *)
 let () =
+  g#shutdown ();
   g#close ()
 
 (* What should the output format be?  If the user specified an
diff --git a/src/guestfs.c b/src/guestfs.c
index e36ad46..93ae247 100644
--- a/src/guestfs.c
+++ b/src/guestfs.c
@@ -172,8 +172,6 @@ guestfs_create (void)
 void
 guestfs_close (guestfs_h *g)
 {
-  int status, sig;
-
   if (g->state == NO_HANDLE) {
     /* Not safe to call ANY callbacks here, so ... */
     fprintf (stderr, _("guestfs_close: called twice on the same handle\n"));
@@ -202,10 +200,6 @@ guestfs_close (guestfs_h *g)
 
   debug (g, "closing guestfs handle %p (state %d)", g, g->state);
 
-  /* Try to sync if autosync flag is set. */
-  if (g->autosync && g->state == READY)
-    ignore_value (guestfs_internal_autosync (g));
-
   /* If we are valgrinding the daemon, then we *don't* want to kill
    * the subprocess because we want the final valgrind messages sent
    * when we close sockets below.  However for normal production use,
@@ -213,42 +207,10 @@ guestfs_close (guestfs_h *g)
    * daemon or qemu is not responding).
    */
 #ifndef VALGRIND_DAEMON
-  /* Kill the qemu subprocess. */
   if (g->state != CONFIG)
-    ignore_value (guestfs_kill_subprocess (g));
+    ignore_value (guestfs_shutdown (g));
 #endif
 
-  /* Close sockets. */
-  if (g->fd[0] >= 0)
-    close (g->fd[0]);
-  if (g->fd[1] >= 0)
-    close (g->fd[1]);
-  if (g->sock >= 0)
-    close (g->sock);
-  g->fd[0] = -1;
-  g->fd[1] = -1;
-  g->sock = -1;
-
-  /* Wait for subprocess(es) to exit. */
-  if (g->pid > 0) {
-    if (waitpid (g->pid, &status, 0) == -1)
-      perror ("waitpid (qemu)");
-    if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
-      fprintf (stderr, "libguestfs: close: qemu failed (status %d)\n",
-               WEXITSTATUS (status));
-    else if (WIFSIGNALED (status)) {
-      sig = WTERMSIG (status);
-      fprintf (stderr, "libguestfs: close: qemu terminated by signal %d (%s)\n",
-               sig, strsignal (sig));
-    }
-    else if (WIFSTOPPED (status)) {
-      sig = WSTOPSIG (status);
-      fprintf (stderr, "libguestfs: close: qemu stopped by signal %d (%s)\n",
-               sig, strsignal (sig));
-    }
-  }
-  if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0);
-
   /* Run user close callbacks. */
   guestfs___call_callbacks_void (g, GUESTFS_EVENT_CLOSE);
 
@@ -290,6 +252,69 @@ guestfs_close (guestfs_h *g)
   free (g);
 }
 
+/* Shutdown the backend. */
+int
+guestfs__shutdown (guestfs_h *g)
+{
+  int ret = 0;
+  int status, sig;
+
+  if (g->state == CONFIG)
+    return 0;
+
+  /* Try to sync if autosync flag is set. */
+  if (g->autosync && g->state == READY) {
+    if (guestfs_internal_autosync (g) == -1)
+      ret = -1;
+  }
+
+  /* Signal qemu to shutdown cleanly, and kill the recovery process. */
+  if (g->pid > 0) {
+    debug (g, "sending SIGTERM to process %d", g->pid);
+    kill (g->pid, SIGTERM);
+  }
+  if (g->recoverypid > 0) kill (g->recoverypid, 9);
+
+  /* Close sockets. */
+  if (g->fd[0] >= 0)
+    close (g->fd[0]);
+  if (g->fd[1] >= 0)
+    close (g->fd[1]);
+  if (g->sock >= 0)
+    close (g->sock);
+  g->fd[0] = -1;
+  g->fd[1] = -1;
+  g->sock = -1;
+
+  /* Wait for subprocess(es) to exit. */
+  if (g->pid > 0) {
+    if (waitpid (g->pid, &status, 0) == -1) {
+      perrorf (g, "waitpid (qemu)");
+      ret = -1;
+    }
+    else if (WIFEXITED (status) && WEXITSTATUS (status) != 0) {
+      error (g, "qemu failed (status %d)", WEXITSTATUS (status));
+      ret = -1;
+    }
+    else if (WIFSIGNALED (status)) {
+      sig = WTERMSIG (status);
+      error (g, "qemu terminated by signal %d (%s)", sig, strsignal (sig));
+      ret = -1;
+    }
+    else if (WIFSTOPPED (status)) {
+      sig = WSTOPSIG (status);
+      error (g, "qemu stopped by signal %d (%s)", sig, strsignal (sig));
+      ret = -1;
+    }
+  }
+  if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0);
+
+  g->pid = g->recoverypid = 0;
+  g->state = CONFIG;
+
+  return ret;
+}
+
 /* Close all open handles (called from atexit(3)). */
 static void
 close_handles (void)
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 6959f50..9538f38 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -14,6 +14,7 @@ guestfs - Library for accessing and modifying virtual machine images
  guestfs_mount (g, "/dev/sda1", "/");
  guestfs_touch (g, "/hello");
  guestfs_umount (g, "/");
+ guestfs_shutdown (g);
  guestfs_close (g);
 
  cc prog.c -o prog -lguestfs
@@ -83,13 +84,10 @@ this:
   */
  guestfs_touch (g, "/hello");
  
- /* This is only needed for libguestfs < 1.5.24.  Since then
-  * it is done automatically when you close the handle.  See
-  * discussion of autosync in this page.
-  */
- guestfs_sync (g);
+ /* Synchronize the disk.  This is the opposite of guestfs_launch. */
+ guestfs_shutdown (g);
  
- /* Close the handle 'g'. */
+ /* Close and free the handle 'g'. */
  guestfs_close (g);
 
 The code above doesn't include any error checking.  In real code you
@@ -1424,9 +1422,18 @@ L</ERROR HANDLING> section below.
 
 This closes the connection handle and frees up all resources used.
 
-If autosync was set on the handle and the handle was launched, then
-this implicitly calls various functions to unmount filesystems and
-sync the disk.  See L</guestfs_set_autosync> for more details.
+The correct way to close the handle is:
+
+ /* guestfs_shutdown is only needed if ALL of the following are true:
+  * (1) one or more disks were added in read-write mode,
+  * (2) guestfs_launch was called,
+  * (3) you made some changes,
+  * (4) you want to handle write errors properly.
+  */
+ if (guestfs_shutdown (g) == -1) {
+   /* handle write errors here */
+ }
+ guestfs_close (g);
 
 If a close callback was set on the handle, then it is called.
 
diff --git a/src/launch.c b/src/launch.c
index 95694ff..af5ca9f 100644
--- a/src/launch.c
+++ b/src/launch.c
@@ -1629,17 +1629,7 @@ guestfs__wait_ready (guestfs_h *g)
 int
 guestfs__kill_subprocess (guestfs_h *g)
 {
-  if (g->state == CONFIG) {
-    error (g, _("no subprocess to kill"));
-    return -1;
-  }
-
-  debug (g, "sending SIGTERM to process %d", g->pid);
-
-  if (g->pid > 0) kill (g->pid, SIGTERM);
-  if (g->recoverypid > 0) kill (g->recoverypid, 9);
-
-  return 0;
+  return guestfs__shutdown (g);
 }
 
 /* Maximum number of disks. */
diff --git a/sysprep/main.ml b/sysprep/main.ml
index ca5664d..05dd57d 100644
--- a/sysprep/main.ml
+++ b/sysprep/main.ml
@@ -239,6 +239,7 @@ let () =
 
 (* Finished. *)
 let () =
+  g#shutdown ();
   g#close ();
 
   if debug_gc then
diff --git a/test-tool/test-tool.c b/test-tool/test-tool.c
index f0e3a31..865720e 100644
--- a/test-tool/test-tool.c
+++ b/test-tool/test-tool.c
@@ -255,6 +255,11 @@ main (int argc, char *argv[])
   }
 
   /* Close the handle. */
+  if (guestfs_shutdown (g) == -1) {
+    fprintf (stderr, _("libguestfs-test-tool: shutdown failed\n"));
+    exit (EXIT_FAILURE);
+  }
+
   guestfs_close (g);
 
   /* Booted and performed some simple operations -- success! */
diff --git a/tests/btrfs/test-btrfs-subvolume-default.pl b/tests/btrfs/test-btrfs-subvolume-default.pl
index 013291b..2c09ce2 100755
--- a/tests/btrfs/test-btrfs-subvolume-default.pl
+++ b/tests/btrfs/test-btrfs-subvolume-default.pl
@@ -90,6 +90,7 @@ $g->mount ("/dev/sda1", "/");
 # Now we're back to the original default volume, so this should work:
 $g->mkdir ("/test1/foo/bar/baz");
 
+$g->shutdown ();
 $g->close ();
 
 unlink $testimg or die "$testimg: unlink: $!";
diff --git a/tests/disks/test-max-disks.pl b/tests/disks/test-max-disks.pl
index 3ced971..a0bba7f 100755
--- a/tests/disks/test-max-disks.pl
+++ b/tests/disks/test-max-disks.pl
@@ -140,7 +140,7 @@ for ($i = 0, $j = 0; $i < $max_disks; ++$i) {
     }
 }
 
-$g->umount_all ();
+$g->shutdown ();
 $g->close ();
 
 for ($i = 0; $i < $max_disks; ++$i) {
diff --git a/tests/guests/guest-aux/make-fedora-img.pl b/tests/guests/guest-aux/make-fedora-img.pl
index 96e4168..6a0a183 100755
--- a/tests/guests/guest-aux/make-fedora-img.pl
+++ b/tests/guests/guest-aux/make-fedora-img.pl
@@ -195,6 +195,9 @@ $g->mkfs_opts ('ext2', '/dev/VG/LV2', blocksize => 1024);
 $g->mkfs_opts ('ext2', '/dev/VG/LV3', blocksize => 2048);
 
 # Cleanup
+$g->shutdown ();
+$g->close ();
+
 unlink ("fstab.tmp.$$") or die;
 foreach my $img (@images) {
   $img =~ /^(.*)\.tmp\.\d+$/ or die;
diff --git a/tests/selinux/run-test.pl b/tests/selinux/run-test.pl
index 6b0ffb5..bbe3a2f 100755
--- a/tests/selinux/run-test.pl
+++ b/tests/selinux/run-test.pl
@@ -154,6 +154,7 @@ if ($test_via eq "direct") {
 }
 
 # Finish up.
+$g->shutdown ();
 $g->close ();
 unlink $testimg or die "$testimg: $!";
 
diff --git a/tools/virt-make-fs b/tools/virt-make-fs
index 7d03280..b53e860 100755
--- a/tools/virt-make-fs
+++ b/tools/virt-make-fs
@@ -539,9 +539,8 @@ eval {
     }
 
     print STDERR "finishing off\n" if $debug;
-    $g->umount_all ();
-    $g->sync ();
-    undef $g;
+    $g->shutdown ();
+    $g->close ()
 };
 if ($@) {
     # Error: delete the output before exiting.
diff --git a/tools/virt-tar b/tools/virt-tar
index 777dbbd..801104f 100755
--- a/tools/virt-tar
+++ b/tools/virt-tar
@@ -279,10 +279,8 @@ if ($mode eq "x") {
     }
 }
 
-$g->sync ();
-$g->umount_all ();
-
-undef $g;
+$g->shutdown ();
+$g->close ();
 
 exit 0;
 
diff --git a/tools/virt-win-reg b/tools/virt-win-reg
index ab5daa7..2caa954 100755
--- a/tools/virt-win-reg
+++ b/tools/virt-win-reg
@@ -337,9 +337,9 @@ else {                          # Import mode.
         upload_hive ($hiveshortname, $hives{$hiveshortname}->{hivefile})
     }
 
-    # Sync everything.
-    $g->umount_all ();
-    $g->sync ();
+    # Close.
+    $g->shutdown ();
+    $g->close ();
 }
 
 exit 0;
-- 
1.7.10.4




More information about the Libguestfs mailing list