New APIs: guestfs_create_flags, guestfs_parse_environment,

guestfs_parse_environment_list.

Add a new function for creating a handle:

 guestfs_h *guestfs_create_flags (unsigned flags [, ...]);

This variant lets you supply flags and extra arguments, although extra
arguments are not used at the moment.

Of particular interest is the ability to separate the creation of the
handle from the parsing of environment variables like
LIBGUESTFS_DEBUG.  guestfs_create does both together, which prevents
us from propagating errors from parsing environment variables back to
the caller (guestfs_create has always printed any errors on stderr and
then just ignored them).

If you are interested in these errors, you can now write:

 g = guestfs_create_flags (GUESTFS_CREATE_NO_ENVIRONMENT);
 if (!g)
   exit (EXIT_FAILURE);
 r = guestfs_parse_environment (g);
 if (!r)
   exit (EXIT_FAILURE);

Also you can *omit* the call to guestfs_parse_environment, which
creates a handle unaffected by the environment (which was not possible
before).

This commit also includes new (backwards compatible) changes to the
OCaml, Perl, Python, Ruby and Java constructors that let you use the
flags.
This commit is contained in:
Richard W.M. Jones
2012-10-15 11:14:59 +01:00
parent 389cb608df
commit 9466060201
12 changed files with 329 additions and 103 deletions

View File

@@ -2480,6 +2480,39 @@ call this function to pass the answer back to libvirt.
See L<guestfs(3)/LIBVIRT AUTHENTICATION> for documentation and example code." };
{ defaults with
name = "parse_environment";
style = RErr, [], [];
tests = [];
shortdesc = "parse the environment and set handle flags accordingly";
longdesc = "\
Parse the program's environment and set flags in the handle
accordingly. For example if C<LIBGUESTFS_DEBUG=1> then the
'verbose' flag is set in the handle.
I<Most programs do not need to call this>. It is done implicitly
when you call C<guestfs_create>.
See L<guestfs(3)/ENVIRONMENT VARIABLES> for a list of environment
variables that can affect libguestfs handles. See also
L<guestfs(3)/guestfs_create_flags>, and
C<guestfs_parse_environment_list>." };
{ defaults with
name = "parse_environment_list";
style = RErr, [StringList "environment"], [];
tests = [];
shortdesc = "parse the environment and set handle flags accordingly";
longdesc = "\
Parse the list of strings in the argument C<environment>
and set flags in the handle accordingly.
For example if C<LIBGUESTFS_DEBUG=1> is a string in the list,
then the 'verbose' flag is set in the handle.
This is the same as C<guestfs_parse_environment> except that
it parses an explicit list of strings instead of the program's
environment." };
]
(* daemon_functions are any functions which cause some action

View File

@@ -416,6 +416,9 @@ typedef struct guestfs_h guestfs_h;
/* Connection management. */
extern GUESTFS_DLL_PUBLIC guestfs_h *guestfs_create (void);
extern GUESTFS_DLL_PUBLIC guestfs_h *guestfs_create_flags (unsigned flags, ...);
#define GUESTFS_CREATE_NO_ENVIRONMENT (1 << 0)
#define GUESTFS_CREATE_NO_CLOSE_ON_EXIT (1 << 1)
extern GUESTFS_DLL_PUBLIC void guestfs_close (guestfs_h *g);
/* Error handling. */
@@ -1586,6 +1589,7 @@ and generate_linker_script () =
let globals = [
"guestfs_create";
"guestfs_create_flags";
"guestfs_close";
"guestfs_delete_event_callback";
"guestfs_first_private";

View File

@@ -55,6 +55,30 @@ public class GuestFS {
*/
long g;
/**
* Create a libguestfs handle, setting flags.
*
* @throws LibGuestFSException
*/
public GuestFS (Map<String, Object> optargs) throws LibGuestFSException
{
int flags = 0;
/* Unpack optional args. */
Object _optobj;
_optobj = null;
if (optargs != null)
_optobj = optargs.get (\"environment\");
if (_optobj != null && !((Boolean) _optobj).booleanValue())
flags |= 1;
if (optargs != null)
_optobj = optargs.get (\"close_on_exit\");
if (_optobj != null && !((Boolean) _optobj).booleanValue())
flags |= 2;
g = _create (flags);
}
/**
* Create a libguestfs handle.
*
@@ -62,9 +86,9 @@ public class GuestFS {
*/
public GuestFS () throws LibGuestFSException
{
g = _create ();
g = _create (0);
}
private native long _create () throws LibGuestFSException;
private native long _create (int flags) throws LibGuestFSException;
/**
* Close a libguestfs handle.
@@ -409,12 +433,12 @@ throw_exception (JNIEnv *env, const char *msg)
}
JNIEXPORT jlong JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1create
(JNIEnv *env, jobject obj)
Java_com_redhat_et_libguestfs_GuestFS__1create (JNIEnv *env,
jobject obj_unused, jint flags)
{
guestfs_h *g;
g = guestfs_create ();
g = guestfs_create_flags ((int) flags);
if (g == NULL) {
throw_exception (env, \"GuestFS.create: failed to allocate handle\");
return 0;

View File

@@ -62,8 +62,14 @@ exception Handle_closed of string
after calling {!close} on it. The string is the name of
the function. *)
val create : unit -> t
(** Create a {!t} handle. *)
val create : ?environment:bool -> ?close_on_exit:bool -> unit -> t
(** Create a {!t} handle.
[?environment] defaults to [true]. If set to false, it sets
the [GUESTFS_CREATE_NO_ENVIRONMENT] flag.
[?close_on_exit] defaults to [true]. If set to false, it sets
the [GUESTFS_CREATE_NO_CLOSE_ON_EXIT] flag. *)
val close : t -> unit
(** Close the {!t} handle and free up all resources used
@@ -171,7 +177,7 @@ val user_cancel : t -> unit
For example [g#]{{!guestfs.get_verbose}get_verbose} [()]
calls the method, whereas [g#get_verbose] is a function. *)
class guestfs : unit -> object
class guestfs : ?environment:bool -> ?close_on_exit:bool -> unit -> object
method close : unit -> unit
method set_event_callback : event_callback -> event list -> event_handle
method delete_event_callback : event_handle -> unit
@@ -211,7 +217,8 @@ type t
exception Error of string
exception Handle_closed of string
external create : unit -> t = \"ocaml_guestfs_create\"
external create : ?environment:bool -> ?close_on_exit:bool -> unit -> t =
\"ocaml_guestfs_create\"
external close : t -> unit = \"ocaml_guestfs_close\"
type event =
@@ -265,8 +272,8 @@ let () =
(* OO API. *)
pr "
class guestfs () =
let g = create () in
class guestfs ?environment ?close_on_exit () =
let g = create ?environment ?close_on_exit () in
object (self)
method close () = close g
method set_event_callback = set_event_callback g

View File

@@ -211,9 +211,10 @@ MODULE = Sys::Guestfs PACKAGE = Sys::Guestfs
PROTOTYPES: ENABLE
guestfs_h *
_create ()
_create (flags)
unsigned flags;
CODE:
RETVAL = guestfs_create ();
RETVAL = guestfs_create_flags (flags);
if (!RETVAL)
croak (\"could not create guestfs handle\");
guestfs_set_error_handler (RETVAL, NULL, NULL);
@@ -713,18 +714,29 @@ XSLoader::load ('Sys::Guestfs');
(* Methods. *)
pr "\
=item $g = Sys::Guestfs->new ();
=item $g = Sys::Guestfs->new ([environment => 0,] [close_on_exit => 0]);
Create a new guestfs handle.
If the optional argument C<environment> is false, then
the C<GUESTFS_CREATE_NO_ENVIRONMENT> flag is set.
If the optional argument C<close_on_exit> is false, then
the C<GUESTFS_CREATE_NO_CLOSE_ON_EXIT> flag is set.
=cut
sub new {
my $proto = shift;
my $class = ref ($proto) || $proto;
my %%flags = @_;
my $g = Sys::Guestfs::_create ();
my $self = { _g => $g };
my $flags = 0;
$flags |= 1 if exists $flags{environment} && !$flags{environment};
$flags |= 2 if exists $flags{close_on_exit} && !$flags{close_on_exit};
my $g = Sys::Guestfs::_create ($flags);
my $self = { _g => $g, _flags => $flags };
bless $self, $class;
return $self;
}
@@ -1013,10 +1025,11 @@ L<guestfs(3)/AVAILABILITY>.
=head1 STORING DATA IN THE HANDLE
The handle returned from L</new> is a hash reference. The hash
normally contains a single element:
normally contains some elements:
{
_g => [private data used by libguestfs]
_g => [private data used by libguestfs],
_flags => [flags provided when creating the handle]
}
Callers can add other elements to this hash to store data for their own

View File

@@ -645,9 +645,12 @@ class ClosedHandle(ValueError):
class GuestFS:
\"\"\"Instances of this class are libguestfs API handles.\"\"\"
def __init__ (self):
def __init__ (self, environment=True, close_on_exit=True):
\"\"\"Create a new libguestfs handle.\"\"\"
self._o = libguestfsmod.create ()
flags = 0
if not environment: flags |= 1
if not close_on_exit: flags |= 2
self._o = libguestfsmod.create (flags)
def __del__ (self):
if self._o:

View File

@@ -126,7 +126,7 @@ ruby_guestfs_free (void *gvp)
/*
* call-seq:
* Guestfs::Guestfs.new() -> Guestfs::Guestfs
* Guestfs::Guestfs.new([{:environment => false, :close_on_exit => false}]) -> Guestfs::Guestfs
*
* Call
* +guestfs_create+[http://libguestfs.org/guestfs.3.html#guestfs_create]
@@ -134,11 +134,26 @@ ruby_guestfs_free (void *gvp)
* Ruby as an instance of the Guestfs::Guestfs class.
*/
static VALUE
ruby_guestfs_create (VALUE m)
ruby_guestfs_create (int argc, VALUE *argv, VALUE m)
{
guestfs_h *g;
g = guestfs_create ();
if (argc > 1)
rb_raise (rb_eArgError, \"expecting 0 or 1 arguments\");
volatile VALUE optargsv = argc == 1 ? argv[0] : rb_hash_new ();
Check_Type (optargsv, T_HASH);
unsigned flags = 0;
volatile VALUE v;
v = rb_hash_lookup (optargsv, ID2SYM (rb_intern (\"environment\")));
if (v != Qnil && !RTEST (v))
flags |= GUESTFS_CREATE_NO_ENVIRONMENT;
v = rb_hash_lookup (optargsv, ID2SYM (rb_intern (\"close_on_exit\")));
if (v != Qnil && !RTEST (v))
flags |= GUESTFS_CREATE_NO_CLOSE_ON_EXIT;
g = guestfs_create_flags (flags);
if (!g)
rb_raise (e_Error, \"failed to create guestfs handle\");
@@ -663,7 +678,7 @@ void Init__guestfs ()
rb_define_alloc_func (c_guestfs, ruby_guestfs_create);
#endif
rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, -1);
rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
rb_define_method (c_guestfs, \"set_event_callback\",
ruby_set_event_callback, 2);

View File

@@ -48,7 +48,7 @@ static void event_callback_wrapper (guestfs_h *g, void *data, uint64_t event, in
#endif
/* These prototypes are solely to quiet gcc warning. */
CAMLprim value ocaml_guestfs_create (value unitv);
CAMLprim value ocaml_guestfs_create (value environmentv, value close_on_exitv, value unitv);
CAMLprim value ocaml_guestfs_close (value gv);
CAMLprim value ocaml_guestfs_set_event_callback (value gv, value closure, value events);
CAMLprim value ocaml_guestfs_delete_event_callback (value gv, value eh);
@@ -141,14 +141,23 @@ ocaml_guestfs_raise_closed (const char *func)
/* Guestfs.create */
CAMLprim value
ocaml_guestfs_create (value unitv)
ocaml_guestfs_create (value environmentv, value close_on_exitv, value unitv)
{
CAMLparam1 (unitv);
CAMLparam3 (environmentv, close_on_exitv, unitv);
CAMLlocal1 (gv);
unsigned flags = 0;
guestfs_h *g;
value *v;
g = guestfs_create ();
if (environmentv != Val_int (0) &&
!Bool_val (Field (environmentv, 0)))
flags |= GUESTFS_CREATE_NO_ENVIRONMENT;
if (close_on_exitv != Val_int (0) &&
!Bool_val (Field (close_on_exitv, 0)))
flags |= GUESTFS_CREATE_NO_CLOSE_ON_EXIT;
g = guestfs_create_flags (flags);
if (g == NULL)
caml_failwith ("failed to create guestfs handle");

View File

@@ -34,8 +34,11 @@ PyObject *
py_guestfs_create (PyObject *self, PyObject *args)
{
guestfs_h *g;
unsigned flags;
g = guestfs_create ();
if (!PyArg_ParseTuple (args, (char *) "I:guestfs_create", &flags))
return NULL;
g = guestfs_create_flags (flags);
if (g == NULL) {
PyErr_SetString (PyExc_RuntimeError,
"guestfs.create: failed to allocate handle");

View File

@@ -193,6 +193,7 @@ struct guestfs_h
bool enable_network; /* Enable the network. */
bool selinux; /* selinux enabled? */
bool pgroup; /* Create process group for children? */
bool close_on_exit; /* Is this handle on the atexit list? */
int smp; /* If > 1, -smp flag passed to qemu. */
int memsize; /* Size of RAM (megabytes). */

View File

@@ -99,15 +99,18 @@ init_libguestfs (void)
guestfs_h *
guestfs_create (void)
{
return guestfs_create_flags (0);
}
guestfs_h *
guestfs_create_flags (unsigned flags, ...)
{
guestfs_h *g;
const char *str;
g = malloc (sizeof (*g));
g = calloc (1, sizeof (*g));
if (!g) return NULL;
memset (g, 0, sizeof (*g));
g->state = CONFIG;
g->fd[0] = -1;
@@ -121,54 +124,7 @@ guestfs_create (void)
g->recovery_proc = 1;
g->autosync = 1;
str = getenv ("LIBGUESTFS_DEBUG");
g->verbose = str != NULL && STREQ (str, "1");
str = getenv ("LIBGUESTFS_TRACE");
g->trace = str != NULL && STREQ (str, "1");
str = getenv ("LIBGUESTFS_PATH");
g->path = str != NULL ? strdup (str) : strdup (GUESTFS_DEFAULT_PATH);
if (!g->path) goto error;
str = getenv ("LIBGUESTFS_QEMU");
g->qemu = str != NULL ? strdup (str) : strdup (QEMU);
if (!g->qemu) goto error;
str = getenv ("LIBGUESTFS_APPEND");
if (str) {
g->append = strdup (str);
if (!g->append) goto error;
}
/* Choose a suitable memory size. Previously we tried to choose
* a minimal memory size, but this isn't really necessary since
* recent QEMU and KVM don't do anything nasty like locking
* memory into core any more. Thus we can safely choose a
* large, generous amount of memory, and it'll just get swapped
* on smaller systems.
*/
str = getenv ("LIBGUESTFS_MEMSIZE");
if (str) {
if (sscanf (str, "%d", &g->memsize) != 1 || g->memsize < 128) {
warning (g, "non-numeric or too small value for LIBGUESTFS_MEMSIZE");
goto error;
}
} else
g->memsize = 500;
str = getenv ("LIBGUESTFS_ATTACH_METHOD");
if (str) {
if (parse_attach_method (g, str) == -1) {
warning (g, _("invalid or unknown value for LIBGUESTFS_ATTACH_METHOD environment variable"));
goto error;
}
} else {
if (parse_attach_method (g, DEFAULT_ATTACH_METHOD) == -1) {
warning (g, _("libguestfs was built with an invalid default attach-method, using 'appliance' instead"));
g->attach_method = ATTACH_METHOD_APPLIANCE;
}
}
g->memsize = 500;
/* Start with large serial numbers so they are easy to spot
* inside the protocol.
@@ -178,17 +134,35 @@ guestfs_create (void)
/* Default is uniprocessor appliance. */
g->smp = 1;
/* Link the handles onto a global list. */
gl_lock_lock (handles_lock);
g->next = handles;
handles = g;
if (!atexit_handler_set) {
atexit (close_handles);
atexit_handler_set = 1;
}
gl_lock_unlock (handles_lock);
g->path = strdup (GUESTFS_DEFAULT_PATH);
if (!g->path) goto error;
debug (g, "new guestfs handle %p", g);
g->qemu = strdup (QEMU);
if (!g->qemu) goto error;
if (parse_attach_method (g, DEFAULT_ATTACH_METHOD) == -1) {
warning (g, _("libguestfs was built with an invalid default attach-method, using 'appliance' instead"));
g->attach_method = ATTACH_METHOD_APPLIANCE;
}
if (!(flags & GUESTFS_CREATE_NO_ENVIRONMENT))
guestfs_parse_environment (g);
if (!(flags & GUESTFS_CREATE_NO_CLOSE_ON_EXIT)) {
g->close_on_exit = true;
/* Link the handles onto a global list. */
gl_lock_lock (handles_lock);
g->next = handles;
handles = g;
if (!atexit_handler_set) {
atexit (close_handles);
atexit_handler_set = 1;
}
gl_lock_unlock (handles_lock);
}
debug (g, "create: flags = %u, handle = %p", flags, g);
return g;
@@ -201,10 +175,92 @@ guestfs_create (void)
return NULL;
}
static int
parse_environment (guestfs_h *g,
char *(*do_getenv) (const void *data, const char *),
const void *data)
{
int memsize;
char *str;
/* Don't bother checking the return values of functions
* that cannot return errors.
*/
str = do_getenv (data, "LIBGUESTFS_DEBUG");
if (str != NULL && STREQ (str, "1"))
guestfs_set_verbose (g, 1);
str = do_getenv (data, "LIBGUESTFS_TRACE");
if (str != NULL && STREQ (str, "1"))
guestfs_set_trace (g, 1);
str = do_getenv (data, "LIBGUESTFS_PATH");
if (str)
guestfs_set_path (g, str);
str = do_getenv (data, "LIBGUESTFS_QEMU");
if (str)
guestfs_set_qemu (g, str);
str = do_getenv (data, "LIBGUESTFS_APPEND");
if (str)
guestfs_set_append (g, str);
str = do_getenv (data, "LIBGUESTFS_MEMSIZE");
if (str) {
if (sscanf (str, "%d", &memsize) != 1 || memsize < 128) {
error (g, "non-numeric or too small value for LIBGUESTFS_MEMSIZE");
return -1;
}
guestfs_set_memsize (g, memsize);
}
str = do_getenv (data, "LIBGUESTFS_ATTACH_METHOD");
if (str) {
if (guestfs_set_attach_method (g, str) == -1)
return -1;
}
return 0;
}
static char *
call_getenv (const void *data, const char *name)
{
return getenv (name);
}
int
guestfs__parse_environment (guestfs_h *g)
{
return parse_environment (g, call_getenv, NULL);
}
static char *
getenv_from_strings (const void *stringsv, const char *name)
{
char **strings = (char **) stringsv;
size_t len = strlen (name);
size_t i;
for (i = 0; strings[i] != NULL; ++i)
if (STRPREFIX (strings[i], name) && strings[i][len] == '=')
return (char *) &strings[i][len+1];
return NULL;
}
int
guestfs__parse_environment_list (guestfs_h *g, char * const *strings)
{
return parse_environment (g, getenv_from_strings, strings);
}
void
guestfs_close (guestfs_h *g)
{
struct qemu_param *qp, *qp_next;
guestfs_h **gg;
if (g->state == NO_HANDLE) {
/* Not safe to call ANY callbacks here, so ... */
@@ -213,17 +269,13 @@ guestfs_close (guestfs_h *g)
}
/* Remove the handle from the handles list. */
gl_lock_lock (handles_lock);
if (handles == g)
handles = g->next;
else {
guestfs_h *gg;
for (gg = handles; gg->next != g; gg = gg->next)
if (g->close_on_exit) {
gl_lock_lock (handles_lock);
for (gg = &handles; *gg != g; gg = &(*gg)->next)
;
gg->next = g->next;
*gg = g->next;
gl_lock_unlock (handles_lock);
}
gl_lock_unlock (handles_lock);
if (g->trace) {
const char trace_msg[] = "close";

View File

@@ -1628,8 +1628,9 @@ appropriate.
=head2 guestfs_h *
C<guestfs_h> is the opaque type representing a connection handle.
Create a handle by calling L</guestfs_create>. Call L</guestfs_close>
to free the handle and release all resources used.
Create a handle by calling L</guestfs_create> or
L</guestfs_create_flags>. Call L</guestfs_close> to free the handle
and release all resources used.
For information on using multiple handles and threads, see the section
L</MULTIPLE HANDLES AND MULTIPLE THREADS> above.
@@ -1652,6 +1653,67 @@ After configuring the handle, you have to call L</guestfs_launch>.
You may also want to configure error handling for the handle. See the
L</ERROR HANDLING> section below.
=head2 guestfs_create_flags
guestfs_h *guestfs_create_flags (unsigned flags [, ...]);
Create a connection handle, supplying extra flags and
extra arguments to control how the handle is created.
On success this returns a non-NULL pointer to a handle. On error it
returns NULL.
L</guestfs_create> is equivalent to calling C<guestfs_create_flags(0)>.
The following flags may be logically ORed together. (Currently
no extra arguments are used).
=over 4
=item C<GUESTFS_CREATE_NO_ENVIRONMENT>
Don't parse any environment variables (such as C<LIBGUESTFS_DEBUG> etc).
You can call L</guestfs_parse_environment> or
L</guestfs_parse_environment_list> afterwards to parse environment
variables. Alternately, I<don't> call these functions if you want the
handle to be unaffected by environment variables. See the example below.
The default (if this flag is not given) is to implicitly call
L</guestfs_parse_environment>.
=item C<GUESTFS_CREATE_NO_CLOSE_ON_EXIT>
Don't try to close the handle in an L<atexit(3)> handler if the
program exits without explicitly closing the handle.
The default (if this flag is not given) is to install such an atexit
handler.
=back
=head3 USING C<GUESTFS_CREATE_NO_ENVIRONMENT>
You might use C<GUESTFS_CREATE_NO_ENVIRONMENT> and
an explicit call to L</guestfs_parse_environment> like this:
guestfs_h *g;
int r;
g = guestfs_create_flags (GUESTFS_CREATE_NO_ENVIRONMENT);
if (!g)
exit (EXIT_FAILURE);
r = guestfs_parse_environment (g);
if (!r)
exit (EXIT_FAILURE);
Or to create a handle which is unaffected by environment variables,
omit the call to C<guestfs_parse_environment> from the above code.
The above code has another advantage which is that any errors from
parsing the environment are passed through the error handler, whereas
C<guestfs_create> prints errors on stderr and ignores them.
=head2 guestfs_close
void guestfs_close (guestfs_h *g);
@@ -2785,7 +2847,7 @@ since these usually results in massive disk corruption).
libguestfs uses a state machine to model the child process:
|
guestfs_create
guestfs_create / guestfs_create_flags
|
|
____V_____