From: Richard Jones Date: Tue, 14 Apr 2009 12:51:12 +0000 (+0100) Subject: Add 'command' and 'command-lines'. Fix args freeing in Perl bindings. X-Git-Tag: 0.9.1~5 X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=commitdiff_plain;h=5365ebd501850ea10d9a5b28fc6480ea34dbe16d;hp=161018ed1e90c796e6e099859979da02d5f3e410 Add 'command' and 'command-lines'. Fix args freeing in Perl bindings. --- diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 400f164..1c52f7a 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 d53729b..90aeb8d 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 0000000..589ca76 --- /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 + +#include +#include +#include + +#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 af0f14c..9a0dc91 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 1fb8b9d..2e536a5 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 \n\nThis call uses the standard L 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. 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 \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 and C. 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 \n\nThis is the same as C, 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 606c5b9..2d0a045 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 and C. 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, 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 32c6d67..551b3e3 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 and C. 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. + +=head2 guestfs_command_lines + + char **guestfs_command_lines (guestfs_h *handle, + char * const* const arguments); + +This is the same as C, but splits the +result into a list of lines. + +This function returns a NULL-terminated array of strings +(like L), or NULL if there was an error. +I. + =head2 guestfs_config int guestfs_config (guestfs_h *handle, diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index 5330794..fa2e341 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 e3cee18..279ed4d 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 decb838..356965d 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 14a9225..2d564f4 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 9d050b4..17ab740 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 and C. 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-Ecommand>, 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 d3d6999..8c7ad22 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 d4cd46d..6cf7e19 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 c9da57e..1017ad1 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -969,6 +969,38 @@ The exact command which runs is C. 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 and C. 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, 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 4b6b982..c6f574a 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 7373964..06efabf 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 82b227c..0849f7a 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 339f069..4b31468 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 d133bb5..68362d6 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 };