[Libguestfs] [PATCH] erlang: Port to libei for Erlang 23

Richard W.M. Jones rjones at redhat.com
Mon Jun 1 19:39:08 UTC 2020


From: Sergei Golovan <sgolovan at gmail.com>

Replace the use of liberl_interface, which is removed in Erlang 23,
by libei. The implementation uses the ei_decode_iodata() function
which has been introduces only for Erlang 23, so it doesnt work with
earlier Erlang versions.
---
 erlang/Makefile.am  |   1 -
 erlang/main.c       | 312 +++++++++++++++++++++++++-------------------
 generator/erlang.ml | 239 ++++++++++++++++-----------------
 3 files changed, 295 insertions(+), 257 deletions(-)

diff --git a/erlang/Makefile.am b/erlang/Makefile.am
index 19b0e973e..3da3f9145 100644
--- a/erlang/Makefile.am
+++ b/erlang/Makefile.am
@@ -90,7 +90,6 @@ erl_guestfs_CFLAGS = \
 	$(WARN_CFLAGS) $(WERROR_CFLAGS)
 
 erl_guestfs_LDADD = \
-	$(ERLANG_LIB_DIR_erl_interface)/lib/liberl_interface.a \
 	$(ERLANG_LIB_DIR_erl_interface)/lib/libei.a \
 	-lpthread \
 	$(top_builddir)/common/utils/libutils.la \
diff --git a/erlang/main.c b/erlang/main.c
index b9b3dced9..a56ee1fab 100644
--- a/erlang/main.c
+++ b/erlang/main.c
@@ -25,11 +25,7 @@
 #include <errno.h>
 #include <arpa/inet.h>
 
-#include <erl_interface.h>
-/* We should switch over to using
-  #include <ei.h>
-instead of erl_interface.
-*/
+#include <ei.h>
 
 #include "error.h"
 #include "full-read.h"
@@ -38,36 +34,25 @@ instead of erl_interface.
 #include "guestfs.h"
 #include "guestfs-utils.h"
 
+#include "actions.h"
+
 guestfs_h *g;
 
-extern ETERM *dispatch (ETERM *message);
-extern int atom_equals (ETERM *atom, const char *name);
-extern ETERM *make_error (const char *funname);
-extern ETERM *unknown_optarg (const char *funname, ETERM *optargname);
-extern ETERM *unknown_function (ETERM *fun);
-extern ETERM *make_string_list (char **r);
-extern ETERM *make_table (char **r);
-extern ETERM *make_bool (int r);
-extern char **get_string_list (ETERM *term);
-extern int get_bool (ETERM *term);
-extern int get_int (ETERM *term);
-extern int64_t get_int64 (ETERM *term);
-
 /* This stops things getting out of hand, but also lets us detect
  * protocol problems quickly.
  */
 #define MAX_MESSAGE_SIZE (32*1024*1024)
 
-static unsigned char *read_message (void);
-static void write_reply (ETERM *);
+static char *read_message (void);
+static void write_reply (ei_x_buff *);
 
 int
 main (void)
 {
-  unsigned char *buf;
-  ETERM *ret, *message;
-
-  erl_init (NULL, 0);
+  char *buff;
+  int index;
+  int version;
+  ei_x_buff reply;
 
   /* This process has a single libguestfs handle.  If the Erlang
    * system creates more than one handle, then more than one of these
@@ -79,15 +64,20 @@ main (void)
 
   guestfs_set_error_handler (g, NULL, NULL);
 
-  while ((buf = read_message ()) != NULL) {
-    message = erl_decode (buf);
-    free (buf);
+  while ((buff = read_message ()) != NULL) {
+    if (ei_x_new_with_version (&reply) != 0)
+      error (EXIT_FAILURE, 0, "could not allocate reply buffer");
 
-    ret = dispatch (message);
-    erl_free_term (message);
+    index = 0;
+    if (ei_decode_version (buff, &index, &version) != 0)
+      error (EXIT_FAILURE, 0, "could not interpret the input message");
 
-    write_reply (ret);
-    erl_free_term (ret);
+    if (dispatch (&reply, buff, &index) != 0)
+      error (EXIT_FAILURE, 0, "could not decode input data or encode reply message");
+
+    free (buff);
+    write_reply (&reply);
+    ei_x_free (&reply);
   }
 
   guestfs_close (g);
@@ -98,12 +88,12 @@ main (void)
 /* The Erlang port always sends the length of the buffer as 4
  * bytes in network byte order, followed by the message buffer.
  */
-static unsigned char *
+static char *
 read_message (void)
 {
   uint32_t buf;
   size_t size;
-  unsigned char *r;
+  char *r;
 
   errno = 0;
   if (full_read (0, &buf, 4) != 4) {
@@ -129,19 +119,10 @@ read_message (void)
 }
 
 static void
-write_reply (ETERM *term)
+write_reply (ei_x_buff *buff)
 {
-  size_t size;
+  size_t size = buff->index;
   unsigned char sbuf[4];
-  unsigned char *buf;
-
-  size = erl_term_len (term);
-
-  buf = malloc (size);
-  if (buf == NULL)
-    error (EXIT_FAILURE, errno, "malloc");
-
-  erl_encode (term, buf);
 
   sbuf[0] = (size >> 24) & 0xff;
   sbuf[1] = (size >> 16) & 0xff;
@@ -151,171 +132,228 @@ write_reply (ETERM *term)
   if (full_write (1, sbuf, 4) != 4)
     error (EXIT_FAILURE, errno, "write message size");
 
-  if (full_write (1, buf, size) != size)
+  if (full_write (1, buff->buff, size) != size)
     error (EXIT_FAILURE, errno, "write message content");
-
-  free (buf);
 }
 
 /* Note that all published Erlang code/examples etc uses strncmp in
  * a buggy way.  This is the right way to do it.
  */
 int
-atom_equals (ETERM *atom, const char *name)
+atom_equals (const char *atom, const char *name)
 {
   const size_t namelen = strlen (name);
-  const size_t atomlen = ERL_ATOM_SIZE (atom);
+  const size_t atomlen = strlen (atom);
   if (namelen != atomlen) return 0;
-  return strncmp (ERL_ATOM_PTR (atom), name, atomlen) == 0;
+  return strncmp (atom, name, atomlen) == 0;
 }
 
-ETERM *
-make_error (const char *funname)
+int
+make_error (ei_x_buff *buff, const char *funname)
 {
-  ETERM *error = erl_mk_atom ("error");
-  ETERM *msg = erl_mk_string (guestfs_last_error (g));
-  ETERM *num = erl_mk_int (guestfs_last_errno (g));
-  ETERM *t[3] = { error, msg, num };
-  return erl_mk_tuple (t, 3);
+  if (ei_x_encode_tuple_header (buff, 3) != 0) return -1;
+  if (ei_x_encode_atom (buff, "error") != 0) return -1;
+  if (ei_x_encode_string (buff, guestfs_last_error (g)) != 0) return -1;
+  if (ei_x_encode_long (buff, guestfs_last_errno (g)) != 0) return -1;
+  return 0;
 }
 
-ETERM *
-unknown_function (ETERM *fun)
+int
+unknown_function (ei_x_buff *buff, const char *fun)
 {
-  ETERM *unknown = erl_mk_atom ("unknown");
-  ETERM *funcopy = erl_copy_term (fun);
-  ETERM *t[2] = { unknown, funcopy };
-  return erl_mk_tuple (t, 2);
+  if (ei_x_encode_tuple_header (buff, 2) != 0) return -1;
+  if (ei_x_encode_atom (buff, "unknown") != 0) return -1;
+  if (ei_x_encode_atom (buff, fun) != 0) return -1;
+  return 0;
 }
 
-ETERM *
-unknown_optarg (const char *funname, ETERM *optargname)
+int
+unknown_optarg (ei_x_buff *buff, const char *funname, const char *optargname)
 {
-  ETERM *unknownarg = erl_mk_atom ("unknownarg");
-  ETERM *copy = erl_copy_term (optargname);
-  ETERM *t[2] = { unknownarg, copy };
-  return erl_mk_tuple (t, 2);
+  if (ei_x_encode_tuple_header (buff, 2) != 0) return -1;
+  if (ei_x_encode_atom (buff, "unknownarg") != 0) return -1;
+  if (ei_x_encode_atom (buff, optargname) != 0) return -1;
+  return 0;
 }
 
-ETERM *
-make_string_list (char **r)
+int
+make_string_list (ei_x_buff *buff, char **r)
 {
   size_t i, size;
-  CLEANUP_FREE ETERM **t = NULL;
 
-  for (size = 0; r[size] != NULL; ++size)
-    ;
+  for (size = 0; r[size] != NULL; ++size);
 
-  t = malloc (sizeof (ETERM *) * size);
-  if (t == NULL)
-    return make_error ("make_string_list");
+  if (ei_x_encode_list_header (buff, size) != 0) return -1;
 
   for (i = 0; r[i] != NULL; ++i)
-    t[i] = erl_mk_string (r[i]);
+    if (ei_x_encode_string (buff, r[i]) != 0) return -1;
 
-  return erl_mk_list (t, size);
+  if (size > 0)
+    if (ei_x_encode_empty_list (buff) != 0) return -1;
+
+  return 0;
 }
 
 /* Make a hash table.  The number of elements returned by the C
  * function is always even.
  */
-ETERM *
-make_table (char **r)
+int
+make_table (ei_x_buff *buff, char **r)
 {
   size_t i, size;
-  CLEANUP_FREE ETERM **t = NULL;
-  ETERM *a[2];
 
-  for (size = 0; r[size] != NULL; ++size)
-    ;
+  for (size = 0; r[size] != NULL; ++size);
 
-  t = malloc (sizeof (ETERM *) * (size/2));
-  if (t == NULL)
-    return make_error ("make_table");
+  if (ei_x_encode_list_header (buff, size/2) != 0) return -1;
 
   for (i = 0; r[i] != NULL; i += 2) {
-    a[0] = erl_mk_string (r[i]);
-    a[1] = erl_mk_string (r[i+1]);
-    t[i/2] = erl_mk_tuple (a, 2);
+    if (ei_x_encode_tuple_header (buff, 2) != 0) return -1;
+    if (ei_x_encode_string (buff, r[i]) != 0) return -1;
+    if (ei_x_encode_string (buff, r[i+1]) != 0) return -1;
   }
 
-  return erl_mk_list (t, size/2);
+  if (size/2 > 0)
+    if (ei_x_encode_empty_list (buff) != 0) return -1;
+
+  return 0;
 }
 
-ETERM *
-make_bool (int r)
+int
+make_bool (ei_x_buff *buff, int r)
 {
   if (r)
-    return erl_mk_atom ("true");
+    return ei_x_encode_atom (buff, "true");
   else
-    return erl_mk_atom ("false");
+    return ei_x_encode_atom (buff, "false");
 }
 
-char **
-get_string_list (ETERM *term)
+int
+decode_string_list (const char *buff, int *index, char ***res)
 {
-  ETERM *t;
-  size_t i, size;
+  int i, size;
   char **r;
 
-  for (size = 0, t = term; !ERL_IS_EMPTY_LIST (t);
-       size++, t = ERL_CONS_TAIL (t))
-    ;
+  if (ei_decode_list_header (buff, index, &size) != 0)
+    error (EXIT_FAILURE, 0, "not a list");
 
   r = malloc ((size+1) * sizeof (char *));
   if (r == NULL)
     error (EXIT_FAILURE, errno, "malloc");
 
-  for (i = 0, t = term; !ERL_IS_EMPTY_LIST (t); i++, t = ERL_CONS_TAIL (t))
-    r[i] = erl_iolist_to_string (ERL_CONS_HEAD (t));
+  for (i = 0; i < size; i++)
+    if (decode_string (buff, index, &r[i]) != 0) return -1;
+
+  // End of a list is encoded by an empty list, so skip it
+  if (size > 0 && buff[*index] == ERL_NIL_EXT)
+    (*index)++;
+
   r[size] = NULL;
+  *res = r;
 
-  return r;
+  return 0;
 }
 
 int
-get_bool (ETERM *term)
+decode_string (const char *buff, int *index, char **res)
 {
-  if (atom_equals (term, "true"))
-    return 1;
+  size_t size;
+
+  if (decode_binary (buff, index, res, &size) != 0) return -1;
+
+  (*res)[size] = 0;
+
+  return 0;
+}
+
+int
+decode_binary (const char *buff, int *index, char **res, size_t *size)
+{
+  int index0;
+  int size0;
+  char *r;
+
+  index0 = *index;
+  if (ei_decode_iodata (buff, index, &size0, NULL) != 0) return -1;
+
+  r = malloc (size0+1); // In case if it's called from decode_string ()
+  if (r == NULL)
+    error (EXIT_FAILURE, errno, "malloc");
+
+  *index = index0;
+  if (ei_decode_iodata (buff, index, NULL, r) != 0) {
+      free (r);
+      return -1;
+  }
+
+  *res = r;
+  *size = (size_t) size0;
+
+  return 0;
+}
+
+int
+decode_bool (const char *buff, int *index, int *res)
+{
+  char atom[MAXATOMLEN];
+
+  if (ei_decode_atom (buff, index, atom) != 0) return -1;
+
+  if (atom_equals (atom, "true"))
+    *res = 1;
   else
-    return 0;
+    *res = 0;
+
+  return 0;
 }
 
 int
-get_int (ETERM *term)
+decode_int (const char *buff, int *index, int *res)
 {
-  switch (ERL_TYPE (term)) {
-  case ERL_INTEGER:
-    return ERL_INT_VALUE (term);
-  case ERL_U_INTEGER:
-    return (int) ERL_INT_UVALUE (term);
-  case ERL_LONGLONG:
+  unsigned char c;
+  long l;
+  long long ll;
+
+  if (ei_decode_char (buff, index, (char *) &c) == 0) {
+    // Byte integers in Erlang are to be treated as unsigned
+    *res = (int) c;
+    return 0;
+  }
+  if (ei_decode_long (buff, index, &l) == 0) {
     /* XXX check for overflow */
-    return (int) ERL_LL_VALUE (term);
-  case ERL_U_LONGLONG:
+    *res = (int) l;
+    return 0;
+  }
+  if (ei_decode_longlong (buff, index, &ll) == 0) {
     /* XXX check for overflow */
-    return (int) ERL_LL_UVALUE (term);
-  default:
-    /* XXX fail in some way */
-    return -1;
+    *res = (int) ll;
+    return 0;
   }
+  /* XXX fail in some way */
+  return -1;
 }
 
-int64_t
-get_int64 (ETERM *term)
+int
+decode_int64 (const char *buff, int *index, int64_t *res)
 {
-  switch (ERL_TYPE (term)) {
-  case ERL_INTEGER:
-    return ERL_INT_VALUE (term);
-  case ERL_U_INTEGER:
-    return ERL_INT_UVALUE (term);
-  case ERL_LONGLONG:
-    return ERL_LL_VALUE (term);
-  case ERL_U_LONGLONG:
-    return (int64_t) ERL_LL_UVALUE (term);
-  default:
-    /* XXX fail in some way */
-    return -1;
+  unsigned char c;
+  long l;
+  long long ll;
+
+  if (ei_decode_char (buff, index, (char *) &c) == 0) {
+    // Byte integers in Erlang are to be treated as unsigned
+    *res = (int64_t) c;
+    return 0;
+  }
+  if (ei_decode_long (buff, index, &l) == 0) {
+    *res = (int64_t) l;
+    return 0;
   }
+  if (ei_decode_longlong (buff, index, &ll) == 0) {
+    /* XXX check for overflow */
+    *res = (int64_t) ll;
+    return 0;
+  }
+  /* XXX fail in some way */
+  return -1;
 }
+
diff --git a/generator/erlang.ml b/generator/erlang.ml
index 0cee9c3ef..65af75aaf 100644
--- a/generator/erlang.ml
+++ b/generator/erlang.ml
@@ -192,30 +192,30 @@ and generate_erlang_actions_h () =
 
 extern guestfs_h *g;
 
-extern ETERM *dispatch (ETERM *args_tuple);
-extern int atom_equals (ETERM *atom, const char *name);
-extern ETERM *make_error (const char *funname);
-extern ETERM *unknown_optarg (const char *funname, ETERM *optargname);
-extern ETERM *unknown_function (ETERM *fun);
-extern ETERM *make_string_list (char **r);
-extern ETERM *make_table (char **r);
-extern ETERM *make_bool (int r);
-extern char **get_string_list (ETERM *term);
-extern int get_bool (ETERM *term);
-extern int get_int (ETERM *term);
-extern int64_t get_int64 (ETERM *term);
-
-#define ARG(i) (ERL_TUPLE_ELEMENT(args_tuple,(i)+1))
+extern int dispatch (ei_x_buff *retbuff, const char *buff, int *index);
+extern int make_error (ei_x_buff *retbuff, const char *funname);
+extern int unknown_optarg (ei_x_buff *retbuff, const char *funname, const char *optargname);
+extern int unknown_function (ei_x_buff *retbuff, const char *fun);
+extern int make_string_list (ei_x_buff *buff, char **r);
+extern int make_table (ei_x_buff *buff, char **r);
+extern int make_bool (ei_x_buff *buff, int r);
+extern int atom_equals (const char *atom, const char *name);
+extern int decode_string_list (const char *buff, int *index, char ***res);
+extern int decode_string (const char *buff, int *index, char **res);
+extern int decode_binary (const char *buff, int *index, char **res, size_t *size);
+extern int decode_bool (const char *buff, int *index, int *res);
+extern int decode_int (const char *buff, int *index, int *res);
+extern int decode_int64 (const char *buff, int *index, int64_t *res);
 
 ";
 
   let emit_copy_list_decl typ =
-    pr "ETERM *make_%s_list (const struct guestfs_%s_list *%ss);\n"
+    pr "int make_%s_list (ei_x_buff *buff, const struct guestfs_%s_list *%ss);\n"
        typ typ typ;
   in
   List.iter (
     fun { s_name = typ; s_cols = cols } ->
-      pr "ETERM *make_%s (const struct guestfs_%s *%s);\n" typ typ typ;
+      pr "int make_%s (ei_x_buff *buff, const struct guestfs_%s *%s);\n" typ typ typ;
   ) external_structs;
 
   List.iter (
@@ -229,7 +229,7 @@ extern int64_t get_int64 (ETERM *term);
 
   List.iter (
     fun { name } ->
-      pr "ETERM *run_%s (ETERM *args_tuple);\n" name
+      pr "int run_%s (ei_x_buff *retbuff, const char *buff, int *index);\n" name
   ) (actions |> external_functions |> sort);
 
   pr "\n";
@@ -247,11 +247,7 @@ and generate_erlang_structs () =
 #include <string.h>
 #include <errno.h>
 
-#include <erl_interface.h>
-/* We should switch over to using
-  #include <ei.h>
-instead of erl_interface.
-*/
+#include <ei.h>
 
 #include \"guestfs.h\"
 #include \"guestfs-utils.h\"
@@ -262,57 +258,61 @@ instead of erl_interface.
   (* Struct copy functions. *)
   let emit_copy_list_function typ =
     pr "\n";
-    pr "ETERM *\n";
-    pr "make_%s_list (const struct guestfs_%s_list *%ss)\n" typ typ typ;
+    pr "int\n";
+    pr "make_%s_list (ei_x_buff *buff, const struct guestfs_%s_list *%ss)\n" typ typ typ;
     pr "{\n";
     pr "  size_t len = %ss->len;\n" typ;
     pr "  size_t i;\n";
-    pr "  CLEANUP_FREE ETERM **t;\n";
     pr "\n";
-    pr "  t = malloc (sizeof (ETERM *) * len);\n";
-    pr "  if (t == NULL)\n";
-    pr "    return make_error (\"make_%s_list\");\n" typ;
+    pr "  if (ei_x_encode_list_header (buff, len) != 0) return -1;\n";
+    pr "  for (i = 0; i < len; ++i) {\n";
+    pr "    if (make_%s (buff, &%ss->val[i]) != 0) return -1;\n" typ typ;
+    pr "  }\n";
+    pr "  if (len > 0)\n";
+    pr "    if (ei_x_encode_empty_list (buff) != 0) return -1;\n";
     pr "\n";
-    pr "  for (i = 0; i < len; ++i)\n";
-    pr "    t[i] = make_%s (&%ss->val[i]);\n" typ typ;
-    pr "\n";
-    pr "  return erl_mk_list (t, len);\n";
+    pr "  return 0;\n";
     pr "}\n";
   in
 
   List.iter (
     fun { s_name = typ; s_cols = cols } ->
       pr "\n";
-      pr "ETERM *\n";
-      pr "make_%s (const struct guestfs_%s *%s)\n" typ typ typ;
+      pr "int\n";
+      pr "make_%s (ei_x_buff *buff, const struct guestfs_%s *%s)\n" typ typ typ;
       pr "{\n";
-      pr "  ETERM *t[%d];\n" (List.length cols);
+      pr "  if (ei_x_encode_list_header (buff, %d) !=0) return -1;\n" (List.length cols);
       pr "\n";
       List.iteri (
         fun i col ->
           (match col with
            | name, FString ->
-               pr "  t[%d] = erl_mk_string (%s->%s);\n" i typ name
+               pr "  if (ei_x_encode_string (buff, %s->%s) != 0) return -1;\n" typ name
            | name, FBuffer ->
-               pr "  t[%d] = erl_mk_estring (%s->%s, %s->%s_len);\n"
-                 i typ name typ name
+               pr "  if (ei_x_encode_string_len (buff, %s->%s, %s->%s_len) != 0) return -1;\n"
+                 typ name typ name
            | name, FUUID ->
-               pr "  t[%d] = erl_mk_estring (%s->%s, 32);\n" i typ name
+               pr "  if (ei_x_encode_string_len (buff, %s->%s, 32) != 0) return -1;\n" typ name
            | name, (FBytes|FInt64|FUInt64) ->
-               pr "  t[%d] = erl_mk_longlong (%s->%s);\n" i typ name
+               pr "  if (ei_x_encode_longlong (buff, %s->%s) != 0) return -1;\n" typ name
            | name, (FInt32|FUInt32) ->
-               pr "  t[%d] = erl_mk_int (%s->%s);\n" i typ name
+               pr "  if (ei_x_encode_long (buff, %s->%s) != 0) return -1;\n" typ name
            | name, FOptPercent ->
-               pr "  if (%s->%s >= 0)\n" typ name;
-               pr "    t[%d] = erl_mk_float (%s->%s);\n" i typ name;
-               pr "  else\n";
-               pr "    t[%d] = erl_mk_atom (\"undefined\");\n" i;
+               pr "  if (%s->%s >= 0) {\n" typ name;
+               pr "    if (ei_x_encode_double (buff, %s->%s) != 0) return -1;\n" typ name;
+               pr "  } else {\n";
+               pr "    if (ei_x_encode_atom (buff, \"undefined\") != 0) return -1;\n";
+               pr "  }\n"
            | name, FChar ->
-               pr "  t[%d] = erl_mk_int (%s->%s);\n" i typ name
+               pr "  if (ei_x_encode_char (buff, %s->%s) != 0) return -1;\n" typ name
           );
       ) cols;
+      if cols <> [] then (
+        pr "\n";
+        pr "  if (ei_x_encode_empty_list (buff) != 0) return -1;\n"
+      );
       pr "\n";
-      pr "  return erl_mk_list (t, %d);\n" (List.length cols);
+      pr "  return 0;\n";
       pr "}\n";
   ) external_structs;
 
@@ -341,11 +341,7 @@ and generate_erlang_actions actions () =
 #include <string.h>
 #include <errno.h>
 
-#include <erl_interface.h>
-/* We should switch over to using
-  #include <ei.h>
-instead of erl_interface.
-*/
+#include <ei.h>
 
 #include \"guestfs.h\"
 #include \"guestfs-utils.h\"
@@ -358,33 +354,43 @@ instead of erl_interface.
     fun { name; style = (ret, args, optargs as style);
           c_function; c_optarg_prefix } ->
       pr "\n";
-      pr "ETERM *\n";
-      pr "run_%s (ETERM *args_tuple)\n" name;
+      pr "int\n";
+      pr "run_%s (ei_x_buff *retbuff, const char *buff, int *idx)\n" name;
       pr "{\n";
 
       List.iteri (
         fun i ->
           function
           | String (_, n) ->
-            pr "  CLEANUP_FREE char *%s = erl_iolist_to_string (ARG (%d));\n" n i
+            pr "  CLEANUP_FREE char *%s;\n" n;
+            pr "  if (decode_string (buff, idx, &%s) != 0) return -1;\n" n
           | OptString n ->
             pr "  CLEANUP_FREE char *%s;\n" n;
-            pr "  if (atom_equals (ARG (%d), \"undefined\"))\n" i;
-            pr "    %s = NULL;\n" n;
-            pr "  else\n";
-            pr "    %s = erl_iolist_to_string (ARG (%d));\n" n i
+            pr "  char %s_opt[MAXATOMLEN];\n" n;
+            pr "  if (ei_decode_atom(buff, idx, %s_opt) == 0) {\n" n;
+            pr "    if (atom_equals (%s_opt, \"undefined\"))\n" n;
+            pr "      %s = NULL;\n" n;
+            pr "    else\n";
+            pr "      %s = %s_opt;\n" n n;
+            pr "  } else {\n";
+            pr "    if (decode_string (buff, idx, &%s) != 0) return -1;\n" n;
+            pr "  }\n"
           | BufferIn n ->
-            pr "  ETERM *%s_bin = erl_iolist_to_binary (ARG (%d));\n" n i;
-            pr "  const void *%s = ERL_BIN_PTR (%s_bin);\n" n n;
-            pr "  size_t %s_size = ERL_BIN_SIZE (%s_bin);\n" n n
+            pr "  CLEANUP_FREE char *%s;\n" n;
+            pr "  size_t %s_size;\n" n;
+            pr "  if (decode_binary (buff, idx, &%s, &%s_size) != 0) return -1;\n" n n
           | StringList (_, n) ->
-            pr "  CLEANUP_FREE_STRING_LIST char **%s = get_string_list (ARG (%d));\n" n i
+            pr "  CLEANUP_FREE_STRING_LIST char **%s;\n" n;
+            pr "  if (decode_string_list (buff, idx, &%s) != 0) return -1;\n" n
           | Bool n ->
-            pr "  int %s = get_bool (ARG (%d));\n" n i
+            pr "  int %s;\n" n;
+            pr "  if (decode_bool (buff, idx, &%s) != 0) return -1;\n" n
           | Int n ->
-            pr "  int %s = get_int (ARG (%d));\n" n i
+            pr "  int %s;\n" n;
+            pr "  if (decode_int (buff, idx, &%s) != 0) return -1;\n" n
           | Int64 n ->
-            pr "  int64_t %s = get_int64 (ARG (%d));\n" n i
+            pr "  int64_t %s;\n" n;
+            pr "  if (decode_int64 (buff, idx, &%s) != 0) return -1;\n" n
           | Pointer (t, n) ->
             pr "  void * /* %s */ %s = POINTER_NOT_IMPLEMENTED (\"%s\");\n" t n t
       ) args;
@@ -394,11 +400,13 @@ instead of erl_interface.
         pr "\n";
         pr "  struct %s optargs_s = { .bitmask = 0 };\n" c_function;
         pr "  struct %s *optargs = &optargs_s;\n" c_function;
-        pr "  ETERM *optargst = ARG (%d);\n" (List.length args);
-        pr "  while (!ERL_IS_EMPTY_LIST (optargst)) {\n";
-        pr "    ETERM *hd = ERL_CONS_HEAD (optargst);\n";
-        pr "    ETERM *hd_name = ERL_TUPLE_ELEMENT (hd, 0);\n";
-        pr "    ETERM *hd_value = ERL_TUPLE_ELEMENT (hd, 1);\n";
+        pr "  int optargsize;\n";
+        pr "  if (ei_decode_list_header (buff, idx, &optargsize) != 0) return -1;\n";
+        pr "  for (int i = 0; i < optargsize; i++) {\n";
+        pr "    int hd;\n";
+        pr "    if (ei_decode_tuple_header (buff, idx, &hd) != 0) return -1;\n";
+        pr "    char hd_name[MAXATOMLEN];\n";
+        pr "    if (ei_decode_atom (buff, idx, hd_name) != 0) return -1;\n";
         pr "\n";
         List.iter (
           fun argt ->
@@ -407,21 +415,22 @@ instead of erl_interface.
             pr "    if (atom_equals (hd_name, \"%s\")) {\n" n;
             pr "      optargs_s.bitmask |= %s_%s_BITMASK;\n"
               c_optarg_prefix uc_n;
-            pr "      optargs_s.%s = " n;
+            pr "      ";
             (match argt with
-             | OBool _ -> pr "get_bool (hd_value)"
-             | OInt _ -> pr "get_int (hd_value)"
-             | OInt64 _ -> pr "get_int64 (hd_value)"
-             | OString _ -> pr "erl_iolist_to_string (hd_value)"
-             | OStringList n -> pr "get_string_list (hd_value)"
+             | OBool _ -> pr "if (decode_bool (buff, idx, &optargs_s.%s) != 0) return -1;" n
+             | OInt _ -> pr "if (decode_int (buff, idx, &optargs_s.%s) != 0) return -1" n
+             | OInt64 _ -> pr "if (decode_int64 (buff, idx, &optargs_s.%s) != 0) return -1" n
+             | OString _ -> pr "if (decode_string (buff, idx, (char **) &optargs_s.%s) != 0) return -1" n
+             | OStringList n -> pr "if (decode_string_list (buff, idx, (char ***) &optargs_s.%s) != 0) return -1" n
             );
             pr ";\n";
             pr "    }\n";
             pr "    else\n";
         ) optargs;
-        pr "      return unknown_optarg (\"%s\", hd_name);\n" name;
-        pr "    optargst = ERL_CONS_TAIL (optargst);\n";
+        pr "      return unknown_optarg (retbuff, \"%s\", hd_name);\n" name;
         pr "  }\n";
+        pr "  if (optargsize > 0 && buff[*idx] == ERL_NIL_EXT)\n";
+        pr "    (*idx)++;\n";
         pr "\n";
       );
 
@@ -471,52 +480,46 @@ instead of erl_interface.
        | `CannotReturnError -> ()
        | `ErrorIsMinusOne ->
            pr "  if (r == -1)\n";
-           pr "    return make_error (\"%s\");\n" name;
+           pr "    return make_error (retbuff, \"%s\");\n" name;
        | `ErrorIsNULL ->
            pr "  if (r == NULL)\n";
-           pr "    return make_error (\"%s\");\n" name;
+           pr "    return make_error (retbuff, \"%s\");\n" name;
       );
       pr "\n";
 
       (match ret with
-       | RErr -> pr "  return erl_mk_atom (\"ok\");\n"
-       | RInt _ -> pr "  return erl_mk_int (r);\n"
-       | RInt64 _ -> pr "  return erl_mk_longlong (r);\n"
-       | RBool _ -> pr "  return make_bool (r);\n"
-       | RConstString _ -> pr "  return erl_mk_string (r);\n"
+       | RErr -> pr "  if (ei_x_encode_atom (retbuff, \"ok\") != 0) return -1;\n"
+       | RInt _ -> pr "  if (ei_x_encode_long (retbuff, r) != 0) return -1;\n"
+       | RInt64 _ -> pr "  if (ei_x_encode_longlong (retbuff, r) != 0) return -1;\n"
+       | RBool _ -> pr "  if (make_bool (retbuff, r) != 0) return -1;\n"
+       | RConstString _ -> pr "  if (ei_x_encode_string (retbuff, r) != 0) return -1;\n"
        | RConstOptString _ ->
-           pr "  ETERM *rt;\n";
-           pr "  if (r)\n";
-           pr "    rt = erl_mk_string (r);\n";
-           pr "  else\n";
-           pr "    rt = erl_mk_atom (\"undefined\");\n";
-           pr "  return rt;\n"
+           pr "  if (r) {\n";
+           pr "    if (ei_x_encode_string (retbuff, r) != 0) return -1;\n";
+           pr "  } else {\n";
+           pr "    if (ei_x_encode_atom (retbuff, \"undefined\") != 0) return -1;\n";
+           pr "  }\n"
        | RString _ ->
-           pr "  ETERM *rt = erl_mk_string (r);\n";
+           pr "  if (ei_x_encode_string (retbuff, r) != 0) return -1;\n";
            pr "  free (r);\n";
-           pr "  return rt;\n"
        | RStringList _ ->
-           pr "  ETERM *rt = make_string_list (r);\n";
-           pr "  guestfs_int_free_string_list (r);\n\n";
-           pr "  return rt;\n"
+           pr "  if (make_string_list (retbuff, r) != 0) return -1;\n";
+           pr "  guestfs_int_free_string_list (r);\n"
        | RStruct (_, typ) ->
-           pr "  ETERM *rt = make_%s (r);\n" typ;
-           pr "  guestfs_free_%s (r);\n" typ;
-           pr "  return rt;\n"
+           pr "  if (make_%s (retbuff, r) != 0) return -1;\n" typ;
+           pr "  guestfs_free_%s (r);\n" typ
        | RStructList (_, typ) ->
-           pr "  ETERM *rt = make_%s_list (r);\n" typ;
-           pr "  guestfs_free_%s_list (r);\n" typ;
-           pr "  return rt;\n"
+           pr "  if (make_%s_list (retbuff, r) != 0) return -1;\n" typ;
+           pr "  guestfs_free_%s_list (r);\n" typ
        | RHashtable _ ->
-           pr "  ETERM *rt = make_table (r);\n";
-           pr "  guestfs_int_free_string_list (r);\n";
-           pr "  return rt;\n"
+           pr "  if (make_table (retbuff, r) != 0) return -1;\n";
+           pr "  guestfs_int_free_string_list (r);\n"
        | RBufferOut _ ->
-           pr "  ETERM *rt = erl_mk_estring (r, size);\n";
+           pr "  if (ei_x_encode_binary (retbuff, r, size) != 0) return -1;\n";
            pr "  free (r);\n";
-           pr "  return rt;\n"
       );
 
+      pr "  return 0;\n";
       pr "}\n";
   ) (actions |> external_functions |> sort);
 
@@ -532,23 +535,21 @@ and generate_erlang_dispatch () =
 #include <string.h>
 #include <errno.h>
 
-#include <erl_interface.h>
-/* We should switch over to using
-  #include <ei.h>
-instead of erl_interface.
-*/
+#include <ei.h>
 
 #include \"guestfs.h\"
 #include \"guestfs-utils.h\"
 
 #include \"actions.h\"
 
-ETERM *
-dispatch (ETERM *args_tuple)
+int
+dispatch (ei_x_buff *retbuff, const char *buff, int *index)
 {
-  ETERM *fun;
+  int arity;
+  char fun[MAXATOMLEN];
 
-  fun = ERL_TUPLE_ELEMENT (args_tuple, 0);
+  if (ei_decode_tuple_header (buff, index, &arity) != 0) return -1;
+  if (ei_decode_atom (buff, index, fun) != 0) return -1;
 
   /* XXX We should use gperf here. */
   ";
@@ -556,10 +557,10 @@ dispatch (ETERM *args_tuple)
   List.iter (
     fun { name; style = ret, args, optargs } ->
       pr "if (atom_equals (fun, \"%s\"))\n" name;
-      pr "    return run_%s (args_tuple);\n" name;
+      pr "    return run_%s (retbuff, buff, index);\n" name;
       pr "  else ";
   ) (actions |> external_functions |> sort);
 
-  pr "return unknown_function (fun);
+  pr "return unknown_function (retbuff, fun);
 }
 ";
-- 
2.26.2




More information about the Libguestfs mailing list