Add 'command' and 'command-lines'. Fix args freeing in Perl bindings.

This commit is contained in:
Richard Jones
2009-04-14 13:51:12 +01:00
parent 161018ed1e
commit 5365ebd501
20 changed files with 938 additions and 99 deletions

View File

@@ -21,6 +21,7 @@ noinst_PROGRAMS = guestfsd
guestfsd_SOURCES = \
actions.h \
augeas.c \
command.c \
daemon.h \
devsparts.c \
dir.c \

View File

@@ -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);

101
daemon/command.c Normal file
View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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");
@@ -275,6 +277,12 @@ void display_command (const char *cmd)
else
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)
@@ -1479,6 +1521,12 @@ int run_action (const char *cmd, int argc, char *argv[])
else
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);

View File

@@ -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

View File

@@ -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,

View File

@@ -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"

View File

@@ -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 *)

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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 }
};

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -846,6 +846,49 @@ xdr_guestfs_file_ret (XDR *xdrs, guestfs_file_ret *objp)
return TRUE;
}
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)
{

View File

@@ -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 ();

View File

@@ -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
};