| NotInDocs (* do not add this function to documentation *)
| DeprecatedBy of string (* function is deprecated, use .. instead *)
| Optional of string (* function is part of an optional group *)
+ | Progress (* function can generate progress messages *)
and fish_output_t =
| FishOutputOctal (* for int return, print in octal *)
Please read L<guestfs(3)/INSPECTION> for more details.
See also C<guestfs_inspect_get_mountpoints>.");
+ ("set_network", (RErr, [Bool "network"]), -1, [FishAlias "network"],
+ [],
+ "set enable network flag",
+ "\
+If C<network> is true, then the network is enabled in the
+libguestfs appliance. The default is false.
+
+This affects whether commands are able to access the network
+(see L<guestfs(3)/RUNNING COMMANDS>).
+
+You must call this before calling C<guestfs_launch>, otherwise
+it has no effect.");
+
+ ("get_network", (RBool "network", []), -1, [],
+ [],
+ "get enable network flag",
+ "\
+This returns the enable network flag.");
+
]
(* daemon_functions are any functions which cause some action
See also C<guestfs_download>.");
- ("download", (RErr, [Dev_or_Path "remotefilename"; FileOut "filename"]), 67, [],
+ ("download", (RErr, [Dev_or_Path "remotefilename"; FileOut "filename"]), 67, [Progress],
[InitBasicFS, Always, TestOutput (
(* Pick a file from cwd which isn't likely to change. *)
[["upload"; "../COPYING.LIB"; "/COPYING.LIB"];
This command is entirely equivalent to running C<fsck -a -t fstype device>.");
- ("zero", (RErr, [Device "device"]), 85, [],
+ ("zero", (RErr, [Device "device"]), 85, [Progress],
[InitBasicFS, Always, TestOutput (
[["umount"; "/dev/sda1"];
["zero"; "/dev/sda1"];
values are possible, although unusual. See C<guestfs_part_init>
for a full list.");
- ("fill", (RErr, [Int "c"; Int "len"; Pathname "path"]), 215, [],
+ ("fill", (RErr, [Int "c"; Int "len"; Pathname "path"]), 215, [Progress],
[InitBasicFS, Always, TestOutputBuffer (
[["fill"; "0x63"; "10"; "/test"];
["read_file"; "/test"]], "cccccccccc")],
See also C<guestfs_vgpvuuids>.");
- ("copy_size", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"; Int64 "size"]), 227, [],
+ ("copy_size", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"; Int64 "size"]), 227, [Progress],
[InitBasicFS, Always, TestOutputBuffer (
[["write"; "/src"; "hello, world"];
["copy_size"; "/src"; "/dest"; "5"];
Note this will fail if the source is too short or if the destination
is not large enough.");
- ("zero_device", (RErr, [Device "device"]), 228, [DangerWillRobinson],
+ ("zero_device", (RErr, [Device "device"]), 228, [DangerWillRobinson; Progress],
[InitBasicFSonLVM, Always, TestRun (
[["zero_device"; "/dev/VG/LV"]])],
"write zeroes to an entire device",
backslash syntax. For more information, see the GNU
coreutils info file.");
- ("fill_pattern", (RErr, [String "pattern"; Int "len"; Pathname "path"]), 245, [],
+ ("fill_pattern", (RErr, [String "pattern"; Int "len"; Pathname "path"]), 245, [Progress],
[InitBasicFS, Always, TestOutputBuffer (
[["fill_pattern"; "abcdefghijklmnopqrstuvwxyz"; "28"; "/test"];
["read_file"; "/test"]], "abcdefghijklmnopqrstuvwxyzab")],
| CallBool of bool
| CallBuffer of string
+(* Used for the guestfish -N (prepared disk images) option.
+ * Note that the longdescs are indented by 2 spaces.
+ *)
+let prepopts = [
+ ("disk",
+ "create a blank disk",
+ [ "size", "100M", "the size of the disk image" ],
+ " Create a blank disk, size 100MB (by default).
+
+ The default size can be changed by supplying an optional parameter.");
+
+ ("part",
+ "create a partitioned disk",
+ [ "size", "100M", "the size of the disk image";
+ "partition", "mbr", "partition table type" ],
+ " Create a disk with a single partition. By default the size of the disk
+ is 100MB (the available space in the partition will be a tiny bit smaller)
+ and the partition table will be MBR (old DOS-style).
+
+ These defaults can be changed by supplying optional parameters.");
+
+ ("fs",
+ "create a filesystem",
+ [ "filesystem", "ext2", "the type of filesystem to use";
+ "size", "100M", "the size of the disk image";
+ "partition", "mbr", "partition table type" ],
+ " Create a disk with a single partition, with the partition containing
+ an empty filesystem. This defaults to creating a 100MB disk (the available
+ space in the filesystem will be a tiny bit smaller) with an MBR (old
+ DOS-style) partition table and an ext2 filesystem.
+
+ These defaults can be changed by supplying optional parameters.");
+
+ ("lv",
+ "create a disk with logical volume",
+ [ "name", "/dev/VG/LV", "the name of the VG and LV to use";
+ "size", "100M", "the size of the disk image";
+ "partition", "mbr", "partition table type" ],
+ " Create a disk with a single partition, set up the partition as an
+ LVM2 physical volume, and place a volume group and logical volume
+ on there. This defaults to creating a 100MB disk with the VG and
+ LV called /dev/VG/LV. You can change the name of the VG and LV
+ by supplying an alternate name as the first optional parameter.
+
+ Note this does not create a filesystem. Use 'lvfs' to do that.");
+
+ ("lvfs",
+ "create a disk with logical volume and filesystem",
+ [ "name", "/dev/VG/LV", "the name of the VG and LV to use";
+ "filesystem", "ext2", "the type of filesystem to use";
+ "size", "100M", "the size of the disk image";
+ "partition", "mbr", "partition table type" ],
+ " Create a disk with a single partition, set up the partition as an
+ LVM2 physical volume, and place a volume group and logical volume
+ on there. Then format the LV with a filesystem. This defaults to
+ creating a 100MB disk with the VG and LV called /dev/VG/LV, with an
+ ext2 filesystem.");
+
+]
+
(* Used to memoize the result of pod2text. *)
let pod2text_memo_filename = "src/.pod2text.data"
let pod2text_memo : ((int * string * string), string list) Hashtbl.t =
| TestLastFail s -> s
(* Handling for function flags. *)
+let progress_message =
+ "This long-running command can generate progress notification messages
+so that the caller can display a progress bar or indicator.
+To receive these messages, the caller must register a progress
+callback. See L<guestfs(3)/guestfs_set_progress_callback>."
+
let protocol_limit_warning =
"Because of the message protocol, there is a transfer limit
of somewhere between 2MB and 4MB. See L<guestfs(3)/PROTOCOL LIMITS>."
The size of the returned buffer is written to C<*size_r>.
I<The caller must free the returned buffer after use>.\n\n"
);
+ if List.mem Progress flags then
+ pr "%s\n\n" progress_message;
if List.mem ProtocolLimitWarning flags then
pr "%s\n\n" protocol_limit_warning;
if List.mem DangerWillRobinson flags then
generate_header CStyle LGPLv2plus;
(* This has to be defined to get around a limitation in Sun's rpcgen. *)
- pr "typedef string str<>;\n";
+ pr "typedef string guestfs_str<>;\n";
pr "\n";
(* Internal structures. *)
function
| Pathname n | Device n | Dev_or_Path n | String n | Key n ->
pr " string %s<>;\n" n
- | OptString n -> pr " str *%s;\n" n
- | StringList n | DeviceList n -> pr " str %s<>;\n" n
+ | OptString n -> pr " guestfs_str *%s;\n" n
+ | StringList n | DeviceList n -> pr " guestfs_str %s<>;\n" n
| Bool n -> pr " bool %s;\n" n
| Int n -> pr " int %s;\n" n
| Int64 n -> pr " hyper %s;\n" n
pr "};\n\n"
| RStringList n ->
pr "struct %s_ret {\n" name;
- pr " str %s<>;\n" n;
+ pr " guestfs_str %s<>;\n" n;
pr "};\n\n"
| RStruct (n, typ) ->
pr "struct %s_ret {\n" name;
pr "};\n\n"
| RHashtable n ->
pr "struct %s_ret {\n" name;
- pr " str %s<>;\n" n;
+ pr " guestfs_str %s<>;\n" n;
pr "};\n\n"
| RBufferOut n ->
pr "struct %s_ret {\n" name;
*/
const GUESTFS_PROGRAM = 0x2000F5F5;
-const GUESTFS_PROTOCOL_VERSION = 1;
+const GUESTFS_PROTOCOL_VERSION = 2;
/* These constants must be larger than any possible message length. */
const GUESTFS_LAUNCH_FLAG = 0xf5f55ff5;
const GUESTFS_CANCEL_FLAG = 0xffffeeee;
+const GUESTFS_PROGRESS_FLAG = 0xffff5555;
enum guestfs_message_direction {
GUESTFS_DIRECTION_CALL = 0, /* client -> daemon */
GUESTFS_STATUS_ERROR = 1
};
-const GUESTFS_ERROR_LEN = 256;
+";
+
+ pr "const GUESTFS_ERROR_LEN = %d;\n" (64 * 1024);
+ pr "\n";
+ pr "\
struct guestfs_message_error {
+ int linux_errno; /* Linux errno if available. */
string error_message<GUESTFS_ERROR_LEN>;
};
/* data size is 0 bytes if the transfer has finished successfully */
opaque data<GUESTFS_MAX_CHUNK_SIZE>;
};
+
+/* Progress notifications. Daemon self-limits these messages to
+ * at most one per second. The daemon can send these messages
+ * at any time, and the caller should discard unexpected messages.
+ * 'position' and 'total' have undefined units; however they may
+ * have meaning for some calls.
+ *
+ * NB. guestfs___recv_from_daemon assumes the XDR-encoded
+ * structure is 24 bytes long.
+ */
+struct guestfs_progress {
+ guestfs_procedure proc; /* @0: GUESTFS_PROC_x */
+ unsigned serial; /* @4: message serial number */
+ unsigned hyper position; /* @8: 0 <= position <= total */
+ unsigned hyper total; /* @16: total size of operation */
+ /* @24: size of structure */
+};
"
(* Generate the guestfs-structs.h file. *)
and generate_actions_h () =
generate_header CStyle LGPLv2plus;
List.iter (
- fun (shortname, style, _, _, _, _, _) ->
+ fun (shortname, style, _, flags, _, _, _) ->
let name = "guestfs_" ^ shortname in
+
+ let deprecated =
+ List.exists (function DeprecatedBy _ -> true | _ -> false) flags in
+ let test0 =
+ String.length shortname >= 5 && String.sub shortname 0 5 = "test0" in
+ let debug =
+ String.length shortname >= 5 && String.sub shortname 0 5 = "debug" in
+ if not deprecated && not test0 && not debug then
+ pr "#define LIBGUESTFS_HAVE_%s 1\n" (String.uppercase shortname);
+
generate_prototype ~single_line:true ~newline:true ~handle:"g"
name style
- ) all_functions
+ ) all_functions_sorted
(* Generate the guestfs-internal-actions.h file. *)
and generate_internal_actions_h () =
"guestfs_close";
"guestfs_get_error_handler";
"guestfs_get_out_of_memory_handler";
+ "guestfs_get_private";
"guestfs_last_error";
"guestfs_set_close_callback";
"guestfs_set_error_handler";
"guestfs_set_launch_done_callback";
"guestfs_set_log_message_callback";
"guestfs_set_out_of_memory_handler";
+ "guestfs_set_private";
+ "guestfs_set_progress_callback";
"guestfs_set_subprocess_quit_callback";
(* Unofficial parts of the API: the bindings code use these
| Some txt -> pr "%s\n\n" txt
) all_functions_sorted
+and generate_fish_prep_options_h () =
+ generate_header CStyle GPLv2plus;
+
+ pr "#ifndef PREPOPTS_H\n";
+ pr "\n";
+
+ pr "\
+struct prep {
+ const char *name; /* eg. \"fs\" */
+
+ size_t nr_params; /* optional parameters */
+ struct prep_param *params;
+
+ const char *shortdesc; /* short description */
+ const char *longdesc; /* long description */
+
+ /* functions to implement it */
+ void (*prelaunch) (const char *filename, prep_data *);
+ void (*postlaunch) (const char *filename, prep_data *, const char *device);
+};
+
+struct prep_param {
+ const char *pname; /* parameter name */
+ const char *pdefault; /* parameter default */
+ const char *pdesc; /* parameter description */
+};
+
+extern const struct prep preps[];
+#define NR_PREPS %d
+
+" (List.length prepopts);
+
+ List.iter (
+ fun (name, shortdesc, args, longdesc) ->
+ pr "\
+extern void prep_prelaunch_%s (const char *filename, prep_data *data);
+extern void prep_postlaunch_%s (const char *filename, prep_data *data, const char *device);
+
+" name name;
+ ) prepopts;
+
+ pr "\n";
+ pr "#endif /* PREPOPTS_H */\n"
+
+and generate_fish_prep_options_c () =
+ generate_header CStyle GPLv2plus;
+
+ pr "\
+#include \"fish.h\"
+#include \"prepopts.h\"
+
+";
+
+ List.iter (
+ fun (name, shortdesc, args, longdesc) ->
+ pr "static struct prep_param %s_args[] = {\n" name;
+ List.iter (
+ fun (n, default, desc) ->
+ pr " { \"%s\", \"%s\", \"%s\" },\n" n default desc
+ ) args;
+ pr "};\n";
+ pr "\n";
+ ) prepopts;
+
+ pr "const struct prep preps[] = {\n";
+ List.iter (
+ fun (name, shortdesc, args, longdesc) ->
+ pr " { \"%s\", %d, %s_args,
+ \"%s\",
+ \"%s\",
+ prep_prelaunch_%s, prep_postlaunch_%s },
+"
+ name (List.length args) name
+ (c_quote shortdesc) (c_quote longdesc)
+ name name;
+ ) prepopts;
+ pr "};\n"
+
(* Generate a C function prototype. *)
and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
?(single_line = false) ?(newline = false) ?(in_daemon = false)
unreferenced, but callers can call this in order to provide
predictable cleanup. *)
+type progress_cb = int -> int -> int64 -> int64 -> unit
+
+val set_progress_callback : t -> progress_cb -> unit
+(** [set_progress_callback g f] sets [f] as the progress callback function.
+ For some long-running functions, [f] will be called repeatedly
+ during the function with progress updates.
+
+ The callback is [f proc_nr serial position total]. See
+ the description of [guestfs_set_progress_callback] in guestfs(3)
+ for the meaning of these four numbers.
+
+ Note that if the closure captures a reference to the handle,
+ this reference will prevent the handle from being
+ automatically closed by the garbage collector. There are
+ three ways to avoid this: be careful not to capture the handle
+ in the closure, or use a weak reference, or call
+ {!Guestfs.clear_progress_callback} to remove the reference. *)
+
+val clear_progress_callback : t -> unit
+(** [clear_progress_callback g] removes any progress callback function
+ associated with the handle. See {!Guestfs.set_progress_callback}. *)
+
";
generate_ocaml_structure_decls ();
external create : unit -> t = \"ocaml_guestfs_create\"
external close : t -> unit = \"ocaml_guestfs_close\"
+type progress_cb = int -> int -> int64 -> int64 -> unit
+
+external set_progress_callback : t -> progress_cb -> unit
+ = \"ocaml_guestfs_set_progress_callback\"
+external clear_progress_callback : t -> unit
+ = \"ocaml_guestfs_clear_progress_callback\"
+
(* Give the exceptions names, so they can be raised from the C code. *)
let () =
Callback.register_exception \"ocaml_guestfs_error\" (Error \"\");
return ret;
}
+#define PROGRESS_KEY \"_perl_progress_cb\"
+
+static void
+_clear_progress_callback (guestfs_h *g)
+{
+ guestfs_set_progress_callback (g, NULL, NULL);
+ SV *cb = guestfs_get_private (g, PROGRESS_KEY);
+ if (cb) {
+ guestfs_set_private (g, PROGRESS_KEY, NULL);
+ SvREFCNT_dec (cb);
+ }
+}
+
+/* http://www.perlmonks.org/?node=338857 */
+static void
+_progress_callback (guestfs_h *g, void *cb,
+ int proc_nr, int serial, uint64_t position, uint64_t total)
+{
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK (SP);
+ XPUSHs (sv_2mortal (newSViv (proc_nr)));
+ XPUSHs (sv_2mortal (newSViv (serial)));
+ XPUSHs (sv_2mortal (my_newSVull (position)));
+ XPUSHs (sv_2mortal (my_newSVull (total)));
+ PUTBACK;
+ call_sv ((SV *) cb, G_VOID | G_DISCARD | G_EVAL);
+ FREETMPS;
+ LEAVE;
+}
+
+static void
+_close_handle (guestfs_h *g)
+{
+ assert (g != NULL);
+ _clear_progress_callback (g);
+ guestfs_close (g);
+}
+
MODULE = Sys::Guestfs PACKAGE = Sys::Guestfs
PROTOTYPES: ENABLE
SV **svp = hv_fetch (hv, \"_g\", 2, 0);
if (svp != NULL) {
guestfs_h *g = (guestfs_h *) SvIV (*svp);
- assert (g != NULL);
- guestfs_close (g);
+ _close_handle (g);
}
void
close (g)
guestfs_h *g;
PPCODE:
- guestfs_close (g);
+ _close_handle (g);
/* Avoid double-free in DESTROY method. */
HV *hv = (HV *) SvRV (ST(0));
(void) hv_delete (hv, \"_g\", 2, G_DISCARD);
+void
+set_progress_callback (g, cb)
+ guestfs_h *g;
+ SV *cb;
+ PPCODE:
+ _clear_progress_callback (g);
+ SvREFCNT_inc (cb);
+ guestfs_set_private (g, PROGRESS_KEY, cb);
+ guestfs_set_progress_callback (g, _progress_callback, cb);
+
+void
+clear_progress_callback (g)
+ guestfs_h *g;
+ PPCODE:
+ _clear_progress_callback (g);
+
";
List.iter (
on the handle (but the implicit call to C<DESTROY> that happens
when the final reference is cleaned up is OK).
+=item $h->set_progress_callback (\\&cb);
+
+Set the progress notification callback for this handle
+to the Perl closure C<cb>.
+
+C<cb> will be called whenever a long-running operation
+generates a progress notification message. The 4 parameters
+to the function are: C<proc_nr>, C<serial>, C<position>
+and C<total>.
+
+You should carefully read the documentation for
+L<guestfs(3)/guestfs_set_progress_callback> before using
+this function.
+
+=item $h->clear_progress_callback ();
+
+This removes any progress callback function associated with
+the handle.
+
=cut
" max_proc_nr;
=back
+=head1 AVAILABILITY
+
+From time to time we add new libguestfs APIs. Also some libguestfs
+APIs won't be available in all builds of libguestfs (the Fedora
+build is full-featured, but other builds may disable features).
+How do you test whether the APIs that your Perl program needs are
+available in the version of C<Sys::Guestfs> that you are using?
+
+To test if a particular function is available in the C<Sys::Guestfs>
+class, use the ordinary Perl UNIVERSAL method C<can(METHOD)>
+(see L<perlobj(1)>). For example:
+
+ use Sys::Guestfs;
+ if (defined (Sys::Guestfs->can (\"set_verbose\"))) {
+ print \"\\$h->set_verbose is available\\n\";
+ }
+
+To test if particular features are supported by the current
+build, use the L</available> method like the example below. Note
+that the appliance must be launched first.
+
+ $h->available ( [\"augeas\"] );
+
+Since the L</available> method croaks if the feature is not supported,
+you might also want to wrap this in an eval and return a boolean.
+In fact this has already been done for you: use
+L<Sys::Guestfs::Lib(3)/feature_available>.
+
+For further discussion on this topic, refer to
+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:
+
+ {
+ _g => [private data used by libguestfs]
+ }
+
+Callers can add other elements to this hash to store data for their own
+purposes. The data lasts for the lifetime of the handle.
+
+Any fields whose names begin with an underscore are reserved
+for private use by libguestfs. We may add more in future.
+
+It is recommended that callers prefix the name of their field(s)
+with some unique string, to avoid conflicts with other users.
+
=head1 COPYRIGHT
Copyright (C) %s Red Hat Inc.
c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject);
e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError);
+#ifdef HAVE_RB_DEFINE_ALLOC_FUNC
+ rb_define_alloc_func (c_guestfs, ruby_guestfs_create);
+#endif
+
rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
}
"
+and generate_php_h () =
+ generate_header CStyle LGPLv2plus;
+
+ pr "\
+#ifndef PHP_GUESTFS_PHP_H
+#define PHP_GUESTFS_PHP_H 1
+
+#ifdef ZTS
+#include \"TSRM.h\"
+#endif
+
+#define PHP_GUESTFS_PHP_EXTNAME \"guestfs_php\"
+#define PHP_GUESTFS_PHP_VERSION \"1.0\"
+
+PHP_MINIT_FUNCTION (guestfs_php);
+
+#define PHP_GUESTFS_HANDLE_RES_NAME \"guestfs_h\"
+
+PHP_FUNCTION (guestfs_create);
+PHP_FUNCTION (guestfs_last_error);
+";
+
+ List.iter (
+ fun (shortname, style, _, _, _, _, _) ->
+ pr "PHP_FUNCTION (guestfs_%s);\n" shortname
+ ) all_functions_sorted;
+
+ pr "\
+
+extern zend_module_entry guestfs_php_module_entry;
+#define phpext_guestfs_php_ptr &guestfs_php_module_entry
+
+#endif /* PHP_GUESTFS_PHP_H */
+"
+
+and generate_php_c () =
+ generate_header CStyle LGPLv2plus;
+
+ pr "\
+/* NOTE: Be very careful with all macros in PHP header files. The
+ * morons who wrote them aren't good at making them safe for inclusion
+ * in arbitrary places in C code, eg. not using 'do ... while(0)'
+ * or parenthesizing any of the arguments.
+ */
+
+/* NOTE (2): Some parts of the API can't be used on 32 bit platforms.
+ * Any 64 bit numbers will be truncated. There's no easy way around
+ * this in PHP.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <php.h>
+#include <php_guestfs_php.h>
+
+#include \"guestfs.h\"
+
+static int res_guestfs_h;
+
+static void
+guestfs_php_handle_dtor (zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ guestfs_h *g = (guestfs_h *) rsrc->ptr;
+ if (g != NULL)
+ guestfs_close (g);
+}
+
+PHP_MINIT_FUNCTION (guestfs_php)
+{
+ res_guestfs_h =
+ zend_register_list_destructors_ex (guestfs_php_handle_dtor,
+ NULL, PHP_GUESTFS_HANDLE_RES_NAME, module_number);
+}
+
+static function_entry guestfs_php_functions[] = {
+ PHP_FE (guestfs_create, NULL)
+ PHP_FE (guestfs_last_error, NULL)
+";
+
+ List.iter (
+ fun (shortname, style, _, _, _, _, _) ->
+ pr " PHP_FE (guestfs_%s, NULL)\n" shortname
+ ) all_functions_sorted;
+
+ pr " { NULL, NULL, NULL }
+};
+
+zend_module_entry guestfs_php_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
+ PHP_GUESTFS_PHP_EXTNAME,
+ guestfs_php_functions,
+ PHP_MINIT (guestfs_php),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+#if ZEND_MODULE_API_NO >= 20010901
+ PHP_GUESTFS_PHP_VERSION,
+#endif
+ STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_GUESTFS_PHP
+ZEND_GET_MODULE (guestfs_php)
+#endif
+
+PHP_FUNCTION (guestfs_create)
+{
+ guestfs_h *g = guestfs_create ();
+ if (g == NULL) {
+ RETURN_FALSE;
+ }
+
+ guestfs_set_error_handler (g, NULL, NULL);
+
+ ZEND_REGISTER_RESOURCE (return_value, g, res_guestfs_h);
+}
+
+PHP_FUNCTION (guestfs_last_error)
+{
+ zval *z_g;
+ guestfs_h *g;
+
+ if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, \"r\",
+ &z_g) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE (g, guestfs_h *, &z_g, -1, PHP_GUESTFS_HANDLE_RES_NAME,
+ res_guestfs_h);
+ if (g == NULL) {
+ RETURN_FALSE;
+ }
+
+ const char *err = guestfs_last_error (g);
+ if (err) {
+ RETURN_STRING (err, 1);
+ } else {
+ RETURN_NULL ();
+ }
+}
+
+";
+
+ (* Now generate the PHP bindings for each action. *)
+ List.iter (
+ fun (shortname, style, _, _, _, _, _) ->
+ pr "PHP_FUNCTION (guestfs_%s)\n" shortname;
+ pr "{\n";
+ pr " zval *z_g;\n";
+ pr " guestfs_h *g;\n";
+
+ List.iter (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | Key n
+ | OptString n
+ | BufferIn n ->
+ pr " char *%s;\n" n;
+ pr " int %s_size;\n" n
+ | StringList n
+ | DeviceList n ->
+ pr " zval *z_%s;\n" n;
+ pr " char **%s;\n" n;
+ | Bool n ->
+ pr " zend_bool %s;\n" n
+ | Int n | Int64 n ->
+ pr " long %s;\n" n
+ ) (snd style);
+
+ pr "\n";
+
+ (* Parse the parameters. *)
+ let param_string = String.concat "" (
+ List.map (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | BufferIn n | Key n -> "s"
+ | OptString n -> "s!"
+ | StringList n | DeviceList n -> "a"
+ | Bool n -> "b"
+ | Int n | Int64 n -> "l"
+ ) (snd style)
+ ) in
+
+ pr " if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, \"r%s\",\n"
+ param_string;
+ pr " &z_g";
+ List.iter (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | BufferIn n | Key n
+ | OptString n ->
+ pr ", &%s, &%s_size" n n
+ | StringList n | DeviceList n ->
+ pr ", &z_%s" n
+ | Bool n ->
+ pr ", &%s" n
+ | Int n | Int64 n ->
+ pr ", &%s" n
+ ) (snd style);
+ pr ") == FAILURE) {\n";
+ pr " RETURN_FALSE;\n";
+ pr " }\n";
+ pr "\n";
+ pr " ZEND_FETCH_RESOURCE (g, guestfs_h *, &z_g, -1, PHP_GUESTFS_HANDLE_RES_NAME,\n";
+ pr " res_guestfs_h);\n";
+ pr " if (g == NULL) {\n";
+ pr " RETURN_FALSE;\n";
+ pr " }\n";
+ pr "\n";
+
+ List.iter (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | Key n
+ | OptString n ->
+ (* Just need to check the string doesn't contain any ASCII
+ * NUL characters, which won't be supported by the C API.
+ *)
+ pr " if (strlen (%s) != %s_size) {\n" n n;
+ pr " fprintf (stderr, \"libguestfs: %s: parameter '%s' contains embedded ASCII NUL.\\n\");\n" shortname n;
+ pr " RETURN_FALSE;\n";
+ pr " }\n";
+ pr "\n"
+ | BufferIn n -> ()
+ | StringList n
+ | DeviceList n ->
+ (* Convert array to list of strings.
+ * http://marc.info/?l=pecl-dev&m=112205192100631&w=2
+ *)
+ pr " {\n";
+ pr " HashTable *a;\n";
+ pr " int n;\n";
+ pr " HashPosition p;\n";
+ pr " zval **d;\n";
+ pr " size_t c = 0;\n";
+ pr "\n";
+ pr " a = Z_ARRVAL_P (z_%s);\n" n;
+ pr " n = zend_hash_num_elements (a);\n";
+ pr " %s = safe_emalloc (n + 1, sizeof (char *), 0);\n" n;
+ pr " for (zend_hash_internal_pointer_reset_ex (a, &p);\n";
+ pr " zend_hash_get_current_data_ex (a, (void **) &d, &p) == SUCCESS;\n";
+ pr " zend_hash_move_forward_ex (a, &p)) {\n";
+ pr " zval t = **d;\n";
+ pr " zval_copy_ctor (&t);\n";
+ pr " convert_to_string (&t);\n";
+ pr " %s[c] = Z_STRVAL (t);\n" n;
+ pr " c++;\n";
+ pr " }\n";
+ pr " %s[c] = NULL;\n" n;
+ pr " }\n";
+ pr "\n"
+ | Bool n | Int n | Int64 n -> ()
+ ) (snd style);
+
+ (* Return value. *)
+ let error_code =
+ match fst style with
+ | RErr -> pr " int r;\n"; "-1"
+ | RBool _
+ | RInt _ -> pr " int r;\n"; "-1"
+ | RInt64 _ -> pr " int64_t r;\n"; "-1"
+ | RConstString _ -> pr " const char *r;\n"; "NULL"
+ | RConstOptString _ -> pr " const char *r;\n"; "NULL"
+ | RString _ ->
+ pr " char *r;\n"; "NULL"
+ | RStringList _ ->
+ pr " char **r;\n"; "NULL"
+ | RStruct (_, typ) ->
+ pr " struct guestfs_%s *r;\n" typ; "NULL"
+ | RStructList (_, typ) ->
+ pr " struct guestfs_%s_list *r;\n" typ; "NULL"
+ | RHashtable _ ->
+ pr " char **r;\n"; "NULL"
+ | RBufferOut _ ->
+ pr " char *r;\n";
+ pr " size_t size;\n";
+ "NULL" in
+
+ (* Call the function. *)
+ pr " r = guestfs_%s " shortname;
+ generate_c_call_args ~handle:"g" style;
+ pr ";\n";
+ pr "\n";
+
+ (* Free up parameters. *)
+ List.iter (
+ function
+ | String n | Device n | Pathname n | Dev_or_Path n
+ | FileIn n | FileOut n | Key n
+ | OptString n -> ()
+ | BufferIn n -> ()
+ | StringList n
+ | DeviceList n ->
+ pr " {\n";
+ pr " size_t c = 0;\n";
+ pr "\n";
+ pr " for (c = 0; %s[c] != NULL; ++c)\n" n;
+ pr " efree (%s[c]);\n" n;
+ pr " efree (%s);\n" n;
+ pr " }\n";
+ pr "\n"
+ | Bool n | Int n | Int64 n -> ()
+ ) (snd style);
+
+ (* Check for errors. *)
+ pr " if (r == %s) {\n" error_code;
+ pr " RETURN_FALSE;\n";
+ pr " }\n";
+ pr "\n";
+
+ (* Convert the return value. *)
+ (match fst style with
+ | RErr ->
+ pr " RETURN_TRUE;\n"
+ | RBool _ ->
+ pr " RETURN_BOOL (r);\n"
+ | RInt _ ->
+ pr " RETURN_LONG (r);\n"
+ | RInt64 _ ->
+ pr " RETURN_LONG (r);\n"
+ | RConstString _ ->
+ pr " RETURN_STRING (r, 1);\n"
+ | RConstOptString _ ->
+ pr " if (r) { RETURN_STRING (r, 1); }\n";
+ pr " else { RETURN_NULL (); }\n"
+ | RString _ ->
+ pr " char *r_copy = estrdup (r);\n";
+ pr " free (r);\n";
+ pr " RETURN_STRING (r_copy, 0);\n"
+ | RBufferOut _ ->
+ pr " char *r_copy = estrndup (r, size);\n";
+ pr " free (r);\n";
+ pr " RETURN_STRING (r_copy, 0);\n"
+ | RStringList _ ->
+ pr " size_t c = 0;\n";
+ pr " array_init (return_value);\n";
+ pr " for (c = 0; r[c] != NULL; ++c) {\n";
+ pr " add_next_index_string (return_value, r[c], 1);\n";
+ pr " free (r[c]);\n";
+ pr " }\n";
+ pr " free (r);\n";
+ | RHashtable _ ->
+ pr " size_t c = 0;\n";
+ pr " array_init (return_value);\n";
+ pr " for (c = 0; r[c] != NULL; c += 2) {\n";
+ pr " add_assoc_string (return_value, r[c], r[c+1], 1);\n";
+ pr " free (r[c]);\n";
+ pr " free (r[c+1]);\n";
+ pr " }\n";
+ pr " free (r);\n";
+ | RStruct (_, typ) ->
+ let cols = cols_of_struct typ in
+ generate_php_struct_code typ cols
+ | RStructList (_, typ) ->
+ let cols = cols_of_struct typ in
+ generate_php_struct_list_code typ cols
+ );
+
+ pr "}\n";
+ pr "\n"
+ ) all_functions_sorted
+
+and generate_php_struct_code typ cols =
+ pr " array_init (return_value);\n";
+ List.iter (
+ function
+ | name, FString ->
+ pr " add_assoc_string (return_value, \"%s\", r->%s, 1);\n" name name
+ | name, FBuffer ->
+ pr " add_assoc_stringl (return_value, \"%s\", r->%s, r->%s_len, 1);\n"
+ name name name
+ | name, FUUID ->
+ pr " add_assoc_stringl (return_value, \"%s\", r->%s, 32, 1);\n"
+ name name
+ | name, (FBytes|FUInt64|FInt64|FInt32|FUInt32) ->
+ pr " add_assoc_long (return_value, \"%s\", r->%s);\n"
+ name name
+ | name, FChar ->
+ pr " add_assoc_stringl (return_value, \"%s\", &r->%s, 1, 1);\n"
+ name name
+ | name, FOptPercent ->
+ pr " add_assoc_double (return_value, \"%s\", r->%s);\n"
+ name name
+ ) cols;
+ pr " guestfs_free_%s (r);\n" typ
+
+and generate_php_struct_list_code typ cols =
+ pr " array_init (return_value);\n";
+ pr " size_t c = 0;\n";
+ pr " for (c = 0; c < r->len; ++c) {\n";
+ pr " zval *z_elem;\n";
+ pr " ALLOC_INIT_ZVAL (z_elem);\n";
+ pr " array_init (z_elem);\n";
+ List.iter (
+ function
+ | name, FString ->
+ pr " add_assoc_string (z_elem, \"%s\", r->val[c].%s, 1);\n"
+ name name
+ | name, FBuffer ->
+ pr " add_assoc_stringl (z_elem, \"%s\", r->val[c].%s, r->val[c].%s_len, 1);\n"
+ name name name
+ | name, FUUID ->
+ pr " add_assoc_stringl (z_elem, \"%s\", r->val[c].%s, 32, 1);\n"
+ name name
+ | name, (FBytes|FUInt64|FInt64|FInt32|FUInt32) ->
+ pr " add_assoc_long (z_elem, \"%s\", r->val[c].%s);\n"
+ name name
+ | name, FChar ->
+ pr " add_assoc_stringl (z_elem, \"%s\", &r->val[c].%s, 1, 1);\n"
+ name name
+ | name, FOptPercent ->
+ pr " add_assoc_double (z_elem, \"%s\", r->val[c].%s);\n"
+ name name
+ ) cols;
+ pr " add_next_index_zval (return_value, z_elem);\n";
+ pr " }\n";
+ pr " guestfs_free_%s_list (r);\n" typ
+
and generate_bindtests () =
generate_header CStyle LGPLv2plus;
output_to "fish/cmds.c" generate_fish_cmds;
output_to "fish/completion.c" generate_fish_completion;
output_to "fish/guestfish-actions.pod" generate_fish_actions_pod;
+ output_to "fish/prepopts.c" generate_fish_prep_options_c;
+ output_to "fish/prepopts.h" generate_fish_prep_options_h;
output_to "ocaml/guestfs.mli" generate_ocaml_mli;
output_to "ocaml/guestfs.ml" generate_ocaml_ml;
output_to "ocaml/guestfs_c_actions.c" generate_ocaml_c;
output_to "haskell/Guestfs.hs" generate_haskell_hs;
output_to "haskell/Bindtests.hs" generate_haskell_bindtests;
output_to "csharp/Libguestfs.cs" generate_csharp;
+ output_to "php/extension/php_guestfs_php.h" generate_php_h;
+ output_to "php/extension/guestfs_php.c" generate_php_c;
(* Always generate this file last, and unconditionally. It's used
* by the Makefile to know when we must re-run the generator.