From 84763d7fca3668c62ee3fe53d0e00a5a672f687b Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 20 Sep 2011 18:03:58 +0100 Subject: Add Erlang bindings. --- erlang/erl-guestfs-proto.c | 278 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 erlang/erl-guestfs-proto.c (limited to 'erlang/erl-guestfs-proto.c') diff --git a/erlang/erl-guestfs-proto.c b/erlang/erl-guestfs-proto.c new file mode 100644 index 00000000..d1eb48b9 --- /dev/null +++ b/erlang/erl-guestfs-proto.c @@ -0,0 +1,278 @@ +/* libguestfs Erlang bindings. + * Copyright (C) 2011 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. + */ + +#include +#include +#include +#include + +#include +#include + +#include "error.h" +#include "full-read.h" +#include "full-write.h" + +#include "guestfs.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 void free_strings (char **r); + +/* 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 *); + +int +main (void) +{ + unsigned char *buf; + ETERM *ret, *message; + + erl_init (NULL, 0); + + /* This process has a single libguestfs handle. If the Erlang + * system creates more than one handle, then more than one of these + * processes will be running. + */ + g = guestfs_create (); + if (g == NULL) + error (EXIT_FAILURE, 0, "could not create guestfs handle"); + + guestfs_set_error_handler (g, NULL, NULL); + + while ((buf = read_message ()) != NULL) { + message = erl_decode (buf); + free (buf); + + ret = dispatch (message); + erl_free_term (message); + + write_reply (ret); + erl_free_term (ret); + } + + guestfs_close (g); + + exit (EXIT_SUCCESS); +} + +/* 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 * +read_message (void) +{ + unsigned char buf[4]; + size_t size; + unsigned char *r; + + errno = 0; + if (full_read (0, buf, 4) != 4) { + if (errno == 0) /* ok - closed connection normally */ + return NULL; + else + error (EXIT_FAILURE, errno, "read message size"); + } + + size = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + + if (size > MAX_MESSAGE_SIZE) + error (EXIT_FAILURE, 0, "message larger than MAX_MESSAGE_SIZE"); + + r = malloc (size); + if (r == NULL) + error (EXIT_FAILURE, errno, "malloc"); + + if (full_read (0, r, size) != size) + error (EXIT_FAILURE, errno, "read message content"); + + return r; +} + +static void +write_reply (ETERM *term) +{ + size_t size; + 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; + sbuf[2] = (size >> 8) & 0xff; + sbuf[3] = size & 0xff; + + if (full_write (1, sbuf, 4) != 4) + error (EXIT_FAILURE, errno, "write message size"); + + if (full_write (1, buf, 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) +{ + size_t namelen = strlen (name); + size_t atomlen = ERL_ATOM_SIZE (atom); + if (namelen != atomlen) return 0; + return strncmp (ERL_ATOM_PTR (atom), name, atomlen) == 0; +} + +ETERM * +make_error (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); +} + +ETERM * +unknown_function (ETERM *fun) +{ + ETERM *unknown = erl_mk_atom ("unknown"); + ETERM *funcopy = erl_copy_term (fun); + ETERM *t[2] = { unknown, funcopy }; + return erl_mk_tuple (t, 2); +} + +ETERM * +unknown_optarg (const char *funname, ETERM *optargname) +{ + ETERM *unknownarg = erl_mk_atom ("unknownarg"); + ETERM *copy = erl_copy_term (optargname); + ETERM *t[2] = { unknownarg, copy }; + return erl_mk_tuple (t, 2); +} + +ETERM * +make_string_list (char **r) +{ + size_t i, size; + + for (size = 0; r[size] != NULL; ++size) + ; + + ETERM *t[size]; + + for (i = 0; r[i] != NULL; ++i) + t[i] = erl_mk_string (r[i]); + + return erl_mk_list (t, size); +} + +/* Make a hash table. The number of elements returned by the C + * function is always even. + */ +ETERM * +make_table (char **r) +{ + size_t i, size; + + for (size = 0; r[size] != NULL; ++size) + ; + + ETERM *t[size/2]; + ETERM *a[2]; + + 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); + } + + return erl_mk_list (t, size/2); +} + +ETERM * +make_bool (int r) +{ + if (r) + return erl_mk_atom ("true"); + else + return erl_mk_atom ("false"); +} + +char ** +get_string_list (ETERM *term) +{ + ETERM *t; + size_t i, size; + char **r; + + for (size = 0, t = term; !ERL_IS_EMPTY_LIST (t); + size++, t = ERL_CONS_TAIL (t)) + ; + + 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)); + r[size] = NULL; + + return r; +} + +int +get_bool (ETERM *term) +{ + if (atom_equals (term, "true")) + return 1; + else + return 0; +} + +void +free_strings (char **r) +{ + size_t i; + + for (i = 0; r[i] != NULL; ++i) + free (r[i]); + free (r); +} -- cgit