From 40ca9a57829f2e82362e391d7d998bf33c8bd671 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Fri, 3 Apr 2009 17:24:35 +0100 Subject: [PATCH] Daemon and library are mostly talking to each other now. --- .gitignore | 1 + daemon/Makefile.am | 12 +++- daemon/actions.h | 6 +- daemon/daemon.h | 41 ++++++++++++ daemon/guestfsd.c | 27 +++++++- daemon/proto.c | 59 ++++++++++++++++ daemon/stubs.c | 18 ++++- daemon/sync.c | 32 +++++++++ examples/Makefile.am | 8 ++- examples/hello.c | 33 +++++++++ guestfs.pod | 8 +-- src/Makefile.am | 26 +++++++- src/generator.ml | 133 +++++++++++++++++++++++++++++------- src/guestfs-actions.c | 45 ++++++++++--- src/guestfs.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++-- src/guestfs_protocol.c | 78 ++++++++++++++++++++++ src/guestfs_protocol.h | 85 +++++++++++++++++++++++ src/guestfs_protocol.x | 30 +++++++++ 18 files changed, 760 insertions(+), 60 deletions(-) create mode 100644 daemon/daemon.h create mode 100644 daemon/proto.c create mode 100644 daemon/sync.c create mode 100644 examples/hello.c create mode 100644 src/guestfs_protocol.c create mode 100644 src/guestfs_protocol.h diff --git a/.gitignore b/.gitignore index a9bd0d6..8771111 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ daemon/missing depcomp emptydisk examples/df +examples/hello guestfs.3 initramfs initramfs.timestamp diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 6a5a4ee..cf55250 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -18,6 +18,14 @@ 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 diff --git a/daemon/actions.h b/daemon/actions.h index c7d64df..61b583c 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -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 index 0000000..b8a9001 --- /dev/null +++ b/daemon/daemon.h @@ -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 +#include +#include + +/* 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 */ diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index c27d1b6..eaba7f0 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -27,7 +27,10 @@ #include #include -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 index 0000000..f045751 --- /dev/null +++ b/daemon/proto.c @@ -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 + +#include +#include +#include +#include +#include +#include +#include + +#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) +{ +} diff --git a/daemon/stubs.c b/daemon/stubs.c index 81c5b72..22fd575 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -22,7 +22,7 @@ #include #include #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 index 0000000..9ade840 --- /dev/null +++ b/daemon/sync.c @@ -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 + +#include +#include + +#include "actions.h" + +int +do_sync () +{ + sync (); + fprintf (stderr, "guestfsd: disk synched\n"); + return 0; +} diff --git a/examples/Makefile.am b/examples/Makefile.am index 66a32d7..9bac5f3 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -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 index 0000000..5f1ab0e --- /dev/null +++ b/examples/hello.c @@ -0,0 +1,33 @@ +/* Create a "/hello" file on /dev/sda1. */ + +#include +#include +#include +#include + +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; +} diff --git a/guestfs.pod b/guestfs.pod index bf75e42..61d51b7 100644 --- a/guestfs.pod +++ b/guestfs.pod @@ -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 set to C. =head2 NON-BLOCKING ACTIONS +XXX NOT IMPLEMENTED YET XXX + C 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 and C functions -corresponding to (very nearly) every C action in the -high-level API. +corresponding to every C action in the high-level API. =head2 guestfs_set_reply_callback diff --git a/src/Makefile.am b/src/Makefile.am index a0066db..68cabba 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,5 +17,27 @@ 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 diff --git a/src/generator.ml b/src/generator.ml index d08e269..8588cf3 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -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."); - ("touch", (Err, P1 (String "path")), + ("touch", (Err, P1 (String "path")), 3, "Update file timestamps or create a new file", "\ Touch acts like the L 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 \n"; pr "#include \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 diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index db701ef..12ae602 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -20,15 +20,17 @@ */ 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; } diff --git a/src/guestfs.c b/src/guestfs.c index bbcf7a6..309cd15 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -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 index 0000000..8a59085 --- /dev/null +++ b/src/guestfs_protocol.c @@ -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 index 0000000..562d72f --- /dev/null +++ b/src/guestfs_protocol.h @@ -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 + + +#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 */ diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index 6569d27..1641b3d 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -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; +}; -- 1.8.3.1