summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Jones <rjones@redhat.com>2009-04-14 13:51:12 +0100
committerRichard Jones <rjones@redhat.com>2009-04-14 13:51:12 +0100
commit5365ebd501850ea10d9a5b28fc6480ea34dbe16d (patch)
tree3c3bedf7581ea8485db6f039f2633ee07361b031
parent161018ed1e90c796e6e099859979da02d5f3e410 (diff)
downloadlibguestfs-5365ebd501850ea10d9a5b28fc6480ea34dbe16d.tar.gz
libguestfs-5365ebd501850ea10d9a5b28fc6480ea34dbe16d.tar.xz
libguestfs-5365ebd501850ea10d9a5b28fc6480ea34dbe16d.zip
Add 'command' and 'command-lines'. Fix args freeing in Perl bindings.
-rw-r--r--daemon/Makefile.am1
-rw-r--r--daemon/actions.h2
-rw-r--r--daemon/command.c101
-rw-r--r--daemon/stubs.c65
-rw-r--r--fish/cmds.c48
-rw-r--r--guestfish-actions.pod32
-rw-r--r--guestfs-actions.pod41
-rw-r--r--ocaml/guestfs.ml2
-rw-r--r--ocaml/guestfs.mli6
-rw-r--r--ocaml/guestfs_c_actions.c52
-rw-r--r--perl/Guestfs.xs263
-rw-r--r--perl/lib/Sys/Guestfs.pm28
-rw-r--r--python/guestfs-py.c60
-rw-r--r--python/guestfs.py6
-rwxr-xr-xsrc/generator.ml72
-rw-r--r--src/guestfs-actions.c154
-rw-r--r--src/guestfs-actions.h2
-rw-r--r--src/guestfs_protocol.c43
-rw-r--r--src/guestfs_protocol.h41
-rw-r--r--src/guestfs_protocol.x18
20 files changed, 938 insertions, 99 deletions
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 400f1641..1c52f7ae 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -21,6 +21,7 @@ noinst_PROGRAMS = guestfsd
guestfsd_SOURCES = \
actions.h \
augeas.c \
+ command.c \
daemon.h \
devsparts.c \
dir.c \
diff --git a/daemon/actions.h b/daemon/actions.h
index d53729bf..90aeb8d0 100644
--- a/daemon/actions.h
+++ b/daemon/actions.h
@@ -70,3 +70,5 @@ extern char **do_mounts (void);
extern int do_umount_all (void);
extern int do_lvm_remove_all (void);
extern char *do_file (const char *path);
+extern char *do_command (char * const* const arguments);
+extern char **do_command_lines (char * const* const arguments);
diff --git a/daemon/command.c b/daemon/command.c
new file mode 100644
index 00000000..589ca769
--- /dev/null
+++ b/daemon/command.c
@@ -0,0 +1,101 @@
+/* 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 "../src/guestfs_protocol.h"
+#include "daemon.h"
+#include "actions.h"
+
+char *
+do_command (char * const * const argv)
+{
+ char *out, *err;
+ int r;
+
+ /* We need a root filesystem mounted to do this. */
+ NEED_ROOT (NULL);
+
+ /* Conveniently, argv is already a NULL-terminated argv-style array
+ * of parameters, so we can pass it straight in to our internal
+ * commandv. We just have to check the list is non-empty.
+ */
+ if (argv[0] == NULL) {
+ reply_with_error ("command: passed an empty list");
+ return NULL;
+ }
+
+ CHROOT_IN;
+ r = commandv (&out, &err, argv);
+ CHROOT_OUT;
+
+ if (r == -1) {
+ reply_with_error ("%s", err);
+ free (out);
+ free (err);
+ return NULL;
+ }
+
+ free (err);
+
+ return out; /* Caller frees. */
+}
+
+char **
+do_command_lines (char * const * const argv)
+{
+ char *out;
+ char **lines = NULL;
+ int size = 0, alloc = 0;
+ char *p, *pend;
+
+ out = do_command (argv);
+ if (out == NULL)
+ return NULL;
+
+ /* Now convert the output to a list of lines. */
+ p = out;
+ while (p) {
+ pend = strchr (p, '\n');
+ if (pend) {
+ *pend = '\0';
+ pend++;
+ }
+
+ /* Final \n? Don't return an empty final element. */
+ if (pend && *pend == '\0') break;
+
+ if (add_string (&lines, &size, &alloc, p) == -1) {
+ free (out);
+ return NULL;
+ }
+
+ p = pend;
+ }
+
+ free (out);
+
+ if (add_string (&lines, &size, &alloc, NULL) == -1)
+ return NULL;
+
+ return lines;
+}
diff --git a/daemon/stubs.c b/daemon/stubs.c
index af0f14c3..9a0dc912 100644
--- a/daemon/stubs.c
+++ b/daemon/stubs.c
@@ -1164,6 +1164,65 @@ done:
xdr_free ((xdrproc_t) xdr_guestfs_file_args, (char *) &args);
}
+static void command_stub (XDR *xdr_in)
+{
+ char *r;
+ struct guestfs_command_args args;
+ char **arguments;
+
+ memset (&args, 0, sizeof args);
+
+ if (!xdr_guestfs_command_args (xdr_in, &args)) {
+ reply_with_error ("%s: daemon failed to decode procedure arguments", "command");
+ return;
+ }
+ args.arguments.arguments_val = realloc (args.arguments.arguments_val, sizeof (char *) * (args.arguments.arguments_len+1));
+ args.arguments.arguments_val[args.arguments.arguments_len] = NULL;
+ arguments = args.arguments.arguments_val;
+
+ r = do_command (arguments);
+ if (r == NULL)
+ /* do_command has already called reply_with_error */
+ goto done;
+
+ struct guestfs_command_ret ret;
+ ret.output = r;
+ reply ((xdrproc_t) &xdr_guestfs_command_ret, (char *) &ret);
+ free (r);
+done:
+ xdr_free ((xdrproc_t) xdr_guestfs_command_args, (char *) &args);
+}
+
+static void command_lines_stub (XDR *xdr_in)
+{
+ char **r;
+ struct guestfs_command_lines_args args;
+ char **arguments;
+
+ memset (&args, 0, sizeof args);
+
+ if (!xdr_guestfs_command_lines_args (xdr_in, &args)) {
+ reply_with_error ("%s: daemon failed to decode procedure arguments", "command_lines");
+ return;
+ }
+ args.arguments.arguments_val = realloc (args.arguments.arguments_val, sizeof (char *) * (args.arguments.arguments_len+1));
+ args.arguments.arguments_val[args.arguments.arguments_len] = NULL;
+ arguments = args.arguments.arguments_val;
+
+ r = do_command_lines (arguments);
+ if (r == NULL)
+ /* do_command_lines has already called reply_with_error */
+ goto done;
+
+ struct guestfs_command_lines_ret ret;
+ ret.lines.lines_len = count_strings (r);
+ ret.lines.lines_val = r;
+ reply ((xdrproc_t) &xdr_guestfs_command_lines_ret, (char *) &ret);
+ free_strings (r);
+done:
+ xdr_free ((xdrproc_t) xdr_guestfs_command_lines_args, (char *) &args);
+}
+
void dispatch_incoming_message (XDR *xdr_in)
{
switch (proc_nr) {
@@ -1314,6 +1373,12 @@ void dispatch_incoming_message (XDR *xdr_in)
case GUESTFS_PROC_FILE:
file_stub (xdr_in);
break;
+ case GUESTFS_PROC_COMMAND:
+ command_stub (xdr_in);
+ break;
+ case GUESTFS_PROC_COMMAND_LINES:
+ command_lines_stub (xdr_in);
+ break;
default:
reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr);
}
diff --git a/fish/cmds.c b/fish/cmds.c
index 1fb8b9df..2e536a50 100644
--- a/fish/cmds.c
+++ b/fish/cmds.c
@@ -49,6 +49,8 @@ void list_commands (void)
printf ("%-20s %s\n", "cat", "list the contents of a file");
printf ("%-20s %s\n", "chmod", "change file mode");
printf ("%-20s %s\n", "chown", "change file owner and group");
+ printf ("%-20s %s\n", "command", "run a command from the guest filesystem");
+ printf ("%-20s %s\n", "command-lines", "run a command, returning lines");
printf ("%-20s %s\n", "config", "add qemu parameters");
printf ("%-20s %s\n", "exists", "test if file or directory exists");
printf ("%-20s %s\n", "file", "determine file type");
@@ -276,6 +278,12 @@ void display_command (const char *cmd)
if (strcasecmp (cmd, "file") == 0)
pod2text ("file - determine file type", " file <path>\n\nThis call uses the standard L<file(1)> command to determine\nthe type or contents of the file. This also works on devices,\nfor example to find out whether a partition contains a filesystem.\n\nThe exact command which runs is C<file -bsL path>. Note in\nparticular that the filename is not prepended to the output\n(the C<-b> option).");
else
+ if (strcasecmp (cmd, "command") == 0)
+ pod2text ("command - run a command from the guest filesystem", " command <arguments>\n\nThis calls runs a command from the guest filesystem. The\nfilesystem must be mounted, and must contain a compatible\noperating system (ie. something Linux, with the same\nor compatible processor architecture).\n\nThe single parameter is an argv-style list of arguments.\nThe first element is the name of the program to run.\nSubsequent elements are parameters. The list must be\nnon-empty (ie. must contain a program name).\n\nThe C<$PATH> environment variable will contain at least\nC</usr/bin> and C</bin>. If you require a program from\nanother location, you should provide the full path in the\nfirst parameter.\n\nShared libraries and data files required by the program\nmust be available on filesystems which are mounted in the\ncorrect places. It is the caller's responsibility to ensure\nall filesystems that are needed are mounted at the right\nlocations.");
+ else
+ if (strcasecmp (cmd, "command_lines") == 0 || strcasecmp (cmd, "command-lines") == 0)
+ pod2text ("command-lines - run a command, returning lines", " command-lines <arguments>\n\nThis is the same as C<command>, but splits the\nresult into a list of lines.");
+ else
display_builtin_command (cmd);
}
@@ -1298,6 +1306,40 @@ static int run_file (const char *cmd, int argc, char *argv[])
return 0;
}
+static int run_command (const char *cmd, int argc, char *argv[])
+{
+ char *r;
+ char **arguments;
+ 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;
+ }
+ arguments = parse_string_list (argv[0]);
+ r = guestfs_command (g, arguments);
+ if (r == NULL) return -1;
+ printf ("%s\n", r);
+ free (r);
+ return 0;
+}
+
+static int run_command_lines (const char *cmd, int argc, char *argv[])
+{
+ char **r;
+ char **arguments;
+ 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;
+ }
+ arguments = parse_string_list (argv[0]);
+ r = guestfs_command_lines (g, arguments);
+ if (r == NULL) return -1;
+ print_strings (r);
+ free_strings (r);
+ return 0;
+}
+
int run_action (const char *cmd, int argc, char *argv[])
{
if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0)
@@ -1480,6 +1522,12 @@ int run_action (const char *cmd, int argc, char *argv[])
if (strcasecmp (cmd, "file") == 0)
return run_file (cmd, argc, argv);
else
+ if (strcasecmp (cmd, "command") == 0)
+ return run_command (cmd, argc, argv);
+ else
+ if (strcasecmp (cmd, "command_lines") == 0 || strcasecmp (cmd, "command-lines") == 0)
+ return run_command_lines (cmd, argc, argv);
+ else
{
fprintf (stderr, "%s: unknown command\n", cmd);
return -1;
diff --git a/guestfish-actions.pod b/guestfish-actions.pod
index 606c5b93..2d0a0453 100644
--- a/guestfish-actions.pod
+++ b/guestfish-actions.pod
@@ -214,6 +214,38 @@ Only numeric uid and gid are supported. If you want to use
names, you will need to locate and parse the password file
yourself (Augeas support makes this relatively easy).
+=head2 command
+
+ command arguments,...
+
+This calls runs a command from the guest filesystem. The
+filesystem must be mounted, and must contain a compatible
+operating system (ie. something Linux, with the same
+or compatible processor architecture).
+
+The single parameter is an argv-style list of arguments.
+The first element is the name of the program to run.
+Subsequent elements are parameters. The list must be
+non-empty (ie. must contain a program name).
+
+The C<$PATH> environment variable will contain at least
+C</usr/bin> and C</bin>. If you require a program from
+another location, you should provide the full path in the
+first parameter.
+
+Shared libraries and data files required by the program
+must be available on filesystems which are mounted in the
+correct places. It is the caller's responsibility to ensure
+all filesystems that are needed are mounted at the right
+locations.
+
+=head2 command-lines
+
+ command-lines arguments,...
+
+This is the same as C<command>, but splits the
+result into a list of lines.
+
=head2 config
config qemuparam qemuvalue
diff --git a/guestfs-actions.pod b/guestfs-actions.pod
index 32c6d672..551b3e32 100644
--- a/guestfs-actions.pod
+++ b/guestfs-actions.pod
@@ -283,6 +283,47 @@ yourself (Augeas support makes this relatively easy).
This function returns 0 on success or -1 on error.
+=head2 guestfs_command
+
+ char *guestfs_command (guestfs_h *handle,
+ char * const* const arguments);
+
+This calls runs a command from the guest filesystem. The
+filesystem must be mounted, and must contain a compatible
+operating system (ie. something Linux, with the same
+or compatible processor architecture).
+
+The single parameter is an argv-style list of arguments.
+The first element is the name of the program to run.
+Subsequent elements are parameters. The list must be
+non-empty (ie. must contain a program name).
+
+The C<$PATH> environment variable will contain at least
+C</usr/bin> and C</bin>. If you require a program from
+another location, you should provide the full path in the
+first parameter.
+
+Shared libraries and data files required by the program
+must be available on filesystems which are mounted in the
+correct places. It is the caller's responsibility to ensure
+all filesystems that are needed are mounted at the right
+locations.
+
+This function returns a string or NULL on error.
+I<The caller must free the returned string after use>.
+
+=head2 guestfs_command_lines
+
+ char **guestfs_command_lines (guestfs_h *handle,
+ char * const* const arguments);
+
+This is the same as C<guestfs_command>, but splits the
+result into a list of lines.
+
+This function returns a NULL-terminated array of strings
+(like L<environ(3)>), or NULL if there was an error.
+I<The caller must free the strings and the array after use>.
+
=head2 guestfs_config
int guestfs_config (guestfs_h *handle,
diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml
index 53307944..fa2e3418 100644
--- a/ocaml/guestfs.ml
+++ b/ocaml/guestfs.ml
@@ -146,3 +146,5 @@ external mounts : t -> string array = "ocaml_guestfs_mounts"
external umount_all : t -> unit = "ocaml_guestfs_umount_all"
external lvm_remove_all : t -> unit = "ocaml_guestfs_lvm_remove_all"
external file : t -> string -> string = "ocaml_guestfs_file"
+external command : t -> string array -> string = "ocaml_guestfs_command"
+external command_lines : t -> string array -> string array = "ocaml_guestfs_command_lines"
diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli
index e3cee18f..279ed4d3 100644
--- a/ocaml/guestfs.mli
+++ b/ocaml/guestfs.mli
@@ -277,3 +277,9 @@ val lvm_remove_all : t -> unit
val file : t -> string -> string
(** determine file type *)
+val command : t -> string array -> string
+(** run a command from the guest filesystem *)
+
+val command_lines : t -> string array -> string array
+(** run a command, returning lines *)
+
diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c
index decb8383..356965dc 100644
--- a/ocaml/guestfs_c_actions.c
+++ b/ocaml/guestfs_c_actions.c
@@ -1689,3 +1689,55 @@ ocaml_guestfs_file (value gv, value pathv)
CAMLreturn (rv);
}
+CAMLprim value
+ocaml_guestfs_command (value gv, value argumentsv)
+{
+ CAMLparam2 (gv, argumentsv);
+ CAMLlocal1 (rv);
+
+ guestfs_h *g = Guestfs_val (gv);
+ if (g == NULL)
+ caml_failwith ("command: used handle after closing it");
+
+ char **arguments = ocaml_guestfs_strings_val (argumentsv);
+ char *r;
+
+ caml_enter_blocking_section ();
+ r = guestfs_command (g, arguments);
+ caml_leave_blocking_section ();
+ ocaml_guestfs_free_strings (arguments);
+ if (r == NULL)
+ ocaml_guestfs_raise_error (g, "command");
+
+ rv = caml_copy_string (r);
+ free (r);
+ CAMLreturn (rv);
+}
+
+CAMLprim value
+ocaml_guestfs_command_lines (value gv, value argumentsv)
+{
+ CAMLparam2 (gv, argumentsv);
+ CAMLlocal1 (rv);
+
+ guestfs_h *g = Guestfs_val (gv);
+ if (g == NULL)
+ caml_failwith ("command_lines: used handle after closing it");
+
+ char **arguments = ocaml_guestfs_strings_val (argumentsv);
+ int i;
+ char **r;
+
+ caml_enter_blocking_section ();
+ r = guestfs_command_lines (g, arguments);
+ caml_leave_blocking_section ();
+ ocaml_guestfs_free_strings (arguments);
+ if (r == NULL)
+ ocaml_guestfs_raise_error (g, "command_lines");
+
+ rv = caml_copy_string_array ((const char **) r);
+ for (i = 0; r[i] != NULL; ++i) free (r[i]);
+ free (r);
+ CAMLreturn (rv);
+}
+
diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs
index 14a92258..2d564f41 100644
--- a/perl/Guestfs.xs
+++ b/perl/Guestfs.xs
@@ -106,63 +106,77 @@ DESTROY (g)
void
launch (g)
guestfs_h *g;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_launch (g) == -1) {
+ r = guestfs_launch (g);
+ if (r == -1)
croak ("launch: %s", guestfs_last_error (g));
- }
void
wait_ready (g)
guestfs_h *g;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_wait_ready (g) == -1) {
+ r = guestfs_wait_ready (g);
+ if (r == -1)
croak ("wait_ready: %s", guestfs_last_error (g));
- }
void
kill_subprocess (g)
guestfs_h *g;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_kill_subprocess (g) == -1) {
+ r = guestfs_kill_subprocess (g);
+ if (r == -1)
croak ("kill_subprocess: %s", guestfs_last_error (g));
- }
void
add_drive (g, filename)
guestfs_h *g;
char *filename;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_add_drive (g, filename) == -1) {
+ r = guestfs_add_drive (g, filename);
+ if (r == -1)
croak ("add_drive: %s", guestfs_last_error (g));
- }
void
add_cdrom (g, filename)
guestfs_h *g;
char *filename;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_add_cdrom (g, filename) == -1) {
+ r = guestfs_add_cdrom (g, filename);
+ if (r == -1)
croak ("add_cdrom: %s", guestfs_last_error (g));
- }
void
config (g, qemuparam, qemuvalue)
guestfs_h *g;
char *qemuparam;
char *qemuvalue;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_config (g, qemuparam, qemuvalue) == -1) {
+ r = guestfs_config (g, qemuparam, qemuvalue);
+ if (r == -1)
croak ("config: %s", guestfs_last_error (g));
- }
void
set_path (g, path)
guestfs_h *g;
char *path;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_set_path (g, path) == -1) {
+ r = guestfs_set_path (g, path);
+ if (r == -1)
croak ("set_path: %s", guestfs_last_error (g));
- }
SV *
get_path (g)
@@ -182,10 +196,12 @@ void
set_autosync (g, autosync)
guestfs_h *g;
int autosync;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_set_autosync (g, autosync) == -1) {
+ r = guestfs_set_autosync (g, autosync);
+ if (r == -1)
croak ("set_autosync: %s", guestfs_last_error (g));
- }
SV *
get_autosync (g)
@@ -205,10 +221,12 @@ void
set_verbose (g, verbose)
guestfs_h *g;
int verbose;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_set_verbose (g, verbose) == -1) {
+ r = guestfs_set_verbose (g, verbose);
+ if (r == -1)
croak ("set_verbose: %s", guestfs_last_error (g));
- }
SV *
get_verbose (g)
@@ -229,27 +247,33 @@ mount (g, device, mountpoint)
guestfs_h *g;
char *device;
char *mountpoint;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_mount (g, device, mountpoint) == -1) {
+ r = guestfs_mount (g, device, mountpoint);
+ if (r == -1)
croak ("mount: %s", guestfs_last_error (g));
- }
void
sync (g)
guestfs_h *g;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_sync (g) == -1) {
+ r = guestfs_sync (g);
+ if (r == -1)
croak ("sync: %s", guestfs_last_error (g));
- }
void
touch (g, path)
guestfs_h *g;
char *path;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_touch (g, path) == -1) {
+ r = guestfs_touch (g, path);
+ if (r == -1)
croak ("touch: %s", guestfs_last_error (g));
- }
SV *
cat (g, path)
@@ -407,8 +431,9 @@ PREINIT:
HV *hv;
PPCODE:
physvols = guestfs_pvs_full (g);
- if (physvols == NULL)
+ if (physvols == NULL) {
croak ("pvs_full: %s", guestfs_last_error (g));
+ }
EXTEND (SP, physvols->len);
for (i = 0; i < physvols->len; ++i) {
hv = newHV ();
@@ -439,8 +464,9 @@ PREINIT:
HV *hv;
PPCODE:
volgroups = guestfs_vgs_full (g);
- if (volgroups == NULL)
+ if (volgroups == NULL) {
croak ("vgs_full: %s", guestfs_last_error (g));
+ }
EXTEND (SP, volgroups->len);
for (i = 0; i < volgroups->len; ++i) {
hv = newHV ();
@@ -476,8 +502,9 @@ PREINIT:
HV *hv;
PPCODE:
logvols = guestfs_lvs_full (g);
- if (logvols == NULL)
+ if (logvols == NULL) {
croak ("lvs_full: %s", guestfs_last_error (g));
+ }
EXTEND (SP, logvols->len);
for (i = 0; i < logvols->len; ++i) {
hv = newHV ();
@@ -526,18 +553,22 @@ aug_init (g, root, flags)
guestfs_h *g;
char *root;
int flags;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_aug_init (g, root, flags) == -1) {
+ r = guestfs_aug_init (g, root, flags);
+ if (r == -1)
croak ("aug_init: %s", guestfs_last_error (g));
- }
void
aug_close (g)
guestfs_h *g;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_aug_close (g) == -1) {
+ r = guestfs_aug_close (g);
+ if (r == -1)
croak ("aug_close: %s", guestfs_last_error (g));
- }
SV *
aug_defvar (g, name, expr)
@@ -594,10 +625,12 @@ aug_set (g, path, val)
guestfs_h *g;
char *path;
char *val;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_aug_set (g, path, val) == -1) {
+ r = guestfs_aug_set (g, path, val);
+ if (r == -1)
croak ("aug_set: %s", guestfs_last_error (g));
- }
void
aug_insert (g, path, label, before)
@@ -605,10 +638,12 @@ aug_insert (g, path, label, before)
char *path;
char *label;
int before;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_aug_insert (g, path, label, before) == -1) {
+ r = guestfs_aug_insert (g, path, label, before);
+ if (r == -1)
croak ("aug_insert: %s", guestfs_last_error (g));
- }
SV *
aug_rm (g, path)
@@ -630,10 +665,12 @@ aug_mv (g, src, dest)
guestfs_h *g;
char *src;
char *dest;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_aug_mv (g, src, dest) == -1) {
+ r = guestfs_aug_mv (g, src, dest);
+ if (r == -1)
croak ("aug_mv: %s", guestfs_last_error (g));
- }
void
aug_match (g, path)
@@ -658,18 +695,22 @@ PREINIT:
void
aug_save (g)
guestfs_h *g;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_aug_save (g) == -1) {
+ r = guestfs_aug_save (g);
+ if (r == -1)
croak ("aug_save: %s", guestfs_last_error (g));
- }
void
aug_load (g)
guestfs_h *g;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_aug_load (g) == -1) {
+ r = guestfs_aug_load (g);
+ if (r == -1)
croak ("aug_load: %s", guestfs_last_error (g));
- }
void
aug_ls (g, path)
@@ -695,56 +736,68 @@ void
rm (g, path)
guestfs_h *g;
char *path;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_rm (g, path) == -1) {
+ r = guestfs_rm (g, path);
+ if (r == -1)
croak ("rm: %s", guestfs_last_error (g));
- }
void
rmdir (g, path)
guestfs_h *g;
char *path;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_rmdir (g, path) == -1) {
+ r = guestfs_rmdir (g, path);
+ if (r == -1)
croak ("rmdir: %s", guestfs_last_error (g));
- }
void
rm_rf (g, path)
guestfs_h *g;
char *path;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_rm_rf (g, path) == -1) {
+ r = guestfs_rm_rf (g, path);
+ if (r == -1)
croak ("rm_rf: %s", guestfs_last_error (g));
- }
void
mkdir (g, path)
guestfs_h *g;
char *path;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_mkdir (g, path) == -1) {
+ r = guestfs_mkdir (g, path);
+ if (r == -1)
croak ("mkdir: %s", guestfs_last_error (g));
- }
void
mkdir_p (g, path)
guestfs_h *g;
char *path;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_mkdir_p (g, path) == -1) {
+ r = guestfs_mkdir_p (g, path);
+ if (r == -1)
croak ("mkdir_p: %s", guestfs_last_error (g));
- }
void
chmod (g, mode, path)
guestfs_h *g;
int mode;
char *path;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_chmod (g, mode, path) == -1) {
+ r = guestfs_chmod (g, mode, path);
+ if (r == -1)
croak ("chmod: %s", guestfs_last_error (g));
- }
void
chown (g, owner, group, path)
@@ -752,10 +805,12 @@ chown (g, owner, group, path)
int owner;
int group;
char *path;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_chown (g, owner, group, path) == -1) {
+ r = guestfs_chown (g, owner, group, path);
+ if (r == -1)
croak ("chown: %s", guestfs_last_error (g));
- }
SV *
exists (g, path)
@@ -806,22 +861,25 @@ void
pvcreate (g, device)
guestfs_h *g;
char *device;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_pvcreate (g, device) == -1) {
+ r = guestfs_pvcreate (g, device);
+ if (r == -1)
croak ("pvcreate: %s", guestfs_last_error (g));
- }
void
vgcreate (g, volgroup, physvols)
guestfs_h *g;
char *volgroup;
char **physvols;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_vgcreate (g, volgroup, physvols) == -1) {
- free (physvols);
+ r = guestfs_vgcreate (g, volgroup, physvols);
+ free (physvols);
+ if (r == -1)
croak ("vgcreate: %s", guestfs_last_error (g));
- }
- free (physvols);
void
lvcreate (g, logvol, volgroup, mbytes)
@@ -829,20 +887,24 @@ lvcreate (g, logvol, volgroup, mbytes)
char *logvol;
char *volgroup;
int mbytes;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_lvcreate (g, logvol, volgroup, mbytes) == -1) {
+ r = guestfs_lvcreate (g, logvol, volgroup, mbytes);
+ if (r == -1)
croak ("lvcreate: %s", guestfs_last_error (g));
- }
void
mkfs (g, fstype, device)
guestfs_h *g;
char *fstype;
char *device;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_mkfs (g, fstype, device) == -1) {
+ r = guestfs_mkfs (g, fstype, device);
+ if (r == -1)
croak ("mkfs: %s", guestfs_last_error (g));
- }
void
sfdisk (g, device, cyls, heads, sectors, lines)
@@ -852,12 +914,13 @@ sfdisk (g, device, cyls, heads, sectors, lines)
int heads;
int sectors;
char **lines;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_sfdisk (g, device, cyls, heads, sectors, lines) == -1) {
- free (lines);
+ r = guestfs_sfdisk (g, device, cyls, heads, sectors, lines);
+ free (lines);
+ if (r == -1)
croak ("sfdisk: %s", guestfs_last_error (g));
- }
- free (lines);
void
write_file (g, path, content, size)
@@ -865,19 +928,23 @@ write_file (g, path, content, size)
char *path;
char *content;
int size;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_write_file (g, path, content, size) == -1) {
+ r = guestfs_write_file (g, path, content, size);
+ if (r == -1)
croak ("write_file: %s", guestfs_last_error (g));
- }
void
umount (g, pathordevice)
guestfs_h *g;
char *pathordevice;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_umount (g, pathordevice) == -1) {
+ r = guestfs_umount (g, pathordevice);
+ if (r == -1)
croak ("umount: %s", guestfs_last_error (g));
- }
void
mounts (g)
@@ -901,18 +968,22 @@ PREINIT:
void
umount_all (g)
guestfs_h *g;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_umount_all (g) == -1) {
+ r = guestfs_umount_all (g);
+ if (r == -1)
croak ("umount_all: %s", guestfs_last_error (g));
- }
void
lvm_remove_all (g)
guestfs_h *g;
+PREINIT:
+ int r;
PPCODE:
- if (guestfs_lvm_remove_all (g) == -1) {
+ r = guestfs_lvm_remove_all (g);
+ if (r == -1)
croak ("lvm_remove_all: %s", guestfs_last_error (g));
- }
SV *
file (g, path)
@@ -930,3 +1001,41 @@ PREINIT:
OUTPUT:
RETVAL
+SV *
+command (g, arguments)
+ guestfs_h *g;
+ char **arguments;
+PREINIT:
+ char *output;
+ CODE:
+ output = guestfs_command (g, arguments);
+ free (arguments);
+ if (output == NULL) {
+ croak ("command: %s", guestfs_last_error (g));
+ }
+ RETVAL = newSVpv (output, 0);
+ free (output);
+ OUTPUT:
+ RETVAL
+
+void
+command_lines (g, arguments)
+ guestfs_h *g;
+ char **arguments;
+PREINIT:
+ char **lines;
+ int i, n;
+ PPCODE:
+ lines = guestfs_command_lines (g, arguments);
+ free (arguments);
+ if (lines == NULL) {
+ croak ("command_lines: %s", guestfs_last_error (g));
+ }
+ for (n = 0; lines[n] != NULL; ++n) /**/;
+ EXTEND (SP, n);
+ for (i = 0; i < n; ++i) {
+ PUSHs (sv_2mortal (newSVpv (lines[i], 0)));
+ free (lines[i]);
+ }
+ free (lines);
+
diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm
index 9d050b47..17ab740c 100644
--- a/perl/lib/Sys/Guestfs.pm
+++ b/perl/lib/Sys/Guestfs.pm
@@ -271,6 +271,34 @@ Only numeric uid and gid are supported. If you want to use
names, you will need to locate and parse the password file
yourself (Augeas support makes this relatively easy).
+=item $output = $h->command (\@arguments);
+
+This calls runs a command from the guest filesystem. The
+filesystem must be mounted, and must contain a compatible
+operating system (ie. something Linux, with the same
+or compatible processor architecture).
+
+The single parameter is an argv-style list of arguments.
+The first element is the name of the program to run.
+Subsequent elements are parameters. The list must be
+non-empty (ie. must contain a program name).
+
+The C<$PATH> environment variable will contain at least
+C</usr/bin> and C</bin>. If you require a program from
+another location, you should provide the full path in the
+first parameter.
+
+Shared libraries and data files required by the program
+must be available on filesystems which are mounted in the
+correct places. It is the caller's responsibility to ensure
+all filesystems that are needed are mounted at the right
+locations.
+
+=item @lines = $h->command_lines (\@arguments);
+
+This is the same as C<$h-E<gt>command>, but splits the
+result into a list of lines.
+
=item $h->config ($qemuparam, $qemuvalue);
This can be used to add arbitrary qemu command line parameters
diff --git a/python/guestfs-py.c b/python/guestfs-py.c
index d3d6999c..8c7ad228 100644
--- a/python/guestfs-py.c
+++ b/python/guestfs-py.c
@@ -1831,6 +1831,64 @@ py_guestfs_file (PyObject *self, PyObject *args)
return py_r;
}
+static PyObject *
+py_guestfs_command (PyObject *self, PyObject *args)
+{
+ PyObject *py_g;
+ guestfs_h *g;
+ PyObject *py_r;
+ char *r;
+ PyObject *py_arguments;
+ const char **arguments;
+
+ if (!PyArg_ParseTuple (args, (char *) "OO:guestfs_command",
+ &py_g, &py_arguments))
+ return NULL;
+ g = get_handle (py_g);
+ arguments = get_string_list (py_arguments);
+ if (!arguments) return NULL;
+
+ r = guestfs_command (g, arguments);
+ free (arguments);
+ if (r == NULL) {
+ PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));
+ return NULL;
+ }
+
+ py_r = PyString_FromString (r);
+ free (r);
+ return py_r;
+}
+
+static PyObject *
+py_guestfs_command_lines (PyObject *self, PyObject *args)
+{
+ PyObject *py_g;
+ guestfs_h *g;
+ PyObject *py_r;
+ char **r;
+ PyObject *py_arguments;
+ const char **arguments;
+
+ if (!PyArg_ParseTuple (args, (char *) "OO:guestfs_command_lines",
+ &py_g, &py_arguments))
+ return NULL;
+ g = get_handle (py_g);
+ arguments = get_string_list (py_arguments);
+ if (!arguments) return NULL;
+
+ r = guestfs_command_lines (g, arguments);
+ free (arguments);
+ if (r == NULL) {
+ PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));
+ return NULL;
+ }
+
+ py_r = put_string_list (r);
+ free_strings (r);
+ return py_r;
+}
+
static PyMethodDef methods[] = {
{ (char *) "create", py_guestfs_create, METH_VARARGS, NULL },
{ (char *) "close", py_guestfs_close, METH_VARARGS, NULL },
@@ -1895,6 +1953,8 @@ static PyMethodDef methods[] = {
{ (char *) "umount_all", py_guestfs_umount_all, METH_VARARGS, NULL },
{ (char *) "lvm_remove_all", py_guestfs_lvm_remove_all, METH_VARARGS, NULL },
{ (char *) "file", py_guestfs_file, METH_VARARGS, NULL },
+ { (char *) "command", py_guestfs_command, METH_VARARGS, NULL },
+ { (char *) "command_lines", py_guestfs_command_lines, METH_VARARGS, NULL },
{ NULL, NULL, 0, NULL }
};
diff --git a/python/guestfs.py b/python/guestfs.py
index d4cd46d9..6cf7e19e 100644
--- a/python/guestfs.py
+++ b/python/guestfs.py
@@ -210,3 +210,9 @@ class GuestFS:
def file (self, path):
return libguestfsmod.file (self._o, path)
+ def command (self, arguments):
+ return libguestfsmod.command (self._o, arguments)
+
+ def command_lines (self, arguments):
+ return libguestfsmod.command_lines (self._o, arguments)
+
diff --git a/src/generator.ml b/src/generator.ml
index c9da57e9..1017ad1c 100755
--- a/src/generator.ml
+++ b/src/generator.ml
@@ -969,6 +969,38 @@ The exact command which runs is C<file -bsL path>. Note in
particular that the filename is not prepended to the output
(the C<-b> option).");
+ ("command", (RString "output", [StringList "arguments"]), 50, [],
+ [], (* XXX how to test? *)
+ "run a command from the guest filesystem",
+ "\
+This calls runs a command from the guest filesystem. The
+filesystem must be mounted, and must contain a compatible
+operating system (ie. something Linux, with the same
+or compatible processor architecture).
+
+The single parameter is an argv-style list of arguments.
+The first element is the name of the program to run.
+Subsequent elements are parameters. The list must be
+non-empty (ie. must contain a program name).
+
+The C<$PATH> environment variable will contain at least
+C</usr/bin> and C</bin>. If you require a program from
+another location, you should provide the full path in the
+first parameter.
+
+Shared libraries and data files required by the program
+must be available on filesystems which are mounted in the
+correct places. It is the caller's responsibility to ensure
+all filesystems that are needed are mounted at the right
+locations.");
+
+ ("command_lines", (RStringList "lines", [StringList "arguments"]), 51, [],
+ [], (* XXX how to test? *)
+ "run a command, returning lines",
+ "\
+This is the same as C<guestfs_command>, but splits the
+result into a list of lines.");
+
]
let all_functions = non_daemon_functions @ daemon_functions
@@ -1163,7 +1195,9 @@ let check_functions () =
failwithf "%s param/ret %s should not contain '-' or '_'"
name n;
if n = "value" then
- failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" n
+ failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" n;
+ if n = "argv" || n = "args" then
+ failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" n
in
(match fst style with
@@ -3351,20 +3385,22 @@ DESTROY (g)
| OptString _
| Bool _
| Int _ -> ()
- | StringList n -> pr " free (%s);\n" n
+ | StringList n -> pr " free (%s);\n" n
) (snd style)
in
(* Code. *)
(match fst style with
| RErr ->
+ pr "PREINIT:\n";
+ pr " int r;\n";
pr " PPCODE:\n";
- pr " if (guestfs_%s " name;
+ pr " r = guestfs_%s " name;
generate_call_args ~handle:"g" style;
- pr " == -1) {\n";
+ pr ";\n";
do_cleanups ();
+ pr " if (r == -1)\n";
pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
- pr " }\n"
| RInt n
| RBool n ->
pr "PREINIT:\n";
@@ -3373,10 +3409,9 @@ DESTROY (g)
pr " %s = guestfs_%s " n name;
generate_call_args ~handle:"g" style;
pr ";\n";
- pr " if (%s == -1) {\n" n;
do_cleanups ();
+ pr " if (%s == -1)\n" n;
pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
- pr " }\n";
pr " RETVAL = newSViv (%s);\n" n;
pr " OUTPUT:\n";
pr " RETVAL\n"
@@ -3387,10 +3422,9 @@ DESTROY (g)
pr " %s = guestfs_%s " n name;
generate_call_args ~handle:"g" style;
pr ";\n";
- pr " if (%s == NULL) {\n" n;
do_cleanups ();
+ pr " if (%s == NULL)\n" n;
pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
- pr " }\n";
pr " RETVAL = newSVpv (%s, 0);\n" n;
pr " OUTPUT:\n";
pr " RETVAL\n"
@@ -3401,10 +3435,9 @@ DESTROY (g)
pr " %s = guestfs_%s " n name;
generate_call_args ~handle:"g" style;
pr ";\n";
- pr " if (%s == NULL) {\n" n;
do_cleanups ();
+ pr " if (%s == NULL)\n" n;
pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
- pr " }\n";
pr " RETVAL = newSVpv (%s, 0);\n" n;
pr " free (%s);\n" n;
pr " OUTPUT:\n";
@@ -3417,10 +3450,9 @@ DESTROY (g)
pr " %s = guestfs_%s " n name;
generate_call_args ~handle:"g" style;
pr ";\n";
- pr " if (%s == NULL) {\n" n;
do_cleanups ();
+ pr " if (%s == NULL)\n" n;
pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
- pr " }\n";
pr " for (n = 0; %s[n] != NULL; ++n) /**/;\n" n;
pr " EXTEND (SP, n);\n";
pr " for (i = 0; i < n; ++i) {\n";
@@ -3435,28 +3467,25 @@ DESTROY (g)
pr " r = guestfs_%s " name;
generate_call_args ~handle:"g" style;
pr ";\n";
- pr " if (r == NULL) {\n";
do_cleanups ();
+ pr " if (r == NULL)\n";
pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
- pr " }\n";
pr " EXTEND (SP, 2);\n";
pr " PUSHs (sv_2mortal (newSViv (r->i)));\n";
pr " PUSHs (sv_2mortal (newSViv (r->b)));\n";
pr " guestfs_free_int_bool (r);\n";
| RPVList n ->
- generate_perl_lvm_code "pv" pv_cols name style n;
+ generate_perl_lvm_code "pv" pv_cols name style n do_cleanups;
| RVGList n ->
- generate_perl_lvm_code "vg" vg_cols name style n;
+ generate_perl_lvm_code "vg" vg_cols name style n do_cleanups;
| RLVList n ->
- generate_perl_lvm_code "lv" lv_cols name style n;
+ generate_perl_lvm_code "lv" lv_cols name style n do_cleanups;
);
- do_cleanups ();
-
pr "\n"
) all_functions
-and generate_perl_lvm_code typ cols name style n =
+and generate_perl_lvm_code typ cols name style n do_cleanups =
pr "PREINIT:\n";
pr " struct guestfs_lvm_%s_list *%s;\n" typ n;
pr " int i;\n";
@@ -3465,6 +3494,7 @@ and generate_perl_lvm_code typ cols name style n =
pr " %s = guestfs_%s " n name;
generate_call_args ~handle:"g" style;
pr ";\n";
+ do_cleanups ();
pr " if (%s == NULL)\n" n;
pr " croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
pr " EXTEND (SP, %s->len);\n" n;
diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c
index 4b6b9822..c6f574a7 100644
--- a/src/guestfs-actions.c
+++ b/src/guestfs-actions.c
@@ -3545,3 +3545,157 @@ char *guestfs_file (guestfs_h *g,
return rv.ret.description; /* caller will free */
}
+struct command_rv {
+ int cb_done; /* flag to indicate callback was called */
+ struct guestfs_message_header hdr;
+ struct guestfs_message_error err;
+ struct guestfs_command_ret ret;
+};
+
+static void command_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+ struct command_rv *rv = (struct command_rv *) data;
+
+ if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+ error (g, "guestfs_command: failed to parse reply header");
+ return;
+ }
+ if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+ if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+ error (g, "guestfs_command: failed to parse reply error");
+ return;
+ }
+ goto done;
+ }
+ if (!xdr_guestfs_command_ret (xdr, &rv->ret)) {
+ error (g, "guestfs_command: failed to parse reply");
+ return;
+ }
+ done:
+ rv->cb_done = 1;
+ main_loop.main_loop_quit (g);
+}
+
+char *guestfs_command (guestfs_h *g,
+ char * const* const arguments)
+{
+ struct guestfs_command_args args;
+ struct command_rv rv;
+ int serial;
+
+ if (g->state != READY) {
+ error (g, "guestfs_command called from the wrong state, %d != READY",
+ g->state);
+ return NULL;
+ }
+
+ memset (&rv, 0, sizeof rv);
+
+ args.arguments.arguments_val = (char **) arguments;
+ for (args.arguments.arguments_len = 0; arguments[args.arguments.arguments_len]; args.arguments.arguments_len++) ;
+ serial = dispatch (g, GUESTFS_PROC_COMMAND,
+ (xdrproc_t) xdr_guestfs_command_args, (char *) &args);
+ if (serial == -1)
+ return NULL;
+
+ rv.cb_done = 0;
+ g->reply_cb_internal = command_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_command failed, see earlier error messages");
+ return NULL;
+ }
+
+ if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_COMMAND, serial) == -1)
+ return NULL;
+
+ if (rv.hdr.status == GUESTFS_STATUS_ERROR) {
+ error (g, "%s", rv.err.error);
+ return NULL;
+ }
+
+ return rv.ret.output; /* caller will free */
+}
+
+struct command_lines_rv {
+ int cb_done; /* flag to indicate callback was called */
+ struct guestfs_message_header hdr;
+ struct guestfs_message_error err;
+ struct guestfs_command_lines_ret ret;
+};
+
+static void command_lines_cb (guestfs_h *g, void *data, XDR *xdr)
+{
+ struct command_lines_rv *rv = (struct command_lines_rv *) data;
+
+ if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {
+ error (g, "guestfs_command_lines: failed to parse reply header");
+ return;
+ }
+ if (rv->hdr.status == GUESTFS_STATUS_ERROR) {
+ if (!xdr_guestfs_message_error (xdr, &rv->err)) {
+ error (g, "guestfs_command_lines: failed to parse reply error");
+ return;
+ }
+ goto done;
+ }
+ if (!xdr_guestfs_command_lines_ret (xdr, &rv->ret)) {
+ error (g, "guestfs_command_lines: failed to parse reply");
+ return;
+ }
+ done:
+ rv->cb_done = 1;
+ main_loop.main_loop_quit (g);
+}
+
+char **guestfs_command_lines (guestfs_h *g,
+ char * const* const arguments)
+{
+ struct guestfs_command_lines_args args;
+ struct command_lines_rv rv;
+ int serial;
+
+ if (g->state != READY) {
+ error (g, "guestfs_command_lines called from the wrong state, %d != READY",
+ g->state);
+ return NULL;
+ }
+
+ memset (&rv, 0, sizeof rv);
+
+ args.arguments.arguments_val = (char **) arguments;
+ for (args.arguments.arguments_len = 0; arguments[args.arguments.arguments_len]; args.arguments.arguments_len++) ;
+ serial = dispatch (g, GUESTFS_PROC_COMMAND_LINES,
+ (xdrproc_t) xdr_guestfs_command_lines_args, (char *) &args);
+ if (serial == -1)
+ return NULL;
+
+ rv.cb_done = 0;
+ g->reply_cb_internal = command_lines_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_command_lines failed, see earlier error messages");
+ return NULL;
+ }
+
+ if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_COMMAND_LINES, 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.lines.lines_val = safe_realloc (g, rv.ret.lines.lines_val,
+ sizeof (char *) * (rv.ret.lines.lines_len + 1));
+ rv.ret.lines.lines_val[rv.ret.lines.lines_len] = NULL;
+ return rv.ret.lines.lines_val;
+}
+
diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h
index 73739647..06efabf0 100644
--- a/src/guestfs-actions.h
+++ b/src/guestfs-actions.h
@@ -80,3 +80,5 @@ extern char **guestfs_mounts (guestfs_h *handle);
extern int guestfs_umount_all (guestfs_h *handle);
extern int guestfs_lvm_remove_all (guestfs_h *handle);
extern char *guestfs_file (guestfs_h *handle, const char *path);
+extern char *guestfs_command (guestfs_h *handle, char * const* const arguments);
+extern char **guestfs_command_lines (guestfs_h *handle, char * const* const arguments);
diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c
index 82b227c9..0849f7ae 100644
--- a/src/guestfs_protocol.c
+++ b/src/guestfs_protocol.c
@@ -847,6 +847,49 @@ xdr_guestfs_file_ret (XDR *xdrs, guestfs_file_ret *objp)
}
bool_t
+xdr_guestfs_command_args (XDR *xdrs, guestfs_command_args *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_array (xdrs, (char **)&objp->arguments.arguments_val, (u_int *) &objp->arguments.arguments_len, ~0,
+ sizeof (str), (xdrproc_t) xdr_str))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_guestfs_command_ret (XDR *xdrs, guestfs_command_ret *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_string (xdrs, &objp->output, ~0))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_guestfs_command_lines_args (XDR *xdrs, guestfs_command_lines_args *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_array (xdrs, (char **)&objp->arguments.arguments_val, (u_int *) &objp->arguments.arguments_len, ~0,
+ sizeof (str), (xdrproc_t) xdr_str))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
+xdr_guestfs_command_lines_ret (XDR *xdrs, guestfs_command_lines_ret *objp)
+{
+ register int32_t *buf;
+
+ if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0,
+ sizeof (str), (xdrproc_t) xdr_str))
+ return FALSE;
+ return TRUE;
+}
+
+bool_t
xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp)
{
register int32_t *buf;
diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h
index 339f0699..4b314682 100644
--- a/src/guestfs_protocol.h
+++ b/src/guestfs_protocol.h
@@ -436,6 +436,35 @@ struct guestfs_file_ret {
};
typedef struct guestfs_file_ret guestfs_file_ret;
+struct guestfs_command_args {
+ struct {
+ u_int arguments_len;
+ str *arguments_val;
+ } arguments;
+};
+typedef struct guestfs_command_args guestfs_command_args;
+
+struct guestfs_command_ret {
+ char *output;
+};
+typedef struct guestfs_command_ret guestfs_command_ret;
+
+struct guestfs_command_lines_args {
+ struct {
+ u_int arguments_len;
+ str *arguments_val;
+ } arguments;
+};
+typedef struct guestfs_command_lines_args guestfs_command_lines_args;
+
+struct guestfs_command_lines_ret {
+ struct {
+ u_int lines_len;
+ str *lines_val;
+ } lines;
+};
+typedef struct guestfs_command_lines_ret guestfs_command_lines_ret;
+
enum guestfs_procedure {
GUESTFS_PROC_MOUNT = 1,
GUESTFS_PROC_SYNC = 2,
@@ -486,7 +515,9 @@ enum guestfs_procedure {
GUESTFS_PROC_UMOUNT_ALL = 47,
GUESTFS_PROC_LVM_REMOVE_ALL = 48,
GUESTFS_PROC_FILE = 49,
- GUESTFS_PROC_dummy = 49 + 1,
+ GUESTFS_PROC_COMMAND = 50,
+ GUESTFS_PROC_COMMAND_LINES = 51,
+ GUESTFS_PROC_dummy = 51 + 1,
};
typedef enum guestfs_procedure guestfs_procedure;
#define GUESTFS_MESSAGE_MAX 4194304
@@ -588,6 +619,10 @@ extern bool_t xdr_guestfs_umount_args (XDR *, guestfs_umount_args*);
extern bool_t xdr_guestfs_mounts_ret (XDR *, guestfs_mounts_ret*);
extern bool_t xdr_guestfs_file_args (XDR *, guestfs_file_args*);
extern bool_t xdr_guestfs_file_ret (XDR *, guestfs_file_ret*);
+extern bool_t xdr_guestfs_command_args (XDR *, guestfs_command_args*);
+extern bool_t xdr_guestfs_command_ret (XDR *, guestfs_command_ret*);
+extern bool_t xdr_guestfs_command_lines_args (XDR *, guestfs_command_lines_args*);
+extern bool_t xdr_guestfs_command_lines_ret (XDR *, guestfs_command_lines_ret*);
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*);
@@ -659,6 +694,10 @@ extern bool_t xdr_guestfs_umount_args ();
extern bool_t xdr_guestfs_mounts_ret ();
extern bool_t xdr_guestfs_file_args ();
extern bool_t xdr_guestfs_file_ret ();
+extern bool_t xdr_guestfs_command_args ();
+extern bool_t xdr_guestfs_command_ret ();
+extern bool_t xdr_guestfs_command_lines_args ();
+extern bool_t xdr_guestfs_command_lines_ret ();
extern bool_t xdr_guestfs_procedure ();
extern bool_t xdr_guestfs_message_direction ();
extern bool_t xdr_guestfs_message_status ();
diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x
index d133bb56..68362d64 100644
--- a/src/guestfs_protocol.x
+++ b/src/guestfs_protocol.x
@@ -336,6 +336,22 @@ struct guestfs_file_ret {
string description<>;
};
+struct guestfs_command_args {
+ str arguments<>;
+};
+
+struct guestfs_command_ret {
+ string output<>;
+};
+
+struct guestfs_command_lines_args {
+ str arguments<>;
+};
+
+struct guestfs_command_lines_ret {
+ str lines<>;
+};
+
enum guestfs_procedure {
GUESTFS_PROC_MOUNT = 1,
GUESTFS_PROC_SYNC = 2,
@@ -386,6 +402,8 @@ enum guestfs_procedure {
GUESTFS_PROC_UMOUNT_ALL = 47,
GUESTFS_PROC_LVM_REMOVE_ALL = 48,
GUESTFS_PROC_FILE = 49,
+ GUESTFS_PROC_COMMAND = 50,
+ GUESTFS_PROC_COMMAND_LINES = 51,
GUESTFS_PROC_dummy
};