[Libguestfs] [PATCH 1/2] Use 'error' function consistently throughout.

Richard W.M. Jones
Mon, 04 Apr 2016 12:28:50 -0000

Wherever we had code which did:

  if (something_bad) {
    perror (...);
    exit (EXIT_FAILURE);
  }

replace this with use of the error(3) function:

  if (something_bad)
    error (EXIT_FAILURE, errno, ...);

The error(3) function is supplied by glibc, or by gnulib on platforms
which don't have it, and is much more flexible than perror(3).  Since
we already use error(3), there seems to be no downside to mandating it
everywhere.

Note there is one nasty catch with error(3): error (EXIT_SUCCESS, ...)
does *not* exit!  This is also the reason why error(3) cannot be
marked as __attribute__((noreturn)).

Because the examples can't use gnulib, I did not change them.

To search for multiline patterns of the above form, pcregrep -M turns
out to be very useful:

  pcregrep --buffer-size 10M -M '\bperror\b.*\n.*\bexit\b' `git
ls-files`
---
 builder/index-validate.c                        |   7 +-
 cat/cat.c                                       |  19 +--
 cat/filesystems.c                               |  67 ++++------
 cat/ls.c                                        | 115 ++++++----------
 daemon/guestfsd.c                               |  10 +-
 daemon/ntfsclone.c                              |   8 +-
 daemon/proto.c                                  |  25 ++--
 daemon/tar.c                                    |   8 +-
 df/domains.c                                    |  31 ++---
 df/main.c                                       |  49 +++----
 diff/diff.c                                     | 169 ++++++++----------------
 edit/edit.c                                     |  19 +--
 fish/config.c                                   |  37 ++----
 fish/events.c                                   |   8 +-
 fish/fish.c                                     |  73 ++++------
 fish/options.c                                  |  31 ++---
 fish/options.h                                  |   6 +-
 fish/prep-boot.c                                |  26 ++--
 fish/prep-fs.c                                  |   8 +-
 fish/prep-lv.c                                  |  26 ++--
 fish/prep.c                                     |  20 ++-
 fish/rc.c                                       |  61 +++------
 fish/tilde.c                                    |  14 +-
 fish/windows.c                                  |  19 +--
 format/format.c                                 |  22 ++-
 fuse/guestmount.c                               |  19 +--
 fuse/guestunmount.c                             |  69 ++++------
 fuse/test-fuse.c                                |  32 ++---
 fuse/test-guestmount-fd.c                       |  31 ++---
 fuse/test-guestunmount-fd.c                     |  25 ++--
 inspector/inspector.c                           |  19 +--
 p2v/config.c                                    |   7 +-
 p2v/conversion.c                                |  27 ++--
 p2v/gui.c                                       |  61 +++------
 p2v/kernel-cmdline.c                            |  13 +-
 p2v/kernel.c                                    |   7 +-
 p2v/main.c                                      |  79 ++++-------
 p2v/ssh.c                                       |  63 +++------
 p2v/utils.c                                     |  14 +-
 rescue/rescue.c                                 |  25 ++--
 test-tool/test-tool.c                           |  14 +-
 tests/c-api/test-add-libvirt-dom.c              |   8 +-
 tests/c-api/test-debug-to-file.c                |   8 +-
 tests/c-api/test-user-cancel.c                  |  60 ++++-----
 tests/c-api/tests-main.c                        |  32 ++---
 tests/events/test-libvirt-auth-callbacks.c      |  14 +-
 tests/mountable/test-internal-parse-mountable.c |  12 +-
 tests/protocol/test-error-messages.c            |   7 +-
 tests/qemu/qemu-boot.c                          |   7 +-
 tests/qemu/qemu-speed-test.c                    |  37 ++----
 tests/regressions/rhbz1055452.c                 |  14 +-
 tests/regressions/rhbz790721.c                  |   7 +-
 tests/regressions/rhbz914931.c                  |   7 +-
 53 files changed, 587 insertions(+), 1009 deletions(-)

diff --git a/builder/index-validate.c b/builder/index-validate.c
index 9bc2e73..22e2ccd 100644
--- a/builder/index-validate.c
+++ b/builder/index-validate.c
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <limits.h>
 #include <getopt.h>
+#include <error.h>
 #include <errno.h>
 #include <locale.h>
 #include <libintl.h>
@@ -108,10 +109,8 @@ main (int argc, char *argv[])
   input = argv[optind++];

   in = fopen (input, "r");
-  if (in == NULL) {
-    perror (input);
-    exit (EXIT_FAILURE);
-  }
+  if (in == NULL)
+    error (EXIT_FAILURE, errno, "fopen: %s", input);

   ret = do_parse (&context, in);

diff --git a/cat/cat.c b/cat/cat.c
index bf8b371..c90fc06 100644
--- a/cat/cat.c
+++ b/cat/cat.c
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <assert.h>
 #include <libintl.h>
@@ -190,24 +191,18 @@ main (int argc, char *argv[])
       if (strchr (argv[optind], '/') ||
           access (argv[optind], F_OK) == 0) { /* simulate -a option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_a;
         drv->a.filename = strdup (argv[optind]);
-        if (!drv->a.filename) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv->a.filename)
+          error (EXIT_FAILURE, errno, "strdup");
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_d;
         drv->d.guest = argv[optind];
         drv->next = drvs;
diff --git a/cat/filesystems.c b/cat/filesystems.c
index 3013115..6f083d3 100644
--- a/cat/filesystems.c
+++ b/cat/filesystems.c
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <assert.h>
 #include <string.h>
@@ -458,10 +459,8 @@ do_output_filesystems (void)
       guestfs_pop_error_handler (g);
       if (vfs_label == NULL) {
         vfs_label = strdup ("");
-        if (!vfs_label) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (!vfs_label)
+          error (EXIT_FAILURE, errno, "strdup");
       }
     }
     if ((columns & COLUMN_UUID)) {
@@ -470,10 +469,8 @@ do_output_filesystems (void)
       guestfs_pop_error_handler (g);
       if (vfs_uuid == NULL) {
         vfs_uuid = strdup ("");
-        if (!vfs_uuid) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (!vfs_uuid)
+          error (EXIT_FAILURE, errno, "strdup");
       }
     }
     if ((columns & COLUMN_SIZE)) {
@@ -518,10 +515,8 @@ do_output_lvs (void)
     }
     if ((columns & COLUMN_PARENTS)) {
       parent_name = strdup (lvs[i]);
-      if (parent_name == NULL) {
-        perror ("strdup");
-        exit (EXIT_FAILURE);
-      }
+      if (parent_name == NULL)
+        error (EXIT_FAILURE, errno, "strdup");
       char *p = strrchr (parent_name, '/');
       if (p)
         *p = '\0';
@@ -549,10 +544,8 @@ do_output_vgs (void)
     char uuid[33];
     CLEANUP_FREE_STRING_LIST char **parents = NULL;

-    if (asprintf (&name, "/dev/%s", vgs->val[i].vg_name) ==
-1) {
-      perror ("asprintf");
-      exit (EXIT_FAILURE);
-    }
+    if (asprintf (&name, "/dev/%s", vgs->val[i].vg_name) ==
-1)
+      error (EXIT_FAILURE, errno, "asprintf");

     memcpy (uuid, vgs->val[i].vg_uuid, 32);
     uuid[32] = '\0';
@@ -719,10 +712,8 @@ no_parents (void)
   char **ret;

   ret = malloc (sizeof (char *));
-  if (!ret) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!ret)
+    error (EXIT_FAILURE, errno, "malloc");

   ret[0] = NULL;

@@ -760,10 +751,8 @@ parents_of_md (char *device)
     exit (EXIT_FAILURE);

   ret = malloc ((stats->len + 1) * sizeof (char *));
-  if (!ret) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!ret)
+    error (EXIT_FAILURE, errno, "malloc");

   for (i = 0; i < stats->len; ++i) {
     ret[i] = guestfs_canonical_device_name (g, stats->val[i].mdstat_device);
@@ -814,10 +803,8 @@ parents_of_vg (char *vg)
   n = guestfs_int_count_strings (pvuuids);

   ret = malloc ((n + 1) * sizeof (char *));
-  if (!ret) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!ret)
+    error (EXIT_FAILURE, errno, "malloc");

   /* Resolve each PV UUID back to a PV. */
   for (i = 0; i < n; ++i) {
@@ -834,10 +821,8 @@ parents_of_vg (char *vg)
     else {
       fprintf (stderr, "%s: warning: unknown PV UUID ignored\n",
__func__);
       ret[i] = strndup (pvuuids[i], 32);
-      if (!ret[i]) {
-        perror ("strndup");
-        exit (EXIT_FAILURE);
-      }
+      if (!ret[i])
+        error (EXIT_FAILURE, errno, "strndup");
     }
   }

@@ -978,18 +963,14 @@ add_row (char **strings, size_t len)
   assert (len <= NR_COLUMNS);

   row = malloc (sizeof (char *) * len);
-  if (row == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (row == NULL)
+    error (EXIT_FAILURE, errno, "malloc");

   for (i = 0; i < len; ++i) {
     if (strings[i]) {
       row[i] = strdup (strings[i]);
-      if (row[i] == NULL) {
-        perror ("strdup");
-        exit (EXIT_FAILURE);
-      }
+      if (row[i] == NULL)
+        error (EXIT_FAILURE, errno, "strdup");

       /* Keep a running total of the max width of each column. */
       slen = strlen (strings[i]);
@@ -1003,10 +984,8 @@ add_row (char **strings, size_t len)
   }

   rows = realloc (rows, sizeof (char **) * (nr_rows + 1));
-  if (rows == NULL) {
-    perror ("realloc");
-    exit (EXIT_FAILURE);
-  }
+  if (rows == NULL)
+    error (EXIT_FAILURE, errno, "realloc");
   rows[nr_rows] = row;
   nr_rows++;
 }
diff --git a/cat/ls.c b/cat/ls.c
index c49d1ce..f92bda1 100644
--- a/cat/ls.c
+++ b/cat/ls.c
@@ -26,6 +26,7 @@
 #include <getopt.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <assert.h>
 #include <time.h>
@@ -291,24 +292,18 @@ main (int argc, char *argv[])
       if (strchr (argv[optind], '/') ||
           access (argv[optind], F_OK) == 0) { /* simulate -a option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_a;
         drv->a.filename = strdup (argv[optind]);
-        if (!drv->a.filename) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv->a.filename)
+          error (EXIT_FAILURE, errno, "strdup");
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_d;
         drv->d.guest = argv[optind];
         drv->next = drvs;
@@ -564,10 +559,8 @@ next_field (void)
   field++;
   if (field == 1) return;

-  if (putchar (c) == EOF) {
-    perror ("putchar");
-    exit (EXIT_FAILURE);
-  }
+  if (putchar (c) == EOF)
+    error (EXIT_FAILURE, errno, "putchar");
 }

 static void
@@ -579,10 +572,8 @@ output_start_line (void)
 static void
 output_end_line (void)
 {
-  if (printf ("\n") < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (printf ("\n") < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -592,10 +583,8 @@ output_string (const char *s)

   if (!csv) {
   print_no_quoting:
-    if (printf ("%s", s) < 0) {
-      perror ("printf");
-      exit (EXIT_FAILURE);
-    }
+    if (printf ("%s", s) < 0)
+      error (EXIT_FAILURE, errno, "printf");
   }
   else {
     /* Quote CSV string without requiring an external module. */
@@ -616,27 +605,19 @@ output_string (const char *s)
       goto print_no_quoting;

     /* Quoting for CSV fields. */
-    if (putchar ('"') == EOF) {
-      perror ("putchar");
-      exit (EXIT_FAILURE);
-    }
+    if (putchar ('"') == EOF)
+      error (EXIT_FAILURE, errno, "putchar");
     for (i = 0; i < len; ++i) {
       if (s[i] == '"') {
-        if (putchar ('"') == EOF || putchar ('"') ==
EOF) {
-          perror ("putchar");
-          exit (EXIT_FAILURE);
-        }
+        if (putchar ('"') == EOF || putchar ('"') ==
EOF)
+          error (EXIT_FAILURE, errno, "putchar");
       } else {
-        if (putchar (s[i]) == EOF) {
-          perror ("putchar");
-          exit (EXIT_FAILURE);
-        }
+        if (putchar (s[i]) == EOF)
+          error (EXIT_FAILURE, errno, "putchar");
       }
     }
-    if (putchar ('"') == EOF) {
-      perror ("putchar");
-      exit (EXIT_FAILURE);
-    }
+    if (putchar ('"') == EOF)
+      error (EXIT_FAILURE, errno, "putchar");
   }
 }

@@ -648,10 +629,8 @@ output_string_link (const char *link)
   else {
     next_field ();

-    if (printf ("-> %s", link) < 0) {
-      perror ("printf");
-      exit (EXIT_FAILURE);
-    }
+    if (printf ("-> %s", link) < 0)
+      error (EXIT_FAILURE, errno, "printf");
   }
 }

@@ -660,10 +639,8 @@ output_int64 (int64_t i)
 {
   next_field ();
   /* csv doesn't need escaping */
-  if (printf ("%" PRIi64, i) < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (printf ("%" PRIi64, i) < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -690,10 +667,8 @@ output_int64_size (int64_t size)
                   human_readable ((uintmax_t) size, buf, hopts, 1, 1));
   }

-  if (r < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (r < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -701,10 +676,8 @@ output_int64_perms (int64_t i)
 {
   next_field ();
   /* csv doesn't need escaping */
-  if (printf ("%04" PRIo64, (uint64_t) i) < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (printf ("%04" PRIo64, (uint64_t) i) < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -735,23 +708,17 @@ output_int64_time (int64_t secs, int64_t nsecs)
     struct tm *tm;

     tm = localtime (&t);
-    if (tm == NULL) {
-      perror ("localtime");
-      exit (EXIT_FAILURE);
-    }
+    if (tm == NULL)
+      error (EXIT_FAILURE, errno, "localtime");

-    if (strftime (buf, sizeof buf, "%F %T", tm) == 0) {
-      perror ("strftime");
-      exit (EXIT_FAILURE);
-    }
+    if (strftime (buf, sizeof buf, "%F %T", tm) == 0)
+      error (EXIT_FAILURE, errno, "strftime");

     r = printf ("%s", buf);
   }

-  if (r < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (r < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -759,10 +726,8 @@ output_int64_uid (int64_t i)
 {
   next_field ();
   /* csv doesn't need escaping */
-  if (printf ("%4" PRIi64, i) < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (printf ("%4" PRIi64, i) < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -774,8 +739,6 @@ output_int64_dev (int64_t i)

   /* csv doesn't need escaping */
   if (printf ("%ju:%ju",
-              (uintmax_t) major (dev), (uintmax_t) minor (dev)) < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+              (uintmax_t) major (dev), (uintmax_t) minor (dev)) < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index f4e0e56..00b4a0a 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -40,6 +40,7 @@
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <errno.h>
+#include <error.h>
 #include <assert.h>
 #include <termios.h>

@@ -300,8 +301,7 @@ main (int argc, char *argv[])
                  "output to the libguestfs developers, either in a bug
report\n"
                  "or on the libguestfs redhat com mailing list.\n"
                  "\n");
-        perror (channel);
-        exit (EXIT_FAILURE);
+        error (EXIT_FAILURE, errno, "open: %s", channel);
       }
     }
   }
@@ -353,10 +353,8 @@ main (int argc, char *argv[])
   xdrmem_create (&xdr, lenbuf, sizeof lenbuf, XDR_ENCODE);
   xdr_u_int (&xdr, &len);

-  if (xwrite (sock, lenbuf, sizeof lenbuf) == -1) {
-    perror ("xwrite");
-    exit (EXIT_FAILURE);
-  }
+  if (xwrite (sock, lenbuf, sizeof lenbuf) == -1)
+    error (EXIT_FAILURE, errno, "xwrite");

   xdr_destroy (&xdr);

diff --git a/daemon/ntfsclone.c b/daemon/ntfsclone.c
index a239111..f04017c 100644
--- a/daemon/ntfsclone.c
+++ b/daemon/ntfsclone.c
@@ -23,6 +23,8 @@
 #include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>

 #include "read-file.h"

@@ -42,10 +44,8 @@ read_error_file (char *error_file)
   str = read_file (error_file, &len);
   if (str == NULL) {
     str = strdup ("(no error)");
-    if (str == NULL) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (str == NULL)
+      error (EXIT_FAILURE, errno, "strdup"); /* XXX */
     len = strlen (str);
   }

diff --git a/daemon/proto.c b/daemon/proto.c
index 61d376e..c3972f1 100644
--- a/daemon/proto.c
+++ b/daemon/proto.c
@@ -26,6 +26,7 @@
 #include <inttypes.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <sys/param.h>		/* defines MIN */
 #include <sys/select.h>
 #include <sys/time.h>
@@ -231,10 +232,8 @@ reply_with_error_errno (int err, const char *fs, ...)
   r = vasprintf (&buf, fs, args);
   va_end (args);

-  if (r == -1) {
-    perror ("vasprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (r == -1)
+    error (EXIT_FAILURE, errno, "vasprintf");

   send_error (err, buf);
 }
@@ -251,11 +250,9 @@ reply_with_perror_errno (int err, const char *fs, ...)
   r = vasprintf (&buf1, fs, args);
   va_end (args);

-  if (r == -1) {
+  if (r == -1)
   error:
-    perror ("vasprintf");
-    exit (EXIT_FAILURE);
-  }
+    error (EXIT_FAILURE, errno, "vasprintf");

   r = asprintf (&buf2, "%s: %s", buf1, strerror (err));
   if (r == -1)
@@ -287,10 +284,8 @@ send_error (int errnum, char *msg)
     msg[GUESTFS_ERROR_LEN] = '\0';

   buf = malloc (GUESTFS_ERROR_LEN + 200);
-  if (!buf) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!buf)
+    error (EXIT_FAILURE, errno, "malloc");
   xdrmem_create (&xdr, buf, GUESTFS_ERROR_LEN + 200, XDR_ENCODE);

   memset (&hdr, 0, sizeof hdr);
@@ -345,10 +340,8 @@ reply (xdrproc_t xdrp, char *ret)
   uint32_t len;

   buf = malloc (GUESTFS_MESSAGE_MAX);
-  if (!buf) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!buf)
+    error (EXIT_FAILURE, errno, "malloc");
   xdrmem_create (&xdr, buf, GUESTFS_MESSAGE_MAX, XDR_ENCODE);

   memset (&hdr, 0, sizeof hdr);
diff --git a/daemon/tar.c b/daemon/tar.c
index baa5403..6d0403a 100644
--- a/daemon/tar.c
+++ b/daemon/tar.c
@@ -23,6 +23,8 @@
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>
 #include <sys/types.h>
 #include <sys/stat.h>

@@ -110,10 +112,8 @@ read_error_file (char *error_file)
   str = read_file (error_file, &len);
   if (str == NULL) {
     str = strdup ("(no error)");
-    if (str == NULL) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (str == NULL)
+      error (EXIT_FAILURE, errno, "strdup"); /* XXX */
     len = strlen (str);
   }

diff --git a/df/domains.c b/df/domains.c
index 9cb0a09..2407eec 100644
--- a/df/domains.c
+++ b/df/domains.c
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <error.h>
 #include <libintl.h>

 #ifdef HAVE_LIBVIRT
@@ -99,10 +100,8 @@ get_all_libvirt_domains (const char *libvirt_uri)
   }

   ids = malloc (sizeof (int) * n);
-  if (ids == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (ids == NULL)
+    error (EXIT_FAILURE, errno, "malloc");
   n = virConnectListDomains (conn, ids, n);
   if (n == -1) {
     err = virGetLastError ();
@@ -124,10 +123,8 @@ get_all_libvirt_domains (const char *libvirt_uri)
   }

   names = malloc (sizeof (char *) * n);
-  if (names == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (names == NULL)
+    error (EXIT_FAILURE, errno, "malloc");
   n = virConnectListDefinedDomains (conn, names, n);
   if (n == -1) {
     err = virGetLastError ();
@@ -187,10 +184,8 @@ add_domain (virDomainPtr dom)
   struct domain *domain;

   domains = realloc (domains, (nr_domains + 1) * sizeof (struct domain));
-  if (domains == NULL) {
-    perror ("realloc");
-    exit (EXIT_FAILURE);
-  }
+  if (domains == NULL)
+    error (EXIT_FAILURE, errno, "realloc");

   domain = &domains[nr_domains];
   nr_domains++;
@@ -198,18 +193,14 @@ add_domain (virDomainPtr dom)
   domain->dom = dom;

   domain->name = strdup (virDomainGetName (dom));
-  if (domain->name == NULL) {
-    perror ("strdup");
-    exit (EXIT_FAILURE);
-  }
+  if (domain->name == NULL)
+    error (EXIT_FAILURE, errno, "strdup");

   char uuid[VIR_UUID_STRING_BUFLEN];
   if (virDomainGetUUIDString (dom, uuid) == 0) {
     domain->uuid = strdup (uuid);
-    if (domain->uuid == NULL) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (domain->uuid == NULL)
+      error (EXIT_FAILURE, errno, "strdup");
   }
   else
     domain->uuid = NULL;
diff --git a/df/main.c b/df/main.c
index 5b9b00a..8866151 100644
--- a/df/main.c
+++ b/df/main.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <assert.h>
 #include <libintl.h>
@@ -216,24 +217,18 @@ main (int argc, char *argv[])
       if (strchr (argv[optind], '/') ||
           access (argv[optind], F_OK) == 0) { /* simulate -a option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_a;
         drv->a.filename = strdup (argv[optind]);
-        if (!drv->a.filename) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv->a.filename)
+          error (EXIT_FAILURE, errno, "strdup");
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_d;
         drv->d.guest = argv[optind];
         drv->next = drvs;
@@ -335,28 +330,22 @@ single_drive_display_name (struct drv *drvs)
     else
       name++;                   /* skip '/' character */
     name = strdup (name);
-    if (name == NULL) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (name == NULL)
+      error (EXIT_FAILURE, errno, "strdup");
     break;

   case drv_uri:
     name = strdup (drvs->uri.orig_uri);
-    if (name == NULL) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (name == NULL)
+      error (EXIT_FAILURE, errno, "strdup");
     /* Try to shorten the URI to just the final element, if it will
      * still make sense.
      */
     p = strrchr (name, '/');
     if (p && strlen (p) > 1) {
       p = strdup (p+1);
-      if (!p) {
-        perror ("strdup");
-        exit (EXIT_FAILURE);
-      }
+      if (!p)
+        error (EXIT_FAILURE, errno, "strdup");
       free (name);
       name = p;
     }
@@ -364,10 +353,8 @@ single_drive_display_name (struct drv *drvs)

   case drv_d:
     name = strdup (drvs->d.guest);
-    if (name == NULL) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (name == NULL)
+      error (EXIT_FAILURE, errno, "strdup");
     break;
   }

@@ -405,10 +392,8 @@ make_display_name (struct drv *drvs)
     len = strlen (ret);

     ret = realloc (ret, len + pluses + 1);
-    if (ret == NULL) {
-      perror ("realloc");
-      exit (EXIT_FAILURE);
-    }
+    if (ret == NULL)
+      error (EXIT_FAILURE, errno, "realloc");
     for (i = len; i < len + pluses; ++i)
       ret[i] = '+';
     ret[i] = '\0';
diff --git a/diff/diff.c b/diff/diff.c
index 7589970..2e099db 100644
--- a/diff/diff.c
+++ b/diff/diff.c
@@ -26,6 +26,7 @@
 #include <getopt.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <assert.h>
 #include <time.h>
@@ -709,20 +710,14 @@ diff (struct file *file1, guestfs_h *g1, struct file
*file2, guestfs_h *g2)
   assert (is_reg (file1->stat->st_mode));
   assert (is_reg (file2->stat->st_mode));

-  if (asprintf (&tmpd, "%s/virtdiffXXXXXX", tmpdir) < 0) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
-  if (mkdtemp (tmpd) == NULL) {
-    perror ("mkdtemp");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&tmpd, "%s/virtdiffXXXXXX", tmpdir) < 0)
+    error (EXIT_FAILURE, errno, "asprintf");
+  if (mkdtemp (tmpd) == NULL)
+    error (EXIT_FAILURE, errno, "mkdtemp");

   if (asprintf (&tmpda, "%s/a", tmpd) < 0 ||
-      asprintf (&tmpdb, "%s/b", tmpd) < 0) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+      asprintf (&tmpdb, "%s/b", tmpd) < 0)
+    error (EXIT_FAILURE, errno, "asprintf");

   if (guestfs_download (g1, file1->path, tmpda) == -1)
     goto out;
@@ -732,10 +727,8 @@ diff (struct file *file1, guestfs_h *g1, struct file
*file2, guestfs_h *g2)
   /* Note that the tmpdir is safe, and the rest of the path
    * should not need quoting.
    */
-  if (asprintf (&cmd, "diff -u '%s' '%s' | tail -n
+3", tmpda, tmpdb) < 0) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&cmd, "diff -u '%s' '%s' | tail -n
+3", tmpda, tmpdb) < 0)
+    error (EXIT_FAILURE, errno, "asprintf");

   if (verbose)
     fprintf (stderr, "%s\n", cmd);
@@ -838,10 +831,8 @@ next_field (void)
   field++;
   if (field == 1) return;

-  if (putchar (c) == EOF) {
-    perror ("putchar");
-    exit (EXIT_FAILURE);
-  }
+  if (putchar (c) == EOF)
+    error (EXIT_FAILURE, errno, "putchar");
 }

 static void
@@ -853,19 +844,15 @@ output_start_line (void)
 static void
 output_end_line (void)
 {
-  if (printf ("\n") < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (printf ("\n") < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
 output_flush (void)
 {
-  if (fflush (stdout) == EOF) {
-    perror ("fflush");
-    exit (EXIT_FAILURE);
-  }
+  if (fflush (stdout) == EOF)
+    error (EXIT_FAILURE, errno, "fflush");
 }

 static void
@@ -875,10 +862,8 @@ output_string (const char *s)

   if (!csv) {
   print_no_quoting:
-    if (printf ("%s", s) < 0) {
-      perror ("printf");
-      exit (EXIT_FAILURE);
-    }
+    if (printf ("%s", s) < 0)
+      error (EXIT_FAILURE, errno, "printf");
   }
   else {
     /* Quote CSV string without requiring an external module. */
@@ -899,27 +884,19 @@ output_string (const char *s)
       goto print_no_quoting;

     /* Quoting for CSV fields. */
-    if (putchar ('"') == EOF) {
-      perror ("putchar");
-      exit (EXIT_FAILURE);
-    }
+    if (putchar ('"') == EOF)
+      error (EXIT_FAILURE, errno, "putchar");
     for (i = 0; i < len; ++i) {
       if (s[i] == '"') {
-        if (putchar ('"') == EOF || putchar ('"') ==
EOF) {
-          perror ("putchar");
-          exit (EXIT_FAILURE);
-        }
+        if (putchar ('"') == EOF || putchar ('"') ==
EOF)
+          error (EXIT_FAILURE, errno, "putchar");
       } else {
-        if (putchar (s[i]) == EOF) {
-          perror ("putchar");
-          exit (EXIT_FAILURE);
-        }
+        if (putchar (s[i]) == EOF)
+          error (EXIT_FAILURE, errno, "putchar");
       }
     }
-    if (putchar ('"') == EOF) {
-      perror ("putchar");
-      exit (EXIT_FAILURE);
-    }
+    if (putchar ('"') == EOF)
+      error (EXIT_FAILURE, errno, "putchar");
   }
 }

@@ -931,10 +908,8 @@ output_string_link (const char *link)
   else {
     next_field ();

-    if (printf ("-> %s", link) < 0) {
-      perror ("printf");
-      exit (EXIT_FAILURE);
-    }
+    if (printf ("-> %s", link) < 0)
+      error (EXIT_FAILURE, errno, "printf");
   }
 }

@@ -949,15 +924,11 @@ output_binary (const char *s, size_t len)
   print_no_quoting:
     for (i = 0; i < len; ++i) {
       if (c_isprint (s[i])) {
-        if (putchar (s[i]) == EOF) {
-          perror ("putchar");
-          exit (EXIT_FAILURE);
-        }
+        if (putchar (s[i]) == EOF)
+          error (EXIT_FAILURE, errno, "putchar");
       } else {
-        if (printf ("\\x%02x", (unsigned char) s[i]) < 0) {
-          perror ("printf");
-          exit (EXIT_FAILURE);
-        }
+        if (printf ("\\x%02x", (unsigned char) s[i]) < 0)
+          error (EXIT_FAILURE, errno, "putchar");
       }
     }
   }
@@ -977,34 +948,24 @@ output_binary (const char *s, size_t len)
       goto print_no_quoting;

     /* Quoting for CSV fields. */
-    if (putchar ('"') == EOF) {
-      perror ("putchar");
-      exit (EXIT_FAILURE);
-    }
+    if (putchar ('"') == EOF)
+      error (EXIT_FAILURE, errno, "putchar");
     for (i = 0; i < len; ++i) {
       if (s[i] == '"') {
-        if (putchar ('"') == EOF || putchar ('"') ==
EOF) {
-          perror ("putchar");
-          exit (EXIT_FAILURE);
-        }
+        if (putchar ('"') == EOF || putchar ('"') ==
EOF)
+          error (EXIT_FAILURE, errno, "putchar");
       } else {
         if (c_isprint (s[i])) {
-          if (putchar (s[i]) == EOF) {
-            perror ("putchar");
-            exit (EXIT_FAILURE);
-          }
+          if (putchar (s[i]) == EOF)
+            error (EXIT_FAILURE, errno, "putchar");
         } else {
-          if (printf ("\\x%2x", (unsigned) s[i]) < 0) {
-            perror ("printf");
-            exit (EXIT_FAILURE);
-          }
+          if (printf ("\\x%2x", (unsigned) s[i]) < 0)
+            error (EXIT_FAILURE, errno, "printf");
         }
       }
     }
-    if (putchar ('"') == EOF) {
-      perror ("putchar");
-      exit (EXIT_FAILURE);
-    }
+    if (putchar ('"') == EOF)
+      error (EXIT_FAILURE, errno, "putchar");
   }
 }

@@ -1013,10 +974,8 @@ output_int64 (int64_t i)
 {
   next_field ();
   /* csv doesn't need escaping */
-  if (printf ("%" PRIi64, i) < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (printf ("%" PRIi64, i) < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -1043,10 +1002,8 @@ output_int64_size (int64_t size)
                   human_readable ((uintmax_t) size, buf, hopts, 1, 1));
   }

-  if (r < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (r < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -1054,10 +1011,8 @@ output_int64_perms (int64_t i)
 {
   next_field ();
   /* csv doesn't need escaping */
-  if (printf ("%04" PRIo64, (uint64_t) i) < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (printf ("%04" PRIo64, (uint64_t) i) < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -1088,23 +1043,17 @@ output_int64_time (int64_t secs, int64_t nsecs)
     struct tm *tm;

     tm = localtime (&t);
-    if (tm == NULL) {
-      perror ("localtime");
-      exit (EXIT_FAILURE);
-    }
+    if (tm == NULL)
+      error (EXIT_FAILURE, errno, "localtime");

-    if (strftime (buf, sizeof buf, "%F %T", tm) == 0) {
-      perror ("strftime");
-      exit (EXIT_FAILURE);
-    }
+    if (strftime (buf, sizeof buf, "%F %T", tm) == 0)
+      error (EXIT_FAILURE, errno, "strftime");

     r = printf ("%s", buf);
   }

-  if (r < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (r < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -1112,10 +1061,8 @@ output_int64_uid (int64_t i)
 {
   next_field ();
   /* csv doesn't need escaping */
-  if (printf (csv ? "%" PRIi64 : "%4" PRIi64, i) < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+  if (printf (csv ? "%" PRIi64 : "%4" PRIi64, i) < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }

 static void
@@ -1127,8 +1074,6 @@ output_int64_dev (int64_t i)

   /* csv doesn't need escaping */
   if (printf ("%ju:%ju",
-              (uintmax_t) major (dev), (uintmax_t) minor (dev)) < 0) {
-    perror ("printf");
-    exit (EXIT_FAILURE);
-  }
+              (uintmax_t) major (dev), (uintmax_t) minor (dev)) < 0)
+    error (EXIT_FAILURE, errno, "printf");
 }
diff --git a/edit/edit.c b/edit/edit.c
index 90b004e..38af8a5 100644
--- a/edit/edit.c
+++ b/edit/edit.c
@@ -26,6 +26,7 @@
 #include <locale.h>
 #include <getopt.h>
 #include <errno.h>
+#include <error.h>
 #include <assert.h>
 #include <libintl.h>
 #include <sys/time.h>
@@ -225,24 +226,18 @@ main (int argc, char *argv[])
       if (strchr (argv[optind], '/') ||
           access (argv[optind], F_OK) == 0) { /* simulate -a option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_a;
         drv->a.filename = strdup (argv[optind]);
-        if (!drv->a.filename) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv->a.filename)
+          error (EXIT_FAILURE, errno, "strdup");
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_d;
         drv->d.guest = argv[optind];
         drv->next = drvs;
diff --git a/fish/config.c b/fish/config.c
index 51014bf..4463d9f 100644
--- a/fish/config.c
+++ b/fish/config.c
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <error.h>
 #include <libintl.h>

 #ifdef HAVE_LIBCONFIG
@@ -68,10 +69,8 @@ read_config_from_file (const char *filename)
       exit (EXIT_FAILURE);
     }

-    if (fclose (fp) == -1) {
-      perror (filename);
-      exit (EXIT_FAILURE);
-    }
+    if (fclose (fp) == -1)
+      error (EXIT_FAILURE, errno, "fclose: %s", filename);

     config_lookup_bool (&conf, "read_only", &read_only);

@@ -101,10 +100,8 @@ parse_config (void)
       CLEANUP_FREE char *path = NULL;
       const char *dir = xdg_config_dirs[i - 1];

-      if (asprintf (&path, "%s/libguestfs/"
GLOBAL_CONFIG_FILENAME, dir) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+      if (asprintf (&path, "%s/libguestfs/"
GLOBAL_CONFIG_FILENAME, dir) == -1)
+        error (EXIT_FAILURE, errno, "asprintf");

       read_config_from_file (path);
     }
@@ -117,10 +114,8 @@ parse_config (void)
       /* Old-style configuration file first. */
       CLEANUP_FREE char *path = NULL;

-      if (asprintf (&path, "%s/%s", home, home_filename) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+      if (asprintf (&path, "%s/%s", home, home_filename) == -1)
+        error (EXIT_FAILURE, errno, "asprintf");

       read_config_from_file (path);
     }
@@ -131,24 +126,18 @@ parse_config (void)
       CLEANUP_FREE char *home_copy = strdup (home);
       const char *xdg_env;

-      if (home_copy == NULL) {
-        perror ("strdup");
-        exit (EXIT_FAILURE);
-      }
+      if (home_copy == NULL)
+        error (EXIT_FAILURE, errno, "strdup");

       xdg_env = getenv ("XDG_CONFIG_HOME");
       if (xdg_env == NULL) {
         if (asprintf (&path, "%s/.config/libguestfs/"
GLOBAL_CONFIG_FILENAME,
-                      home_copy) == -1) {
-          perror ("asprintf");
-          exit (EXIT_FAILURE);
-        }
+                      home_copy) == -1)
+          error (EXIT_FAILURE, errno, "asprintf");
       } else {
         if (asprintf (&path, "%s/libguestfs/"
GLOBAL_CONFIG_FILENAME,
-                      xdg_env) == -1) {
-          perror ("asprintf");
-          exit (EXIT_FAILURE);
-        }
+                      xdg_env) == -1)
+          error (EXIT_FAILURE, errno, "asprintf");
       }

       read_config_from_file (path);
diff --git a/fish/events.c b/fish/events.c
index 0349c55..8660fc4 100644
--- a/fish/events.c
+++ b/fish/events.c
@@ -27,6 +27,8 @@
 #include <assert.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <errno.h>
+#include <error.h>

 #include <guestfs.h>

@@ -261,10 +263,8 @@ print_event_set (uint64_t event_bitmask, FILE *fp)
     fputs ("*", fp);
   else {
     CLEANUP_FREE char *str = guestfs_event_to_string (event_bitmask);
-    if (!str) {
-      perror ("guestfs_event_to_string");
-      exit (EXIT_FAILURE);
-    }
+    if (!str)
+      error (EXIT_FAILURE, errno, "guestfs_event_to_string");
     fputs (str, fp);
   }
 }
diff --git a/fish/fish.c b/fish/fish.c
index 1e29639..6f9c784 100644
--- a/fish/fish.c
+++ b/fish/fish.c
@@ -27,6 +27,7 @@
 #include <getopt.h>
 #include <signal.h>
 #include <errno.h>
+#include <error.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <locale.h>
@@ -360,10 +361,8 @@ main (int argc, char *argv[])
         exit (EXIT_SUCCESS);
       }
       drv = calloc (1, sizeof (struct drv));
-      if (!drv) {
-        perror ("calloc");
-        exit (EXIT_FAILURE);
-      }
+      if (!drv)
+        error (EXIT_FAILURE, errno, "calloc");
       drv->type = drv_N;
       drv->nr_drives = -1;
       p = strchr (optarg, '=');
@@ -371,16 +370,12 @@ main (int argc, char *argv[])
         *p = '\0';
         p = p+1;
         drv->N.filename = strdup (optarg);
-        if (drv->N.filename == NULL) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (drv->N.filename == NULL)
+          error (EXIT_FAILURE, errno, "strdup");
       } else {
         if (asprintf (&drv->N.filename, "test%d.img",
-                      next_prepared_drive) == -1) {
-          perror ("asprintf");
-          exit (EXIT_FAILURE);
-        }
+                      next_prepared_drive) == -1)
+          error (EXIT_FAILURE, errno, "asprintf");
         p = optarg;
       }
       drv->N.data = create_prepared_file (p, drv->N.filename);
@@ -448,24 +443,18 @@ main (int argc, char *argv[])
       if (strchr (argv[optind], '/') ||
           access (argv[optind], F_OK) == 0) { /* simulate -a option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_a;
         drv->a.filename = strdup (argv[optind]);
-        if (!drv->a.filename) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv->a.filename)
+          error (EXIT_FAILURE, errno, "strdup");
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_d;
         drv->d.guest = argv[optind];
         drv->next = drvs;
@@ -539,10 +528,8 @@ main (int argc, char *argv[])
   /* -f (file) parameter? */
   if (file) {
     close (0);
-    if (open (file, O_RDONLY|O_CLOEXEC) == -1) {
-      perror (file);
-      exit (EXIT_FAILURE);
-    }
+    if (open (file, O_RDONLY|O_CLOEXEC) == -1)
+      error (EXIT_FAILURE, errno, "open: %s", file);
   }

   /* Get the name of the input file, for error messages, and replace
@@ -565,10 +552,8 @@ main (int argc, char *argv[])

   if (progress_bars) {
     bar = progress_bar_init (0);
-    if (!bar) {
-      perror ("progress_bar_init");
-      exit (EXIT_FAILURE);
-    }
+    if (!bar)
+      error (EXIT_FAILURE, errno, "progress_bar_init");

     guestfs_set_event_callback (g, progress_callback,
                                 GUESTFS_EVENT_PROGRESS, 0, NULL);
@@ -1481,30 +1466,24 @@ initialize_readline (void)
   if (str) {
     free (ps1);
     ps1 = strdup (str);
-    if (!ps1) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (!ps1)
+      error (EXIT_FAILURE, errno, "strdup");
   }

   str = getenv ("GUESTFISH_OUTPUT");
   if (str) {
     free (ps_output);
     ps_output = strdup (str);
-    if (!ps_output) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (!ps_output)
+      error (EXIT_FAILURE, errno, "strdup");
   }

   str = getenv ("GUESTFISH_INIT");
   if (str) {
     free (ps_init);
     ps_init = strdup (str);
-    if (!ps_init) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (!ps_init)
+      error (EXIT_FAILURE, errno, "strdup");
   }
 #endif
 }
@@ -1557,10 +1536,8 @@ decode_ps1 (const char *str)
    * future.
    */
   ret = malloc (len + 1);
-  if (!ret) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!ret)
+    error (EXIT_FAILURE, errno, "malloc");

   for (i = j = 0; i < len; ++i) {
     if (str[i] == '\\') {       /* Start of an escape sequence. */
diff --git a/fish/options.c b/fish/options.c
index 0dffff5..cc3d4c0 100644
--- a/fish/options.c
+++ b/fish/options.c
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <libintl.h>

 #include "guestfs.h"
@@ -37,20 +38,16 @@ option_a (const char *arg, const char *format, struct drv
**drvsp)
   struct drv *drv;

   drv = calloc (1, sizeof (struct drv));
-  if (!drv) {
-    perror ("calloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!drv)
+    error (EXIT_FAILURE, errno, "calloc");

   if (parse_uri (arg, &uri) == -1)
     exit (EXIT_FAILURE);

   if (STREQ (uri.protocol, "file")) {
     /* Ordinary file. */
-    if (access (uri.path, R_OK) != 0) {
-      perror (uri.path);
-      exit (EXIT_FAILURE);
-    }
+    if (access (uri.path, R_OK) != 0)
+      error (EXIT_FAILURE, errno, "access: %s", uri.path);

     drv->type = drv_a;
     drv->nr_drives = -1;
@@ -83,10 +80,8 @@ option_d (const char *arg, struct drv **drvsp)
   struct drv *drv;

   drv = calloc (1, sizeof (struct drv));
-  if (!drv) {
-    perror ("calloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!drv)
+    error (EXIT_FAILURE, errno, "calloc");

   drv->type = drv_d;
   drv->nr_drives = -1;
@@ -115,10 +110,8 @@ add_drives_handle (guestfs_h *g, struct drv *drv, char
next_drive)
     free (drv->device);
     drv->device = NULL;

-    if (asprintf (&drv->device, "/dev/sd%c", next_drive) ==
-1) {
-      perror ("asprintf");
-      exit (EXIT_FAILURE);
-    }
+    if (asprintf (&drv->device, "/dev/sd%c", next_drive) ==
-1)
+      error (EXIT_FAILURE, errno, "asprintf");

     switch (drv->type) {
     case drv_a:
@@ -299,10 +292,8 @@ display_mountpoints_on_failure (const char *mp_device,

     /* Reformat the internal btrfsvol string into a valid mount option */
     if (device && subvolume) {
-      if (asprintf (&p, "%s:/:subvol=%s", device, subvolume) ==
-1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+      if (asprintf (&p, "%s:/:subvol=%s", device, subvolume) ==
-1)
+        error (EXIT_FAILURE, errno, "asprintf");
     } else {
       p = guestfs_canonical_device_name (g, fses[i]);
     }
diff --git a/fish/options.h b/fish/options.h
index 0348676..bbe38aa 100644
--- a/fish/options.h
+++ b/fish/options.h
@@ -178,10 +178,8 @@ extern void display_long_options (const struct option *)
__attribute__((noreturn

 #define OPTION_m                                \
   mp = malloc (sizeof (struct mp));             \
-  if (!mp) {                                    \
-    perror ("malloc");                          \
-    exit (EXIT_FAILURE);                        \
-  }                                             \
+  if (!mp)                                      \
+    error (EXIT_FAILURE, errno, "malloc");      \
   mp->fstype = NULL;                            \
   mp->options = NULL;                           \
   mp->mountpoint = (char *) "/";                \
diff --git a/fish/prep-boot.c b/fish/prep-boot.c
index 8fdacfe..45a5a8f 100644
--- a/fish/prep-boot.c
+++ b/fish/prep-boot.c
@@ -23,6 +23,8 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>
 #include <libintl.h>

 #include "fish.h"
@@ -61,19 +63,15 @@ prep_postlaunch_bootroot (const char *filename, prep_data
*data, const char *dev
                 guestfs_last_error (g));

   CLEANUP_FREE char *part;
-  if (asprintf (&part, "%s1", device) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&part, "%s1", device) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");
   if (guestfs_mkfs (g, data->params[0], part) == -1)
     prep_error (data, filename, _("failed to create boot filesystem:
%s"),
                 guestfs_last_error (g));

   CLEANUP_FREE char *part2;
-  if (asprintf (&part2, "%s2", device) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&part2, "%s2", device) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");
   if (guestfs_mkfs (g, data->params[1], part2) == -1)
     prep_error (data, filename, _("failed to create root filesystem:
%s"),
                 guestfs_last_error (g));
@@ -120,19 +118,15 @@ prep_postlaunch_bootrootlv (const char *filename,
prep_data *data, const char *d
     prep_error (data, filename, _("incorrect format for LV name, use
'/dev/VG/LV'"));

   CLEANUP_FREE char *part;
-  if (asprintf (&part, "%s1", device) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&part, "%s1", device) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");
   if (guestfs_mkfs (g, data->params[1], part) == -1)
     prep_error (data, filename, _("failed to create boot filesystem:
%s"),
                 guestfs_last_error (g));

   CLEANUP_FREE char *part2;
-  if (asprintf (&part2, "%s2", device) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&part2, "%s2", device) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");
   if (guestfs_pvcreate (g, part2) == -1)
     prep_error (data, filename, _("failed to create PV: %s: %s"),
                 part2, guestfs_last_error (g));
diff --git a/fish/prep-fs.c b/fish/prep-fs.c
index 55193ff..6670aa6 100644
--- a/fish/prep-fs.c
+++ b/fish/prep-fs.c
@@ -23,6 +23,8 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>
 #include <libintl.h>

 #include "fish.h"
@@ -43,10 +45,8 @@ prep_postlaunch_fs (const char *filename, prep_data *data,
const char *device)
                 guestfs_last_error (g));

   CLEANUP_FREE char *part;
-  if (asprintf (&part, "%s1", device) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&part, "%s1", device) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");

   if (guestfs_mkfs (g, data->params[0], part) == -1)
     prep_error (data, filename, _("failed to create filesystem (%s):
%s"),
diff --git a/fish/prep-lv.c b/fish/prep-lv.c
index d09f26e..93de9ae 100644
--- a/fish/prep-lv.c
+++ b/fish/prep-lv.c
@@ -23,6 +23,8 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>
 #include <libintl.h>

 #include "fish.h"
@@ -43,18 +45,14 @@ vg_lv_parse (const char *device, char **vg, char **lv)

   if (vg) {
     *vg = strndup (device, p - device);
-    if (*vg == NULL) {
-      perror ("strndup");
-      exit (EXIT_FAILURE);
-    }
+    if (*vg == NULL)
+      error (EXIT_FAILURE, errno, "strndup");
   }

   if (lv) {
     *lv = strdup (p+1);
-    if (*lv == NULL) {
-      perror ("strndup");
-      exit (EXIT_FAILURE);
-    }
+    if (*lv == NULL)
+      error (EXIT_FAILURE, errno, "strndup");
   }

   return 0;
@@ -83,10 +81,8 @@ prep_postlaunch_lv (const char *filename, prep_data *data,
const char *device)
     prep_error (data, filename, _("incorrect format for LV name, use
'/dev/VG/LV'"));

   CLEANUP_FREE char *part;
-  if (asprintf (&part, "%s1", device) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&part, "%s1", device) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");

   if (guestfs_pvcreate (g, part) == -1)
     prep_error (data, filename, _("failed to create PV: %s: %s"),
@@ -126,10 +122,8 @@ prep_postlaunch_lvfs (const char *filename, prep_data
*data, const char *device)
     prep_error (data, filename, _("incorrect format for LV name, use
'/dev/VG/LV'"));

   CLEANUP_FREE char *part;
-  if (asprintf (&part, "%s1", device) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&part, "%s1", device) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");

   if (guestfs_pvcreate (g, part) == -1)
     prep_error (data, filename, _("failed to create PV: %s: %s"),
diff --git a/fish/prep.c b/fish/prep.c
index 4dae4d6..b8976e9 100644
--- a/fish/prep.c
+++ b/fish/prep.c
@@ -23,6 +23,8 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>
 #include <libintl.h>

 #include "fish.h"
@@ -106,19 +108,15 @@ Use 'guestfish -N help' to list possible values
for the -N parameter.\n"),
   }

   prep_data *data = malloc (sizeof *data);
-  if (data == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (data == NULL)
+    error (EXIT_FAILURE, errno, "malloc");
   data->prep = &preps[i];
   data->orig_type_string = type_string;

   /* Set up the optional parameters to all-defaults. */
   data->params = malloc (data->prep->nr_params * sizeof (char *));
-  if (data->params == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (data->params == NULL)
+    error (EXIT_FAILURE, errno, "malloc");

   for (i = 0; i < data->prep->nr_params; ++i)
     data->params[i] = (char *) data->prep->params[i].pdefault;
@@ -131,10 +129,8 @@ Use 'guestfish -N help' to list possible values for
the -N parameter.\n"),
   while (*p) {
     len = strcspn (p, ":");
     data->params[i] = strndup (p, len);
-    if (data->params[i] == NULL) {
-      perror ("strndup");
-      exit (EXIT_FAILURE);
-    }
+    if (data->params[i] == NULL)
+      error (EXIT_FAILURE, errno, "strndup");

     p += len;
     if (*p) p++; /* skip colon char */
diff --git a/fish/rc.c b/fish/rc.c
index d4d2015..ad673f8 100644
--- a/fish/rc.c
+++ b/fish/rc.c
@@ -30,6 +30,7 @@
 #include <signal.h>
 #include <sys/socket.h>
 #include <errno.h>
+#include <error.h>

 #include <rpc/types.h>
 #include <rpc/xdr.h>
@@ -54,11 +55,9 @@ create_sockdir (void)
   /* Create the directory, and ensure it is owned by the user. */
   snprintf (dir, sizeof dir, SOCKET_DIR, (uintmax_t) euid);
   r = mkdir (dir, 0700);
-  if (r == -1 && errno != EEXIST) {
+  if (r == -1 && errno != EEXIST)
   error:
-    perror (dir);
-    exit (EXIT_FAILURE);
-  }
+    error (EXIT_FAILURE, errno, "%s", dir);
   if (lstat (dir, &statbuf) == -1)
     goto error;
   if (!S_ISDIR (statbuf.st_mode) ||
@@ -99,10 +98,8 @@ receive_stdout (int s)

   if (NULL == cmptr) {
     cmptr = malloc (controllen);
-    if (NULL == cmptr) {
-      perror ("malloc");
-      exit (EXIT_FAILURE);
-    }
+    if (NULL == cmptr)
+      error (EXIT_FAILURE, errno, "malloc");
   }

   /* Don't specify a source */
@@ -122,10 +119,8 @@ receive_stdout (int s)

   /* Read a message from the socket */
   ssize_t n = recvmsg (s, &msg, 0);
-  if (n < 0) {
-    perror ("recvmsg stdout fd");
-    exit (EXIT_FAILURE);
-  }
+  if (n < 0)
+    error (EXIT_FAILURE, errno, "recvmsg stdout fd");

   h = CMSG_FIRSTHDR(&msg);
   if (NULL == h) {
@@ -170,10 +165,8 @@ send_stdout (int s)
   /* Initialize the control data */
   if (NULL == cmptr) {
     cmptr = malloc (controllen);
-    if (NULL == cmptr) {
-      perror ("malloc");
-      exit (EXIT_FAILURE);
-    }
+    if (NULL == cmptr)
+      error (EXIT_FAILURE, errno, "malloc");
   }
   cmptr->cmsg_level = SOL_SOCKET;
   cmptr->cmsg_type  = SCM_RIGHTS;
@@ -187,10 +180,8 @@ send_stdout (int s)
   void *data = CMSG_DATA (cmptr);
   *(int *)data = STDOUT_FILENO;

-  if (sendmsg (s, &msg, 0) != 1) {
-    perror ("sendmsg stdout fd");
-    exit (EXIT_FAILURE);
-  }
+  if (sendmsg (s, &msg, 0) != 1)
+    error (EXIT_FAILURE, errno, "sendmsg stdout fd");
 }

 static void
@@ -230,10 +221,8 @@ rc_listen (void)
   create_sockdir ();

   pid = fork ();
-  if (pid == -1) {
-    perror ("fork");
-    exit (EXIT_FAILURE);
-  }
+  if (pid == -1)
+    error (EXIT_FAILURE, errno, "fork");

   if (pid > 0) {
     /* Parent process. */
@@ -260,19 +249,13 @@ rc_listen (void)
   create_sockpath (pid, sockpath, sizeof sockpath, &addr);

   sock = socket (AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
-  if (sock == -1) {
-    perror ("socket");
-    exit (EXIT_FAILURE);
-  }
+  if (sock == -1)
+    error (EXIT_FAILURE, errno, "socket");
   unlink (sockpath);
-  if (bind (sock, (struct sockaddr *) &addr, sizeof addr) == -1) {
-    perror (sockpath);
-    exit (EXIT_FAILURE);
-  }
-  if (listen (sock, 4) == -1) {
-    perror ("listen");
-    exit (EXIT_FAILURE);
-  }
+  if (bind (sock, (struct sockaddr *) &addr, sizeof addr) == -1)
+    error (EXIT_FAILURE, errno, "bind: %s", sockpath);
+  if (listen (sock, 4) == -1)
+    error (EXIT_FAILURE, errno, "listen: %s", sockpath);

   /* Read commands and execute them. */
   while (!quit) {
@@ -309,10 +292,8 @@ rc_listen (void)
         /* We have to extend and NULL-terminate the argv array. */
         argc = call.args.args_len;
         argv = realloc (call.args.args_val, (argc+1) * sizeof (char *));
-        if (argv == NULL) {
-          perror ("realloc");
-          exit (EXIT_FAILURE);
-        }
+        if (argv == NULL)
+          error (EXIT_FAILURE, errno, "realloc");
         call.args.args_val = argv;
         argv[argc] = NULL;

diff --git a/fish/tilde.c b/fish/tilde.c
index 058145c..0d7dfa2 100644
--- a/fish/tilde.c
+++ b/fish/tilde.c
@@ -25,6 +25,8 @@
 #include <assert.h>
 #include <pwd.h>
 #include <sys/types.h>
+#include <errno.h>
+#include <error.h>

 #include "fish.h"

@@ -59,10 +61,8 @@ try_tilde_expansion (char *str)
     if (home) {
       len = strlen (home) + strlen (rest) + 1;
       str = malloc (len);
-      if (str == NULL) {
-        perror ("malloc");
-        exit (EXIT_FAILURE);
-      }
+      if (str == NULL)
+        error (EXIT_FAILURE, errno, "malloc");
       strcpy (str, home);
       strcat (str, rest);
       return str;
@@ -93,10 +93,8 @@ expand_home (char *orig, const char *append)

   len = strlen (home) + (append ? strlen (append) : 0) + 1;
   str = malloc (len);
-  if (str == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (str == NULL)
+    error (EXIT_FAILURE, errno, "malloc");

   strcpy (str, home);
   if (append)
diff --git a/fish/windows.c b/fish/windows.c
index fad5d3b..0634406 100644
--- a/fish/windows.c
+++ b/fish/windows.c
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <langinfo.h>
 #include <libintl.h>
@@ -59,24 +60,18 @@ windows_path (guestfs_h *g, const char *root, const char
*path, int readonly)
     /* This returns the newly allocated string. */
     mount_drive_letter (g, drive_letter, root, readonly);
     ret = strdup (path + 2);
-    if (ret == NULL) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (ret == NULL)
+      error (EXIT_FAILURE, errno, "strdup");
   }
   else if (!*path) {
     ret = strdup ("/");
-    if (ret == NULL) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (ret == NULL)
+      error (EXIT_FAILURE, errno, "strdup");
   }
   else {
     ret = strdup (path);
-    if (ret == NULL) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (ret == NULL)
+      error (EXIT_FAILURE, errno, "strdup");
   }

   /* Blindly convert any backslashes into forward slashes.  Is this good? */
diff --git a/format/format.c b/format/format.c
index 23c44da..781423f 100644
--- a/format/format.c
+++ b/format/format.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <assert.h>
 #include <libintl.h>
@@ -163,7 +164,8 @@ main (int argc, char *argv[])
         if (optarg == NULL) {
           vg = strdup ("VG");
           lv = strdup ("LV");
-          if (!vg || !lv) { perror ("strdup"); exit (EXIT_FAILURE); }
+          if (!vg || !lv)
+            error (EXIT_FAILURE, errno, "strdup");
         }
         else if (STREQ (optarg, "none"))
           vg = lv = NULL;
@@ -307,10 +309,8 @@ parse_vg_lv (const char *lvm)
     vg = strndup (lvm, i);
     lv = strdup (lvm + i + 1);

-    if (!vg || !lv) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (!vg || !lv)
+      error (EXIT_FAILURE, errno, "strdup");
   } else {
   cannot_parse:
     fprintf (stderr, _("%s: cannot parse --lvm option (%s)\n"),
@@ -381,10 +381,8 @@ do_format (void)

       if (guestfs_part_disk (g, devices[i], ptype) == -1)
         exit (EXIT_FAILURE);
-      if (asprintf (&dev, "%s1", devices[i]) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+      if (asprintf (&dev, "%s1", devices[i]) == -1)
+        error (EXIT_FAILURE, errno, "asprintf");
       free_dev = 1;

       /* Set the partition type byte appropriately, otherwise Windows
@@ -427,10 +425,8 @@ do_format (void)

       if (free_dev)
         free (dev);
-      if (asprintf (&dev, "/dev/%s/%s", vg, lv) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+      if (asprintf (&dev, "/dev/%s/%s", vg, lv) == -1)
+        error (EXIT_FAILURE, errno, "asprintf");
       free_dev = 1;
     }

diff --git a/fuse/guestmount.c b/fuse/guestmount.c
index 70fb87e..6ab654a 100644
--- a/fuse/guestmount.c
+++ b/fuse/guestmount.c
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <getopt.h>
 #include <signal.h>
 #include <locale.h>
@@ -55,10 +56,8 @@ fuse_opt_add_opt_escaped (char **opts, const char *opt)
   unsigned oldlen = *opts ? strlen(*opts) : 0;
   char *d = realloc (*opts, oldlen + 1 + strlen(opt) * 2 + 1);

-  if (!d) {
-    perror ("realloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!d)
+    error (EXIT_FAILURE, errno, "realloc");

   *opts = d;
   if (oldlen) {
@@ -419,10 +418,8 @@ main (int argc, char *argv[])
     int fd;

     pid = fork ();
-    if (pid == -1) {
-      perror ("fork");
-      exit (EXIT_FAILURE);
-    }
+    if (pid == -1)
+      error (EXIT_FAILURE, errno, "fork");

     if (pid != 0) {             /* parent */
       if (write_pid_file (pid_file, pid) == -1)
@@ -434,10 +431,8 @@ main (int argc, char *argv[])
     }

     /* Emulate what old fuse_daemonize used to do. */
-    if (setsid () == -1) {
-      perror ("setsid");
-      exit (EXIT_FAILURE);
-    }
+    if (setsid () == -1)
+      error (EXIT_FAILURE, errno, "setsid");

     ignore_value (chdir ("/"));

diff --git a/fuse/guestunmount.c b/fuse/guestunmount.c
index 8be9876..06db4ef 100644
--- a/fuse/guestunmount.c
+++ b/fuse/guestunmount.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <libintl.h>
 #include <poll.h>
@@ -92,7 +93,7 @@ main (int argc, char *argv[])
   const char *mountpoint;
   struct sigaction sa;
   struct pollfd pollfd;
-  char *error = NULL;
+  char *error_str = NULL;
   size_t i;

   setlocale (LC_ALL, "");
@@ -172,10 +173,8 @@ main (int argc, char *argv[])
       pollfd.events = POLLIN;
       pollfd.revents = 0;
       if (poll (&pollfd, 1, -1) == -1) {
-        if (errno != EAGAIN && errno != EINTR) {
-          perror ("poll");
-          exit (EXIT_FAILURE);
-        }
+        if (errno != EAGAIN && errno != EINTR)
+          error (EXIT_FAILURE, errno, "poll");
       }
       else {
         if ((pollfd.revents & POLLHUP) != 0)
@@ -189,15 +188,15 @@ main (int argc, char *argv[])
     if (i > 0)
       sleep (1 << (i-1));

-    free (error);
-    error = NULL;
+    free (error_str);
+    error_str = NULL;

-    if (do_fusermount (mountpoint, &error) == 0)
+    if (do_fusermount (mountpoint, &error_str) == 0)
       goto done;

     /* Did fusermount fail because the mountpoint is not mounted? */
-    if (error &&
-        strstr (error, "fusermount: entry for") != NULL) {
+    if (error_str &&
+        strstr (error_str, "fusermount: entry for") != NULL) {
       goto not_mounted;
     }
   }
@@ -205,10 +204,10 @@ main (int argc, char *argv[])
   /* fusermount failed after N retries */
   if (!quiet) {
     fprintf (stderr, _("%s: failed to unmount %s: %s\n"),
-             guestfs_int_program_name, mountpoint, error);
+             guestfs_int_program_name, mountpoint, error_str);
     do_fuser (mountpoint);
   }
-  free (error);
+  free (error_str);

   exit (2);

@@ -216,9 +215,9 @@ main (int argc, char *argv[])
  not_mounted:
   if (!quiet)
     fprintf (stderr, _("%s: %s is not mounted: %s\n"),
-             guestfs_int_program_name, mountpoint, error);
+             guestfs_int_program_name, mountpoint, error_str);

-  free (error);
+  free (error_str);

   exit (3);

@@ -236,20 +235,16 @@ do_fusermount (const char *mountpoint, char **error_rtn)
   char *buf = NULL;
   size_t allocsize = 0, len = 0;

-  if (pipe (fd) == -1) {
-    perror ("pipe");
-    exit (EXIT_FAILURE);
-  }
+  if (pipe (fd) == -1)
+    error (EXIT_FAILURE, errno, "pipe");

   if (verbose)
     fprintf (stderr, "%s: running: fusermount -u %s\n",
              guestfs_int_program_name, mountpoint);

   pid = fork ();
-  if (pid == -1) {
-    perror ("fork");
-    exit (EXIT_FAILURE);
-  }
+  if (pid == -1)
+    error (EXIT_FAILURE, errno, "fork");

   if (pid == 0) {               /* Child - run fusermount. */
     close (fd[0]);
@@ -277,18 +272,14 @@ do_fusermount (const char *mountpoint, char **error_rtn)
     if (len >= allocsize) {
       allocsize += 256;
       buf = realloc (buf, allocsize);
-      if (buf == NULL) {
-        perror ("realloc");
-        exit (EXIT_FAILURE);
-      }
+      if (buf == NULL)
+        error (EXIT_FAILURE, errno, "realloc");
     }

     /* Leave space in the buffer for a terminating \0 character. */
     r = read (fd[0], &buf[len], allocsize - len - 1);
-    if (r == -1) {
-      perror ("read");
-      exit (EXIT_FAILURE);
-    }
+    if (r == -1)
+      error (EXIT_FAILURE, errno, "read");

     if (r == 0)
       break;
@@ -296,10 +287,8 @@ do_fusermount (const char *mountpoint, char **error_rtn)
     len += r;
   }

-  if (close (fd[0]) == -1) {
-    perror ("close");
-    exit (EXIT_FAILURE);
-  }
+  if (close (fd[0]) == -1)
+    error (EXIT_FAILURE, errno, "close");

   if (buf) {
     /* Remove any trailing \n from the error message. */
@@ -313,10 +302,8 @@ do_fusermount (const char *mountpoint, char **error_rtn)
       buf[len] = '\0';
   }

-  if (waitpid (pid, &r, 0) == -1) {
-    perror ("waitpid");
-    exit (EXIT_FAILURE);
-  }
+  if (waitpid (pid, &r, 0) == -1)
+    error (EXIT_FAILURE, errno, "waitpid");

   if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
     if (verbose)
@@ -343,10 +330,8 @@ do_fuser (const char *mountpoint)
   pid_t pid;

   pid = fork ();
-  if (pid == -1) {
-    perror ("fork");
-    exit (EXIT_FAILURE);
-  }
+  if (pid == -1)
+    error (EXIT_FAILURE, errno, "fork");

   if (pid == 0) {               /* Child - run fuser. */
 #ifdef __linux__
diff --git a/fuse/test-fuse.c b/fuse/test-fuse.c
index 95a2b3d..35d75d5 100644
--- a/fuse/test-fuse.c
+++ b/fuse/test-fuse.c
@@ -33,6 +33,8 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <errno.h>
+#include <error.h>

 #ifdef HAVE_ACL
 #include <sys/acl.h>
@@ -86,16 +88,12 @@ main (int argc, char *argv[])
     exit (77);
   }

-  if (access ("/dev/fuse", W_OK) == -1) {
-    perror ("/dev/fuse");
-    exit (77);
-  }
+  if (access ("/dev/fuse", W_OK) == -1)
+    error (77, errno, "access: /dev/fuse");

   g = guestfs_create ();
-  if (g == NULL) {
-    perror ("guestfs_create");
-    exit (EXIT_FAILURE);
-  }
+  if (g == NULL)
+    error (EXIT_FAILURE, errno, "guestfs_create");

   if (guestfs_add_drive_scratch (g, SIZE, -1) == -1)
     exit (EXIT_FAILURE);
@@ -126,10 +124,8 @@ main (int argc, char *argv[])

   /* Fork to run the next part of the test. */
   pid = fork ();
-  if (pid == -1) {
-    perror ("fork");
-    exit (EXIT_FAILURE);
-  }
+  if (pid == -1)
+    error (EXIT_FAILURE, errno, "fork");

   if (pid == 0) {               /* Child. */
     /* Move into the mountpoint for the tests. */
@@ -176,15 +172,11 @@ main (int argc, char *argv[])
     exit (EXIT_FAILURE);

   /* Clean up and exit. */
-  if (waitpid (pid, &r, 0) == -1) {
-    perror ("waitpid");
-    exit (EXIT_FAILURE);
-  }
+  if (waitpid (pid, &r, 0) == -1)
+    error (EXIT_FAILURE, errno, "waitpid");

-  if (rmdir (mountpoint) == -1) {
-    perror (mountpoint);
-    exit (EXIT_FAILURE);
-  }
+  if (rmdir (mountpoint) == -1)
+    error (EXIT_FAILURE, errno, "rmdir: %s", mountpoint);

   if (guestfs_shutdown (g) == -1)
     exit (EXIT_FAILURE);
diff --git a/fuse/test-guestmount-fd.c b/fuse/test-guestmount-fd.c
index f3456d9..5f358bb 100644
--- a/fuse/test-guestmount-fd.c
+++ b/fuse/test-guestmount-fd.c
@@ -24,6 +24,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -64,38 +65,28 @@ main (int argc, char *argv[])
   }

   /* Skip the test if the test image can't be found. */
-  if (access (TEST_IMAGE, R_OK) == -1) {
-    perror (TEST_IMAGE);
-    exit (77);
-  }
+  if (access (TEST_IMAGE, R_OK) == -1)
+    error (77, errno, "access: %s", TEST_IMAGE);

   /* Skip the test if /dev/fuse is not writable, because guestmount
    * will fail.
    */
-  if (access ("/dev/fuse", W_OK) == -1) {
-    perror ("/dev/fuse");
-    exit (77);
-  }
+  if (access ("/dev/fuse", W_OK) == -1)
+    error (77, errno, "access: %s", "/dev/fuse");

   /* Create the pipe. */
-  if (pipe (pipefd) == -1) {
-    perror ("pipe");
-    exit (EXIT_FAILURE);
-  }
+  if (pipe (pipefd) == -1)
+    error (EXIT_FAILURE, errno, "pipe");

   /* Create the mount point. */
   ignore_value (rmdir (MOUNTPOINT));
-  if (mkdir (MOUNTPOINT, 0700) == -1) {
-    perror ("mkdir: " MOUNTPOINT);
-    exit (EXIT_FAILURE);
-  }
+  if (mkdir (MOUNTPOINT, 0700) == -1)
+    error (EXIT_FAILURE, errno, "mkdir: %s", MOUNTPOINT);

   /* Create the guestmount subprocess. */
   pid = fork ();
-  if (pid == -1) {
-    perror ("fork");
-    exit (EXIT_FAILURE);
-  }
+  if (pid == -1)
+    error (EXIT_FAILURE, errno, "fork");

   if (pid == 0) {               /* child - guestmount */
     char fd_str[64];
diff --git a/fuse/test-guestunmount-fd.c b/fuse/test-guestunmount-fd.c
index b09a60f..a937893 100644
--- a/fuse/test-guestunmount-fd.c
+++ b/fuse/test-guestunmount-fd.c
@@ -28,6 +28,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <sys/types.h>
 #include <sys/wait.h>

@@ -60,17 +61,13 @@ main (int argc, char *argv[])
   }

   /* Create the pipe. */
-  if (pipe (pipefd) == -1) {
-    perror ("pipe");
-    exit (EXIT_FAILURE);
-  }
+  if (pipe (pipefd) == -1)
+    error (EXIT_FAILURE, errno, "pipe");

   /* Create the guestunmount subprocess. */
   pid = fork ();
-  if (pid == -1) {
-    perror ("fork");
-    exit (EXIT_FAILURE);
-  }
+  if (pid == -1)
+    error (EXIT_FAILURE, errno, "fork");

   if (pid == 0) {               /* child - guestunmount */
     char fd_str[64];
@@ -92,10 +89,8 @@ main (int argc, char *argv[])
   sleep (2);

   r = waitpid (pid, &status, WNOHANG);
-  if (r == -1) {
-    perror ("waitpid");
-    exit (EXIT_FAILURE);
-  }
+  if (r == -1)
+    error (EXIT_FAILURE, errno, "waitpid");
   if (r != 0) {
     char status_string[80];

@@ -113,10 +108,8 @@ main (int argc, char *argv[])
   close (pipefd[1]);

   r = waitpid (pid, &status, 0);
-  if (r == -1) {
-    perror ("waitpid");
-    exit (EXIT_FAILURE);
-  }
+  if (r == -1)
+    error (EXIT_FAILURE, errno, "waitpid");
   if (!WIFEXITED (status) || WEXITSTATUS (status) != 3) {
     char status_string[80];

diff --git a/inspector/inspector.c b/inspector/inspector.c
index 1dbef50..2e54924 100644
--- a/inspector/inspector.c
+++ b/inspector/inspector.c
@@ -24,6 +24,7 @@
 #include <inttypes.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <getopt.h>
 #include <locale.h>
 #include <assert.h>
@@ -205,24 +206,18 @@ main (int argc, char *argv[])
       if (strchr (argv[optind], '/') ||
           access (argv[optind], F_OK) == 0) { /* simulate -a option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_a;
         drv->a.filename = strdup (argv[optind]);
-        if (!drv->a.filename) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv->a.filename)
+          error (EXIT_FAILURE, errno, "strdup");
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_d;
         drv->d.guest = argv[optind];
         drv->next = drvs;
diff --git a/p2v/config.c b/p2v/config.c
index baa7b45..9c0d76f 100644
--- a/p2v/config.c
+++ b/p2v/config.c
@@ -24,6 +24,7 @@
 #include <inttypes.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <libintl.h>

@@ -35,10 +36,8 @@ new_config (void)
   struct config *c;

   c = calloc (1, sizeof *c);
-  if (c == NULL) {
-    perror ("calloc");
-    exit (EXIT_FAILURE);
-  }
+  if (c == NULL)
+    error (EXIT_FAILURE, errno, "calloc");

 #if FORCE_REMOTE_DEBUG
   c->verbose = 1;
diff --git a/p2v/conversion.c b/p2v/conversion.c
index d076718..103073e 100644
--- a/p2v/conversion.c
+++ b/p2v/conversion.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <time.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <libintl.h>
 #include <netdb.h>
@@ -101,11 +102,9 @@ set_conversion_error (const char *fs, ...)
   len = vasprintf (&msg, fs, args);
   va_end (args);

-  if (len < 0) {
-    perror ("vasprintf");
-    fprintf (stderr, "original error format string: %s\n", fs);
-    exit (EXIT_FAILURE);
-  }
+  if (len < 0)
+    error (EXIT_FAILURE, errno,
+           "vasprintf (original error format string: %s)", fs);

   free (conversion_error);
   conversion_error = msg;
@@ -185,10 +184,8 @@ start_conversion (struct config *config,
   set_cancel_requested (0);

   data_conns = malloc (sizeof (struct data_conn) * nr_disks);
-  if (data_conns == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (data_conns == NULL)
+    error (EXIT_FAILURE, errno, "malloc");

   for (i = 0; config->disks[i] != NULL; ++i) {
     data_conns[i].h = NULL;
@@ -205,10 +202,8 @@ start_conversion (struct config *config,
       CLEANUP_FREE char *msg;
       if (asprintf (&msg,
                     _("Opening data connection for %s ..."),
-                    config->disks[i]) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+                    config->disks[i]) == -1)
+        error (EXIT_FAILURE, errno, "asprintf");
       notify_ui (NOTIFY_STATUS, msg);
     }

@@ -910,10 +905,8 @@ generate_libvirt_xml (struct config *config, struct
data_conn *data_conns)
             map_interface_to_network (config, config->interfaces[i]);

           if (asprintf (&mac_filename,
"/sys/class/net/%s/address",
-                        config->interfaces[i]) == -1) {
-            perror ("asprintf");
-            exit (EXIT_FAILURE);
-          }
+                        config->interfaces[i]) == -1)
+            error (EXIT_FAILURE, errno, "asprintf");
           if (g_file_get_contents (mac_filename, &mac, NULL, NULL)) {
             size_t len = strlen (mac);

diff --git a/p2v/gui.c b/p2v/gui.c
index 8835793..c5fbc99 100644
--- a/p2v/gui.c
+++ b/p2v/gui.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <assert.h>
 #include <libintl.h>
@@ -800,24 +801,18 @@ populate_disks (GtkTreeView *disks_list)
       uint64_t size;

       if (asprintf (&size_filename, "/sys/block/%s/size",
-                    all_disks[i]) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+                    all_disks[i]) == -1)
+        error (EXIT_FAILURE, errno, "asprintf");
       if (g_file_get_contents (size_filename, &size_str, NULL, NULL)
&&
           sscanf (size_str, "%" SCNu64, &size) == 1) {
         size /= 2*1024*1024; /* size from kernel is given in sectors? */
-        if (asprintf (&size_gb, "%" PRIu64, size) == -1) {
-          perror ("asprintf");
-          exit (EXIT_FAILURE);
-        }
+        if (asprintf (&size_gb, "%" PRIu64, size) == -1)
+          error (EXIT_FAILURE, errno, "asprintf");
       }

       if (asprintf (&model_filename,
"/sys/block/%s/device/model",
-                    all_disks[i]) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+                    all_disks[i]) == -1)
+        error (EXIT_FAILURE, errno, "asprintf");
       if (g_file_get_contents (model_filename, &model, NULL, NULL)) {
         /* Need to chomp trailing \n from the content. */
         size_t len = strlen (model);
@@ -941,10 +936,8 @@ populate_interfaces (GtkTreeView *interfaces_list)
"<span
foreground=\"blue\">Identify
interface",
                     if_name,
                     if_addr ? : _("Unknown"),
-                    if_vendor ? : _("Unknown")) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+                    if_vendor ? : _("Unknown")) == -1)
+        error (EXIT_FAILURE, errno, "asprintf");

       gtk_list_store_append (interfaces_store, &iter);
       gtk_list_store_set (interfaces_store, &iter,
@@ -1071,10 +1064,8 @@ maybe_identify_click (GtkWidget *interfaces_list,
GdkEventButton *event,
         if_name = all_interfaces[row_index];

         /* Issue the ethtool command in the background. */
-        if (asprintf (&cmd, "ethtool --identify '%s' 10
&", if_name) == -1) {
-          perror ("asprintf");
-          exit (EXIT_FAILURE);
-        }
+        if (asprintf (&cmd, "ethtool --identify '%s' 10
&", if_name) == -1)
+          error (EXIT_FAILURE, errno, "asprintf");
         printf ("%s\n", cmd);
         ignore_value (system (cmd));

@@ -1108,10 +1099,8 @@ set_from_ui_generic (char **all, char ***ret, GtkTreeView
*list)

   guestfs_int_free_string_list (*ret);
   *ret = malloc ((1 + guestfs_int_count_strings (all)) * sizeof (char *));
-  if (*ret == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (*ret == NULL)
+    error (EXIT_FAILURE, errno, "malloc");
   i = j = 0;

   b = gtk_tree_model_get_iter_first (model, &iter);
@@ -1172,10 +1161,8 @@ set_network_map_from_ui (struct config *config)
   config->network_map      malloc ((1 + guestfs_int_count_strings
(all_interfaces))
             * sizeof (char *));
-  if (config->network_map == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (config->network_map == NULL)
+    error (EXIT_FAILURE, errno, "malloc");
   i = j = 0;

   b = gtk_tree_model_get_iter_first (model, &iter);
@@ -1184,10 +1171,8 @@ set_network_map_from_ui (struct config *config)
     if (s) {
       assert (all_interfaces[i] != NULL);
       if (asprintf (&config->network_map[j], "%s:%s",
-                    all_interfaces[i], s) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
+                    all_interfaces[i], s) == -1)
+        error (EXIT_FAILURE, errno, "asprintf");
       ++j;
     }
     b = gtk_tree_model_iter_next (model, &iter);
@@ -1230,11 +1215,9 @@ concat_warning (char *warning, const char *fs, ...)

   if (warning == NULL) {
     warning = strdup ("");
-    if (warning == NULL) {
+    if (warning == NULL)
     malloc_fail:
-      perror ("malloc");
-      exit (EXIT_FAILURE);
-    }
+      error (EXIT_FAILURE, errno, "malloc");
   }

   len = strlen (warning);
@@ -1410,10 +1393,8 @@ set_log_dir (const char *remote_dir)
                   "is saved to this directory "
                   "on the conversion server:\n"
                   "%s"),
-                remote_dir ? remote_dir : "") == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+                remote_dir ? remote_dir : "") == -1)
+    error (EXIT_FAILURE, errno, "asprintf");

   gtk_label_set_text (GTK_LABEL (log_label), msg);
 }
diff --git a/p2v/kernel-cmdline.c b/p2v/kernel-cmdline.c
index 142108a..ee4c0e9 100644
--- a/p2v/kernel-cmdline.c
+++ b/p2v/kernel-cmdline.c
@@ -35,6 +35,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>

 #include "p2v.h"

@@ -43,10 +44,8 @@ add_null (char ***argv, size_t *lenp)
 {
   (*lenp)++;
   *argv = realloc (*argv, *lenp * sizeof (char *));
-  if (*argv == NULL) {
-    perror ("realloc");
-    exit (EXIT_FAILURE);
-  }
+  if (*argv == NULL)
+    error (EXIT_FAILURE, errno, "realloc");
   (*argv)[(*lenp)-1] = NULL;
 }

@@ -55,10 +54,8 @@ add_string (char ***argv, size_t *lenp, const char *str,
size_t len)
 {
   add_null (argv, lenp);
   (*argv)[(*lenp)-1] = strndup (str, len);
-  if ((*argv)[(*lenp)-1] == NULL) {
-    perror ("strndup");
-    exit (EXIT_FAILURE);
-  }
+  if ((*argv)[(*lenp)-1] == NULL)
+    error (EXIT_FAILURE, errno, "strndup");
 }

 char **
diff --git a/p2v/kernel.c b/p2v/kernel.c
index ab8a7bc..61fbecd 100644
--- a/p2v/kernel.c
+++ b/p2v/kernel.c
@@ -26,6 +26,7 @@
 #include <inttypes.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <assert.h>
 #include <locale.h>
 #include <libintl.h>
@@ -282,10 +283,8 @@ run_command (int verbose, const char *stage, const char
*command)
   }

   r = system (command);
-  if (r == -1) {
-    perror ("system");
-    exit (EXIT_FAILURE);
-  }
+  if (r == -1)
+    error (EXIT_FAILURE, errno, "system: %s", command);
   if ((WIFEXITED (r) && WEXITSTATUS (r) != 0) || !WIFEXITED (r)) {
     fprintf (stderr, "%s: %s: unexpected failure of external
command\n",
              guestfs_int_program_name, stage);
diff --git a/p2v/main.c b/p2v/main.c
index a1cf3dc..abec9bf 100644
--- a/p2v/main.c
+++ b/p2v/main.c
@@ -26,6 +26,7 @@
 #include <getopt.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <error.h>
 #include <dirent.h>
 #include <locale.h>
 #include <libintl.h>
@@ -228,10 +229,8 @@ set_config_defaults (struct config *config)
   if (gethostname (hostname, sizeof hostname) == -1) {
     perror ("gethostname");
     /* Generate a simple random name. */
-    if (guestfs_int_random_string (hostname, 8) == -1) {
-      perror ("/dev/urandom");
-      exit (EXIT_FAILURE);
-    }
+    if (guestfs_int_random_string (hostname, 8) == -1)
+      error (EXIT_FAILURE, errno, "/dev/random");
   } else {
     char *p;

@@ -326,19 +325,15 @@ partition_parent (dev_t part_dev)

   if (asprintf (&path, "/sys/dev/block/%ju:%ju/../dev",
                 (uintmax_t) major (part_dev),
-                (uintmax_t) minor (part_dev)) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+                (uintmax_t) minor (part_dev)) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");

   fp = fopen (path, "r");
   if (fp == NULL)
     return 0;

-  if (getline (&content, &len, fp) == -1) {
-    perror ("getline");
-    exit (EXIT_FAILURE);
-  }
+  if (getline (&content, &len, fp) == -1)
+    error (EXIT_FAILURE, errno, "getline");

   if (sscanf (content, "%u:%u", &parent_major, &parent_minor)
!= 2)
     return 0;
@@ -361,10 +356,8 @@ device_contains (const char *dev, dev_t root_device)
   CLEANUP_FREE char *dev_name = NULL;
   dev_t root_device_parent;

-  if (asprintf (&dev_name, "/dev/%s", dev) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&dev_name, "/dev/%s", dev) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");

   if (stat (dev_name, &statbuf) == -1)
     return 0;
@@ -399,10 +392,8 @@ find_all_disks (void)
    * matches the common patterns for disk names.
    */
   dir = opendir ("/sys/block");
-  if (!dir) {
-    perror ("opendir");
-    exit (EXIT_FAILURE);
-  }
+  if (!dir)
+    error (EXIT_FAILURE, errno, "opendir");

   for (;;) {
     errno = 0;
@@ -422,10 +413,8 @@ find_all_disks (void)

       nr_disks++;
       all_disks = realloc (all_disks, sizeof (char *) * (nr_disks + 1));
-      if (!all_disks) {
-        perror ("realloc");
-        exit (EXIT_FAILURE);
-      }
+      if (!all_disks)
+        error (EXIT_FAILURE, errno, "realloc");

       all_disks[nr_disks-1] = strdup (d->d_name);

@@ -439,26 +428,20 @@ find_all_disks (void)
       nr_removable++;
       all_removable = realloc (all_removable,
                                sizeof (char *) * (nr_removable + 1));
-      if (!all_removable) {
-        perror ("realloc");
-        exit (EXIT_FAILURE);
-      }
+      if (!all_removable)
+        error (EXIT_FAILURE, errno, "realloc");
       all_removable[nr_removable-1] = strdup (d->d_name);
       all_removable[nr_removable] = NULL;
     }
   }

   /* Check readdir didn't fail */
-  if (errno != 0) {
-    perror ("readdir: /sys/block");
-    exit (EXIT_FAILURE);
-  }
+  if (errno != 0)
+    error (EXIT_FAILURE, errno, "readdir: %s",
"/sys/block");

   /* Close the directory handle */
-  if (closedir (dir) == -1) {
-    perror ("closedir: /sys/block");
-    exit (EXIT_FAILURE);
-  }
+  if (closedir (dir) == -1)
+    error (EXIT_FAILURE, errno, "closedir: %s",
"/sys/block");

   if (all_disks)
     qsort (all_disks, nr_disks, sizeof (char *), compare);
@@ -477,10 +460,8 @@ find_all_interfaces (void)
    * /sys/class/net which matches some common patterns.
    */
   dir = opendir ("/sys/class/net");
-  if (!dir) {
-    perror ("opendir");
-    exit (EXIT_FAILURE);
-  }
+  if (!dir)
+    error (EXIT_FAILURE, errno, "opendir: %s",
"/sys/class/net");

   for (;;) {
     errno = 0;
@@ -499,26 +480,20 @@ find_all_interfaces (void)
       nr_interfaces++;
       all_interfaces          realloc (all_interfaces, sizeof (char *) *
(nr_interfaces + 1));
-      if (!all_interfaces) {
-        perror ("realloc");
-        exit (EXIT_FAILURE);
-      }
+      if (!all_interfaces)
+        error (EXIT_FAILURE, errno, "realloc");
       all_interfaces[nr_interfaces-1] = strdup (d->d_name);
       all_interfaces[nr_interfaces] = NULL;
     }
   }

   /* Check readdir didn't fail */
-  if (errno != 0) {
-    perror ("readdir: /sys/class/net");
-    exit (EXIT_FAILURE);
-  }
+  if (errno != 0)
+    error (EXIT_FAILURE, errno, "readdir: %s",
"/sys/class/net");

   /* Close the directory handle */
-  if (closedir (dir) == -1) {
-    perror ("closedir: /sys/class/net");
-    exit (EXIT_FAILURE);
-  }
+  if (closedir (dir) == -1)
+    error (EXIT_FAILURE, errno, "closedir: %s",
"/sys/class/net");

   if (all_interfaces)
     qsort (all_interfaces, nr_interfaces, sizeof (char *), compare);
diff --git a/p2v/ssh.c b/p2v/ssh.c
index 6aeafd7..6ddfcb2 100644
--- a/p2v/ssh.c
+++ b/p2v/ssh.c
@@ -45,6 +45,7 @@
 #include <inttypes.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <assert.h>
 #include <libintl.h>
@@ -78,11 +79,9 @@ set_ssh_error (const char *fs, ...)
   len = vasprintf (&msg, fs, args);
   va_end (args);

-  if (len < 0) {
-    perror ("vasprintf");
-    fprintf (stderr, "original error format string: %s\n", fs);
-    exit (EXIT_FAILURE);
-  }
+  if (len < 0)
+    error (EXIT_FAILURE, errno,
+           "vasprintf (original error format string: %s)", fs);

   free (ssh_error);
   ssh_error = msg;
@@ -177,15 +176,11 @@ curl_download (const char *url, const char *local_file)

   /* Use a secure curl config file because escaping is easier. */
   fd = mkstemp (curl_config_file);
-  if (fd == -1) {
-    perror ("mkstemp");
-    exit (EXIT_FAILURE);
-  }
+  if (fd == -1)
+    error (EXIT_FAILURE, errno, "mkstemp: %s", curl_config_file);
   fp = fdopen (fd, "w");
-  if (fp == NULL) {
-    perror ("fdopen");
-    exit (EXIT_FAILURE);
-  }
+  if (fp == NULL)
+    error (EXIT_FAILURE, errno, "fdopen: %s", curl_config_file);
   fprintf (fp, "url = \"");
   len = strlen (url);
   for (i = 0; i < len; ++i) {
@@ -204,17 +199,13 @@ curl_download (const char *url, const char *local_file)

   /* Run curl to download the URL to a file. */
   if (asprintf (&curl_cmd, "curl -f -o %s -K %s",
-                local_file, curl_config_file) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+                local_file, curl_config_file) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");

   r = system (curl_cmd);
   /* unlink (curl_config_file); - useful for debugging */
-  if (r == -1) {
-    perror ("system");
-    exit (EXIT_FAILURE);
-  }
+  if (r == -1)
+    error (EXIT_FAILURE, errno, "system: %s", curl_cmd);

   /* Did curl subprocess fail? */
   if (WIFEXITED (r) && WEXITSTATUS (r) != 0) {
@@ -246,15 +237,11 @@ cache_ssh_identity (struct config *config)
   /* Generate a random filename. */
   free (config->identity_file);
   config->identity_file = strdup ("/tmp/id.XXXXXX");
-  if (config->identity_file == NULL) {
-    perror ("strdup");
-    exit (EXIT_FAILURE);
-  }
+  if (config->identity_file == NULL)
+    error (EXIT_FAILURE, errno, "strdup");
   fd = mkstemp (config->identity_file);
-  if (fd == -1) {
-    perror ("mkstemp");
-    exit (EXIT_FAILURE);
-  }
+  if (fd == -1)
+    error (EXIT_FAILURE, errno, "mkstemp");
   close (fd);

   /* Curl download URL to file. */
@@ -299,10 +286,8 @@ start_ssh (struct config *config, char **extra_args, int
wait_prompt)
   else
     nr_args += 13;
   args = malloc (sizeof (char *) * nr_args);
-  if (args == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (args == NULL)
+    error (EXIT_FAILURE, errno, "malloc");

   j = 0;
   args[j++] = "ssh";
@@ -716,16 +701,12 @@ add_option (const char *type, char ***drivers, const char
*name, size_t len)
   n++;

   *drivers = realloc (*drivers, (n+1) * sizeof (char *));
-  if (*drivers == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (*drivers == NULL)
+    error (EXIT_FAILURE, errno, "malloc");

   (*drivers)[n-1] = strndup (name, len);
-  if ((*drivers)[n-1] == NULL) {
-    perror ("strndup");
-    exit (EXIT_FAILURE);
-  }
+  if ((*drivers)[n-1] == NULL)
+    error (EXIT_FAILURE, errno, "strndup");
   (*drivers)[n] = NULL;

 #if DEBUG_STDERR
diff --git a/p2v/utils.c b/p2v/utils.c
index 3781a8d..183e406 100644
--- a/p2v/utils.c
+++ b/p2v/utils.c
@@ -23,6 +23,8 @@
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <libintl.h>

@@ -48,10 +50,8 @@ get_if_addr (const char *if_name)
   size_t len = 0;
   ssize_t n;

-  if (asprintf (&path, "/sys/class/net/%s/address", if_name) ==
-1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&path, "/sys/class/net/%s/address", if_name) ==
-1)
+    error (EXIT_FAILURE, errno, "asprintf");
   fp = fopen (path, "r");
   if (fp == NULL)
     return NULL;
@@ -78,10 +78,8 @@ get_if_vendor (const char *if_name, int truncate)
   ssize_t n;
   char vendor[5];

-  if (asprintf (&path, "/sys/class/net/%s/device/vendor",
if_name) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&path, "/sys/class/net/%s/device/vendor",
if_name) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");
   fp = fopen (path, "r");
   if (fp == NULL) {
     perror (path);
diff --git a/rescue/rescue.c b/rescue/rescue.c
index 8180ba0..37a0c3f 100644
--- a/rescue/rescue.c
+++ b/rescue/rescue.c
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <errno.h>
+#include <error.h>
 #include <locale.h>
 #include <assert.h>
 #include <libintl.h>
@@ -251,24 +252,18 @@ main (int argc, char *argv[])
       if (strchr (argv[optind], '/') ||
           access (argv[optind], F_OK) == 0) { /* simulate -a option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_a;
         drv->a.filename = strdup (argv[optind]);
-        if (!drv->a.filename) {
-          perror ("strdup");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv->a.filename)
+          error (EXIT_FAILURE, errno, "strdup");
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
         drv = calloc (1, sizeof (struct drv));
-        if (!drv) {
-          perror ("calloc");
-          exit (EXIT_FAILURE);
-        }
+        if (!drv)
+          error (EXIT_FAILURE, errno, "calloc");
         drv->type = drv_d;
         drv->d.guest = argv[optind];
         drv->next = drvs;
@@ -530,10 +525,8 @@ add_scratch_disk (struct drv **drvs)

   /* Add the scratch disk to the drives list. */
   drv = calloc (1, sizeof (struct drv));
-  if (!drv) {
-    perror ("calloc");
-    exit (EXIT_FAILURE);
-  }
+  if (!drv)
+    error (EXIT_FAILURE, errno, "calloc");
   drv->type = drv_scratch;
   drv->nr_drives = -1;
   drv->scratch.size = INT64_C (10737418240);
diff --git a/test-tool/test-tool.c b/test-tool/test-tool.c
index 495316b..1aa9bc4 100644
--- a/test-tool/test-tool.c
+++ b/test-tool/test-tool.c
@@ -26,6 +26,8 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <errno.h>
+#include <error.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
@@ -366,10 +368,8 @@ set_qemu (guestfs_h *g, const char *path, int use_wrapper)
   }

   /* This should be a source directory, so check it. */
-  if (asprintf (&buffer, "%s/pc-bios", path) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&buffer, "%s/pc-bios", path) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");
   if (stat (buffer, &statbuf) == -1 ||
       !S_ISDIR (statbuf.st_mode)) {
     fprintf (stderr,
@@ -382,10 +382,8 @@ set_qemu (guestfs_h *g, const char *path, int use_wrapper)

   /* Make a wrapper script. */
   fd = mkstemp (qemuwrapper);
-  if (fd == -1) {
-    perror (qemuwrapper);
-    exit (EXIT_FAILURE);
-  }
+  if (fd == -1)
+    error (EXIT_FAILURE, errno, "mkstemp: %s", qemuwrapper);

   fchmod (fd, 0700);

diff --git a/tests/c-api/test-add-libvirt-dom.c
b/tests/c-api/test-add-libvirt-dom.c
index 4c061ec..566a3de 100644
--- a/tests/c-api/test-add-libvirt-dom.c
+++ b/tests/c-api/test-add-libvirt-dom.c
@@ -22,6 +22,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>

 #include <libvirt/libvirt.h>
 #include <libvirt/virterror.h>
@@ -100,10 +102,8 @@ main (int argc, char *argv[])
    * directory.
    */
   fp = fopen ("test-add-libvirt-dom.xml", "w");
-  if (fp == NULL) {
-    perror ("test-add-libvirt-dom.xml");
-    exit (EXIT_FAILURE);
-  }
+  if (fp == NULL)
+    error (EXIT_FAILURE, errno, "fopen: %s",
"test-add-libvirt-dom.xml");
   make_test_xml (fp, cwd);
   fclose (fp);

diff --git a/tests/c-api/test-debug-to-file.c b/tests/c-api/test-debug-to-file.c
index 99e14e8..c36ae14 100644
--- a/tests/c-api/test-debug-to-file.c
+++ b/tests/c-api/test-debug-to-file.c
@@ -26,6 +26,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>

 #include "guestfs.h"
 #include "guestfs-internal-frontend.h"
@@ -54,10 +56,8 @@ main (int argc, char *argv[])
   FILE *debugfp;

   debugfp = fopen (filename, "w");
-  if (debugfp == NULL) {
-    perror (filename);
-    exit (EXIT_FAILURE);
-  }
+  if (debugfp == NULL)
+    error (EXIT_FAILURE, errno, "fopen: %s", filename);

   g = guestfs_create ();
   if (g == NULL) {
diff --git a/tests/c-api/test-user-cancel.c b/tests/c-api/test-user-cancel.c
index c0e2540..029c405 100644
--- a/tests/c-api/test-user-cancel.c
+++ b/tests/c-api/test-user-cancel.c
@@ -39,6 +39,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <error.h>
 #include <sys/time.h>
 #include <math.h>

@@ -99,17 +100,13 @@ main (int argc, char *argv[])
   data.g = g;
   data.direction = DIRECTION_UP;

-  if (pipe (fds) == -1) {
-    perror ("pipe");
-    exit (EXIT_FAILURE);
-  }
+  if (pipe (fds) == -1)
+    error (EXIT_FAILURE, errno, "pipe");

   /* We don't want the pipe to be passed to subprocesses. */
   if (fcntl (fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
-      fcntl (fds[1], F_SETFD, FD_CLOEXEC) == -1) {
-    perror ("fcntl");
-    exit (EXIT_FAILURE);
-  }
+      fcntl (fds[1], F_SETFD, FD_CLOEXEC) == -1)
+    error (EXIT_FAILURE, errno, "fcntl");

   data.fd = fds[1];
   snprintf (dev_fd, sizeof dev_fd, "/dev/fd/%d", fds[0]);
@@ -167,17 +164,13 @@ main (int argc, char *argv[])
   data.g = g;
   data.direction = DIRECTION_DOWN;

-  if (pipe (fds) == -1) {
-    perror ("pipe");
-    exit (EXIT_FAILURE);
-  }
+  if (pipe (fds) == -1)
+    error (EXIT_FAILURE, errno, "pipe");

   /* We don't want the pipe to be passed to subprocesses. */
   if (fcntl (fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
-      fcntl (fds[1], F_SETFD, FD_CLOEXEC) == -1) {
-    perror ("fcntl");
-    exit (EXIT_FAILURE);
-  }
+      fcntl (fds[1], F_SETFD, FD_CLOEXEC) == -1)
+    error (EXIT_FAILURE, errno, "fcntl");

   data.fd = fds[0];
   snprintf (dev_fd, sizeof dev_fd, "/dev/fd/%d", fds[1]);
@@ -241,10 +234,9 @@ start_test_thread (void *datav)
       n = MIN (sizeof buffer,
                (size_t) (data->cancel_posn - data->transfer_size));
       r = write (data->fd, buffer, n);
-      if (r == -1) {
-        perror ("test thread: write to pipe before user cancel");
-        exit (EXIT_FAILURE);
-      }
+      if (r == -1)
+        error (EXIT_FAILURE, errno,
+               "test thread: write to pipe before user cancel");
       data->transfer_size += r;
     }

@@ -259,10 +251,9 @@ start_test_thread (void *datav)
       guestfs_user_cancel (data->g);

       r = write (data->fd, buffer, sizeof buffer);
-      if (r == -1) {
-        perror ("test thread: write to pipe after user cancel");
-        exit (EXIT_FAILURE);
-      }
+      if (r == -1)
+        error (EXIT_FAILURE, errno,
+               "test thread: write to pipe after user cancel");
       data->transfer_size += r;
     }
   } else {                      /* thread is reading */
@@ -271,14 +262,12 @@ start_test_thread (void *datav)
       n = MIN (sizeof buffer,
                (size_t) (data->cancel_posn - data->transfer_size));
       r = read (data->fd, buffer, n);
-      if (r == -1) {
-        perror ("test thread: read from pipe before user cancel");
-        exit (EXIT_FAILURE);
-      }
-      if (r == 0) {
-        perror ("test thread: unexpected end of file before user
cancel");
-        exit (EXIT_FAILURE);
-      }
+      if (r == -1)
+        error (EXIT_FAILURE, errno,
+               "test thread: read from pipe before user cancel");
+      if (r == 0)
+        error (EXIT_FAILURE, errno,
+               "test thread: unexpected end of file before user
cancel");
       data->transfer_size += r;
     }

@@ -288,10 +277,9 @@ start_test_thread (void *datav)
     /* Keep sinking data as long as the main thread is writing. */
     while (1) {
       r = read (data->fd, buffer, sizeof buffer);
-      if (r == -1) {
-        perror ("test thread: read from pipe after user cancel");
-        exit (EXIT_FAILURE);
-      }
+      if (r == -1)
+        error (EXIT_FAILURE, errno,
+               "test thread: read from pipe after user cancel");
       if (r == 0)
         break;
       data->transfer_size += r;
diff --git a/tests/c-api/tests-main.c b/tests/c-api/tests-main.c
index 85208b1..dd5c2b6 100644
--- a/tests/c-api/tests-main.c
+++ b/tests/c-api/tests-main.c
@@ -23,6 +23,8 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>
 #include <fcntl.h>
 #include <assert.h>
 #include <sys/utsname.h>
@@ -271,18 +273,12 @@ md5sum (const char *filename, char *result)
   char cmd[256];
   snprintf (cmd, sizeof cmd, "md5sum %s", filename);
   FILE *pp = popen (cmd, "r");
-  if (pp == NULL) {
-    perror (cmd);
-    exit (EXIT_FAILURE);
-  }
-  if (fread (result, 1, 32, pp) != 32) {
-    perror ("md5sum: fread");
-    exit (EXIT_FAILURE);
-  }
-  if (pclose (pp) != 0) {
-    perror ("pclose");
-    exit (EXIT_FAILURE);
-  }
+  if (pp == NULL)
+    error (EXIT_FAILURE, errno, "popen: %s", cmd);
+  if (fread (result, 1, 32, pp) != 32)
+    error (EXIT_FAILURE, errno, "md5sum: fread");
+  if (pclose (pp) != 0)
+    error (EXIT_FAILURE, errno, "pclose");
   result[32] = '\0';
 }

@@ -382,17 +378,13 @@ substitute_srcdir (const char *path)
       exit (EXIT_FAILURE);
     }

-    if (asprintf (&ret, "%s%s", srcdir, path + 7) == -1) {
-      perror ("asprintf");
-      exit (EXIT_FAILURE);
-    }
+    if (asprintf (&ret, "%s%s", srcdir, path + 7) == -1)
+      error (EXIT_FAILURE, errno, "asprintf");
   }
   else {
     ret = strdup (path);
-    if (!ret) {
-      perror ("strdup");
-      exit (EXIT_FAILURE);
-    }
+    if (!ret)
+      error (EXIT_FAILURE, errno, "strdup");
   }

   return ret;
diff --git a/tests/events/test-libvirt-auth-callbacks.c
b/tests/events/test-libvirt-auth-callbacks.c
index 383f20f..86e26f8 100644
--- a/tests/events/test-libvirt-auth-callbacks.c
+++ b/tests/events/test-libvirt-auth-callbacks.c
@@ -22,6 +22,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>

 #include <libvirt/libvirt.h>

@@ -71,15 +73,11 @@ main (int argc, char *argv[])
   }

   cwd = getcwd (NULL, 0);
-  if (cwd == NULL) {
-    perror ("getcwd");
-    exit (EXIT_FAILURE);
-  }
+  if (cwd == NULL)
+    error (EXIT_FAILURE, errno, "getcwd");

-  if (asprintf (&test_uri, "test://%s/%s/libvirt-auth.xml", cwd,
srcdir) == -1) {
-    perror ("asprintf");
-    exit (EXIT_FAILURE);
-  }
+  if (asprintf (&test_uri, "test://%s/%s/libvirt-auth.xml", cwd,
srcdir) == -1)
+    error (EXIT_FAILURE, errno, "asprintf");

   free (cwd);

diff --git a/tests/mountable/test-internal-parse-mountable.c
b/tests/mountable/test-internal-parse-mountable.c
index ce2f62d..ab86ccb 100644
--- a/tests/mountable/test-internal-parse-mountable.c
+++ b/tests/mountable/test-internal-parse-mountable.c
@@ -18,11 +18,13 @@

 #include <config.h>

-#include <fcntl.h>
-#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <error.h>

 #include "guestfs.h"
 #include "guestfs-internal-all.h"
@@ -44,10 +46,8 @@ main (int argc, char *argv[])
   }

   g = guestfs_create ();
-  if (g == NULL) {
-    perror ("could not create handle");
-    exit (EXIT_FAILURE);
-  }
+  if (g == NULL)
+    error (EXIT_FAILURE, errno, "guestfs_create");

   if (guestfs_add_drive_scratch (g, 1024*1024*1024, -1) == -1) {
   error:
diff --git a/tests/protocol/test-error-messages.c
b/tests/protocol/test-error-messages.c
index 3d668c0..57a4a21 100644
--- a/tests/protocol/test-error-messages.c
+++ b/tests/protocol/test-error-messages.c
@@ -27,6 +27,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <error.h>

 #include "guestfs.h"
 #include "guestfs_protocol.h" /* For GUESTFS_ERROR_LEN. */
@@ -45,10 +46,8 @@ main (int argc, char *argv[])
   char *args[2];

   g = guestfs_create ();
-  if (g == NULL) {
-    perror ("guestfs_create");
-    exit (EXIT_FAILURE);
-  }
+  if (g == NULL)
+    error (EXIT_FAILURE, errno, "guestfs_create");

   if (guestfs_add_drive (g, "/dev/null") == -1)
     exit (EXIT_FAILURE);
diff --git a/tests/qemu/qemu-boot.c b/tests/qemu/qemu-boot.c
index 94d2f97..fe7ce95 100644
--- a/tests/qemu/qemu-boot.c
+++ b/tests/qemu/qemu-boot.c
@@ -32,6 +32,7 @@
 #include <getopt.h>
 #include <limits.h>
 #include <errno.h>
+#include <error.h>
 #include <pthread.h>

 #include "guestfs.h"
@@ -197,10 +198,8 @@ run_test (size_t P)

   thread_data = malloc (sizeof (struct thread_data) * P);
   threads = malloc (sizeof (pthread_t) * P);
-  if (thread_data == NULL || threads == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
+  if (thread_data == NULL || threads == NULL)
+    error (EXIT_FAILURE, errno, "malloc");

   /* Start the worker threads. */
   for (i = 0; i < P; ++i) {
diff --git a/tests/qemu/qemu-speed-test.c b/tests/qemu/qemu-speed-test.c
index 1d3c5a3..375a9a4 100644
--- a/tests/qemu/qemu-speed-test.c
+++ b/tests/qemu/qemu-speed-test.c
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <inttypes.h>
 #include <errno.h>
+#include <error.h>
 #include <getopt.h>
 #include <unistd.h>
 #include <signal.h>
@@ -253,24 +254,16 @@ test_virtio_serial (void)
    * source file is a regular file.
    */
   fd = mkstemp (tmpfile);
-  if (fd == -1) {
-    perror ("mkstemp");
-    exit (EXIT_FAILURE);
-  }
-  if (ftruncate (fd, TEST_SERIAL_MAX_SIZE) == -1) {
-    perror ("ftruncate");
-    exit (EXIT_FAILURE);
-  }
-  if (close (fd) == -1) {
-    perror ("close");
-    exit (EXIT_FAILURE);
-  }
+  if (fd == -1)
+    error (EXIT_FAILURE, errno, "mkstemp: %s", tmpfile);
+  if (ftruncate (fd, TEST_SERIAL_MAX_SIZE) == -1)
+    error (EXIT_FAILURE, errno, "ftruncate");
+  if (close (fd) == -1)
+    error (EXIT_FAILURE, errno, "close");

   g = guestfs_create ();
-  if (!g) {
-    perror ("guestfs_create");
-    exit (EXIT_FAILURE);
-  }
+  if (!g)
+    error (EXIT_FAILURE, errno, "guestfs_create");

   if (guestfs_add_drive_scratch (g, INT64_C (100*1024*1024), -1) == -1)
     exit (EXIT_FAILURE);
@@ -395,19 +388,15 @@ test_block_device (void)
   snprintf (tbuf, sizeof tbuf, "%d", t);

   g = guestfs_create ();
-  if (!g) {
-    perror ("guestfs_create");
-    exit (EXIT_FAILURE);
-  }
+  if (!g)
+    error (EXIT_FAILURE, errno, "guestfs_create");

   /* Create a fully allocated backing file.  Note we are not testing
    * the speed of allocation on the host.
    */
   fd = mkstemp (tmpfile);
-  if (fd == -1) {
-    perror ("mkstemp");
-    exit (EXIT_FAILURE);
-  }
+  if (fd == -1)
+    error (EXIT_FAILURE, errno, "mkstemp: %s", tmpfile);
   close (fd);

   if (guestfs_disk_create (g, tmpfile, "raw",
diff --git a/tests/regressions/rhbz1055452.c b/tests/regressions/rhbz1055452.c
index f656861..79cd540 100644
--- a/tests/regressions/rhbz1055452.c
+++ b/tests/regressions/rhbz1055452.c
@@ -29,6 +29,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
+#include <error.h>

 #include "guestfs.h"
 #include "guestfs-internal-frontend.h"
@@ -49,10 +51,8 @@ main (int argc, char *argv[])
       setenv (var[i], value[j], 1);

       g = guestfs_create_flags (GUESTFS_CREATE_NO_ENVIRONMENT);
-      if (!g) {
-        perror ("guestfs_create_flags");
-        exit (EXIT_FAILURE);
-      }
+      if (!g)
+        error (EXIT_FAILURE, errno, "guestfs_create_flags");

       if (guestfs_parse_environment (g) == -1)
         exit (EXIT_FAILURE);
@@ -66,10 +66,8 @@ main (int argc, char *argv[])
   /* Check that guestfs_get_attach_method returns "appliance" ... */

   g = guestfs_create ();
-  if (!g) {
-    perror ("guestfs_create");
-    exit (EXIT_FAILURE);
-  }
+  if (!g)
+    error (EXIT_FAILURE, errno, "guestfs_create");
   if (guestfs_set_backend (g, "direct") == -1)
     exit (EXIT_FAILURE);

diff --git a/tests/regressions/rhbz790721.c b/tests/regressions/rhbz790721.c
index 37f0754..ae44e68 100644
--- a/tests/regressions/rhbz790721.c
+++ b/tests/regressions/rhbz790721.c
@@ -38,6 +38,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>

 #include <pthread.h>

@@ -61,10 +62,8 @@ main (int argc, char *argv[])

   /* Test is only meaningful if the backend "direct" is used. */
   g = guestfs_create ();
-  if (!g) {
-    perror ("guestfs_create");
-    exit (EXIT_FAILURE);
-  }
+  if (!g)
+    error (EXIT_FAILURE, errno, "guestfs_create");
   backend = guestfs_get_backend (g);
   if (backend == NULL) {
     guestfs_close (g);
diff --git a/tests/regressions/rhbz914931.c b/tests/regressions/rhbz914931.c
index bce2924..61b81ba 100644
--- a/tests/regressions/rhbz914931.c
+++ b/tests/regressions/rhbz914931.c
@@ -28,6 +28,7 @@
 #include <unistd.h>
 #include <assert.h>
 #include <errno.h>
+#include <error.h>

 #include "guestfs.h"
 #include "guestfs-internal-frontend.h"
@@ -48,10 +49,8 @@ main (int argc, char *argv[])
   }

   g = guestfs_create ();
-  if (!g) {
-    perror ("guestfs_create");
-    exit (EXIT_FAILURE);
-  }
+  if (!g)
+    error (EXIT_FAILURE, errno, "guestfs_create");

   if (guestfs_add_drive_opts (g, "/dev/null",
                               GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
--
2.7.4