From: Richard W.M. Jones Date: Wed, 3 Nov 2010 15:49:36 +0000 (+0000) Subject: lib: Expose errno through new API guestfs_last_errno. X-Git-Tag: 1.7.1~9 X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=commitdiff_plain;h=2066805a5d93b62beaf6653324715f0b62b06a05 lib: Expose errno through new API guestfs_last_errno. 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. --- diff --git a/.gitignore b/.gitignore index e5c9ffa..14060ad 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,8 @@ configure cscope.out csharp/ daemon/actions.h +daemon/errnostring_gperf.c +daemon/errnostring_gperf.gperf 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 +src/errnostring_gperf.c +src/errnostring_gperf.gperf src/errnostring.c src/errnostring.h src/guestfs-actions.h diff --git a/README b/README index a88f4cc..07915e4 100644 --- a/README +++ b/README @@ -55,6 +55,8 @@ Requirements - libxml2 +- gperf + - Augeas (http://augeas.net/) - squashfs-tools (mksquashfs only) diff --git a/configure.ac b/configure.ac index 1abeb6b..e2a6b91 100644 --- a/configure.ac +++ b/configure.ac @@ -209,6 +209,11 @@ AC_CHECK_PROG([CPIO],[cpio],[cpio],[no]) 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" && diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 2b585c2..ee1959f 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -37,6 +37,8 @@ BUILT_SOURCES = \ $(generator_built) \ guestfs_protocol.c \ guestfs_protocol.h \ + errnostring_gperf.c \ + errnostring_gperf.gperf \ errnostring.c \ errnostring.h @@ -62,6 +64,22 @@ $(libsrcdir)/guestfs_protocol.c: force $(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 $< $@ @@ -91,8 +109,6 @@ guestfsd_SOURCES = \ dropcaches.c \ du.c \ echo_daemon.c \ - errnostring.h \ - errnostring.c \ ext2.c \ fallocate.c \ file.c \ @@ -148,6 +164,7 @@ guestfsd_SOURCES = \ zero.c \ zerofree.c guestfsd_LDADD = \ + liberrnostring.a \ libprotocol.a \ lib/libgnu.a \ $(GETADDRINFO_LIB) \ diff --git a/daemon/configure.ac b/daemon/configure.ac index 27bb997..2be84a8 100644 --- a/daemon/configure.ac +++ b/daemon/configure.ac @@ -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'])]) +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" diff --git a/generator/generator_c.ml b/generator/generator_c.ml index 513bc1d..c1b2e63 100644 --- a/generator/generator_c.ml +++ b/generator/generator_c.ml @@ -438,6 +438,7 @@ and generate_client_actions () = #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 @@ -802,7 +803,16 @@ check_state (guestfs_h *g, const char *caller) 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"; @@ -995,6 +1005,7 @@ and generate_linker_script () = "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"; diff --git a/generator/generator_errnostring.ml b/generator/generator_errnostring.ml index d0a5734..a024989 100644 --- a/generator/generator_errnostring.ml +++ b/generator/generator_errnostring.ml @@ -224,7 +224,11 @@ extern const char *guestfs___errno_to_string (int errnum); * 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_ */ " @@ -236,6 +240,7 @@ let generate_errnostring_c () = #include #include +#include #include #include \"errnostring.h\" @@ -270,4 +275,63 @@ guestfs___errno_to_string (int 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 + +#include +#include +#include + +#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 diff --git a/generator/generator_main.ml b/generator/generator_main.ml index 1aa15d0..2d4b241 100644 --- a/generator/generator_main.ml +++ b/generator/generator_main.ml @@ -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/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; diff --git a/po/POTFILES.in b/po/POTFILES.in index 17b98b4..5d4bdf7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -17,6 +17,7 @@ daemon/dropcaches.c daemon/du.c daemon/echo_daemon.c daemon/errnostring.c +daemon/errnostring_gperf.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/errnostring_gperf.c src/guestfs.c src/inspect.c src/launch.c diff --git a/src/Makefile.am b/src/Makefile.am index 4c5468b..4bcf7e9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,15 +24,19 @@ generator_built = \ 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 = \ - $(generator_built) \ - guestfs_protocol.c \ - guestfs_protocol.h + $(generator_built) \ + guestfs_protocol.c \ + guestfs_protocol.h \ + errnostring_gperf.c EXTRA_DIST = \ $(BUILT_SOURCES) \ @@ -52,6 +56,19 @@ libprotocol_la_SOURCES = \ 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. @@ -115,9 +132,9 @@ libguestfs_la_SOURCES = \ 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"' \ diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index ffd8cf1..9c7c96c 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -114,6 +114,7 @@ struct guestfs_h int selinux; /* selinux enabled? */ char *last_error; + int last_errnum; /* errno, or 0 if there was no errno */ /* 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_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); @@ -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); -#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 diff --git a/src/guestfs.c b/src/guestfs.c index df13d51..e4f74e0 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -270,11 +270,18 @@ guestfs_last_error (guestfs_h *g) return g->last_error; } +int +guestfs_last_errno (guestfs_h *g) +{ + return g->last_errnum; +} + 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); + g->last_errnum = errnum; } static void @@ -284,7 +291,7 @@ default_error_cb (guestfs_h *g, void *data, const char *msg) } 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; @@ -295,8 +302,11 @@ guestfs_error (guestfs_h *g, const char *fs, ...) 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); - set_last_error (g, msg); free (msg); } @@ -327,8 +337,11 @@ guestfs_perrorf (guestfs_h *g, const char *fs, ...) 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); - set_last_error (g, msg); free (msg); } diff --git a/src/guestfs.h b/src/guestfs.h index 911e956..977b4c5 100644 --- a/src/guestfs.h +++ b/src/guestfs.h @@ -44,6 +44,8 @@ extern void guestfs_close (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__)); diff --git a/src/guestfs.pod b/src/guestfs.pod index 603fe20..da03128 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -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. -=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 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 @@ -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 is that they return -C<-1> to indicate an error. You can get additional information on -errors by calling L and/or by setting up an error -handler with L. +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, L, +and/or by setting up an error handler with +L. The default error handler prints the information string to C. @@ -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. +=head2 guestfs_last_errno + + int guestfs_last_errno (guestfs_h *g); + +This returns the last error number (errno) that happened on C. + +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. In practice this should only happen in very rare +circumstances. + =head2 guestfs_set_error_handler typedef void (*guestfs_error_handler_cb) (guestfs_h *g, @@ -930,6 +962,9 @@ The callback C will be called if there is an error. The parameters passed to the callback are an opaque data pointer and the error message string. +C is not passed to the callback. To get that the callback must +call L. + Note that the message string C is freed as soon as the callback function returns, so if you want to stash it somewhere you must make your own copy.