Daemon and library are mostly talking to each other now.
authorRichard Jones <rjones@redhat.com>
Fri, 3 Apr 2009 16:24:35 +0000 (17:24 +0100)
committerRichard Jones <rjones@redhat.com>
Fri, 3 Apr 2009 16:24:35 +0000 (17:24 +0100)
18 files changed:
.gitignore
daemon/Makefile.am
daemon/actions.h
daemon/daemon.h [new file with mode: 0644]
daemon/guestfsd.c
daemon/proto.c [new file with mode: 0644]
daemon/stubs.c
daemon/sync.c [new file with mode: 0644]
examples/Makefile.am
examples/hello.c [new file with mode: 0644]
guestfs.pod
src/Makefile.am
src/generator.ml
src/guestfs-actions.c
src/guestfs.c
src/guestfs_protocol.c [new file with mode: 0644]
src/guestfs_protocol.h [new file with mode: 0644]
src/guestfs_protocol.x

index a9bd0d6..8771111 100644 (file)
@@ -22,6 +22,7 @@ daemon/missing
 depcomp
 emptydisk
 examples/df
+examples/hello
 guestfs.3
 initramfs
 initramfs.timestamp
index 6a5a4ee..cf55250 100644 (file)
 ACLOCAL_AMFLAGS = -I m4
 
 noinst_PROGRAMS = guestfsd
-guestfsd_SOURCES = guestfsd.c
+guestfsd_SOURCES = \
+       actions.h \
+       daemon.h \
+       guestfsd.c \
+       proto.c \
+       stubs.c \
+       sync.c \
+       ../src/guestfs_protocol.h \
+       ../src/guestfs_protocol.c
 
-guestfsd_CFLAGS = -Wall -Werror
\ No newline at end of file
+guestfsd_CFLAGS = -Wall
\ No newline at end of file
index c7d64df..61b583c 100644 (file)
@@ -19,6 +19,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-extern int do_mount (guestfs_h *handle, const char *device, const char *mountpoint);
-extern int do_sync (guestfs_h *handle);
-extern int do_touch (guestfs_h *handle, const char *path);
+extern int do_mount (const char *device, const char *mountpoint);
+extern int do_sync ();
+extern int do_touch (const char *path);
diff --git a/daemon/daemon.h b/daemon/daemon.h
new file mode 100644 (file)
index 0000000..b8a9001
--- /dev/null
@@ -0,0 +1,41 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef GUESTFSD_DAEMON_H
+#define GUESTFSD_DAEMON_H
+
+#include <stdarg.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+
+/* in guestfsd.c */
+extern void xwrite (int sock, const void *buf, size_t len);
+
+/* in proto.c */
+extern int proc_nr;
+extern int serial;
+
+/* in stubs.c (auto-generated) */
+extern void dispatch_incoming_message (XDR *);
+
+/* in proto.c */
+extern void main_loop (int sock);
+extern void reply_with_error (const char *fs, ...);
+extern void reply (xdrproc_t, XDR *);
+
+#endif /* GUESTFSD_DAEMON_H */
index c27d1b6..eaba7f0 100644 (file)
 #include <getopt.h>
 #include <netdb.h>
 
-static void xwrite (int sock, const void *buf, size_t len);
+#include "daemon.h"
+
+void xwrite (int sock, const void *buf, size_t len);
+
 static void usage (void);
 
 /* Also in guestfs.c */
@@ -177,12 +180,15 @@ main (int argc, char *argv[])
 
 
 
-  sleep (1000000);
+
+
+
+  main_loop (sock);
 
   exit (0);
 }
 
-static void
+void
 xwrite (int sock, const void *buf, size_t len)
 {
   int r;
@@ -203,3 +209,18 @@ usage (void)
 {
   fprintf (stderr, "guestfsd [-f] [-h host -p port]\n");
 }
+
+/* Some unimplemented actions. */
+int
+do_mount (const char *device, const char *mountpoint)
+{
+  reply_with_error ("mount not implemented");
+  return -1;
+}
+
+int
+do_touch (const char *path)
+{
+  reply_with_error ("touch not implemented");
+  return -1;
+}
diff --git a/daemon/proto.c b/daemon/proto.c
new file mode 100644 (file)
index 0000000..f045751
--- /dev/null
@@ -0,0 +1,59 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+
+#include "daemon.h"
+
+/* The message currently being processed. */
+int proc_nr;
+int serial;
+
+/* The daemon communications socket. */
+static int sock;
+
+void
+main_loop (int _sock)
+{
+  sock = _sock;
+
+  
+
+
+}
+
+void
+reply_with_error (const char *fs, ...)
+{
+  
+
+
+}
+
+void
+reply (xdrproc_t xdrp, XDR *xdr)
+{
+}
index 81c5b72..22fd575 100644 (file)
@@ -22,7 +22,7 @@
 #include <rpc/types.h>
 #include <rpc/xdr.h>
 #include "daemon.h"
-#include "../src/guest_protocol.h"
+#include "../src/guestfs_protocol.h"
 #include "actions.h"
 
 static void mount_stub (XDR *xdr_in)
@@ -79,3 +79,19 @@ static void touch_stub (XDR *xdr_in)
   reply (NULL, NULL);
 }
 
+void dispatch_incoming_message (XDR *xdr_in)
+{
+  switch (proc_nr) {
+    case GUESTFS_PROC_MOUNT:
+      mount_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_SYNC:
+      sync_stub (xdr_in);
+      break;
+    case GUESTFS_PROC_TOUCH:
+      touch_stub (xdr_in);
+      break;
+    default:
+      reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr);
+  }
+}
diff --git a/daemon/sync.c b/daemon/sync.c
new file mode 100644 (file)
index 0000000..9ade840
--- /dev/null
@@ -0,0 +1,32 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "actions.h"
+
+int
+do_sync ()
+{
+  sync ();
+  fprintf (stderr, "guestfsd: disk synched\n");
+  return 0;
+}
index 66a32d7..9bac5f3 100644 (file)
@@ -1,7 +1,11 @@
 # libguestfs examples
 
-noinst_PROGRAMS = df
+noinst_PROGRAMS = df hello
 
 df_SOURCES = df.c
-df_CFLAGS = -I$(top_builddir)/src
+df_CFLAGS = -I$(top_builddir)/src -Wall
 df_LDADD = $(top_builddir)/src/libguestfs.la
+
+hello_SOURCES = hello.c
+hello_CFLAGS = -I$(top_builddir)/src -Wall
+hello_LDADD = $(top_builddir)/src/libguestfs.la
diff --git a/examples/hello.c b/examples/hello.c
new file mode 100644 (file)
index 0000000..5f1ab0e
--- /dev/null
@@ -0,0 +1,33 @@
+/* Create a "/hello" file on /dev/sda1. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <guestfs.h>
+
+int
+main (int argc, char *argv[])
+{
+  guestfs_h *g;
+
+  if (argc != 2 || access (argv[1], F_OK) != 0) {
+    fprintf (stderr, "Usage: hello disk-image\n");
+    exit (1);
+  }
+
+  if (!(g = guestfs_create ())) exit (1);
+
+  guestfs_set_verbose (g, 1);
+  if (guestfs_add_drive (g, argv[1]) == -1) exit (1);
+
+  if (guestfs_launch (g) == -1) exit (1);
+  if (guestfs_wait_ready (g) == -1) exit (1);
+
+  if (guestfs_mount (g, "/dev/sda1", "/") == -1) exit (1);
+
+  if (guestfs_touch (g, "/hello") == -1) exit (1);
+
+  guestfs_sync (g);
+  guestfs_close (g);
+  return 0;
+}
index bf75e42..61d51b7 100644 (file)
@@ -34,8 +34,7 @@ schemes, qcow, qcow2, vmdk.
 
 Libguestfs provides ways to enumerate guest storage (eg. partitions,
 LVs, what filesystem is in each LV, etc.).  It can also run commands
-in the context of the guest.  Also you can mount guest filesystems on
-the host (requires root privs and NFS).
+in the context of the guest.  Also you can access filesystems over FTP.
 
 Libguestfs is a library that can be linked with C and C++ management
 programs (or management programs written in other languages, if people
@@ -366,6 +365,8 @@ this function with C<cb> set to C<NULL>.
 
 =head2 NON-BLOCKING ACTIONS
 
+XXX NOT IMPLEMENTED YET XXX
+
 C<guestfs_set_reply_callback> is the most interesting callback to
 play with, since it allows you to perform actions without blocking.
 
@@ -391,8 +392,7 @@ For example:
  }
 
 There are C<guestfs_nb_*> and C<guestfs_nb_*_r> functions
-corresponding to (very nearly) every C<guestfs_*> action in the
-high-level API.
+corresponding to every C<guestfs_*> action in the high-level API.
 
 =head2 guestfs_set_reply_callback
 
index a0066db..68cabba 100644 (file)
 
 lib_LTLIBRARIES = libguestfs.la
 
-libguestfs_la_SOURCES = guestfs.c guestfs.h
-libguestfs_la_CFLAGS = -Wall -Werror
\ No newline at end of file
+# NB. guestfs-actions.c is #include'd into guestfs.c, so it should not
+# be listed as a source file.
+EXTRA_DIST = guestfs-actions.c
+
+libguestfs_la_SOURCES = \
+       guestfs.c \
+       guestfs.h \
+       guestfs_protocol.c \
+       guestfs_protocol.h \
+       guestfs-actions.h
+
+libguestfs_la_CFLAGS = -Wall
+
+if RPCGEN
+guestfs_protocol.c: guestfs_protocol.x
+       rm -f $@-t
+       $(RPCGEN) -c -o $@-t $<
+       mv $@-t $@
+
+guestfs_protocol.h: guestfs_protocol.x
+       rm -f $@-t
+       $(RPCGEN) -h -o $@-t $<
+       mv $@-t $@
+endif
\ No newline at end of file
index d08e269..8588cf3 100755 (executable)
@@ -41,7 +41,7 @@ and argt =
   | String of string   (* const char *name, cannot be NULL *)
 
 let functions = [
-  ("mount", (Err, P2 (String "device", String "mountpoint")),
+  ("mount", (Err, P2 (String "device", String "mountpoint")), 1,
    "Mount a guest disk at a position in the filesystem",
    "\
 Mount a guest disk at a position in the filesystem.  Block devices
@@ -55,7 +55,7 @@ first be mounted on C</> before others can be mounted.  Other
 filesystems can only be mounted on directories which already
 exist.");
 
-  ("sync", (Err, P0),
+  ("sync", (Err, P0), 2,
    "Sync disks, writes are flushed through to the disk image",
    "\
 This syncs the disk, so that any writes are flushed through to the
@@ -64,7 +64,7 @@ underlying disk image.
 You should always call this if you have modified a disk image, before
 calling C<guestfs_close>.");
 
-  ("touch", (Err, P1 (String "path")),
+  ("touch", (Err, P1 (String "path")), 3,
    "Update file timestamps or create a new file",
    "\
 Touch acts like the L<touch(1)> command.  It can be used to
@@ -137,11 +137,11 @@ let rec generate_header comment license =
 (* Generate the pod documentation for the C API. *)
 and generate_pod () =
   List.iter (
-    fun (shortname, style, _, longdesc) ->
+    fun (shortname, style, _, _, longdesc) ->
       let name = "guestfs_" ^ shortname in
       pr "=head2 %s\n\n" name;
       pr " ";
-      generate_prototype ~extern:false name style;
+      generate_prototype ~extern:false ~handle:"handle" name style;
       pr "\n\n";
       pr "%s\n\n" longdesc;
       (match style with
@@ -153,8 +153,9 @@ and generate_pod () =
 (* Generate the protocol (XDR) file. *)
 and generate_xdr () =
   generate_header CStyle LGPLv2;
+
   List.iter (
-    fun (shortname, style, _, _) ->
+    fun (shortname, style, _, _, _) ->
       let name = "guestfs_" ^ shortname in
       pr "/* %s */\n\n" name;
       (match style with
@@ -169,30 +170,76 @@ and generate_xdr () =
       );
       (match style with
        | (Err, _) -> () 
-    (* | ... -> pr "struct %s_ret ...\n" name; *)
+          (* | ... -> pr "struct %s_ret ...\n" name; *)
       );
-  ) functions
+  ) functions;
+
+  (* Table of procedure numbers. *)
+  pr "enum guestfs_procedure {\n";
+  List.iter (
+    fun (shortname, _, proc_nr, _, _) ->
+      pr "  GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
+  ) functions;
+  pr "  GUESTFS_PROC_dummy\n"; (* so we don't have a "hanging comma" *)
+  pr "};\n";
+  pr "\n";
+
+  (* Having to choose a maximum message size is annoying for several
+   * reasons (it limits what we can do in the API), but it (a) makes
+   * the protocol a lot simpler, and (b) provides a bound on the size
+   * of the daemon which operates in limited memory space.  For large
+   * file transfers you should use FTP.
+   *)
+  pr "const GUESTFS_MESSAGE_MAX = %d;\n" (4 * 1024 * 1024);
+  pr "\n";
+
+  (* Message header, etc. *)
+  pr "\
+const GUESTFS_PROGRAM = 0x2000F5F5;
+const GUESTFS_PROTOCOL_VERSION = 1;
+
+enum guestfs_message_direction {
+  GUESTFS_DIRECTION_CALL = 0,        /* client -> daemon */
+  GUESTFS_DIRECTION_REPLY = 1        /* daemon -> client */
+};
+
+enum guestfs_message_status {
+  GUESTFS_STATUS_OK = 0,
+  GUESTFS_STATUS_ERROR = 1
+};
+
+struct guestfs_message_header {
+  unsigned prog;                     /* GUESTFS_PROGRAM */
+  unsigned vers;                     /* GUESTFS_PROTOCOL_VERSION */
+  guestfs_procedure proc;            /* GUESTFS_PROC_x */
+  guestfs_message_direction direction;
+  unsigned serial;                   /* message serial number */
+  guestfs_message_status status;
+};
+"
 
 (* Generate the guestfs-actions.h file. *)
 and generate_actions_h () =
   generate_header CStyle LGPLv2;
   List.iter (
-    fun (shortname, style, _, _) ->
+    fun (shortname, style, _, _, _) ->
       let name = "guestfs_" ^ shortname in
-      generate_prototype ~single_line:true ~newline:true name style
+      generate_prototype ~single_line:true ~newline:true ~handle:"handle"
+       name style
   ) functions
 
 (* Generate the client-side dispatch stubs. *)
 and generate_client_actions () =
   generate_header CStyle LGPLv2;
   List.iter (
-    fun (shortname, style, _, _) ->
+    fun (shortname, style, _, _, _) ->
       let name = "guestfs_" ^ shortname in
 
       (* Generate the return value struct. *)
       pr "struct %s_rv {\n" shortname;
-      pr "  int err_code; /* 0 or -1 */\n";
-      pr "  char err_str[256];\n";
+      pr "  int err_code;      /* 0 OK or -1 error */\n";
+      pr "  int serial;        /* serial number of reply */\n";
+      pr "  char err_str[256]; /* error from daemon */\n";
       (match style with
        | (Err, _) -> ()
     (* | _ -> pr "  struct %s_ret ret;\n" name; *)
@@ -204,7 +251,8 @@ and generate_client_actions () =
       pr "{\n";
       pr "  struct %s_rv *rv = (struct %s_rv *) data;\n" shortname shortname;
       pr "\n";
-      pr "  /* XXX */ rv.code = 0;\n";
+      pr "  /* XXX */ rv->err_code = 0;\n";
+      pr "  /* XXX rv->serial = ?; */\n";
       pr "  main_loop.main_loop_quit (g);\n";
       pr "}\n\n";
 
@@ -224,6 +272,7 @@ and generate_client_actions () =
       );
 
       pr "  struct %s_rv rv;\n" shortname;
+      pr "  int serial;\n";
       pr "\n";
       pr "  if (g->state != READY) {\n";
       pr "    error (g, \"%s called from the wrong state, %%d != READY\",\n"
@@ -233,18 +282,23 @@ and generate_client_actions () =
       pr "  }\n";
 
       (match style with
-       | (_, P0) -> ()
+       | (_, P0) ->
+          pr "  serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n"
+            (String.uppercase shortname)
        | (_, args) ->
           pr "\n";
           iter_args (
             function
             | String name -> pr "  args.%s = (char *) %s;\n" name name
           ) args;
-          pr "  if (dispatch (g, (xdrproc_t) xdr_%s_args, (char *) &args) == -1)\n"
+          pr "  serial = dispatch (g, GUESTFS_PROC_%s,\n"
+            (String.uppercase shortname);
+          pr "                     (xdrproc_t) xdr_%s_args, (char *) &args);\n"
             name;
-          pr "    return %s;\n" error_code;
-          pr "\n";
       );
+      pr "  if (serial == -1)\n";
+      pr "    return %s;\n" error_code;
+      pr "\n";
 
       pr "  rv.err_code = 42;\n";
       pr "  g->reply_cb_internal = %s_cb;\n" shortname;
@@ -262,6 +316,8 @@ and generate_client_actions () =
       pr "  }\n";
       pr "\n";
 
+      pr "  /* XXX check serial number agrees */\n\n";
+
       (match style with
        | (Err, _) -> pr "  return 0;\n"
       );
@@ -273,7 +329,7 @@ and generate_client_actions () =
 and generate_daemon_actions_h () =
   generate_header CStyle GPLv2;
   List.iter (
-    fun (name, style, _, _) ->
+    fun (name, style, _, _, _) ->
       generate_prototype ~single_line:true ~newline:true ("do_" ^ name) style;
   ) functions
 
@@ -284,12 +340,12 @@ and generate_daemon_actions () =
   pr "#include <rpc/types.h>\n";
   pr "#include <rpc/xdr.h>\n";
   pr "#include \"daemon.h\"\n";
-  pr "#include \"../src/guest_protocol.h\"\n";
+  pr "#include \"../src/guestfs_protocol.h\"\n";
   pr "#include \"actions.h\"\n";
   pr "\n";
 
   List.iter (
-    fun (name, style, _, _) ->
+    fun (name, style, _, _, _) ->
       (* Generate server-side stubs. *)
       pr "static void %s_stub (XDR *xdr_in)\n" name;
       pr "{\n";
@@ -335,19 +391,46 @@ and generate_daemon_actions () =
       );
 
       pr "}\n\n";
-  ) functions
+  ) functions;
+
+  (* Dispatch function. *)
+  pr "void dispatch_incoming_message (XDR *xdr_in)\n";
+  pr "{\n";
+  pr "  switch (proc_nr) {\n";
+
+  List.iter (
+    fun (name, style, _, _, _) ->
+      pr "    case GUESTFS_PROC_%s:\n" (String.uppercase name);
+      pr "      %s_stub (xdr_in);\n" name;
+      pr "      break;\n"
+  ) functions;
+
+  pr "    default:\n";
+  pr "      reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
+  pr "  }\n";
+  pr "}\n";
 
 (* Generate a C function prototype. *)
 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
     ?(single_line = false) ?(newline = false)
-    ?(handle = "handle") name style =
+    ?handle name style =
   if extern then pr "extern ";
   if static then pr "static ";
   (match style with
    | (Err, _) -> pr "int "
   );
-  pr "%s (guestfs_h *%s" name handle;
-  let next () = if single_line then pr ", " else pr ",\n\t\t" in
+  pr "%s (" name;
+  let comma = ref false in
+  (match handle with
+   | None -> ()
+   | Some handle -> pr "guestfs_h *%s" handle; comma := true
+  );
+  let next () =
+    if !comma then (
+      if single_line then pr ", " else pr ",\n\t\t"
+    );
+    comma := true
+  in
   iter_args (
     function
     | String name -> next (); pr "const char *%s" name
index db701ef..12ae602 100644 (file)
  */
 
 struct mount_rv {
-  int err_code; /* 0 or -1 */
-  char err_str[256];
+  int err_code;      /* 0 OK or -1 error */
+  int serial;        /* serial number of reply */
+  char err_str[256]; /* error from daemon */
 };
 
 static void mount_cb (guestfs_h *g, void *data, XDR *xdr)
 {
   struct mount_rv *rv = (struct mount_rv *) data;
 
-  /* XXX */ rv.code = 0;
+  /* XXX */ rv->err_code = 0;
+  /* XXX rv->serial = ?; */
   main_loop.main_loop_quit (g);
 }
 
@@ -38,6 +40,7 @@ int guestfs_mount (guestfs_h *g,
 {
   struct guestfs_mount_args args;
   struct mount_rv rv;
+  int serial;
 
   if (g->state != READY) {
     error (g, "guestfs_mount called from the wrong state, %d != READY",
@@ -47,7 +50,9 @@ int guestfs_mount (guestfs_h *g,
 
   args.device = (char *) device;
   args.mountpoint = (char *) mountpoint;
-  if (dispatch (g, (xdrproc_t) xdr_guestfs_mount_args, (char *) &args) == -1)
+  serial = dispatch (g, GUESTFS_PROC_MOUNT,
+                     (xdrproc_t) xdr_guestfs_mount_args, (char *) &args);
+  if (serial == -1)
     return -1;
 
   rv.err_code = 42;
@@ -65,31 +70,40 @@ int guestfs_mount (guestfs_h *g,
     return -1;
   }
 
+  /* XXX check serial number agrees */
+
   return 0;
 }
 
 struct sync_rv {
-  int err_code; /* 0 or -1 */
-  char err_str[256];
+  int err_code;      /* 0 OK or -1 error */
+  int serial;        /* serial number of reply */
+  char err_str[256]; /* error from daemon */
 };
 
 static void sync_cb (guestfs_h *g, void *data, XDR *xdr)
 {
   struct sync_rv *rv = (struct sync_rv *) data;
 
-  /* XXX */ rv.code = 0;
+  /* XXX */ rv->err_code = 0;
+  /* XXX rv->serial = ?; */
   main_loop.main_loop_quit (g);
 }
 
 int guestfs_sync (guestfs_h *g)
 {
   struct sync_rv rv;
+  int serial;
 
   if (g->state != READY) {
     error (g, "guestfs_sync called from the wrong state, %d != READY",
       g->state);
     return -1;
   }
+  serial = dispatch (g, GUESTFS_PROC_SYNC, NULL, NULL);
+  if (serial == -1)
+    return -1;
+
   rv.err_code = 42;
   g->reply_cb_internal = sync_cb;
   g->reply_cb_internal_data = &rv;
@@ -105,19 +119,23 @@ int guestfs_sync (guestfs_h *g)
     return -1;
   }
 
+  /* XXX check serial number agrees */
+
   return 0;
 }
 
 struct touch_rv {
-  int err_code; /* 0 or -1 */
-  char err_str[256];
+  int err_code;      /* 0 OK or -1 error */
+  int serial;        /* serial number of reply */
+  char err_str[256]; /* error from daemon */
 };
 
 static void touch_cb (guestfs_h *g, void *data, XDR *xdr)
 {
   struct touch_rv *rv = (struct touch_rv *) data;
 
-  /* XXX */ rv.code = 0;
+  /* XXX */ rv->err_code = 0;
+  /* XXX rv->serial = ?; */
   main_loop.main_loop_quit (g);
 }
 
@@ -126,6 +144,7 @@ int guestfs_touch (guestfs_h *g,
 {
   struct guestfs_touch_args args;
   struct touch_rv rv;
+  int serial;
 
   if (g->state != READY) {
     error (g, "guestfs_touch called from the wrong state, %d != READY",
@@ -134,7 +153,9 @@ int guestfs_touch (guestfs_h *g,
   }
 
   args.path = (char *) path;
-  if (dispatch (g, (xdrproc_t) xdr_guestfs_touch_args, (char *) &args) == -1)
+  serial = dispatch (g, GUESTFS_PROC_TOUCH,
+                     (xdrproc_t) xdr_guestfs_touch_args, (char *) &args);
+  if (serial == -1)
     return -1;
 
   rv.err_code = 42;
@@ -152,6 +173,8 @@ int guestfs_touch (guestfs_h *g,
     return -1;
   }
 
+  /* XXX check serial number agrees */
+
   return 0;
 }
 
index bbcf7a6..309cd15 100644 (file)
@@ -54,6 +54,7 @@
 #endif
 
 #include "guestfs.h"
+#include "guestfs_protocol.h"
 
 static void error (guestfs_h *g, const char *fs, ...);
 static void perrorf (guestfs_h *g, const char *fs, ...);
@@ -64,7 +65,7 @@ static char *safe_strdup (guestfs_h *g, const char *str);
 static void default_error_cb (guestfs_h *g, void *data, const char *msg);
 static void stdout_event (void *data, int watch, int fd, int events);
 static void sock_read_event (void *data, int watch, int fd, int events);
-//static void sock_write_event (void *data, int watch, int fd, int events);
+static void sock_write_event (void *data, int watch, int fd, int events);
 
 static int select_add_handle (guestfs_h *g, int fd, int events, guestfs_handle_event_cb cb, void *data);
 static int select_remove_handle (guestfs_h *g, int watch);
@@ -138,7 +139,9 @@ struct guestfs_h
   char *msg_in;
   int msg_in_size, msg_in_allocated;
   char *msg_out;
-  int msg_out_size;
+  int msg_out_size, msg_out_pos;
+
+  int msg_next_serial;
 };
 
 guestfs_h *
@@ -587,7 +590,8 @@ guestfs_launch (guestfs_h *g)
     if ((r == -1 && errno == EINPROGRESS) || r == 0)
       goto connected;
 
-    perrorf (g, "connect");
+    if (errno != ENOENT)
+      perrorf (g, "connect");
     tries--;
   }
 
@@ -603,6 +607,7 @@ guestfs_launch (guestfs_h *g)
   free (g->msg_out);
   g->msg_out = NULL;
   g->msg_out_size = 0;
+  g->msg_out_pos = 0;
 
   g->stdout_watch =
     main_loop.add_handle (g, g->fd[1],
@@ -615,9 +620,7 @@ guestfs_launch (guestfs_h *g)
 
   g->sock_watch =
     main_loop.add_handle (g, g->sock,
-                         GUESTFS_HANDLE_READABLE |
-                         GUESTFS_HANDLE_HANGUP |
-                         GUESTFS_HANDLE_ERROR,
+                         GUESTFS_HANDLE_READABLE,
                          sock_read_event, g);
   if (g->sock_watch == -1) {
     error (g, "could not watch daemon communications socket");
@@ -793,7 +796,7 @@ sock_read_event (void *data, int watch, int fd, int events)
 
   if (g->verbose)
     fprintf (stderr,
-            "sock_event: %p g->state = %d, fd = %d, events = 0x%x\n",
+            "sock_read_event: %p g->state = %d, fd = %d, events = 0x%x\n",
             g, g->state, fd, events);
 
   if (g->sock != fd) {
@@ -852,6 +855,13 @@ sock_read_event (void *data, int watch, int fd, int events)
     goto cleanup;
   }
 
+  /* If this happens, it's pretty bad and we've probably lost synchronization.*/
+  if (len > GUESTFS_MESSAGE_MAX) {
+    error (g, "message length (%u) > maximum possible size (%d)",
+          len, GUESTFS_MESSAGE_MAX);
+    goto cleanup;
+  }
+
   if (g->msg_in_size < len) return; /* Need more of this message. */
 
   /* This should not happen, and if it does it probably means we've
@@ -888,6 +898,160 @@ sock_read_event (void *data, int watch, int fd, int events)
   xdr_destroy (&xdr);
 }
 
+/* The function is called whenever we can write something on the
+ * guestfsd (daemon inside the guest) communication socket.
+ */
+static void
+sock_write_event (void *data, int watch, int fd, int events)
+{
+  guestfs_h *g = (guestfs_h *) data;
+  int n;
+
+  if (g->verbose)
+    fprintf (stderr,
+            "sock_write_event: %p g->state = %d, fd = %d, events = 0x%x\n",
+            g, g->state, fd, events);
+
+  if (g->sock != fd) {
+    error (g, "sock_write_event: internal error: %d != %d", g->sock, fd);
+    return;
+  }
+
+  if (g->state != BUSY) {
+    error (g, "sock_write_event: state %d != BUSY", g->state);
+    return;
+  }
+
+  if (g->verbose)
+    fprintf (stderr, "sock_write_event: writing %d bytes ...\n",
+            g->msg_out_size - g->msg_out_pos);
+
+  n = write (g->sock, g->msg_out + g->msg_out_pos,
+            g->msg_out_size - g->msg_out_pos);
+  if (n == -1) {
+    if (errno != EAGAIN)
+      perrorf (g, "write");
+    return;
+  }
+
+  if (g->verbose)
+    fprintf (stderr, "sock_write_event: wrote %d bytes\n", n);
+
+  g->msg_out_pos += n;
+
+  /* More to write? */
+  if (g->msg_out_pos < g->msg_out_size)
+    return;
+
+  if (g->verbose)
+    fprintf (stderr, "sock_write_event: done writing, switching back to reading events\n", n);
+
+  free (g->msg_out);
+  g->msg_out_pos = g->msg_out_size = 0;
+
+  if (main_loop.remove_handle (g, g->sock_watch) == -1) {
+    error (g, "remove_handle failed in sock_write_event");
+    return;
+  }
+  g->sock_watch =
+    main_loop.add_handle (g, g->sock,
+                         GUESTFS_HANDLE_READABLE,
+                         sock_read_event, g);
+  if (g->sock_watch == -1) {
+    error (g, "add_handle failed in sock_write_event");
+    return;
+  }
+}
+
+/* Dispatch a call to the remote daemon.  This function just queues
+ * the call in msg_out, to be sent when we next enter the main loop.
+ * Returns -1 for error, or the message serial number.
+ */
+static int
+dispatch (guestfs_h *g, int proc_nr, xdrproc_t xdrp, char *args)
+{
+  char buffer[GUESTFS_MESSAGE_MAX];
+  struct guestfs_message_header hdr;
+  XDR xdr;
+  unsigned len;
+  int serial = g->msg_next_serial++;
+
+  if (g->state != READY) {
+    error (g, "dispatch: state %d != READY", g->state);
+    return -1;
+  }
+
+  /* Serialize the header. */
+  hdr.prog = GUESTFS_PROGRAM;
+  hdr.vers = GUESTFS_PROTOCOL_VERSION;
+  hdr.proc = proc_nr;
+  hdr.direction = GUESTFS_DIRECTION_CALL;
+  hdr.serial = serial;
+  hdr.status = GUESTFS_STATUS_OK;
+
+  xdrmem_create (&xdr, buffer, sizeof buffer, XDR_ENCODE);
+  if (!xdr_guestfs_message_header (&xdr, &hdr)) {
+    error (g, "xdr_guestfs_message_header failed");
+    return -1;
+  }
+
+  /* Serialize the args.  If any, because some message types
+   * have no parameters.
+   */
+  if (xdrp) {
+    if (!(*xdrp) (&xdr, args)) {
+      error (g, "dispatch failed to marshal args");
+      return -1;
+    }
+  }
+
+  len = xdr_getpos (&xdr);
+  xdr_destroy (&xdr);
+
+  /* Allocate the outgoing message buffer. */
+  g->msg_out = safe_malloc (g, len + 4);
+
+  g->msg_out_size = len + 4;
+  g->msg_out_pos = 0;
+  g->state = BUSY;
+
+  xdrmem_create (&xdr, g->msg_out, 4, XDR_ENCODE);
+  if (!xdr_uint32_t (&xdr, &len)) {
+    error (g, "xdr_uint32_t failed in dispatch");
+    goto cleanup1;
+  }
+
+  memcpy (g->msg_out + 4, buffer, len);
+
+  /* Change the handle to sock_write_event. */
+  if (main_loop.remove_handle (g, g->sock_watch) == -1) {
+    error (g, "remove_handle failed in dispatch");
+    goto cleanup1;
+  }
+  g->sock_watch =
+    main_loop.add_handle (g, g->sock,
+                         GUESTFS_HANDLE_WRITABLE,
+                         sock_write_event, g);
+  if (g->sock_watch == -1) {
+    error (g, "add_handle failed in dispatch");
+    goto cleanup1;
+  }
+
+  return serial;
+
+ cleanup1:
+  free (g->msg_out);
+  g->msg_out = NULL;
+  g->msg_out_size = 0;
+  g->state = READY;
+  return -1;
+}
+
+/* The high-level actions are autogenerated by generator.ml.  Include
+ * them here.
+ */
+#include "guestfs-actions.c"
+
 /* This is the default main loop implementation, using select(2). */
 
 struct handle_cb_data {
diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c
new file mode 100644 (file)
index 0000000..8a59085
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "guestfs_protocol.h"
+
+bool_t
+xdr_guestfs_mount_args (XDR *xdrs, guestfs_mount_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->device, ~0))
+                return FALSE;
+        if (!xdr_string (xdrs, &objp->mountpoint, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_touch_args (XDR *xdrs, guestfs_touch_args *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_string (xdrs, &objp->path, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_message_direction (XDR *xdrs, guestfs_message_direction *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_message_status (XDR *xdrs, guestfs_message_status *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_guestfs_message_header (XDR *xdrs, guestfs_message_header *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_u_int (xdrs, &objp->prog))
+                return FALSE;
+        if (!xdr_u_int (xdrs, &objp->vers))
+                return FALSE;
+        if (!xdr_guestfs_procedure (xdrs, &objp->proc))
+                return FALSE;
+        if (!xdr_guestfs_message_direction (xdrs, &objp->direction))
+                return FALSE;
+        if (!xdr_u_int (xdrs, &objp->serial))
+                return FALSE;
+        if (!xdr_guestfs_message_status (xdrs, &objp->status))
+                return FALSE;
+       return TRUE;
+}
diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h
new file mode 100644 (file)
index 0000000..562d72f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _GUESTFS_PROTOCOL_H_RPCGEN
+#define _GUESTFS_PROTOCOL_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct guestfs_mount_args {
+       char *device;
+       char *mountpoint;
+};
+typedef struct guestfs_mount_args guestfs_mount_args;
+
+struct guestfs_touch_args {
+       char *path;
+};
+typedef struct guestfs_touch_args guestfs_touch_args;
+
+enum guestfs_procedure {
+       GUESTFS_PROC_MOUNT = 1,
+       GUESTFS_PROC_SYNC = 2,
+       GUESTFS_PROC_TOUCH = 3,
+       GUESTFS_PROC_dummy = 3 + 1,
+};
+typedef enum guestfs_procedure guestfs_procedure;
+#define GUESTFS_MESSAGE_MAX 4194304
+#define GUESTFS_PROGRAM 0x2000F5F5
+#define GUESTFS_PROTOCOL_VERSION 1
+
+enum guestfs_message_direction {
+       GUESTFS_DIRECTION_CALL = 0,
+       GUESTFS_DIRECTION_REPLY = 1,
+};
+typedef enum guestfs_message_direction guestfs_message_direction;
+
+enum guestfs_message_status {
+       GUESTFS_STATUS_OK = 0,
+       GUESTFS_STATUS_ERROR = 1,
+};
+typedef enum guestfs_message_status guestfs_message_status;
+
+struct guestfs_message_header {
+       u_int prog;
+       u_int vers;
+       guestfs_procedure proc;
+       guestfs_message_direction direction;
+       u_int serial;
+       guestfs_message_status status;
+};
+typedef struct guestfs_message_header guestfs_message_header;
+
+/* the xdr functions */
+
+#if defined(__STDC__) || defined(__cplusplus)
+extern  bool_t xdr_guestfs_mount_args (XDR *, guestfs_mount_args*);
+extern  bool_t xdr_guestfs_touch_args (XDR *, guestfs_touch_args*);
+extern  bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*);
+extern  bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*);
+extern  bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*);
+extern  bool_t xdr_guestfs_message_header (XDR *, guestfs_message_header*);
+
+#else /* K&R C */
+extern bool_t xdr_guestfs_mount_args ();
+extern bool_t xdr_guestfs_touch_args ();
+extern bool_t xdr_guestfs_procedure ();
+extern bool_t xdr_guestfs_message_direction ();
+extern bool_t xdr_guestfs_message_status ();
+extern bool_t xdr_guestfs_message_header ();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_GUESTFS_PROTOCOL_H_RPCGEN */
index 6569d27..1641b3d 100644 (file)
@@ -34,3 +34,33 @@ struct guestfs_touch_args {
   string path<>;
 };
 
+enum guestfs_procedure {
+  GUESTFS_PROC_MOUNT = 1,
+  GUESTFS_PROC_SYNC = 2,
+  GUESTFS_PROC_TOUCH = 3,
+  GUESTFS_PROC_dummy
+};
+
+const GUESTFS_MESSAGE_MAX = 4194304;
+
+const GUESTFS_PROGRAM = 0x2000F5F5;
+const GUESTFS_PROTOCOL_VERSION = 1;
+
+enum guestfs_message_direction {
+  GUESTFS_DIRECTION_CALL = 0,        /* client -> daemon */
+  GUESTFS_DIRECTION_REPLY = 1        /* daemon -> client */
+};
+
+enum guestfs_message_status {
+  GUESTFS_STATUS_OK = 0,
+  GUESTFS_STATUS_ERROR = 1
+};
+
+struct guestfs_message_header {
+  unsigned prog;                     /* GUESTFS_PROGRAM */
+  unsigned vers;                     /* GUESTFS_PROTOCOL_VERSION */
+  guestfs_procedure proc;            /* GUESTFS_PROC_x */
+  guestfs_message_direction direction;
+  unsigned serial;                   /* message serial number */
+  guestfs_message_status status;
+};