New APIs: set-network and get-network to enable network support.
[libguestfs.git] / src / generator.ml
index a3333ed..00caa6a 100755 (executable)
@@ -856,7 +856,7 @@ see L<guestfs(3)>.");
    "enable or disable command traces",
    "\
 If the command trace flag is set to 1, then commands are
-printed on stdout before they are executed in a format
+printed on stderr before they are executed in a format
 which is very similar to the one used by guestfish.  In
 other words, you can run a program with this enabled, and
 you will get out a script which you can feed to guestfish
@@ -940,6 +940,374 @@ to specify the QEMU interface emulation to use at run time.");
 This is the same as C<guestfs_add_drive_ro> but it allows you
 to specify the QEMU interface emulation to use at run time.");
 
+  ("file_architecture", (RString "arch", [Pathname "filename"]), -1, [],
+   [InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/bin-i586-dynamic"]], "i386");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/bin-sparc-dynamic"]], "sparc");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/bin-win32.exe"]], "i386");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/bin-win64.exe"]], "x86_64");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/bin-x86_64-dynamic"]], "x86_64");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/lib-i586.so"]], "i386");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/lib-sparc.so"]], "sparc");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/lib-win32.dll"]], "i386");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/lib-win64.dll"]], "x86_64");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/lib-x86_64.so"]], "x86_64");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/initrd-x86_64.img"]], "x86_64");
+    InitISOFS, Always, TestOutput (
+      [["file_architecture"; "/initrd-x86_64.img.gz"]], "x86_64");],
+   "detect the architecture of a binary file",
+   "\
+This detects the architecture of the binary C<filename>,
+and returns it if known.
+
+Currently defined architectures are:
+
+=over 4
+
+=item \"i386\"
+
+This string is returned for all 32 bit i386, i486, i586, i686 binaries
+irrespective of the precise processor requirements of the binary.
+
+=item \"x86_64\"
+
+64 bit x86-64.
+
+=item \"sparc\"
+
+32 bit SPARC.
+
+=item \"sparc64\"
+
+64 bit SPARC V9 and above.
+
+=item \"ia64\"
+
+Intel Itanium.
+
+=item \"ppc\"
+
+32 bit Power PC.
+
+=item \"ppc64\"
+
+64 bit Power PC.
+
+=back
+
+Libguestfs may return other architecture strings in future.
+
+The function works on at least the following types of files:
+
+=over 4
+
+=item *
+
+many types of Un*x and Linux binary
+
+=item *
+
+many types of Un*x and Linux shared library
+
+=item *
+
+Windows Win32 and Win64 binaries
+
+=item *
+
+Windows Win32 and Win64 DLLs
+
+Win32 binaries and DLLs return C<i386>.
+
+Win64 binaries and DLLs return C<x86_64>.
+
+=item *
+
+Linux kernel modules
+
+=item *
+
+Linux new-style initrd images
+
+=item *
+
+some non-x86 Linux vmlinuz kernels
+
+=back
+
+What it can't do currently:
+
+=over 4
+
+=item *
+
+static libraries (libfoo.a)
+
+=item *
+
+Linux old-style initrd as compressed ext2 filesystem (RHEL 3)
+
+=item *
+
+x86 Linux vmlinuz kernels
+
+x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and
+compressed code, and are horribly hard to unpack.  If you want to find
+the architecture of a kernel, use the architecture of the associated
+initrd or kernel module(s) instead.
+
+=back");
+
+  ("inspect_os", (RStringList "roots", []), -1, [],
+   [],
+   "inspect disk and return list of operating systems found",
+   "\
+This function uses other libguestfs functions and certain
+heuristics to inspect the disk(s) (usually disks belonging to
+a virtual machine), looking for operating systems.
+
+The list returned is empty if no operating systems were found.
+
+If one operating system was found, then this returns a list with
+a single element, which is the name of the root filesystem of
+this operating system.  It is also possible for this function
+to return a list containing more than one element, indicating
+a dual-boot or multi-boot virtual machine, with each element being
+the root filesystem of one of the operating systems.
+
+You can pass the root string(s) returned to other
+C<guestfs_inspect_get_*> functions in order to query further
+information about each operating system, such as the name
+and version.
+
+This function uses other libguestfs features such as
+C<guestfs_mount_ro> and C<guestfs_umount_all> in order to mount
+and unmount filesystems and look at the contents.  This should
+be called with no disks currently mounted.  The function may also
+use Augeas, so any existing Augeas handle will be closed.
+
+This function cannot decrypt encrypted disks.  The caller
+must do that first (supplying the necessary keys) if the
+disk is encrypted.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
+  ("inspect_get_type", (RString "name", [Device "root"]), -1, [],
+   [],
+   "get type of inspected operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This returns the type of the inspected operating system.
+Currently defined types are:
+
+=over 4
+
+=item \"linux\"
+
+Any Linux-based operating system.
+
+=item \"windows\"
+
+Any Microsoft Windows operating system.
+
+=item \"unknown\"
+
+The operating system type could not be determined.
+
+=back
+
+Future versions of libguestfs may return other strings here.
+The caller should be prepared to handle any string.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
+  ("inspect_get_arch", (RString "arch", [Device "root"]), -1, [],
+   [],
+   "get architecture of inspected operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This returns the architecture of the inspected operating system.
+The possible return values are listed under
+C<guestfs_file_architecture>.
+
+If the architecture could not be determined, then the
+string C<unknown> is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
+  ("inspect_get_distro", (RString "distro", [Device "root"]), -1, [],
+   [],
+   "get distro of inspected operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This returns the distro (distribution) of the inspected operating
+system.
+
+Currently defined distros are:
+
+=over 4
+
+=item \"debian\"
+
+Debian or a Debian-derived distro such as Ubuntu.
+
+=item \"fedora\"
+
+Fedora.
+
+=item \"redhat-based\"
+
+Some Red Hat-derived distro.
+
+=item \"rhel\"
+
+Red Hat Enterprise Linux and some derivatives.
+
+=item \"windows\"
+
+Windows does not have distributions.  This string is
+returned if the OS type is Windows.
+
+=item \"unknown\"
+
+The distro could not be determined.
+
+=back
+
+Future versions of libguestfs may return other strings here.
+The caller should be prepared to handle any string.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
+  ("inspect_get_major_version", (RInt "major", [Device "root"]), -1, [],
+   [],
+   "get major version of inspected operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This returns the major version number of the inspected operating
+system.
+
+Windows uses a consistent versioning scheme which is I<not>
+reflected in the popular public names used by the operating system.
+Notably the operating system known as \"Windows 7\" is really
+version 6.1 (ie. major = 6, minor = 1).  You can find out the
+real versions corresponding to releases of Windows by consulting
+Wikipedia or MSDN.
+
+If the version could not be determined, then C<0> is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
+  ("inspect_get_minor_version", (RInt "minor", [Device "root"]), -1, [],
+   [],
+   "get minor version of inspected operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This returns the minor version number of the inspected operating
+system.
+
+If the version could not be determined, then C<0> is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details.
+See also C<guestfs_inspect_get_major_version>.");
+
+  ("inspect_get_product_name", (RString "product", [Device "root"]), -1, [],
+   [],
+   "get product name of inspected operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This returns the product name of the inspected operating
+system.  The product name is generally some freeform string
+which can be displayed to the user, but should not be
+parsed by programs.
+
+If the product name could not be determined, then the
+string C<unknown> is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details.");
+
+  ("inspect_get_mountpoints", (RHashtable "mountpoints", [Device "root"]), -1, [],
+   [],
+   "get mountpoints of inspected operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This returns a hash of where we think the filesystems
+associated with this operating system should be mounted.
+Callers should note that this is at best an educated guess
+made by reading configuration files such as C</etc/fstab>.
+
+Each element in the returned hashtable has a key which
+is the path of the mountpoint (eg. C</boot>) and a value
+which is the filesystem that would be mounted there
+(eg. C</dev/sda1>).
+
+Non-mounted devices such as swap devices are I<not>
+returned in this list.
+
+Please read L<guestfs(3)/INSPECTION> for more details.
+See also C<guestfs_inspect_get_filesystems>.");
+
+  ("inspect_get_filesystems", (RStringList "filesystems", [Device "root"]), -1, [],
+   [],
+   "get filesystems associated with inspected operating system",
+   "\
+This function should only be called with a root device string
+as returned by C<guestfs_inspect_os>.
+
+This returns a list of all the filesystems that we think
+are associated with this operating system.  This includes
+the root filesystem, other ordinary filesystems, and
+non-mounted devices like swap partitions.
+
+In the case of a multi-boot virtual machine, it is possible
+for a filesystem to be shared between operating systems.
+
+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
@@ -4824,7 +5192,9 @@ a file in the host and attach it as a device.");
 This returns the filesystem label of the filesystem on
 C<device>.
 
-If the filesystem is unlabeled, this returns the empty string.");
+If the filesystem is unlabeled, this returns the empty string.
+
+To find a filesystem from the label, use C<guestfs_findfs_label>.");
 
   ("vfs_uuid", (RString "uuid", [Device "device"]), 254, [],
    (let uuid = uuidgen () in
@@ -4836,7 +5206,9 @@ If the filesystem is unlabeled, this returns the empty string.");
 This returns the filesystem UUID of the filesystem on
 C<device>.
 
-If the filesystem does not have a UUID, this returns the empty string.");
+If the filesystem does not have a UUID, this returns the empty string.
+
+To find a filesystem from the UUID, use C<guestfs_findfs_uuid>.");
 
   ("lvm_set_filter", (RErr, [DeviceList "devices"]), 255, [Optional "lvm2"],
    (* Can't be tested with the current framework because
@@ -4963,6 +5335,26 @@ I<other> keys.");
 This command tests whether C<device> is a logical volume, and
 returns true iff this is the case.");
 
+  ("findfs_uuid", (RString "device", [String "uuid"]), 265, [],
+   [],
+   "find a filesystem by UUID",
+   "\
+This command searches the filesystems and returns the one
+which has the given UUID.  An error is returned if no such
+filesystem can be found.
+
+To find the UUID of a filesystem, use C<guestfs_vfs_uuid>.");
+
+  ("findfs_label", (RString "device", [String "label"]), 266, [],
+   [],
+   "find a filesystem by label",
+   "\
+This command searches the filesystems and returns the one
+which has the given label.  An error is returned if no such
+filesystem can be found.
+
+To find the label of a filesystem, use C<guestfs_vfs_label>.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
@@ -5951,9 +6343,14 @@ enum guestfs_message_status {
   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>;
 };
 
@@ -6059,13 +6456,6 @@ and generate_client_actions () =
 #include \"guestfs-internal-actions.h\"
 #include \"guestfs_protocol.h\"
 
-#define error guestfs_error
-//#define perrorf guestfs_perrorf
-#define safe_malloc guestfs_safe_malloc
-#define safe_realloc guestfs_safe_realloc
-//#define safe_strdup guestfs_safe_strdup
-#define safe_memdup guestfs_safe_memdup
-
 /* Check the return message from a call for validity. */
 static int
 check_reply_header (guestfs_h *g,
@@ -6174,7 +6564,7 @@ check_state (guestfs_h *g, const char *caller)
       pr "\n"
     );
 
-    pr "    printf (\"%s\");\n" shortname;
+    pr "    fprintf (stderr, \"%s\");\n" shortname;
     List.iter (
       function
       | String n                       (* strings *)
@@ -6186,27 +6576,27 @@ check_state (guestfs_h *g, const char *caller)
       | BufferIn n
       | Key n ->
           (* guestfish doesn't support string escaping, so neither do we *)
-          pr "    printf (\" \\\"%%s\\\"\", %s);\n" n
+          pr "    fprintf (stderr, \" \\\"%%s\\\"\", %s);\n" n
       | OptString n ->                 (* string option *)
-          pr "    if (%s) printf (\" \\\"%%s\\\"\", %s);\n" n n;
-          pr "    else printf (\" null\");\n"
+          pr "    if (%s) fprintf (stderr, \" \\\"%%s\\\"\", %s);\n" n n;
+          pr "    else fprintf (stderr, \" null\");\n"
       | StringList n
       | DeviceList n ->                        (* string list *)
-          pr "    putchar (' ');\n";
-          pr "    putchar ('\"');\n";
+          pr "    fputc (' ', stderr);\n";
+          pr "    fputc ('\"', stderr);\n";
           pr "    for (i = 0; %s[i]; ++i) {\n" n;
-          pr "      if (i > 0) putchar (' ');\n";
-          pr "      fputs (%s[i], stdout);\n" n;
+          pr "      if (i > 0) fputc (' ', stderr);\n";
+          pr "      fputs (%s[i], stderr);\n" n;
           pr "    }\n";
-          pr "    putchar ('\"');\n";
+          pr "    fputc ('\"', stderr);\n";
       | Bool n ->                      (* boolean *)
-          pr "    fputs (%s ? \" true\" : \" false\", stdout);\n" n
+          pr "    fputs (%s ? \" true\" : \" false\", stderr);\n" n
       | Int n ->                       (* int *)
-          pr "    printf (\" %%d\", %s);\n" n
+          pr "    fprintf (stderr, \" %%d\", %s);\n" n
       | Int64 n ->
-          pr "    printf (\" %%\" PRIi64, %s);\n" n
+          pr "    fprintf (stderr, \" %%\" PRIi64, %s);\n" n
     ) (snd style);
-    pr "    putchar ('\\n');\n";
+    pr "    fputc ('\\n', stderr);\n";
     pr "  }\n";
     pr "\n";
   in
@@ -11829,494 +12219,6 @@ and generate_lang_bindtests call =
 
 (* XXX Add here tests of the return and error functions. *)
 
-(* Code to generator bindings for virt-inspector.  Currently only
- * implemented for OCaml code (for virt-p2v 2.0).
- *)
-let rng_input = "inspector/virt-inspector.rng"
-
-(* Read the input file and parse it into internal structures.  This is
- * by no means a complete RELAX NG parser, but is just enough to be
- * able to parse the specific input file.
- *)
-type rng =
-  | Element of string * rng list        (* <element name=name/> *)
-  | Attribute of string * rng list        (* <attribute name=name/> *)
-  | Interleave of rng list                (* <interleave/> *)
-  | ZeroOrMore of rng                        (* <zeroOrMore/> *)
-  | OneOrMore of rng                        (* <oneOrMore/> *)
-  | Optional of rng                        (* <optional/> *)
-  | Choice of string list                (* <choice><value/>*</choice> *)
-  | Value of string                        (* <value>str</value> *)
-  | Text                                (* <text/> *)
-
-let rec string_of_rng = function
-  | Element (name, xs) ->
-      "Element (\"" ^ name ^ "\", (" ^ string_of_rng_list xs ^ "))"
-  | Attribute (name, xs) ->
-      "Attribute (\"" ^ name ^ "\", (" ^ string_of_rng_list xs ^ "))"
-  | Interleave xs -> "Interleave (" ^ string_of_rng_list xs ^ ")"
-  | ZeroOrMore rng -> "ZeroOrMore (" ^ string_of_rng rng ^ ")"
-  | OneOrMore rng -> "OneOrMore (" ^ string_of_rng rng ^ ")"
-  | Optional rng -> "Optional (" ^ string_of_rng rng ^ ")"
-  | Choice values -> "Choice [" ^ String.concat ", " values ^ "]"
-  | Value value -> "Value \"" ^ value ^ "\""
-  | Text -> "Text"
-
-and string_of_rng_list xs =
-  String.concat ", " (List.map string_of_rng xs)
-
-let rec parse_rng ?defines context = function
-  | [] -> []
-  | Xml.Element ("element", ["name", name], children) :: rest ->
-      Element (name, parse_rng ?defines context children)
-      :: parse_rng ?defines context rest
-  | Xml.Element ("attribute", ["name", name], children) :: rest ->
-      Attribute (name, parse_rng ?defines context children)
-      :: parse_rng ?defines context rest
-  | Xml.Element ("interleave", [], children) :: rest ->
-      Interleave (parse_rng ?defines context children)
-      :: parse_rng ?defines context rest
-  | Xml.Element ("zeroOrMore", [], [child]) :: rest ->
-      let rng = parse_rng ?defines context [child] in
-      (match rng with
-       | [child] -> ZeroOrMore child :: parse_rng ?defines context rest
-       | _ ->
-           failwithf "%s: <zeroOrMore> contains more than one child element"
-             context
-      )
-  | Xml.Element ("oneOrMore", [], [child]) :: rest ->
-      let rng = parse_rng ?defines context [child] in
-      (match rng with
-       | [child] -> OneOrMore child :: parse_rng ?defines context rest
-       | _ ->
-           failwithf "%s: <oneOrMore> contains more than one child element"
-             context
-      )
-  | Xml.Element ("optional", [], [child]) :: rest ->
-      let rng = parse_rng ?defines context [child] in
-      (match rng with
-       | [child] -> Optional child :: parse_rng ?defines context rest
-       | _ ->
-           failwithf "%s: <optional> contains more than one child element"
-             context
-      )
-  | Xml.Element ("choice", [], children) :: rest ->
-      let values = List.map (
-        function Xml.Element ("value", [], [Xml.PCData value]) -> value
-        | _ ->
-            failwithf "%s: can't handle anything except <value> in <choice>"
-              context
-      ) children in
-      Choice values
-      :: parse_rng ?defines context rest
-  | Xml.Element ("value", [], [Xml.PCData value]) :: rest ->
-      Value value :: parse_rng ?defines context rest
-  | Xml.Element ("text", [], []) :: rest ->
-      Text :: parse_rng ?defines context rest
-  | Xml.Element ("ref", ["name", name], []) :: rest ->
-      (* Look up the reference.  Because of limitations in this parser,
-       * we can't handle arbitrarily nested <ref> yet.  You can only
-       * use <ref> from inside <start>.
-       *)
-      (match defines with
-       | None ->
-           failwithf "%s: contains <ref>, but no refs are defined yet" context
-       | Some map ->
-           let rng = StringMap.find name map in
-           rng @ parse_rng ?defines context rest
-      )
-  | x :: _ ->
-      failwithf "%s: can't handle '%s' in schema" context (Xml.to_string x)
-
-let grammar =
-  let xml = Xml.parse_file rng_input in
-  match xml with
-  | Xml.Element ("grammar", _,
-                 Xml.Element ("start", _, gram) :: defines) ->
-      (* The <define/> elements are referenced in the <start> section,
-       * so build a map of those first.
-       *)
-      let defines = List.fold_left (
-        fun map ->
-          function Xml.Element ("define", ["name", name], defn) ->
-            StringMap.add name defn map
-          | _ ->
-              failwithf "%s: expected <define name=name/>" rng_input
-      ) StringMap.empty defines in
-      let defines = StringMap.mapi parse_rng defines in
-
-      (* Parse the <start> clause, passing the defines. *)
-      parse_rng ~defines "<start>" gram
-  | _ ->
-      failwithf "%s: input is not <grammar><start/><define>*</grammar>"
-        rng_input
-
-let name_of_field = function
-  | Element (name, _) | Attribute (name, _)
-  | ZeroOrMore (Element (name, _))
-  | OneOrMore (Element (name, _))
-  | Optional (Element (name, _)) -> name
-  | Optional (Attribute (name, _)) -> name
-  | Text -> (* an unnamed field in an element *)
-      "data"
-  | rng ->
-      failwithf "name_of_field failed at: %s" (string_of_rng rng)
-
-(* At the moment this function only generates OCaml types.  However we
- * should parameterize it later so it can generate types/structs in a
- * variety of languages.
- *)
-let generate_types xs =
-  (* A simple type is one that can be printed out directly, eg.
-   * "string option".  A complex type is one which has a name and has
-   * to be defined via another toplevel definition, eg. a struct.
-   *
-   * generate_type generates code for either simple or complex types.
-   * In the simple case, it returns the string ("string option").  In
-   * the complex case, it returns the name ("mountpoint").  In the
-   * complex case it has to print out the definition before returning,
-   * so it should only be called when we are at the beginning of a
-   * new line (BOL context).
-   *)
-  let rec generate_type = function
-    | Text ->                                (* string *)
-        "string", true
-    | Choice values ->                        (* [`val1|`val2|...] *)
-        "[" ^ String.concat "|" (List.map ((^)"`") values) ^ "]", true
-    | ZeroOrMore rng ->                        (* <rng> list *)
-        let t, is_simple = generate_type rng in
-        t ^ " list (* 0 or more *)", is_simple
-    | OneOrMore rng ->                        (* <rng> list *)
-        let t, is_simple = generate_type rng in
-        t ^ " list (* 1 or more *)", is_simple
-                                        (* virt-inspector hack: bool *)
-    | Optional (Attribute (name, [Value "1"])) ->
-        "bool", true
-    | Optional rng ->                        (* <rng> list *)
-        let t, is_simple = generate_type rng in
-        t ^ " option", is_simple
-                                        (* type name = { fields ... } *)
-    | Element (name, fields) when is_attrs_interleave fields ->
-        generate_type_struct name (get_attrs_interleave fields)
-    | Element (name, [field])                (* type name = field *)
-    | Attribute (name, [field]) ->
-        let t, is_simple = generate_type field in
-        if is_simple then (t, true)
-        else (
-          pr "type %s = %s\n" name t;
-          name, false
-        )
-    | Element (name, fields) ->              (* type name = { fields ... } *)
-        generate_type_struct name fields
-    | rng ->
-        failwithf "generate_type failed at: %s" (string_of_rng rng)
-
-  and is_attrs_interleave = function
-    | [Interleave _] -> true
-    | Attribute _ :: fields -> is_attrs_interleave fields
-    | Optional (Attribute _) :: fields -> is_attrs_interleave fields
-    | _ -> false
-
-  and get_attrs_interleave = function
-    | [Interleave fields] -> fields
-    | ((Attribute _) as field) :: fields
-    | ((Optional (Attribute _)) as field) :: fields ->
-        field :: get_attrs_interleave fields
-    | _ -> assert false
-
-  and generate_types xs =
-    List.iter (fun x -> ignore (generate_type x)) xs
-
-  and generate_type_struct name fields =
-    (* Calculate the types of the fields first.  We have to do this
-     * before printing anything so we are still in BOL context.
-     *)
-    let types = List.map fst (List.map generate_type fields) in
-
-    (* Special case of a struct containing just a string and another
-     * field.  Turn it into an assoc list.
-     *)
-    match types with
-    | ["string"; other] ->
-        let fname1, fname2 =
-          match fields with
-          | [f1; f2] -> name_of_field f1, name_of_field f2
-          | _ -> assert false in
-        pr "type %s = string * %s (* %s -> %s *)\n" name other fname1 fname2;
-        name, false
-
-    | types ->
-        pr "type %s = {\n" name;
-        List.iter (
-          fun (field, ftype) ->
-            let fname = name_of_field field in
-            pr "  %s_%s : %s;\n" name fname ftype
-        ) (List.combine fields types);
-        pr "}\n";
-        (* Return the name of this type, and
-         * false because it's not a simple type.
-         *)
-        name, false
-  in
-
-  generate_types xs
-
-let generate_parsers xs =
-  (* As for generate_type above, generate_parser makes a parser for
-   * some type, and returns the name of the parser it has generated.
-   * Because it (may) need to print something, it should always be
-   * called in BOL context.
-   *)
-  let rec generate_parser = function
-    | Text ->                                (* string *)
-        "string_child_or_empty"
-    | Choice values ->                        (* [`val1|`val2|...] *)
-        sprintf "(fun x -> match Xml.pcdata (first_child x) with %s | str -> failwith (\"unexpected field value: \" ^ str))"
-          (String.concat "|"
-             (List.map (fun v -> sprintf "%S -> `%s" v v) values))
-    | ZeroOrMore rng ->                        (* <rng> list *)
-        let pa = generate_parser rng in
-        sprintf "(fun x -> List.map %s (Xml.children x))" pa
-    | OneOrMore rng ->                        (* <rng> list *)
-        let pa = generate_parser rng in
-        sprintf "(fun x -> List.map %s (Xml.children x))" pa
-                                        (* virt-inspector hack: bool *)
-    | Optional (Attribute (name, [Value "1"])) ->
-        sprintf "(fun x -> try ignore (Xml.attrib x %S); true with Xml.No_attribute _ -> false)" name
-    | Optional rng ->                        (* <rng> list *)
-        let pa = generate_parser rng in
-        sprintf "(function None -> None | Some x -> Some (%s x))" pa
-                                        (* type name = { fields ... } *)
-    | Element (name, fields) when is_attrs_interleave fields ->
-        generate_parser_struct name (get_attrs_interleave fields)
-    | Element (name, [field]) ->        (* type name = field *)
-        let pa = generate_parser field in
-        let parser_name = sprintf "parse_%s_%d" name (unique ()) in
-        pr "let %s =\n" parser_name;
-        pr "  %s\n" pa;
-        pr "let parse_%s = %s\n" name parser_name;
-        parser_name
-    | Attribute (name, [field]) ->
-        let pa = generate_parser field in
-        let parser_name = sprintf "parse_%s_%d" name (unique ()) in
-        pr "let %s =\n" parser_name;
-        pr "  %s\n" pa;
-        pr "let parse_%s = %s\n" name parser_name;
-        parser_name
-    | Element (name, fields) ->              (* type name = { fields ... } *)
-        generate_parser_struct name ([], fields)
-    | rng ->
-        failwithf "generate_parser failed at: %s" (string_of_rng rng)
-
-  and is_attrs_interleave = function
-    | [Interleave _] -> true
-    | Attribute _ :: fields -> is_attrs_interleave fields
-    | Optional (Attribute _) :: fields -> is_attrs_interleave fields
-    | _ -> false
-
-  and get_attrs_interleave = function
-    | [Interleave fields] -> [], fields
-    | ((Attribute _) as field) :: fields
-    | ((Optional (Attribute _)) as field) :: fields ->
-        let attrs, interleaves = get_attrs_interleave fields in
-        (field :: attrs), interleaves
-    | _ -> assert false
-
-  and generate_parsers xs =
-    List.iter (fun x -> ignore (generate_parser x)) xs
-
-  and generate_parser_struct name (attrs, interleaves) =
-    (* Generate parsers for the fields first.  We have to do this
-     * before printing anything so we are still in BOL context.
-     *)
-    let fields = attrs @ interleaves in
-    let pas = List.map generate_parser fields in
-
-    (* Generate an intermediate tuple from all the fields first.
-     * If the type is just a string + another field, then we will
-     * return this directly, otherwise it is turned into a record.
-     *
-     * RELAX NG note: This code treats <interleave> and plain lists of
-     * fields the same.  In other words, it doesn't bother enforcing
-     * any ordering of fields in the XML.
-     *)
-    pr "let parse_%s x =\n" name;
-    pr "  let t = (\n    ";
-    let comma = ref false in
-    List.iter (
-      fun x ->
-        if !comma then pr ",\n    ";
-        comma := true;
-        match x with
-        | Optional (Attribute (fname, [field])), pa ->
-            pr "%s x" pa
-        | Optional (Element (fname, [field])), pa ->
-            pr "%s (optional_child %S x)" pa fname
-        | Attribute (fname, [Text]), _ ->
-            pr "attribute %S x" fname
-        | (ZeroOrMore _ | OneOrMore _), pa ->
-            pr "%s x" pa
-        | Text, pa ->
-            pr "%s x" pa
-        | (field, pa) ->
-            let fname = name_of_field field in
-            pr "%s (child %S x)" pa fname
-    ) (List.combine fields pas);
-    pr "\n  ) in\n";
-
-    (match fields with
-     | [Element (_, [Text]) | Attribute (_, [Text]); _] ->
-         pr "  t\n"
-
-     | _ ->
-         pr "  (Obj.magic t : %s)\n" name
-(*
-         List.iter (
-           function
-           | (Optional (Attribute (fname, [field])), pa) ->
-               pr "  %s_%s =\n" name fname;
-               pr "    %s x;\n" pa
-           | (Optional (Element (fname, [field])), pa) ->
-               pr "  %s_%s =\n" name fname;
-               pr "    (let x = optional_child %S x in\n" fname;
-               pr "     %s x);\n" pa
-           | (field, pa) ->
-               let fname = name_of_field field in
-               pr "  %s_%s =\n" name fname;
-               pr "    (let x = child %S x in\n" fname;
-               pr "     %s x);\n" pa
-         ) (List.combine fields pas);
-         pr "}\n"
-*)
-    );
-    sprintf "parse_%s" name
-  in
-
-  generate_parsers xs
-
-(* Generate ocaml/guestfs_inspector.mli. *)
-let generate_ocaml_inspector_mli () =
-  generate_header ~extra_inputs:[rng_input] OCamlStyle LGPLv2plus;
-
-  pr "\
-(** This is an OCaml language binding to the external [virt-inspector]
-    program.
-
-    For more information, please read the man page [virt-inspector(1)].
-*)
-
-";
-
-  generate_types grammar;
-  pr "(** The nested information returned from the {!inspect} function. *)\n";
-  pr "\n";
-
-  pr "\
-val inspect : ?connect:string -> ?xml:string -> string list -> operatingsystems
-(** To inspect a libvirt domain called [name], pass a singleton
-    list: [inspect [name]].  When using libvirt only, you may
-    optionally pass a libvirt URI using [inspect ~connect:uri ...].
-
-    To inspect a disk image or images, pass a list of the filenames
-    of the disk images: [inspect filenames]
-
-    This function inspects the given guest or disk images and
-    returns a list of operating system(s) found and a large amount
-    of information about them.  In the vast majority of cases,
-    a virtual machine only contains a single operating system.
-
-    If the optional [~xml] parameter is given, then this function
-    skips running the external virt-inspector program and just
-    parses the given XML directly (which is expected to be XML
-    produced from a previous run of virt-inspector).  The list of
-    names and connect URI are ignored in this case.
-
-    This function can throw a wide variety of exceptions, for example
-    if the external virt-inspector program cannot be found, or if
-    it doesn't generate valid XML.
-*)
-"
-
-(* Generate ocaml/guestfs_inspector.ml. *)
-let generate_ocaml_inspector_ml () =
-  generate_header ~extra_inputs:[rng_input] OCamlStyle LGPLv2plus;
-
-  pr "open Unix\n";
-  pr "\n";
-
-  generate_types grammar;
-  pr "\n";
-
-  pr "\
-(* Misc functions which are used by the parser code below. *)
-let first_child = function
-  | Xml.Element (_, _, c::_) -> c
-  | Xml.Element (name, _, []) ->
-      failwith (\"expected <\" ^ name ^ \"/> to have a child node\")
-  | Xml.PCData str ->
-      failwith (\"expected XML tag, but read PCDATA '\" ^ str ^ \"' instead\")
-
-let string_child_or_empty = function
-  | Xml.Element (_, _, [Xml.PCData s]) -> s
-  | Xml.Element (_, _, []) -> \"\"
-  | Xml.Element (x, _, _) ->
-      failwith (\"expected XML tag with a single PCDATA child, but got \" ^
-                x ^ \" instead\")
-  | Xml.PCData str ->
-      failwith (\"expected XML tag, but read PCDATA '\" ^ str ^ \"' instead\")
-
-let optional_child name xml =
-  let children = Xml.children xml in
-  try
-    Some (List.find (function
-                     | Xml.Element (n, _, _) when n = name -> true
-                     | _ -> false) children)
-  with
-    Not_found -> None
-
-let child name xml =
-  match optional_child name xml with
-  | Some c -> c
-  | None ->
-      failwith (\"mandatory field <\" ^ name ^ \"/> missing in XML output\")
-
-let attribute name xml =
-  try Xml.attrib xml name
-  with Xml.No_attribute _ ->
-    failwith (\"mandatory attribute \" ^ name ^ \" missing in XML output\")
-
-";
-
-  generate_parsers grammar;
-  pr "\n";
-
-  pr "\
-(* Run external virt-inspector, then use parser to parse the XML. *)
-let inspect ?connect ?xml names =
-  let xml =
-    match xml with
-    | None ->
-        if names = [] then invalid_arg \"inspect: no names given\";
-        let cmd = [ \"virt-inspector\"; \"--xml\" ] @
-          (match connect with None -> [] | Some uri -> [ \"--connect\"; uri ]) @
-          names in
-        let cmd = List.map Filename.quote cmd in
-        let cmd = String.concat \" \" cmd in
-        let chan = open_process_in cmd in
-        let xml = Xml.parse_in chan in
-        (match close_process_in chan with
-         | WEXITED 0 -> ()
-         | WEXITED _ -> failwith \"external virt-inspector command failed\"
-         | WSIGNALED i | WSTOPPED i ->
-             failwith (\"external virt-inspector command died or stopped on sig \" ^
-                       string_of_int i)
-        );
-        xml
-    | Some doc ->
-        Xml.parse_string doc in
-  parse_operatingsystems xml
-"
-
 and generate_max_proc_nr () =
   pr "%d\n" max_proc_nr
 
@@ -12396,8 +12298,6 @@ Run it from the top source directory using the command
   output_to "ocaml/guestfs.ml" generate_ocaml_ml;
   output_to "ocaml/guestfs_c_actions.c" generate_ocaml_c;
   output_to "ocaml/bindtests.ml" generate_ocaml_bindtests;
-  output_to "ocaml/guestfs_inspector.mli" generate_ocaml_inspector_mli;
-  output_to "ocaml/guestfs_inspector.ml" generate_ocaml_inspector_ml;
   output_to "perl/Guestfs.xs" generate_perl_xs;
   output_to "perl/lib/Sys/Guestfs.pm" generate_perl_pm;
   output_to "perl/bindtests.pl" generate_perl_bindtests;