- implement 'll' command.
- outlines for 'ls' and 'cat' commands.
daemon.h \
file.c \
guestfsd.c \
+ ls.c \
mount.c \
proto.c \
stubs.c \
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+extern char *do_cat (const char *path);
+extern char *do_ll (const char *directory);
+extern char **do_ls (const char *directory);
extern int do_mount (const char *device, const char *mountpoint);
extern int do_sync ();
extern int do_touch (const char *path);
/* in guestfsd.c */
extern void xwrite (int sock, const void *buf, size_t len);
extern void xread (int sock, void *buf, size_t len);
+extern int count_strings (char **argv);
+extern void free_strings (char **argv);
extern int command (char **stdoutput, char **stderror, const char *name, ...);
/* in proto.c */
close (fd);
return 0;
}
+
+char *
+do_cat (const char *path)
+{
+ reply_with_error ("cat command is not yet implemented");
+ return NULL;
+}
#include "daemon.h"
-void xwrite (int sock, const void *buf, size_t len);
-
static void usage (void);
/* Also in guestfs.c */
fprintf (stderr, "guestfsd [-f] [-h host -p port]\n");
}
+int
+count_strings (char **argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ ;
+ return argc;
+}
+
+void
+free_strings (char **argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ free (argv[argc]);
+ free (argv);
+}
+
/* This is a more sane version of 'system(3)' for running external
* commands. It uses fork/execvp, so we don't need to worry about
* quoting of parameters, and it allows us to capture any error
}
/* Make sure the output buffers are \0-terminated. Also remove any
- * trailing \n characters.
+ * trailing \n characters from the error buffer (not from stdout).
*/
if (stdoutput) {
*stdoutput = realloc (*stdoutput, so_size+1);
if (*stdoutput == NULL) {
perror ("realloc");
*stdoutput = NULL;
- } else {
+ } else
(*stdoutput)[so_size] = '\0';
- so_size--;
- while (so_size >= 0 && (*stdoutput)[so_size] == '\n')
- (*stdoutput)[so_size--] = '\0';
- }
}
if (stderror) {
*stderror = realloc (*stderror, se_size+1);
--- /dev/null
+/* 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 <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "daemon.h"
+#include "actions.h"
+
+char **
+do_ls (const char *path)
+{
+ reply_with_error ("ls command is not yet implemented");
+ return NULL;
+}
+
+char *
+do_ll (const char *path)
+{
+ int r, len;
+ char *out, *err;
+ char *spath;
+
+ if (path[0] != '/') {
+ reply_with_error ("ll: path must start with a / character");
+ return NULL;
+ }
+
+ /* This exposes the /sysroot, because we can't chroot and run the ls
+ * command (since 'ls' won't necessarily exist in the chroot). This
+ * command is not meant for serious use anyway, just for quick
+ * interactive sessions. For the same reason, you can also "escape"
+ * the sysroot (eg. 'll /..').
+ */
+ len = strlen (path) + 9;
+ spath = malloc (len);
+ if (!spath) {
+ reply_with_perror ("malloc");
+ return NULL;
+ }
+ snprintf (spath, len, "/sysroot%s", path);
+
+ r = command (&out, &err, "ls", "-la", spath, NULL);
+ free (spath);
+ if (r == -1) {
+ reply_with_error ("%s", err);
+ free (out);
+ free (err);
+ return NULL;
+ }
+
+ free (err);
+ return out; /* caller frees */
+}
#include "../src/guestfs_protocol.h"
#include "actions.h"
+static void cat_stub (XDR *xdr_in)
+{
+ char *r;
+ struct guestfs_cat_args args;
+ const char *path;
+
+ memset (&args, 0, sizeof args);
+
+ if (!xdr_guestfs_cat_args (xdr_in, &args)) {
+ reply_with_error ("cat: daemon failed to decode procedure arguments");
+ return;
+ }
+ path = args.path;
+
+ r = do_cat (path);
+ if (r == NULL)
+ /* do_cat has already called reply_with_error, so just return */
+ return;
+
+ struct guestfs_cat_ret ret;
+ ret.content = r;
+ reply ((xdrproc_t) &xdr_guestfs_cat_ret, (char *) &ret);
+ free (r);
+}
+
+static void ll_stub (XDR *xdr_in)
+{
+ char *r;
+ struct guestfs_ll_args args;
+ const char *directory;
+
+ memset (&args, 0, sizeof args);
+
+ if (!xdr_guestfs_ll_args (xdr_in, &args)) {
+ reply_with_error ("ll: daemon failed to decode procedure arguments");
+ return;
+ }
+ directory = args.directory;
+
+ r = do_ll (directory);
+ if (r == NULL)
+ /* do_ll has already called reply_with_error, so just return */
+ return;
+
+ struct guestfs_ll_ret ret;
+ ret.listing = r;
+ reply ((xdrproc_t) &xdr_guestfs_ll_ret, (char *) &ret);
+ free (r);
+}
+
+static void ls_stub (XDR *xdr_in)
+{
+ char **r;
+ struct guestfs_ls_args args;
+ const char *directory;
+
+ memset (&args, 0, sizeof args);
+
+ if (!xdr_guestfs_ls_args (xdr_in, &args)) {
+ reply_with_error ("ls: daemon failed to decode procedure arguments");
+ return;
+ }
+ directory = args.directory;
+
+ r = do_ls (directory);
+ if (r == NULL)
+ /* do_ls has already called reply_with_error, so just return */
+ return;
+
+ struct guestfs_ls_ret ret;
+ ret.listing.listing_len = count_strings (r);
+ ret.listing.listing_val = r;
+ reply ((xdrproc_t) &xdr_guestfs_ls_ret, (char *) &ret);
+ free_strings (r);
+}
+
static void mount_stub (XDR *xdr_in)
{
int r;
void dispatch_incoming_message (XDR *xdr_in)
{
switch (proc_nr) {
+ case GUESTFS_PROC_CAT:
+ cat_stub (xdr_in);
+ break;
+ case GUESTFS_PROC_LL:
+ ll_stub (xdr_in);
+ break;
+ case GUESTFS_PROC_LS:
+ ls_stub (xdr_in);
+ break;
case GUESTFS_PROC_MOUNT:
mount_stub (xdr_in);
break;
{
printf (" %-16s %s\n", "Command", "Description");
list_builtin_commands ();
+ printf ("%-20s %s\n", "cat", "list the files in a directory (long format)");
+ printf ("%-20s %s\n", "ll", "list the files in a directory (long format)");
+ printf ("%-20s %s\n", "ls", "list the files in a directory");
printf ("%-20s %s\n", "mount", "mount a guest disk at a position in the filesystem");
printf ("%-20s %s\n", "sync", "sync disks, writes are flushed through to the disk image");
printf ("%-20s %s\n", "touch", "update file timestamps or create a new file");
void display_command (const char *cmd)
{
+ if (strcasecmp (cmd, "cat") == 0)
+ pod2text ("cat - list the files in a directory (long format)", " cat <path>\n\nReturn the contents of the file named C<path>.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP.");
+ else
+ if (strcasecmp (cmd, "ll") == 0)
+ pod2text ("ll - list the files in a directory (long format)", " ll <directory>\n\nList the files in C<directory> (relative to the root directory,\nthere is no cwd) in the format of 'ls -la'.\n\nThis command is mostly useful for interactive sessions. It\nis I<not> intended that you try to parse the output string.");
+ else
+ if (strcasecmp (cmd, "ls") == 0)
+ pod2text ("ls - list the files in a directory", " ls <directory>\n\nList the files in C<directory> (relative to the root directory,\nthere is no cwd). The '.' and '..' entries are not returned, but\nhidden files are shown.\n\nThis command is mostly useful for interactive sessions.");
+ else
if (strcasecmp (cmd, "mount") == 0)
pod2text ("mount - mount a guest disk at a position in the filesystem", " mount <device> <mountpoint>\n\nMount a guest disk at a position in the filesystem. Block devices\nare named C</dev/sda>, C</dev/sdb> and so on, as they were added to\nthe guest. If those block devices contain partitions, they will have\nthe usual names (eg. C</dev/sda1>). Also LVM C</dev/VG/LV>-style\nnames can be used.\n\nThe rules are the same as for L<mount(2)>: A filesystem must\nfirst be mounted on C</> before others can be mounted. Other\nfilesystems can only be mounted on directories which already\nexist.\n\nThe mounted filesystem is writable, if we have sufficient permissions\non the underlying device.\n\nThe filesystem options C<sync> and C<noatime> are set with this\ncall, in order to improve reliability.");
else
display_builtin_command (cmd);
}
+static int run_cat (const char *cmd, int argc, char *argv[])
+{
+ char *r;
+ const char *path;
+ if (argc != 1) {
+ fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+ fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+ return -1;
+ }
+ path = argv[0];
+ r = guestfs_cat (g, path);
+ if (r == NULL) return -1;
+ printf ("%s", r);
+ free (r);
+ return 0;
+}
+
+static int run_ll (const char *cmd, int argc, char *argv[])
+{
+ char *r;
+ const char *directory;
+ if (argc != 1) {
+ fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+ fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+ return -1;
+ }
+ directory = argv[0];
+ r = guestfs_ll (g, directory);
+ if (r == NULL) return -1;
+ printf ("%s", r);
+ free (r);
+ return 0;
+}
+
+static int run_ls (const char *cmd, int argc, char *argv[])
+{
+ char **r;
+ const char *directory;
+ if (argc != 1) {
+ fprintf (stderr, "%s should have 1 parameter(s)\n", cmd);
+ fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd);
+ return -1;
+ }
+ directory = argv[0];
+ r = guestfs_ls (g, directory);
+ if (r == NULL) return -1;
+ print_strings (r);
+ free_strings (r);
+ return 0;
+}
+
static int run_mount (const char *cmd, int argc, char *argv[])
{
int r;
int run_action (const char *cmd, int argc, char *argv[])
{
+ if (strcasecmp (cmd, "cat") == 0)
+ return run_cat (cmd, argc, argv);
+ else
+ if (strcasecmp (cmd, "ll") == 0)
+ return run_ll (cmd, argc, argv);
+ else
+ if (strcasecmp (cmd, "ls") == 0)
+ return run_ls (cmd, argc, argv);
+ else
if (strcasecmp (cmd, "mount") == 0)
return run_mount (cmd, argc, argv);
else
return 0;
}
+
+void
+free_strings (char **argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ free (argv[argc]);
+ free (argv);
+}
+
+void
+print_strings (char **argv)
+{
+ int argc;
+
+ for (argc = 0; argv[argc] != NULL; ++argc)
+ printf ("%s\n", argv[argc]);
+}
extern void pod2text (const char *heading, const char *body);
extern void list_builtin_commands (void);
extern void display_builtin_command (const char *cmd);
+extern void free_strings (char **argv);
+extern void print_strings (char **argv);
/* in cmds.c (auto-generated) */
extern void list_commands (void);
+=head2 guestfs_cat
+
+ char *guestfs_cat (guestfs_h *handle,
+ const char *path);
+
+Return the contents of the file named C<path>.
+
+This function returns a string or NULL on error. The caller
+must free the returned string after use.
+
+Because of the message protocol, there is a transfer limit
+of somewhere between 2MB and 4MB. To transfer large files you should use
+FTP.
+
+=head2 guestfs_ll
+
+ char *guestfs_ll (guestfs_h *handle,
+ const char *directory);
+
+List the files in C<directory> (relative to the root directory,
+there is no cwd) in the format of 'ls -la'.
+
+This command is mostly useful for interactive sessions. It
+is I<not> intended that you try to parse the output string.
+
+This function returns a string or NULL on error. The caller
+must free the returned string after use.
+
+=head2 guestfs_ls
+
+ char **guestfs_ls (guestfs_h *handle,
+ const char *directory);
+
+List the files in C<directory> (relative to the root directory,
+there is no cwd). The '.' and '..' entries are not returned, but
+hidden files are shown.
+
+This command is mostly useful for interactive sessions.
+
+This function returns a NULL-terminated array of strings
+(like L<environ(3)>), or NULL if there was an error.
+
+The caller must free the strings I<and> the array after use.
+
=head2 guestfs_mount
int guestfs_mount (guestfs_h *handle,
The filesystem options C<sync> and C<noatime> are set with this
call, in order to improve reliability.
-This function return 0 on success or -1 on error.
+This function returns 0 on success or -1 on error.
=head2 guestfs_sync
You should always call this if you have modified a disk image, before
calling C<guestfs_close>.
-This function return 0 on success or -1 on error.
+This function returns 0 on success or -1 on error.
=head2 guestfs_touch
update the timestamps on a file, or, if the file does not exist,
to create a new zero-length file.
-This function return 0 on success or -1 on error.
+This function returns 0 on success or -1 on error.
* indication, ie. 0 or -1.
*)
| Err
+ (* "RString" and "RStringList" require special treatment because
+ * the caller must free them.
+ *)
+ | RString of string
+ | RStringList of string
and args =
(* 0 arguments, 1 argument, etc. The guestfs_h param is implicit. *)
| P0
and argt =
| String of string (* const char *name, cannot be NULL *)
+type flags = ProtocolLimitWarning
+
let functions = [
- ("mount", (Err, P2 (String "device", String "mountpoint")), 1,
+ ("cat", (RString "content", P1 (String "path")), 4, [ProtocolLimitWarning],
+ "list the files in a directory (long format)",
+ "\
+Return the contents of the file named C<path>.");
+
+ ("ll", (RString "listing", P1 (String "directory")), 5, [],
+ "list the files in a directory (long format)",
+ "\
+List the files in C<directory> (relative to the root directory,
+there is no cwd) in the format of 'ls -la'.
+
+This command is mostly useful for interactive sessions. It
+is I<not> intended that you try to parse the output string.");
+
+ ("ls", (RStringList "listing", P1 (String "directory")), 6, [],
+ "list the files in a directory",
+ "\
+List the files in C<directory> (relative to the root directory,
+there is no cwd). The '.' and '..' entries are not returned, but
+hidden files are shown.
+
+This command is mostly useful for interactive sessions.");
+
+ ("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
The filesystem options C<sync> and C<noatime> are set with this
call, in order to improve reliability.");
- ("sync", (Err, P0), 2,
+ ("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
You should always call this if you have modified a disk image, before
calling C<guestfs_close>.");
- ("touch", (Err, P1 (String "path")), 3,
+ ("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
(* Generate the pod documentation for the C API. *)
and generate_pod () =
List.iter (
- fun (shortname, style, _, _, longdesc) ->
+ fun (shortname, style, _, flags, _, longdesc) ->
let name = "guestfs_" ^ shortname in
pr "=head2 %s\n\n" name;
pr " ";
generate_prototype ~extern:false ~handle:"handle" name style;
pr "\n\n";
pr "%s\n\n" longdesc;
- (match style with
- | (Err, _) ->
- pr "This function return 0 on success or -1 on error.\n\n"
+ (match fst style with
+ | Err ->
+ pr "This function returns 0 on success or -1 on error.\n\n"
+ | RString _ ->
+ pr "This function returns a string or NULL on error. The caller
+must free the returned string after use.\n\n"
+ | RStringList _ ->
+ pr "This function returns a NULL-terminated array of strings
+(like L<environ(3)>), or NULL if there was an error.
+
+The caller must free the strings I<and> the array after use.\n\n"
);
+ if List.mem ProtocolLimitWarning flags then
+ pr "Because of the message protocol, there is a transfer limit
+of somewhere between 2MB and 4MB. To transfer large files you should use
+FTP.\n\n";
) functions
(* Generate the protocol (XDR) file. *)
and generate_xdr () =
generate_header CStyle LGPLv2;
+ (* This has to be defined to get around a limitation in Sun's rpcgen. *)
+ pr "typedef string str<>;\n";
+ pr "\n";
+
List.iter (
- fun (shortname, style, _, _, _) ->
+ fun (shortname, style, _, _, _, _) ->
let name = "guestfs_" ^ shortname in
pr "/* %s */\n\n" name;
- (match style with
- | (_, P0) -> ()
- | (_, args) ->
+ (match snd style with
+ | P0 -> ()
+ | args ->
pr "struct %s_args {\n" name;
iter_args (
function
) args;
pr "};\n\n"
);
- (match style with
- | (Err, _) -> ()
- (* | ... -> pr "struct %s_ret ...\n" name; *)
+ (match fst style with
+ | Err -> ()
+ | RString n ->
+ pr "struct %s_ret {\n" name;
+ pr " string %s<>;\n" n;
+ pr "};\n\n"
+ | RStringList n ->
+ pr "struct %s_ret {\n" name;
+ pr " str %s<>;\n" n;
+ pr "};\n\n"
);
) functions;
(* Table of procedure numbers. *)
pr "enum guestfs_procedure {\n";
List.iter (
- fun (shortname, _, proc_nr, _, _) ->
+ 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" *)
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 ~handle:"handle"
name style
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 " int cb_done; /* flag to indicate callback was called */\n";
pr " struct guestfs_message_header hdr;\n";
pr " struct guestfs_message_error err;\n";
- (match style with
- | (Err, _) -> ()
- (* | _ -> pr " struct %s_ret ret;\n" name; *)
+ (match fst style with
+ | Err -> ()
+ | RString _ | RStringList _ -> pr " struct %s_ret ret;\n" name;
);
pr "};\n\n";
pr " goto done;\n";
pr " }\n";
- (match style with
- | (Err, _) -> ()
- (* | _ -> pr " if (!xdr_%s_ret (&xdr, &rv->ret)) ..." *)
+ (match fst style with
+ | Err -> ()
+ | RString _ | RStringList _ ->
+ pr " if (!xdr_%s_ret (xdr, &rv->ret)) {\n" name;
+ pr " error (g, \"%s: failed to parse reply\");\n" name;
+ pr " return;\n";
+ pr " }\n";
);
pr " done:\n";
~handle:"g" name style;
let error_code =
- match style with
- | (Err, _) -> "-1" in
+ match fst style with
+ | Err -> "-1"
+ | RString _ | RStringList _ -> "NULL" in
pr "{\n";
- (match style with
- | (_, P0) -> ()
+ (match snd style with
+ | P0 -> ()
| _ -> pr " struct %s_args args;\n" name
);
pr " memset (&rv, 0, sizeof rv);\n";
pr "\n";
- (match style with
- | (_, P0) ->
+ (match snd style with
+ | P0 ->
pr " serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n"
(String.uppercase shortname)
- | (_, args) ->
+ | args ->
iter_args (
function
| String name -> pr " args.%s = (char *) %s;\n" name name
pr " }\n";
pr "\n";
- (match style with
- | (Err, _) -> pr " return 0;\n"
+ (match fst style with
+ | Err -> pr " return 0;\n"
+ | RString n ->
+ pr " return rv.ret.%s; /* caller will free */\n" n
+ | RStringList n ->
+ pr " /* caller will free this, but we need to add a NULL entry */\n";
+ pr " rv.ret.%s.%s_val = safe_realloc (g, rv.ret.%s.%s_val, rv.ret.%s.%s_len + 1);\n" n n n n n n;
+ pr " rv.ret.%s.%s_val[rv.ret.%s.%s_len] = NULL;\n" n n n n;
+ pr " return rv.ret.%s.%s_val;\n" n n
);
pr "}\n\n"
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
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";
let error_code =
- match style with
- | (Err, _) -> pr " int r;\n"; "-1" in
- (match style with
- | (_, P0) -> ()
- | (_, args) ->
+ match fst style with
+ | Err -> pr " int r;\n"; "-1"
+ | RString _ -> pr " char *r;\n"; "NULL"
+ | RStringList _ -> pr " char **r;\n"; "NULL" in
+ (match snd style with
+ | P0 -> ()
+ | args ->
pr " struct guestfs_%s_args args;\n" name;
iter_args (
function
);
pr "\n";
- (match style with
- | (_, P0) -> ()
- | (_, args) ->
+ (match snd style with
+ | P0 -> ()
+ | args ->
pr " memset (&args, 0, sizeof args);\n";
pr "\n";
pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
pr " return;\n";
pr "\n";
- (match style with
- | (Err, _) -> pr " reply (NULL, NULL);\n"
+ (match fst style with
+ | Err -> pr " reply (NULL, NULL);\n"
+ | RString n ->
+ pr " struct guestfs_%s_ret ret;\n" name;
+ pr " ret.%s = r;\n" n;
+ pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
+ pr " free (r);\n"
+ | RStringList n ->
+ pr " struct guestfs_%s_ret ret;\n" name;
+ pr " ret.%s.%s_len = count_strings (r);\n" n n;
+ pr " ret.%s.%s_val = r;\n" n n;
+ pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
+ pr " free_strings (r);\n"
);
pr "}\n\n";
pr " switch (proc_nr) {\n";
List.iter (
- fun (name, style, _, _, _) ->
+ fun (name, style, _, _, _, _) ->
pr " case GUESTFS_PROC_%s:\n" (String.uppercase name);
pr " %s_stub (xdr_in);\n" name;
pr " break;\n"
pr " printf (\" %%-16s %%s\\n\", \"Command\", \"Description\");\n";
pr " list_builtin_commands ();\n";
List.iter (
- fun (name, _, _, shortdesc, _) ->
+ fun (name, _, _, _, shortdesc, _) ->
pr " printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
name shortdesc
) functions;
pr "void display_command (const char *cmd)\n";
pr "{\n";
List.iter (
- fun (name, style, _, shortdesc, longdesc) ->
+ fun (name, style, _, flags, shortdesc, longdesc) ->
let synopsis =
- match style with
- | (Err, P0) -> name
- | (Err, args) ->
+ match snd style with
+ | P0 -> name
+ | args ->
sprintf "%s <%s>"
name (
String.concat "> <" (
)
) in
+ let warnings =
+ if List.mem ProtocolLimitWarning flags then
+ "\n\nBecause of the message protocol, there is a transfer limit
+of somewhere between 2MB and 4MB. To transfer large files you should use
+FTP."
+ else "" in
+
pr " if (strcasecmp (cmd, \"%s\") == 0)\n" name;
pr " pod2text (\"%s - %s\", %S);\n"
name shortdesc
- (" " ^ synopsis ^ "\n\n" ^ longdesc);
+ (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings);
pr " else\n"
) functions;
pr " display_builtin_command (cmd);\n";
(* run_<action> actions *)
List.iter (
- fun (name, style, _, _, _) ->
+ fun (name, style, _, _, _, _) ->
pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
pr "{\n";
- (match style with
- | (Err, _) -> pr " int r;\n"
+ (match fst style with
+ | Err -> pr " int r;\n"
+ | RString _ -> pr " char *r;\n"
+ | RStringList _ -> pr " char **r;\n"
);
iter_args (
function
generate_call_args ~handle:"g" style;
pr ";\n";
- (* Check return value for errors. *)
- (match style with
- | (Err, _) -> pr " return r;\n"
+ (* Check return value for errors and display command results. *)
+ (match fst style with
+ | Err -> pr " return r;\n"
+ | RString _ ->
+ pr " if (r == NULL) return -1;\n";
+ pr " printf (\"%%s\", r);\n";
+ pr " free (r);\n";
+ pr " return 0;\n"
+ | RStringList _ ->
+ pr " if (r == NULL) return -1;\n";
+ pr " print_strings (r);\n";
+ pr " free_strings (r);\n";
+ pr " return 0;\n"
);
pr "}\n";
pr "\n"
pr "int run_action (const char *cmd, int argc, char *argv[])\n";
pr "{\n";
List.iter (
- fun (name, _, _, _, _) ->
+ fun (name, _, _, _, _, _) ->
pr " if (strcasecmp (cmd, \"%s\") == 0)\n" name;
pr " return run_%s (cmd, argc, argv);\n" name;
pr " else\n";
?handle name style =
if extern then pr "extern ";
if static then pr "static ";
- (match style with
- | (Err, _) -> pr "int "
+ (match fst style with
+ | Err -> pr "int "
+ | RString _ -> pr "char *"
+ | RStringList _ -> pr "char **"
);
pr "%s (" name;
let comma = ref false in
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+struct cat_rv {
+ int cb_done; /* flag to indicate callback was called */
+ struct guestfs_message_header hdr;
+ struct guestfs_message_error err;
+ struct guestfs_cat_ret ret;
+};
+
+static void cat_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+ struct cat_rv *rv = (struct cat_rv *) data;
+
+ if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+ error (g, "guestfs_cat: failed to parse reply header");
+ return;
+ }
+ if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+ if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+ error (g, "guestfs_cat: failed to parse reply error");
+ return;
+ }
+ goto done;
+ }
+ if (!xdr_guestfs_cat_ret (xdr, &rv->ret)) {
+ error (g, "guestfs_cat: failed to parse reply");
+ return;
+ }
+ done:
+ rv->cb_done = 1;
+ main_loop.main_loop_quit (g);
+}
+
+char *guestfs_cat (guestfs_h *g,
+ const char *path)
+{
+ struct guestfs_cat_args args;
+ struct cat_rv rv;
+ int serial;
+
+ if (g->state != READY) {
+ error (g, "guestfs_cat called from the wrong state, %d != READY",
+ g->state);
+ return NULL;
+ }
+
+ memset (&rv, 0, sizeof rv);
+
+ args.path = (char *) path;
+ serial = dispatch (g, GUESTFS_PROC_CAT,
+ (xdrproc_t) xdr_guestfs_cat_args, (char *) &args);
+ if (serial == -1)
+ return NULL;
+
+ rv.cb_done = 0;
+ g->reply_cb_internal = cat_cb;
+ g->reply_cb_internal_data = &rv;
+ main_loop.main_loop_run (g);
+ g->reply_cb_internal = NULL;
+ g->reply_cb_internal_data = NULL;
+ if (!rv.cb_done) {
+ error (g, "guestfs_cat failed, see earlier error messages");
+ return NULL;
+ }
+
+ if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_CAT, serial) == -1)
+ return NULL;
+
+ if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+ error (g, "%s", rv.err.error);
+ return NULL;
+ }
+
+ return rv.ret.content; /* caller will free */
+}
+
+struct ll_rv {
+ int cb_done; /* flag to indicate callback was called */
+ struct guestfs_message_header hdr;
+ struct guestfs_message_error err;
+ struct guestfs_ll_ret ret;
+};
+
+static void ll_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+ struct ll_rv *rv = (struct ll_rv *) data;
+
+ if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+ error (g, "guestfs_ll: failed to parse reply header");
+ return;
+ }
+ if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+ if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+ error (g, "guestfs_ll: failed to parse reply error");
+ return;
+ }
+ goto done;
+ }
+ if (!xdr_guestfs_ll_ret (xdr, &rv->ret)) {
+ error (g, "guestfs_ll: failed to parse reply");
+ return;
+ }
+ done:
+ rv->cb_done = 1;
+ main_loop.main_loop_quit (g);
+}
+
+char *guestfs_ll (guestfs_h *g,
+ const char *directory)
+{
+ struct guestfs_ll_args args;
+ struct ll_rv rv;
+ int serial;
+
+ if (g->state != READY) {
+ error (g, "guestfs_ll called from the wrong state, %d != READY",
+ g->state);
+ return NULL;
+ }
+
+ memset (&rv, 0, sizeof rv);
+
+ args.directory = (char *) directory;
+ serial = dispatch (g, GUESTFS_PROC_LL,
+ (xdrproc_t) xdr_guestfs_ll_args, (char *) &args);
+ if (serial == -1)
+ return NULL;
+
+ rv.cb_done = 0;
+ g->reply_cb_internal = ll_cb;
+ g->reply_cb_internal_data = &rv;
+ main_loop.main_loop_run (g);
+ g->reply_cb_internal = NULL;
+ g->reply_cb_internal_data = NULL;
+ if (!rv.cb_done) {
+ error (g, "guestfs_ll failed, see earlier error messages");
+ return NULL;
+ }
+
+ if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LL, serial) == -1)
+ return NULL;
+
+ if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+ error (g, "%s", rv.err.error);
+ return NULL;
+ }
+
+ return rv.ret.listing; /* caller will free */
+}
+
+struct ls_rv {
+ int cb_done; /* flag to indicate callback was called */
+ struct guestfs_message_header hdr;
+ struct guestfs_message_error err;
+ struct guestfs_ls_ret ret;
+};
+
+static void ls_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+ struct ls_rv *rv = (struct ls_rv *) data;
+
+ if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+ error (g, "guestfs_ls: failed to parse reply header");
+ return;
+ }
+ if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+ if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+ error (g, "guestfs_ls: failed to parse reply error");
+ return;
+ }
+ goto done;
+ }
+ if (!xdr_guestfs_ls_ret (xdr, &rv->ret)) {
+ error (g, "guestfs_ls: failed to parse reply");
+ return;
+ }
+ done:
+ rv->cb_done = 1;
+ main_loop.main_loop_quit (g);
+}
+
+char **guestfs_ls (guestfs_h *g,
+ const char *directory)
+{
+ struct guestfs_ls_args args;
+ struct ls_rv rv;
+ int serial;
+
+ if (g->state != READY) {
+ error (g, "guestfs_ls called from the wrong state, %d != READY",
+ g->state);
+ return NULL;
+ }
+
+ memset (&rv, 0, sizeof rv);
+
+ args.directory = (char *) directory;
+ serial = dispatch (g, GUESTFS_PROC_LS,
+ (xdrproc_t) xdr_guestfs_ls_args, (char *) &args);
+ if (serial == -1)
+ return NULL;
+
+ rv.cb_done = 0;
+ g->reply_cb_internal = ls_cb;
+ g->reply_cb_internal_data = &rv;
+ main_loop.main_loop_run (g);
+ g->reply_cb_internal = NULL;
+ g->reply_cb_internal_data = NULL;
+ if (!rv.cb_done) {
+ error (g, "guestfs_ls failed, see earlier error messages");
+ return NULL;
+ }
+
+ if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_LS, serial) == -1)
+ return NULL;
+
+ if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+ error (g, "%s", rv.err.error);
+ return NULL;
+ }
+
+ /* caller will free this, but we need to add a NULL entry */
+ rv.ret.listing.listing_val = safe_realloc (g, rv.ret.listing.listing_val, rv.ret.listing.listing_len + 1);
+ rv.ret.listing.listing_val[rv.ret.listing.listing_len] = NULL;
+ return rv.ret.listing.listing_val;
+}
+
struct mount_rv {
int cb_done; /* flag to indicate callback was called */
struct guestfs_message_header hdr;
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+extern char *guestfs_cat (guestfs_h *handle, const char *path);
+extern char *guestfs_ll (guestfs_h *handle, const char *directory);
+extern char **guestfs_ls (guestfs_h *handle, const char *directory);
extern int guestfs_mount (guestfs_h *handle, const char *device, const char *mountpoint);
extern int guestfs_sync (guestfs_h *handle);
extern int guestfs_touch (guestfs_h *handle, const char *path);
#include "guestfs_protocol.h"
bool_t
+xdr_str (XDR *xdrs, str *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_string (xdrs, objp, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_guestfs_cat_args (XDR *xdrs, guestfs_cat_args *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_string (xdrs, &objp->path, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_guestfs_cat_ret (XDR *xdrs, guestfs_cat_ret *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_string (xdrs, &objp->content, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_guestfs_ll_args (XDR *xdrs, guestfs_ll_args *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_string (xdrs, &objp->directory, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_guestfs_ll_ret (XDR *xdrs, guestfs_ll_ret *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_string (xdrs, &objp->listing, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_guestfs_ls_args (XDR *xdrs, guestfs_ls_args *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_string (xdrs, &objp->directory, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_guestfs_ls_ret (XDR *xdrs, guestfs_ls_ret *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_array (xdrs, (char **)&objp->listing.listing_val, (u_int *) &objp->listing.listing_len, ~0,
+ sizeof (str), (xdrproc_t) xdr_str))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
xdr_guestfs_mount_args (XDR *xdrs, guestfs_mount_args *objp)
{
register int32_t *buf;
#endif
+typedef char *str;
+
+struct guestfs_cat_args {
+ char *path;
+};
+typedef struct guestfs_cat_args guestfs_cat_args;
+
+struct guestfs_cat_ret {
+ char *content;
+};
+typedef struct guestfs_cat_ret guestfs_cat_ret;
+
+struct guestfs_ll_args {
+ char *directory;
+};
+typedef struct guestfs_ll_args guestfs_ll_args;
+
+struct guestfs_ll_ret {
+ char *listing;
+};
+typedef struct guestfs_ll_ret guestfs_ll_ret;
+
+struct guestfs_ls_args {
+ char *directory;
+};
+typedef struct guestfs_ls_args guestfs_ls_args;
+
+struct guestfs_ls_ret {
+ struct {
+ u_int listing_len;
+ str *listing_val;
+ } listing;
+};
+typedef struct guestfs_ls_ret guestfs_ls_ret;
+
struct guestfs_mount_args {
char *device;
char *mountpoint;
typedef struct guestfs_touch_args guestfs_touch_args;
enum guestfs_procedure {
+ GUESTFS_PROC_CAT = 4,
+ GUESTFS_PROC_LL = 5,
+ GUESTFS_PROC_LS = 6,
GUESTFS_PROC_MOUNT = 1,
GUESTFS_PROC_SYNC = 2,
GUESTFS_PROC_TOUCH = 3,
/* the xdr functions */
#if defined(__STDC__) || defined(__cplusplus)
+extern bool_t xdr_str (XDR *, str*);
+extern bool_t xdr_guestfs_cat_args (XDR *, guestfs_cat_args*);
+extern bool_t xdr_guestfs_cat_ret (XDR *, guestfs_cat_ret*);
+extern bool_t xdr_guestfs_ll_args (XDR *, guestfs_ll_args*);
+extern bool_t xdr_guestfs_ll_ret (XDR *, guestfs_ll_ret*);
+extern bool_t xdr_guestfs_ls_args (XDR *, guestfs_ls_args*);
+extern bool_t xdr_guestfs_ls_ret (XDR *, guestfs_ls_ret*);
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_header (XDR *, guestfs_message_header*);
#else /* K&R C */
+extern bool_t xdr_str ();
+extern bool_t xdr_guestfs_cat_args ();
+extern bool_t xdr_guestfs_cat_ret ();
+extern bool_t xdr_guestfs_ll_args ();
+extern bool_t xdr_guestfs_ll_ret ();
+extern bool_t xdr_guestfs_ls_args ();
+extern bool_t xdr_guestfs_ls_ret ();
extern bool_t xdr_guestfs_mount_args ();
extern bool_t xdr_guestfs_touch_args ();
extern bool_t xdr_guestfs_procedure ();
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+typedef string str<>;
+
+/* guestfs_cat */
+
+struct guestfs_cat_args {
+ string path<>;
+};
+
+struct guestfs_cat_ret {
+ string content<>;
+};
+
+/* guestfs_ll */
+
+struct guestfs_ll_args {
+ string directory<>;
+};
+
+struct guestfs_ll_ret {
+ string listing<>;
+};
+
+/* guestfs_ls */
+
+struct guestfs_ls_args {
+ string directory<>;
+};
+
+struct guestfs_ls_ret {
+ str listing<>;
+};
+
/* guestfs_mount */
struct guestfs_mount_args {
};
enum guestfs_procedure {
+ GUESTFS_PROC_CAT = 4,
+ GUESTFS_PROC_LL = 5,
+ GUESTFS_PROC_LS = 6,
GUESTFS_PROC_MOUNT = 1,
GUESTFS_PROC_SYNC = 2,
GUESTFS_PROC_TOUCH = 3,