daemon: Send back the errno as a string.
authorRichard W.M. Jones <rjones@redhat.com>
Wed, 3 Nov 2010 12:53:00 +0000 (12:53 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Wed, 3 Nov 2010 17:44:30 +0000 (17:44 +0000)
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.

.gitignore
daemon/Makefile.am
daemon/proto.c
generator/.depend
generator/Makefile.am
generator/generator_c.ml
generator/generator_errnostring.ml [new file with mode: 0644]
generator/generator_main.ml
generator/generator_xdr.ml
po/POTFILES.in
src/guestfs.pod

index 7228828..e5c9ffa 100644 (file)
@@ -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
index 0e2ac6e..2b585c2 100644 (file)
@@ -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 \
index f3b1fc0..63d1cc9 100644 (file)
@@ -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)) {
index ff9263e..c9d1a51 100644 (file)
@@ -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
index f6dfd6b..469265b 100644 (file)
@@ -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))
index dad3ac3..513bc1d 100644 (file)
@@ -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 (file)
index 0000000..d0a5734
--- /dev/null
@@ -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 <config.h>
+
+#include <stdlib.h>
+#include <errno.h>
+
+#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];
+}
+
+"
index 401ae60..1aa15d0 100644 (file)
@@ -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;
index 4942149..b44e2ef 100644 (file)
@@ -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<GUESTFS_ERROR_LEN>;
 };
 
index 699e90b..17b98b4 100644 (file)
@@ -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
index 50e9f50..603fe20 100644 (file)
@@ -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