lib: Expose errno through new API guestfs_last_errno.
authorRichard W.M. Jones <rjones@redhat.com>
Wed, 3 Nov 2010 15:49:36 +0000 (15:49 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Wed, 3 Nov 2010 18:48:56 +0000 (18:48 +0000)
If either the daemon sends back an errno, or a system call
fails in the library, save the errno in the handle and then
make it available to callers through the guestfs_last_errno
function.

14 files changed:
.gitignore
README
configure.ac
daemon/Makefile.am
daemon/configure.ac
generator/generator_c.ml
generator/generator_errnostring.ml
generator/generator_main.ml
po/POTFILES.in
src/Makefile.am
src/guestfs-internal.h
src/guestfs.c
src/guestfs.h
src/guestfs.pod

index e5c9ffa..14060ad 100644 (file)
@@ -46,6 +46,8 @@ configure
 cscope.out
 csharp/
 daemon/actions.h
 cscope.out
 csharp/
 daemon/actions.h
+daemon/errnostring_gperf.c
+daemon/errnostring_gperf.gperf
 daemon/errnostring.c
 daemon/errnostring.h
 daemon/guestfsd
 daemon/errnostring.c
 daemon/errnostring.h
 daemon/guestfsd
@@ -252,6 +254,8 @@ ruby/ext/guestfs/mkmf.log
 ruby/Rakefile
 src/actions.c
 src/bindtests.c
 ruby/Rakefile
 src/actions.c
 src/bindtests.c
+src/errnostring_gperf.c
+src/errnostring_gperf.gperf
 src/errnostring.c
 src/errnostring.h
 src/guestfs-actions.h
 src/errnostring.c
 src/errnostring.h
 src/guestfs-actions.h
diff --git a/README b/README
index a88f4cc..07915e4 100644 (file)
--- a/README
+++ b/README
@@ -55,6 +55,8 @@ Requirements
 
 - libxml2
 
 
 - libxml2
 
+- gperf
+
 - Augeas (http://augeas.net/)
 
 - squashfs-tools (mksquashfs only)
 - Augeas (http://augeas.net/)
 
 - squashfs-tools (mksquashfs only)
index 1abeb6b..e2a6b91 100644 (file)
@@ -209,6 +209,11 @@ AC_CHECK_PROG([CPIO],[cpio],[cpio],[no])
 test "x$CPIO" = "xno" &&
      AC_MSG_ERROR([cpio must be installed])
 
 test "x$CPIO" = "xno" &&
      AC_MSG_ERROR([cpio must be installed])
 
+dnl Check for gperf.
+AC_CHECK_PROG([GPERF],[gperf],[gperf],[no])
+test "x$GPERF" = "xno" &&
+     AC_MSG_ERROR([gperf must be installed])
+
 dnl Check for pod2man and pod2text.
 AC_CHECK_PROG([POD2MAN],[pod2man],[pod2man],[no])
 test "x$POD2MAN" = "xno" &&
 dnl Check for pod2man and pod2text.
 AC_CHECK_PROG([POD2MAN],[pod2man],[pod2man],[no])
 test "x$POD2MAN" = "xno" &&
index 2b585c2..ee1959f 100644 (file)
@@ -37,6 +37,8 @@ BUILT_SOURCES = \
        $(generator_built) \
        guestfs_protocol.c \
        guestfs_protocol.h \
        $(generator_built) \
        guestfs_protocol.c \
        guestfs_protocol.h \
+       errnostring_gperf.c \
+       errnostring_gperf.gperf \
        errnostring.c \
        errnostring.h
 
        errnostring.c \
        errnostring.h
 
@@ -62,6 +64,22 @@ $(libsrcdir)/guestfs_protocol.c: force
 $(libsrcdir)/guestfs_protocol.h: force
        $(MAKE) -C $(libsrcdir) guestfs_protocol.h
 
 $(libsrcdir)/guestfs_protocol.h: force
        $(MAKE) -C $(libsrcdir) guestfs_protocol.h
 
+# Build the errnostring perfect hash code.  The generated code has lots
+# of warnings so we must compile it in a separate mini-library.
+noinst_LIBRARIES += liberrnostring.a
+liberrnostring_a_SOURCES = \
+       errnostring_gperf.c \
+       errnostring.h \
+       errnostring.c
+liberrnostring_a_CFLAGS =
+
+errnostring_gperf.c: errnostring_gperf.gperf
+       rm -f $@
+       $(GPERF) -t $< > $@-t
+       mv $@-t $@
+errnostring_gperf.gperf: $(libsrcdir)/errnostring_gperf.gperf
+       rm -f $@
+       ln $< $@
 errnostring.c: $(libsrcdir)/errnostring.c
        rm -f $@
        ln $< $@
 errnostring.c: $(libsrcdir)/errnostring.c
        rm -f $@
        ln $< $@
@@ -91,8 +109,6 @@ guestfsd_SOURCES = \
        dropcaches.c \
        du.c \
        echo_daemon.c \
        dropcaches.c \
        du.c \
        echo_daemon.c \
-       errnostring.h \
-       errnostring.c \
        ext2.c \
        fallocate.c \
        file.c \
        ext2.c \
        fallocate.c \
        file.c \
@@ -148,6 +164,7 @@ guestfsd_SOURCES = \
        zero.c \
        zerofree.c
 guestfsd_LDADD = \
        zero.c \
        zerofree.c
 guestfsd_LDADD = \
+       liberrnostring.a \
        libprotocol.a \
        lib/libgnu.a \
        $(GETADDRINFO_LIB) \
        libprotocol.a \
        lib/libgnu.a \
        $(GETADDRINFO_LIB) \
index 27bb997..2be84a8 100644 (file)
@@ -137,6 +137,11 @@ AC_STRUCT_ST_BLOCKS
 AC_CHECK_MEMBER([struct stat.st_blksize],[
         AC_DEFINE([HAVE_STRUCT_STAT_ST_BLKSIZE],[1],[Define to 1 if 'st_blksize' is a member of 'struct stat'])])
 
 AC_CHECK_MEMBER([struct stat.st_blksize],[
         AC_DEFINE([HAVE_STRUCT_STAT_ST_BLKSIZE],[1],[Define to 1 if 'st_blksize' is a member of 'struct stat'])])
 
+dnl Check for gperf.
+AC_CHECK_PROG([GPERF],[gperf],[gperf],[no])
+test "x$GPERF" = "xno" &&
+     AC_MSG_ERROR([gperf must be installed])
+
 dnl Check for Augeas (now optional).
 AC_CHECK_LIB([augeas],[aug_match],[
         LIBS="-laugeas $LIBS"
 dnl Check for Augeas (now optional).
 AC_CHECK_LIB([augeas],[aug_match],[
         LIBS="-laugeas $LIBS"
index 513bc1d..c1b2e63 100644 (file)
@@ -438,6 +438,7 @@ and generate_client_actions () =
 #include \"guestfs-internal.h\"
 #include \"guestfs-internal-actions.h\"
 #include \"guestfs_protocol.h\"
 #include \"guestfs-internal.h\"
 #include \"guestfs-internal-actions.h\"
 #include \"guestfs_protocol.h\"
+#include \"errnostring.h\"
 
 /* Check the return message from a call for validity. */
 static int
 
 /* Check the return message from a call for validity. */
 static int
@@ -802,7 +803,16 @@ check_state (guestfs_h *g, const char *caller)
       pr "\n";
 
       pr "  if (hdr.status == GUESTFS_STATUS_ERROR) {\n";
       pr "\n";
 
       pr "  if (hdr.status == GUESTFS_STATUS_ERROR) {\n";
-      pr "    error (g, \"%%s: %%s\", \"%s\", err.error_message);\n" shortname;
+      pr "    int errnum = 0;\n";
+      pr "    if (err.errno_string[0] != '\\0')\n";
+      pr "      errnum = guestfs___string_to_errno (err.errno_string);\n";
+      pr "    if (errnum <= 0)\n";
+      pr "      error (g, \"%%s: %%s\", \"%s\", err.error_message);\n"
+        shortname;
+      pr "    else\n";
+      pr "      guestfs_error_errno (g, errnum, \"%%s: %%s\", \"%s\",\n"
+        shortname;
+      pr "                           err.error_message);\n";
       pr "    free (err.error_message);\n";
       pr "    free (err.errno_string);\n";
       pr "    guestfs___end_busy (g);\n";
       pr "    free (err.error_message);\n";
       pr "    free (err.errno_string);\n";
       pr "    guestfs___end_busy (g);\n";
@@ -995,6 +1005,7 @@ and generate_linker_script () =
     "guestfs_get_error_handler";
     "guestfs_get_out_of_memory_handler";
     "guestfs_get_private";
     "guestfs_get_error_handler";
     "guestfs_get_out_of_memory_handler";
     "guestfs_get_private";
+    "guestfs_last_errno";
     "guestfs_last_error";
     "guestfs_set_close_callback";
     "guestfs_set_error_handler";
     "guestfs_last_error";
     "guestfs_set_close_callback";
     "guestfs_set_error_handler";
index d0a5734..a024989 100644 (file)
@@ -224,7 +224,11 @@ extern const char *guestfs___errno_to_string (int errnum);
  * system, EINVAL is returned (all POSIX-conforming systems must
  * support EINVAL).
  */
  * system, EINVAL is returned (all POSIX-conforming systems must
  * support EINVAL).
  */
-//extern int guestfs___string_to_errno (const char *errnostr);
+extern int guestfs___string_to_errno (const char *errnostr);
+
+/* Private structure and function used by the perfect hash implementation. */
+struct errnostring_entry { char *name; int errnum; };
+extern const struct errnostring_entry *guestfs___string_to_errno_lookup (register const char *str, register unsigned int len);
 
 #endif /* GUESTFS_ERRNOSTRING_H_ */
 "
 
 #endif /* GUESTFS_ERRNOSTRING_H_ */
 "
@@ -236,6 +240,7 @@ let generate_errnostring_c () =
 #include <config.h>
 
 #include <stdlib.h>
 #include <config.h>
 
 #include <stdlib.h>
+#include <string.h>
 #include <errno.h>
 
 #include \"errnostring.h\"
 #include <errno.h>
 
 #include \"errnostring.h\"
@@ -270,4 +275,63 @@ guestfs___errno_to_string (int errnum)
     return errno_to_string[errnum];
 }
 
     return errno_to_string[errnum];
 }
 
+int
+guestfs___string_to_errno (const char *errnostr)
+{
+  const struct errnostring_entry *v =
+    guestfs___string_to_errno_lookup (errnostr, strlen (errnostr));
+  if (v /* not necessary to check v->name != NULL here */)
+    return v->errnum;
+  else
+    return EINVAL;
+}
 "
 "
+
+let generate_errnostring_gperf () =
+  generate_header CStyle LGPLv2plus;
+
+  pr "\
+%%language=ANSI-C
+%%define lookup-function-name guestfs___string_to_errno_lookup
+%%readonly-tables
+%%null-strings
+
+%%{
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include \"errnostring.h\"
+
+";
+
+  (* Some of these errnos might not exist on the target platform, but
+   * we are going to include E_ macros directly in the C output of
+   * gperf.  To avoid this causing errors, we include macros to define
+   * unknown errors as EINVAL (see specification of
+   * guestfs___string_to_errno above).  Note this only affects the
+   * single output file containing gperf-generated code.
+   *)
+  List.iter (
+    fun e ->
+      pr "#ifndef %s\n" e;
+      pr "#define %s EINVAL\n" e;
+      pr "#endif\n";
+  ) errnos;
+
+  pr "\
+
+%%}
+
+struct errnostring_entry;
+
+%%%%
+";
+
+  List.iter (
+    fun e ->
+      pr "%s, %s\n" e e
+  ) errnos
index 1aa15d0..2d4b241 100644 (file)
@@ -81,6 +81,7 @@ Run it from the top source directory using the command
   output_to "src/guestfs-structs.pod" generate_structs_pod;
   output_to "src/guestfs-actions.pod" generate_actions_pod;
   output_to "src/guestfs-availability.pod" generate_availability_pod;
   output_to "src/guestfs-structs.pod" generate_structs_pod;
   output_to "src/guestfs-actions.pod" generate_actions_pod;
   output_to "src/guestfs-availability.pod" generate_availability_pod;
+  output_to "src/errnostring_gperf.gperf" generate_errnostring_gperf;
   output_to "src/errnostring.c" generate_errnostring_c;
   output_to "src/errnostring.h" generate_errnostring_h;
   output_to "src/MAX_PROC_NR" generate_max_proc_nr;
   output_to "src/errnostring.c" generate_errnostring_c;
   output_to "src/errnostring.h" generate_errnostring_h;
   output_to "src/MAX_PROC_NR" generate_max_proc_nr;
index 17b98b4..5d4bdf7 100644 (file)
@@ -17,6 +17,7 @@ daemon/dropcaches.c
 daemon/du.c
 daemon/echo_daemon.c
 daemon/errnostring.c
 daemon/du.c
 daemon/echo_daemon.c
 daemon/errnostring.c
+daemon/errnostring_gperf.c
 daemon/ext2.c
 daemon/fallocate.c
 daemon/file.c
 daemon/ext2.c
 daemon/fallocate.c
 daemon/file.c
@@ -120,6 +121,7 @@ src/actions.c
 src/appliance.c
 src/bindtests.c
 src/errnostring.c
 src/appliance.c
 src/bindtests.c
 src/errnostring.c
+src/errnostring_gperf.c
 src/guestfs.c
 src/inspect.c
 src/launch.c
 src/guestfs.c
 src/inspect.c
 src/launch.c
index 4c5468b..4bcf7e9 100644 (file)
@@ -24,15 +24,19 @@ generator_built = \
        guestfs-internal-actions.h \
        actions.c \
        bindtests.c \
        guestfs-internal-actions.h \
        actions.c \
        bindtests.c \
+       errnostring_gperf.gperf \
+       errnostring.c \
+       errnostring.h
        guestfs-actions.pod \
        guestfs-availability.pod \
        guestfs-structs.pod \
        libguestfs.syms
 
 BUILT_SOURCES = \
        guestfs-actions.pod \
        guestfs-availability.pod \
        guestfs-structs.pod \
        libguestfs.syms
 
 BUILT_SOURCES = \
-  $(generator_built) \
-  guestfs_protocol.c \
-  guestfs_protocol.h
+       $(generator_built) \
+       guestfs_protocol.c \
+       guestfs_protocol.h \
+       errnostring_gperf.c
 
 EXTRA_DIST = \
        $(BUILT_SOURCES) \
 
 EXTRA_DIST = \
        $(BUILT_SOURCES) \
@@ -52,6 +56,19 @@ libprotocol_la_SOURCES = \
 
 libprotocol_la_CFLAGS =
 
 
 libprotocol_la_CFLAGS =
 
+# Build the errnostring perfect hash code.  The generated code has lots
+# of warnings so we must compile it in a separate mini-library.
+liberrnostring_la_SOURCES = \
+       errnostring_gperf.c \
+       errnostring.h \
+       errnostring.c
+liberrnostring_la_CFLAGS =
+
+errnostring_gperf.c: errnostring_gperf.gperf
+       rm -f $@
+       $(GPERF) -t $< > $@-t
+       mv $@-t $@
+
 # From the libtool info file, with comments:
 #
 # |  1. Start with version information of `0:0:0' for each libtool library.
 # From the libtool info file, with comments:
 #
 # |  1. Start with version information of `0:0:0' for each libtool library.
@@ -115,9 +132,9 @@ libguestfs_la_SOURCES = \
 
 libguestfs_la_LIBADD = $(HIVEX_LIBS) $(AUGEAS_LIBS) $(LIBPCRE) $(LIBMAGIC) $(LTLIBTHREAD) ../gnulib/lib/libgnu.la
 
 
 libguestfs_la_LIBADD = $(HIVEX_LIBS) $(AUGEAS_LIBS) $(LIBPCRE) $(LIBMAGIC) $(LTLIBTHREAD) ../gnulib/lib/libgnu.la
 
-# Make libguestfs include the convenience library.
-noinst_LTLIBRARIES = libprotocol.la
-libguestfs_la_LIBADD += libprotocol.la
+# Make libguestfs include the convenience libraries.
+noinst_LTLIBRARIES = liberrnostring.la libprotocol.la
+libguestfs_la_LIBADD += liberrnostring.la libprotocol.la
 
 libguestfs_la_CFLAGS = \
   -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \
 
 libguestfs_la_CFLAGS = \
   -DGUESTFS_DEFAULT_PATH='"$(libdir)/guestfs"' \
index ffd8cf1..9c7c96c 100644 (file)
@@ -114,6 +114,7 @@ struct guestfs_h
   int selinux;                  /* selinux enabled? */
 
   char *last_error;
   int selinux;                  /* selinux enabled? */
 
   char *last_error;
+  int last_errnum;              /* errno, or 0 if there was no errno */
 
   /* Callbacks. */
   guestfs_abort_cb           abort_cb;
 
   /* Callbacks. */
   guestfs_abort_cb           abort_cb;
@@ -200,6 +201,8 @@ struct guestfs_message_error;
 
 extern void guestfs_error (guestfs_h *g, const char *fs, ...)
   __attribute__((format (printf,2,3)));
 
 extern void guestfs_error (guestfs_h *g, const char *fs, ...)
   __attribute__((format (printf,2,3)));
+extern void guestfs_error_errno (guestfs_h *g, int errnum, const char *fs, ...)
+  __attribute__((format (printf,3,4)));
 extern void guestfs_perrorf (guestfs_h *g, const char *fs, ...)
   __attribute__((format (printf,2,3)));
 extern void *guestfs_safe_realloc (guestfs_h *g, void *ptr, int nbytes);
 extern void guestfs_perrorf (guestfs_h *g, const char *fs, ...)
   __attribute__((format (printf,2,3)));
 extern void *guestfs_safe_realloc (guestfs_h *g, void *ptr, int nbytes);
@@ -221,7 +224,7 @@ extern int guestfs___accept_from_daemon (guestfs_h *g);
 extern int guestfs___build_appliance (guestfs_h *g, char **kernel, char **initrd, char **appliance);
 extern void guestfs___print_BufferIn (FILE *out, const char *buf, size_t buf_size);
 
 extern int guestfs___build_appliance (guestfs_h *g, char **kernel, char **initrd, char **appliance);
 extern void guestfs___print_BufferIn (FILE *out, const char *buf, size_t buf_size);
 
-#define error guestfs_error
+#define error(g,...) guestfs_error_errno((g),0,__VA_ARGS__)
 #define perrorf guestfs_perrorf
 #define safe_calloc guestfs_safe_calloc
 #define safe_malloc guestfs_safe_malloc
 #define perrorf guestfs_perrorf
 #define safe_calloc guestfs_safe_calloc
 #define safe_malloc guestfs_safe_malloc
index df13d51..e4f74e0 100644 (file)
@@ -270,11 +270,18 @@ guestfs_last_error (guestfs_h *g)
   return g->last_error;
 }
 
   return g->last_error;
 }
 
+int
+guestfs_last_errno (guestfs_h *g)
+{
+  return g->last_errnum;
+}
+
 static void
 static void
-set_last_error (guestfs_h *g, const char *msg)
+set_last_error (guestfs_h *g, int errnum, const char *msg)
 {
   free (g->last_error);
   g->last_error = strdup (msg);
 {
   free (g->last_error);
   g->last_error = strdup (msg);
+  g->last_errnum = errnum;
 }
 
 static void
 }
 
 static void
@@ -284,7 +291,7 @@ default_error_cb (guestfs_h *g, void *data, const char *msg)
 }
 
 void
 }
 
 void
-guestfs_error (guestfs_h *g, const char *fs, ...)
+guestfs_error_errno (guestfs_h *g, int errnum, const char *fs, ...)
 {
   va_list args;
   char *msg;
 {
   va_list args;
   char *msg;
@@ -295,8 +302,11 @@ guestfs_error (guestfs_h *g, const char *fs, ...)
 
   if (err < 0) return;
 
 
   if (err < 0) return;
 
+  /* set_last_error first so that the callback can access the error
+   * message and errno through the handle if it wishes.
+   */
+  set_last_error (g, errnum, msg);
   if (g->error_cb) g->error_cb (g, g->error_cb_data, msg);
   if (g->error_cb) g->error_cb (g, g->error_cb_data, msg);
-  set_last_error (g, msg);
 
   free (msg);
 }
 
   free (msg);
 }
@@ -327,8 +337,11 @@ guestfs_perrorf (guestfs_h *g, const char *fs, ...)
   strcat (msg, ": ");
   strcat (msg, buf);
 
   strcat (msg, ": ");
   strcat (msg, buf);
 
+  /* set_last_error first so that the callback can access the error
+   * message and errno through the handle if it wishes.
+   */
+  set_last_error (g, errnum, msg);
   if (g->error_cb) g->error_cb (g, g->error_cb_data, msg);
   if (g->error_cb) g->error_cb (g, g->error_cb_data, msg);
-  set_last_error (g, msg);
 
   free (msg);
 }
 
   free (msg);
 }
index 911e956..977b4c5 100644 (file)
@@ -44,6 +44,8 @@ extern void guestfs_close (guestfs_h *g);
 
 /*--- Error handling ---*/
 extern const char *guestfs_last_error (guestfs_h *g);
 
 /*--- Error handling ---*/
 extern const char *guestfs_last_error (guestfs_h *g);
+#define LIBGUESTFS_HAVE_LAST_ERRNO 1
+extern int guestfs_last_errno (guestfs_h *g);
 
 typedef void (*guestfs_error_handler_cb) (guestfs_h *g, void *opaque, const char *msg);
 typedef void (*guestfs_abort_cb) (void) __attribute__((__noreturn__));
 
 typedef void (*guestfs_error_handler_cb) (guestfs_h *g, void *opaque, const char *msg);
 typedef void (*guestfs_abort_cb) (void) __attribute__((__noreturn__));
index 603fe20..da03128 100644 (file)
@@ -770,20 +770,6 @@ The error message you get from this is also a little obscure.
 This could be fixed in the generator by specially marking parameters
 and return values which take bytes or other units.
 
 This could be fixed in the generator by specially marking parameters
 and return values which take bytes or other units.
 
-=item Library should return errno with error messages.
-
-It would be a nice-to-have to be able to get the original value of
-'errno' from inside the appliance along error paths (where set).
-Currently L<guestmount(1)> goes through hoops to try to reverse the
-error message string into an errno, see the function error() in
-fuse/guestmount.c.
-
-In libguestfs 1.5.4, the protocol was changed so that the
-Linux errno is sent back from the daemon.
-
-In libguestfs 1.7.1, the protocol was changed again to send the
-errno back as a string (for portability between differing Un*xes).
-
 =item Ambiguity between devices and paths
 
 There is a subtle ambiguity in the API between a device name
 =item Ambiguity between devices and paths
 
 There is a subtle ambiguity in the API between a device name
@@ -892,9 +878,16 @@ This closes the connection handle and frees up all resources used.
 =head1 ERROR HANDLING
 
 The convention in all functions that return C<int> is that they return
 =head1 ERROR HANDLING
 
 The convention in all functions that return C<int> is that they return
-C<-1> to indicate an error.  You can get additional information on
-errors by calling L</guestfs_last_error> and/or by setting up an error
-handler with L</guestfs_set_error_handler>.
+C<-1> to indicate an error.
+
+Additional information is available for errors: an error message
+string and optionally an error number (errno) if the thing that failed
+was a system call.
+
+You can get at the additional information about the last error on the
+handle by calling L</guestfs_last_error>, L</guestfs_last_errno>,
+and/or by setting up an error handler with
+L</guestfs_set_error_handler>.
 
 The default error handler prints the information string to C<stderr>.
 
 
 The default error handler prints the information string to C<stderr>.
 
@@ -917,6 +910,45 @@ The error string is not localized (ie. is always in English), because
 this makes searching for error messages in search engines give the
 largest number of results.
 
 this makes searching for error messages in search engines give the
 largest number of results.
 
+=head2 guestfs_last_errno
+
+ int guestfs_last_errno (guestfs_h *g);
+
+This returns the last error number (errno) that happened on C<g>.
+
+If successful, an errno integer not equal to zero is returned.
+
+If no error, this returns 0.  This call can return 0 in three
+situations:
+
+=over 4
+
+=item 1.
+
+There has not been any error on the handle.
+
+=item 2.
+
+There has been an error but the errno was meaningless.  This
+corresponds to the case where the error did not come from a
+failed system call, but for some other reason.
+
+=item 3.
+
+There was an error from a failed system call, but for some
+reason the errno was not captured and returned.  This usually
+indicates a bug in libguestfs.
+
+=back
+
+Libguestfs tries to convert the errno from inside the applicance into
+a corresponding errno for the caller (not entirely trivial: the
+appliance might be running a completely different operating system
+from the library and error numbers are not standardized across
+Un*xen).  If this could not be done, then the error is translated to
+C<EINVAL>.  In practice this should only happen in very rare
+circumstances.
+
 =head2 guestfs_set_error_handler
 
  typedef void (*guestfs_error_handler_cb) (guestfs_h *g,
 =head2 guestfs_set_error_handler
 
  typedef void (*guestfs_error_handler_cb) (guestfs_h *g,
@@ -930,6 +962,9 @@ The callback C<cb> will be called if there is an error.  The
 parameters passed to the callback are an opaque data pointer and the
 error message string.
 
 parameters passed to the callback are an opaque data pointer and the
 error message string.
 
+C<errno> is not passed to the callback.  To get that the callback must
+call L</guestfs_last_errno>.
+
 Note that the message string C<msg> is freed as soon as the callback
 function returns, so if you want to stash it somewhere you must make
 your own copy.
 Note that the message string C<msg> is freed as soon as the callback
 function returns, so if you want to stash it somewhere you must make
 your own copy.