From: Richard W.M. Jones Date: Wed, 3 Nov 2010 12:53:00 +0000 (+0000) Subject: daemon: Send back the errno as a string. X-Git-Tag: 1.7.1~10 X-Git-Url: http://git.annexia.org/?a=commitdiff_plain;h=d859c283c469b9d9124d90d0ac32024671372ed5;p=libguestfs.git daemon: Send back the errno as a string. This changes the protocol again so that if the errno is available, it is converted to a string (like "EIO") and sent back over the protocol to the library. In this commit the library just discards the string. --- diff --git a/.gitignore b/.gitignore index 7228828..e5c9ffa 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,8 @@ configure cscope.out csharp/ daemon/actions.h +daemon/errnostring.c +daemon/errnostring.h daemon/guestfsd daemon/guestfsd.exe daemon/guestfs_protocol.c @@ -250,6 +252,8 @@ ruby/ext/guestfs/mkmf.log ruby/Rakefile src/actions.c src/bindtests.c +src/errnostring.c +src/errnostring.h src/guestfs-actions.h src/guestfs-internal-actions.h src/guestfs_protocol.c diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 0e2ac6e..2b585c2 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -36,7 +36,9 @@ $(generatorsrcdir)/stamp-generator: force BUILT_SOURCES = \ $(generator_built) \ guestfs_protocol.c \ - guestfs_protocol.h + guestfs_protocol.h \ + errnostring.c \ + errnostring.h EXTRA_DIST = $(BUILT_SOURCES) \ .gitignore @@ -60,6 +62,13 @@ $(libsrcdir)/guestfs_protocol.c: force $(libsrcdir)/guestfs_protocol.h: force $(MAKE) -C $(libsrcdir) guestfs_protocol.h +errnostring.c: $(libsrcdir)/errnostring.c + rm -f $@ + ln $< $@ +errnostring.h: $(libsrcdir)/errnostring.h + rm -f $@ + ln $< $@ + noinst_PROGRAMS = guestfsd guestfsd_SOURCES = \ actions.h \ @@ -82,6 +91,8 @@ guestfsd_SOURCES = \ dropcaches.c \ du.c \ echo_daemon.c \ + errnostring.h \ + errnostring.c \ ext2.c \ fallocate.c \ file.c \ diff --git a/daemon/proto.c b/daemon/proto.c index f3b1fc0..63d1cc9 100644 --- a/daemon/proto.c +++ b/daemon/proto.c @@ -39,6 +39,7 @@ #include "daemon.h" #include "guestfs_protocol.h" +#include "errnostring.h" /* The message currently being processed. */ int proc_nr; @@ -247,7 +248,11 @@ send_error (int errnum, const char *msg) exit (EXIT_FAILURE); } - err.linux_errno = errnum; + /* These strings are not going to be freed. We just cast them + * to (char *) because they are defined that way in the XDR structs. + */ + err.errno_string = + (char *) (errnum > 0 ? guestfs___errno_to_string (errnum) : ""); err.error_message = (char *) msg; if (!xdr_guestfs_message_error (&xdr, &err)) { diff --git a/generator/.depend b/generator/.depend index ff9263e..c9d1a51 100644 --- a/generator/.depend +++ b/generator/.depend @@ -115,15 +115,19 @@ generator_bindtests.cmo: generator_utils.cmi generator_types.cmo \ generator_bindtests.cmx: generator_utils.cmx generator_types.cmx \ generator_structs.cmx generator_pr.cmx generator_optgroups.cmx \ generator_docstrings.cmx generator_c.cmx generator_actions.cmx +generator_errnostring.cmo: generator_utils.cmi generator_types.cmo \ + generator_pr.cmi generator_docstrings.cmo +generator_errnostring.cmx: generator_utils.cmx generator_types.cmx \ + generator_pr.cmx generator_docstrings.cmx generator_main.cmo: generator_xdr.cmo generator_structs.cmi \ generator_ruby.cmo generator_python.cmo generator_pr.cmi \ generator_php.cmo generator_perl.cmo generator_ocaml.cmo \ generator_java.cmo generator_haskell.cmo generator_fish.cmo \ - generator_daemon.cmo generator_csharp.cmo generator_capitests.cmo \ - generator_c.cmo generator_bindtests.cmo + generator_errnostring.cmo generator_daemon.cmo generator_csharp.cmo \ + generator_capitests.cmo generator_c.cmo generator_bindtests.cmo generator_main.cmx: generator_xdr.cmx generator_structs.cmx \ generator_ruby.cmx generator_python.cmx generator_pr.cmx \ generator_php.cmx generator_perl.cmx generator_ocaml.cmx \ generator_java.cmx generator_haskell.cmx generator_fish.cmx \ - generator_daemon.cmx generator_csharp.cmx generator_capitests.cmx \ - generator_c.cmx generator_bindtests.cmx + generator_errnostring.cmx generator_daemon.cmx generator_csharp.cmx \ + generator_capitests.cmx generator_c.cmx generator_bindtests.cmx diff --git a/generator/Makefile.am b/generator/Makefile.am index f6dfd6b..469265b 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -44,6 +44,7 @@ SOURCES = \ generator_csharp.ml \ generator_php.ml \ generator_bindtests.ml \ + generator_errnostring.ml \ generator_main.ml SOURCES_ML = $(filter %.ml,$(SOURCES)) diff --git a/generator/generator_c.ml b/generator/generator_c.ml index dad3ac3..513bc1d 100644 --- a/generator/generator_c.ml +++ b/generator/generator_c.ml @@ -804,6 +804,7 @@ check_state (guestfs_h *g, const char *caller) pr " if (hdr.status == GUESTFS_STATUS_ERROR) {\n"; pr " error (g, \"%%s: %%s\", \"%s\", err.error_message);\n" shortname; pr " free (err.error_message);\n"; + pr " free (err.errno_string);\n"; pr " guestfs___end_busy (g);\n"; pr " return %s;\n" error_code; pr " }\n"; diff --git a/generator/generator_errnostring.ml b/generator/generator_errnostring.ml new file mode 100644 index 0000000..d0a5734 --- /dev/null +++ b/generator/generator_errnostring.ml @@ -0,0 +1,273 @@ +(* libguestfs + * Copyright (C) 2010 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + *) + +(* Please read generator/README first. *) + +open Printf + +open Generator_types +open Generator_utils +open Generator_pr +open Generator_docstrings + +(* Generate the functions errno_to_string and string_to_errno which + * convert errno (eg. EINVAL) into string ("EINVAL") and back again, + * allowing us to portably pass error values over the protocol between + * different versions of Un*x. + *) + +(* Errors found in POSIX plus additional errors found in the Linux + * header files. NOTE keep this sorted and avoid duplicates. + *) +let errnos = [ + "E2BIG"; + "EACCES"; + "EADDRINUSE"; + "EADDRNOTAVAIL"; + "EADV"; + "EAFNOSUPPORT"; + "EAGAIN"; + "EALREADY"; + "EBADE"; + "EBADF"; + "EBADFD"; + "EBADMSG"; + "EBADR"; + "EBADRQC"; + "EBADSLT"; + "EBFONT"; + "EBUSY"; + "ECANCELED"; + "ECHILD"; + "ECHRNG"; + "ECOMM"; + "ECONNABORTED"; + "ECONNREFUSED"; + "ECONNRESET"; + (*"EDEADLK"; - same as EDEADLOCK*) + "EDEADLOCK"; + "EDESTADDRREQ"; + "EDOM"; + "EDOTDOT"; + "EDQUOT"; + "EEXIST"; + "EFAULT"; + "EFBIG"; + "EHOSTDOWN"; + "EHOSTUNREACH"; + "EIDRM"; + "EILSEQ"; + "EINPROGRESS"; + "EINTR"; + "EINVAL"; + "EIO"; + "EISCONN"; + "EISDIR"; + "EISNAM"; + "EKEYEXPIRED"; + "EKEYREJECTED"; + "EKEYREVOKED"; + "EL2HLT"; + "EL2NSYNC"; + "EL3HLT"; + "EL3RST"; + "ELIBACC"; + "ELIBBAD"; + "ELIBEXEC"; + "ELIBMAX"; + "ELIBSCN"; + "ELNRNG"; + "ELOOP"; + "EMEDIUMTYPE"; + "EMFILE"; + "EMLINK"; + "EMSGSIZE"; + "EMULTIHOP"; + "ENAMETOOLONG"; + "ENAVAIL"; + "ENETDOWN"; + "ENETRESET"; + "ENETUNREACH"; + "ENFILE"; + "ENOANO"; + "ENOBUFS"; + "ENOCSI"; + "ENODATA"; + "ENODEV"; + "ENOENT"; + "ENOEXEC"; + "ENOKEY"; + "ENOLCK"; + "ENOLINK"; + "ENOMEDIUM"; + "ENOMEM"; + "ENOMSG"; + "ENONET"; + "ENOPKG"; + "ENOPROTOOPT"; + "ENOSPC"; + "ENOSR"; + "ENOSTR"; + "ENOSYS"; + "ENOTBLK"; + "ENOTCONN"; + "ENOTDIR"; + "ENOTEMPTY"; + "ENOTNAM"; + "ENOTRECOVERABLE"; + "ENOTSOCK"; + "ENOTSUP"; + "ENOTTY"; + "ENOTUNIQ"; + "ENXIO"; + (*"EOPNOTSUPP"; - duplicates another error, and we don't care because + it's a network error *) + "EOVERFLOW"; + "EOWNERDEAD"; + "EPERM"; + "EPFNOSUPPORT"; + "EPIPE"; + "EPROTO"; + "EPROTONOSUPPORT"; + "EPROTOTYPE"; + "ERANGE"; + "EREMCHG"; + "EREMOTE"; + "EREMOTEIO"; + "ERESTART"; + "ERFKILL"; + "EROFS"; + "ESHUTDOWN"; + "ESOCKTNOSUPPORT"; + "ESPIPE"; + "ESRCH"; + "ESRMNT"; + "ESTALE"; + "ESTRPIPE"; + "ETIME"; + "ETIMEDOUT"; + "ETOOMANYREFS"; + "ETXTBSY"; + "EUCLEAN"; + "EUNATCH"; + "EUSERS"; + (*"EWOULDBLOCK"; - same as EAGAIN*) + "EXDEV"; + "EXFULL"; + + (* This is a non-existent errno which is simply used to test that + * the generated code can handle such cases on future platforms + * where one of the above error codes might not exist. + *) + "EZZZ"; +] + +let () = + (* Check list is sorted and no duplicates. *) + let file = "generator/generator_errnostring.ml" in + let check str = + let len = String.length str in + if len == 0 || len > 32 then + failwithf "%s: errno empty or length > 32 (%s)" file str; + if str.[0] <> 'E' then + failwithf "%s: errno string does not begin with letter 'E' (%s)" file str; + for i = 0 to len-1 do + let c = str.[i] in + if Char.uppercase c <> c then + failwithf "%s: errno string is not all uppercase (%s)" file str + done + in + let rec loop = function + | [] -> () + | x :: y :: xs when x = y -> + failwithf "%s: errnos list contains duplicates (%s)" file x + | x :: y :: xs when x > y -> + failwithf "%s: errnos list is not sorted (%s > %s)" file x y + | x :: xs -> check x; loop xs + in + loop errnos + +let generate_errnostring_h () = + generate_header CStyle LGPLv2plus; + + pr " +#ifndef GUESTFS_ERRNOSTRING_H_ +#define GUESTFS_ERRNOSTRING_H_ + +/* Convert errno (eg. EIO) to its string representation (\"EIO\"). + * This only works for a set of errors that are listed in the generator + * AND are supported on the local operating system. For other errors + * the string (\"EINVAL\") is returned. + * + * NOTE: It is an error to call this function with errnum == 0. + */ +extern const char *guestfs___errno_to_string (int errnum); + +/* Convert string representation of an error (eg. \"EIO\") to the errno + * value (EIO). As for the function above, this only works for a + * subset of errors. For errors not supported by the local operating + * system, EINVAL is returned (all POSIX-conforming systems must + * support EINVAL). + */ +//extern int guestfs___string_to_errno (const char *errnostr); + +#endif /* GUESTFS_ERRNOSTRING_H_ */ +" + +let generate_errnostring_c () = + generate_header CStyle LGPLv2plus; + + pr "\ +#include + +#include +#include + +#include \"errnostring.h\" + +static const char *errno_to_string[] = { +"; + + List.iter ( + fun e -> + pr "#ifdef %s\n" e; + pr " [%s] = \"%s\",\n" e e; + pr "#endif\n" + ) errnos; + + pr "\ +}; + +#define ERRNO_TO_STRING_SIZE \\ + (sizeof errno_to_string / sizeof errno_to_string[0]) + +const char * +guestfs___errno_to_string (int errnum) +{ + /* See function documentation. */ + if (errnum == 0) + abort (); + + if (errnum < 0 || (size_t) errnum >= ERRNO_TO_STRING_SIZE || + errno_to_string[errnum] == NULL) + return \"EINVAL\"; + else + return errno_to_string[errnum]; +} + +" diff --git a/generator/generator_main.ml b/generator/generator_main.ml index 401ae60..1aa15d0 100644 --- a/generator/generator_main.ml +++ b/generator/generator_main.ml @@ -38,6 +38,7 @@ open Generator_haskell open Generator_csharp open Generator_php open Generator_bindtests +open Generator_errnostring let perror msg = function | Unix_error (err, _, _) -> @@ -80,6 +81,8 @@ 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.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/libguestfs.syms" generate_linker_script; output_to "daemon/actions.h" generate_daemon_actions_h; diff --git a/generator/generator_xdr.ml b/generator/generator_xdr.ml index 4942149..b44e2ef 100644 --- a/generator/generator_xdr.ml +++ b/generator/generator_xdr.ml @@ -157,7 +157,7 @@ let generate_xdr () = */ const GUESTFS_PROGRAM = 0x2000F5F5; -const GUESTFS_PROTOCOL_VERSION = 2; +const GUESTFS_PROTOCOL_VERSION = 3; /* These constants must be larger than any possible message length. */ const GUESTFS_LAUNCH_FLAG = 0xf5f55ff5; @@ -181,7 +181,8 @@ enum guestfs_message_status { pr "\ struct guestfs_message_error { - int linux_errno; /* Linux errno if available. */ + string errno_string<32>; /* errno eg. \"EINVAL\", empty string + if errno not available */ string error_message; }; diff --git a/po/POTFILES.in b/po/POTFILES.in index 699e90b..17b98b4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -16,6 +16,7 @@ daemon/dmesg.c daemon/dropcaches.c daemon/du.c daemon/echo_daemon.c +daemon/errnostring.c daemon/ext2.c daemon/fallocate.c daemon/file.c @@ -118,6 +119,7 @@ ruby/ext/guestfs/_guestfs.c src/actions.c src/appliance.c src/bindtests.c +src/errnostring.c src/guestfs.c src/inspect.c src/launch.c diff --git a/src/guestfs.pod b/src/guestfs.pod index 50e9f50..603fe20 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -781,6 +781,9 @@ 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