Move the following APIs into the daemon, reimplemented in OCaml:
* inspect_os
* inspect_get_roots
* inspect_get_mountpoints
* inspect_get_filesystems
* inspect_get_format
* inspect_get_type
* inspect_get_distro
* inspect_get_package_format
* inspect_get_package_management
* inspect_get_product_name
* inspect_get_product_variant
* inspect_get_major_version
* inspect_get_minor_version
* inspect_get_arch
* inspect_get_hostname
* inspect_get_windows_systemroot
* inspect_get_windows_software_hive
* inspect_get_windows_system_hive
* inspect_get_windows_current_control_set
* inspect_is_live
* inspect_is_netinst
* inspect_is_multipart
XXX inspect_get_drive_mappings
The following inspection APIs have NOT been reimplemented in this commit:
* inspect_list_applications [deprecated]
* inspect_list_applications2
* inspect_get_icon
This also embeds the ocaml-augeas library (upstream here:
http://git.annexia.org/?p=ocaml-augeas.git;a=summary), but it's
identical to the upstream version and should remain so.
---
.gitignore | 2 +
daemon/Makefile.am | 17 +-
daemon/augeas-c.c | 288 ++++++++++++++++++
daemon/augeas.README | 8 +
daemon/augeas.ml | 59 ++++
daemon/augeas.mli | 95 ++++++
daemon/inspect.ml | 249 +++++++++++++++
daemon/inspect.mli | 40 +++
daemon/inspect_fs.ml | 410 +++++++++++++++++++++++++
daemon/inspect_fs.mli | 23 ++
daemon/inspect_fs_cd.ml | 23 ++
daemon/inspect_fs_cd.mli | 25 ++
daemon/inspect_fs_unix.ml | 642 +++++++++++++++++++++++++++++++++++++++
daemon/inspect_fs_unix.mli | 53 ++++
daemon/inspect_fs_windows.ml | 23 ++
daemon/inspect_fs_windows.mli | 25 ++
daemon/inspect_types.ml | 325 ++++++++++++++++++++
daemon/inspect_types.mli | 175 +++++++++++
daemon/mount.ml | 61 ++++
daemon/mount.mli | 2 +
daemon/utils.ml | 75 +++++
daemon/utils.mli | 8 +
docs/C_SOURCE_FILES | 1 +
generator/actions.ml | 1 +
generator/actions_inspection.ml | 481 +++++++++++++++--------------
generator/actions_inspection.mli | 1 +
generator/daemon.ml | 60 +++-
generator/proc_nr.ml | 22 ++
lib/MAX_PROC_NR | 2 +-
lib/guestfs-internal.h | 2 +-
lib/inspect.c | 632 --------------------------------------
31 files changed, 2966 insertions(+), 864 deletions(-)
create mode 100644 daemon/augeas-c.c
create mode 100644 daemon/augeas.README
create mode 100644 daemon/augeas.ml
create mode 100644 daemon/augeas.mli
create mode 100644 daemon/inspect.ml
create mode 100644 daemon/inspect.mli
create mode 100644 daemon/inspect_fs.ml
create mode 100644 daemon/inspect_fs.mli
create mode 100644 daemon/inspect_fs_cd.ml
create mode 100644 daemon/inspect_fs_cd.mli
create mode 100644 daemon/inspect_fs_unix.ml
create mode 100644 daemon/inspect_fs_unix.mli
create mode 100644 daemon/inspect_fs_windows.ml
create mode 100644 daemon/inspect_fs_windows.mli
create mode 100644 daemon/inspect_types.ml
create mode 100644 daemon/inspect_types.mli
diff --git a/.gitignore b/.gitignore
index bca927afc..b645229c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -181,6 +181,8 @@ Makefile.in
/daemon/optgroups.c
/daemon/optgroups.h
/daemon/stamp-guestfsd.pod
+/daemon/stringMap.ml
+/daemon/stringMap.mli
/daemon/structs-cleanups.c
/daemon/structs-cleanups.h
/daemon/structs.ml
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 15b78d6d8..c9ac6be4c 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -75,6 +75,7 @@ guestfsd_SOURCES = \
actions.h \
available.c \
augeas.c \
+ augeas-c.c \
base64.c \
blkdiscard.c \
blkid.c \
@@ -257,6 +258,7 @@ guestfsd_CFLAGS = \
# library and then linked to the daemon. See
#
https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html
SOURCES_MLI = \
+ augeas.mli \
blkid.mli \
btrfs.mli \
chroot.mli \
@@ -265,6 +267,12 @@ SOURCES_MLI = \
file.mli \
filearch.mli \
findfs.mli \
+ inspect.mli \
+ inspect_fs_cd.mli \
+ inspect_fs.mli \
+ inspect_fs_unix.mli \
+ inspect_fs_windows.mli \
+ inspect_types.mli \
is.mli \
ldm.mli \
link.mli \
@@ -278,12 +286,13 @@ SOURCES_MLI = \
utils.mli
SOURCES_ML = \
+ augeas.ml \
types.ml \
- utils.ml \
structs.ml \
sysroot.ml \
mountable.ml \
chroot.ml \
+ utils.ml \
blkid.ml \
btrfs.ml \
devsparts.ml \
@@ -299,6 +308,12 @@ SOURCES_ML = \
parted.ml \
listfs.ml \
realpath.ml \
+ inspect_types.ml \
+ inspect_fs_cd.ml \
+ inspect_fs_unix.ml \
+ inspect_fs_windows.ml \
+ inspect_fs.ml \
+ inspect.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/augeas-c.c b/daemon/augeas-c.c
new file mode 100644
index 000000000..c06bf92da
--- /dev/null
+++ b/daemon/augeas-c.c
@@ -0,0 +1,288 @@
+/* Augeas OCaml bindings
+ * Copyright (C) 2008-2012 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * $Id: augeas_c.c,v 1.1 2008/05/06 10:48:20 rjones Exp $
+ */
+
+#include "config.h"
+
+#include <augeas.h>
+
+#include <caml/alloc.h>
+#include <caml/memory.h>
+#include <caml/mlvalues.h>
+#include <caml/fail.h>
+#include <caml/callback.h>
+#include <caml/custom.h>
+
+typedef augeas *augeas_t;
+
+/* Raise an Augeas.Error exception. */
+static void
+raise_error (const char *msg)
+{
+ caml_raise_with_string (*caml_named_value ("Augeas.Error"), msg);
+}
+
+/* Map OCaml flags to C flags. */
+static int flag_map[] = {
+ /* AugSaveBackup */ AUG_SAVE_BACKUP,
+ /* AugSaveNewFile */ AUG_SAVE_NEWFILE,
+ /* AugTypeCheck */ AUG_TYPE_CHECK,
+ /* AugNoStdinc */ AUG_NO_STDINC,
+ /* AugSaveNoop */ AUG_SAVE_NOOP,
+ /* AugNoLoad */ AUG_NO_LOAD,
+};
+
+/* Wrap and unwrap augeas_t handles, with a finalizer. */
+#define Augeas_t_val(rv) (*(augeas_t *)Data_custom_val(rv))
+
+static void
+augeas_t_finalize (value tv)
+{
+ augeas_t t = Augeas_t_val (tv);
+ if (t) aug_close (t);
+}
+
+static struct custom_operations custom_operations = {
+ (char *) "augeas_t_custom_operations",
+ augeas_t_finalize,
+ custom_compare_default,
+ custom_hash_default,
+ custom_serialize_default,
+ custom_deserialize_default
+};
+
+static value Val_augeas_t (augeas_t t)
+{
+ CAMLparam0 ();
+ CAMLlocal1 (rv);
+ /* We could choose these so that the GC can make better decisions.
+ * See 18.9.2 of the OCaml manual.
+ */
+ const int used = 0;
+ const int max = 1;
+
+ rv = caml_alloc_custom (&custom_operations,
+ sizeof (augeas_t), used, max);
+ Augeas_t_val(rv) = t;
+
+ CAMLreturn (rv);
+}
+
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+/* val create : string -> string option -> flag list -> t */
+CAMLprim value
+ocaml_augeas_create (value rootv, value loadpathv, value flagsv)
+{
+ CAMLparam1 (rootv);
+ char *root = String_val (rootv);
+ char *loadpath;
+ int flags = 0, i;
+ augeas_t t;
+
+ /* Optional loadpath. */
+ loadpath =
+ loadpathv == Val_int (0)
+ ? NULL
+ : String_val (Field (loadpathv, 0));
+
+ /* Convert list of flags to C. */
+ for (; flagsv != Val_int (0); flagsv = Field (flagsv, 1)) {
+ i = Int_val (Field (flagsv, 0));
+ flags |= flag_map[i];
+ }
+
+ t = aug_init (root, loadpath, flags);
+
+ if (t == NULL)
+ raise_error ("Augeas.create");
+
+ CAMLreturn (Val_augeas_t (t));
+}
+
+/* val close : t -> unit */
+CAMLprim value
+ocaml_augeas_close (value tv)
+{
+ CAMLparam1 (tv);
+ augeas_t t = Augeas_t_val (tv);
+
+ if (t) {
+ aug_close (t);
+ Augeas_t_val(tv) = NULL; /* So the finalizer doesn't double-free. */
+ }
+
+ CAMLreturn (Val_unit);
+}
+
+/* val get : t -> path -> value option */
+CAMLprim value
+ocaml_augeas_get (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ CAMLlocal2 (optv, v);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ const char *val;
+ int r;
+
+ r = aug_get (t, path, &val);
+ if (r == 1) { /* Return Some val */
+ v = caml_copy_string (val);
+ optv = caml_alloc (1, 0);
+ Field (optv, 0) = v;
+ } else if (r == 0) /* Return None */
+ optv = Val_int (0);
+ else if (r == -1) /* Error or multiple matches */
+ raise_error ("Augeas.get");
+ else
+ failwith ("Augeas.get: bad return value");
+
+ CAMLreturn (optv);
+}
+
+/* val exists : t -> path -> bool */
+CAMLprim value
+ocaml_augeas_exists (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ CAMLlocal1 (v);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ int r;
+
+ r = aug_get (t, path, NULL);
+ if (r == 1) /* Return true. */
+ v = Val_int (1);
+ else if (r == 0) /* Return false */
+ v = Val_int (0);
+ else if (r == -1) /* Error or multiple matches */
+ raise_error ("Augeas.exists");
+ else
+ failwith ("Augeas.exists: bad return value");
+
+ CAMLreturn (v);
+}
+
+/* val insert : t -> ?before:bool -> path -> string -> unit */
+CAMLprim value
+ocaml_augeas_insert (value tv, value beforev, value pathv, value labelv)
+{
+ CAMLparam4 (tv, beforev, pathv, labelv);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ char *label = String_val (labelv);
+ int before;
+
+ before = beforev == Val_int (0) ? 0 : Int_val (Field (beforev, 0));
+
+ if (aug_insert (t, path, label, before) == -1)
+ raise_error ("Augeas.insert");
+
+ CAMLreturn (Val_unit);
+}
+
+/* val rm : t -> path -> int */
+CAMLprim value
+ocaml_augeas_rm (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ int r;
+
+ r = aug_rm (t, path);
+ if (r == -1)
+ raise_error ("Augeas.rm");
+
+ CAMLreturn (Val_int (r));
+}
+
+/* val matches : t -> path -> path list */
+CAMLprim value
+ocaml_augeas_match (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ CAMLlocal3 (rv, v, cons);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ char **matches;
+ int r, i;
+
+ r = aug_match (t, path, &matches);
+ if (r == -1)
+ raise_error ("Augeas.matches");
+
+ /* Copy the paths to a list. */
+ rv = Val_int (0);
+ for (i = 0; i < r; ++i) {
+ v = caml_copy_string (matches[i]);
+ free (matches[i]);
+ cons = caml_alloc (2, 0);
+ Field (cons, 1) = rv;
+ Field (cons, 0) = v;
+ rv = cons;
+ }
+
+ free (matches);
+
+ CAMLreturn (rv);
+}
+
+/* val count_matches : t -> path -> int */
+CAMLprim value
+ocaml_augeas_count_matches (value tv, value pathv)
+{
+ CAMLparam2 (tv, pathv);
+ augeas_t t = Augeas_t_val (tv);
+ char *path = String_val (pathv);
+ int r;
+
+ r = aug_match (t, path, NULL);
+ if (r == -1)
+ raise_error ("Augeas.count_matches");
+
+ CAMLreturn (Val_int (r));
+}
+
+/* val save : t -> unit */
+CAMLprim value
+ocaml_augeas_save (value tv)
+{
+ CAMLparam1 (tv);
+ augeas_t t = Augeas_t_val (tv);
+
+ if (aug_save (t) == -1)
+ raise_error ("Augeas.save");
+
+ CAMLreturn (Val_unit);
+}
+
+/* val load : t -> unit */
+CAMLprim value
+ocaml_augeas_load (value tv)
+{
+ CAMLparam1 (tv);
+ augeas_t t = Augeas_t_val (tv);
+
+ if (aug_load (t) == -1)
+ raise_error ("Augeas.load");
+
+ CAMLreturn (Val_unit);
+}
diff --git a/daemon/augeas.README b/daemon/augeas.README
new file mode 100644
index 000000000..938dfd255
--- /dev/null
+++ b/daemon/augeas.README
@@ -0,0 +1,8 @@
+The files augeas-c.c, augeas.ml and augeas.mli come from the
+ocaml-augeas library:
+
+
http://git.annexia.org/?p=ocaml-augeas.git
+
+which is released under a compatible license. We try to keep them
+identical, so if you make changes to these files then you must also
+submit the changes to ocaml-augeas, and vice versa.
\ No newline at end of file
diff --git a/daemon/augeas.ml b/daemon/augeas.ml
new file mode 100644
index 000000000..f556df0f1
--- /dev/null
+++ b/daemon/augeas.ml
@@ -0,0 +1,59 @@
+(* Augeas OCaml bindings
+ * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * $Id: augeas.ml,v 1.2 2008/05/06 10:48:20 rjones Exp $
+ *)
+
+type t
+
+exception Error of string
+
+type flag =
+ | AugSaveBackup
+ | AugSaveNewFile
+ | AugTypeCheck
+ | AugNoStdinc
+ | AugSaveNoop
+ | AugNoLoad
+
+type path = string
+
+type value = string
+
+external create : string -> string option -> flag list -> t
+ = "ocaml_augeas_create"
+external close : t -> unit
+ = "ocaml_augeas_close"
+external get : t -> path -> value option
+ = "ocaml_augeas_get"
+external exists : t -> path -> bool
+ = "ocaml_augeas_exists"
+external insert : t -> ?before:bool -> path -> string -> unit
+ = "ocaml_augeas_insert"
+external rm : t -> path -> int
+ = "ocaml_augeas_rm"
+external matches : t -> path -> path list
+ = "ocaml_augeas_match"
+external count_matches : t -> path -> int
+ = "ocaml_augeas_count_matches"
+external save : t -> unit
+ = "ocaml_augeas_save"
+external load : t -> unit
+ = "ocaml_augeas_load"
+
+let () =
+ Callback.register_exception "Augeas.Error" (Error "")
diff --git a/daemon/augeas.mli b/daemon/augeas.mli
new file mode 100644
index 000000000..64e824014
--- /dev/null
+++ b/daemon/augeas.mli
@@ -0,0 +1,95 @@
+(** Augeas OCaml bindings *)
+(* Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * $Id: augeas.mli,v 1.2 2008/05/06 10:48:20 rjones Exp $
+ *)
+
+type t
+ (** Augeas library handle. *)
+
+exception Error of string
+ (** This exception is thrown when the underlying Augeas library
+ returns an error. *)
+
+type flag =
+ | AugSaveBackup (** Rename original with .augsave *)
+ | AugSaveNewFile (** Save changes to .augnew *)
+ | AugTypeCheck (** Type-check lenses *)
+ | AugNoStdinc
+ | AugSaveNoop
+ | AugNoLoad
+ (** Flags passed to the {!create} function. *)
+
+type path = string
+ (** A path expression.
+
+ Note in future we may replace this with a type-safe path constructor. *)
+
+type value = string
+ (** A value. *)
+
+val create : string -> string option -> flag list -> t
+ (** [create root loadpath flags] creates an Augeas handle.
+
+ [root] is a file system path describing the location
+ of the configuration files.
+
+ [loadpath] is an optional colon-separated list of directories
+ which are searched for schema definitions.
+
+ [flags] is a list of flags. *)
+
+val close : t -> unit
+ (** [close handle] closes the handle.
+
+ You don't need to close handles explicitly with this function:
+ they will be finalized eventually by the garbage collector.
+ However calling this function frees up any resources used by the
+ underlying Augeas library immediately.
+
+ Do not use the handle after closing it. *)
+
+val get : t -> path -> value option
+ (** [get t path] returns the value at [path], or [None] if there
+ is no value. *)
+
+val exists : t -> path -> bool
+ (** [exists t path] returns true iff there is a value at [path]. *)
+
+val insert : t -> ?before:bool -> path -> string -> unit
+ (** [insert t ?before path label] inserts [label] as a sibling
+ of [path]. By default it is inserted after [path], unless
+ [~before:true] is specified. *)
+
+val rm : t -> path -> int
+ (** [rm t path] removes all nodes matching [path].
+
+ Returns the number of nodes removed (which may be 0). *)
+
+val matches : t -> path -> path list
+ (** [matches t path] returns a list of path expressions
+ of all nodes matching [path]. *)
+
+val count_matches : t -> path -> int
+ (** [count_matches t path] counts the number of nodes matching
+ [path] but does not return them (see {!matches}). *)
+
+val save : t -> unit
+ (** [save t] saves all pending changes to disk. *)
+
+val load : t -> unit
+ (** [load t] loads files into the tree. *)
diff --git a/daemon/inspect.ml b/daemon/inspect.ml
new file mode 100644
index 000000000..1309b0d83
--- /dev/null
+++ b/daemon/inspect.ml
@@ -0,0 +1,249 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Std_utils
+
+open Utils
+open Inspect_types
+
+let rec inspect_os () =
+ Mount.umount_all ();
+
+ (* Iterate over all detected filesystems. Inspect each one in turn. *)
+ let fses = Listfs.list_filesystems () in
+
+ let fses =
+ filter_map (
+ fun (mountable, vfs_type) ->
+ Inspect_fs.check_for_filesystem_on mountable vfs_type
+ ) fses in
+ if verbose () then (
+ eprintf "inspect_os: fses:\n";
+ List.iter (fun fs -> eprintf "\t%s\n" (string_of_fs fs)) fses;
+ flush stderr
+ );
+
+(* XXX
+ (* The OS inspection information for CoreOS are gathered by inspecting
+ * multiple filesystems. Gather all the inspected information in the
+ * inspect_fs struct of the root filesystem.
+ *)
+ let fses = collect_coreos_inspection_info fses in
+
+ (* Check if the same filesystem was listed twice as root in fses.
+ * This may happen for the *BSD root partition where an MBR partition
+ * is a shadow of the real root partition probably /dev/sda5
+ *)
+ let fses = check_for_duplicated_bsd_root fses in
+
+ (* For Linux guests with a separate /usr filesystem, merge some of the
+ * inspected information in that partition to the inspect_fs struct
+ * of the root filesystem.
+ *)
+ let fses = collect_linux_inspection_info fses in
+ *)
+
+ (* Save what we found in a global variable. *)
+ Inspect_types.inspect_fses := fses;
+
+ (* At this point we have, in the handle, a list of all filesystems
+ * found and data about each one. Now we assemble the list of
+ * filesystems which are root devices.
+ *
+ * Fall through to inspect_get_roots to do that.
+ *)
+ inspect_get_roots ()
+
+and inspect_get_roots () =
+ let fses = !Inspect_types.inspect_fses in
+
+ let roots =
+ filter_map (
+ fun fs -> try Some (root_of_fs fs) with Invalid_argument _ -> None
+ ) fses in
+ if verbose () then (
+ eprintf "inspect_get_roots: roots:\n";
+ List.iter (fun root -> eprintf "%s" (string_of_root root)) roots;
+ flush stderr
+ );
+
+ (* Only return the list of mountables, since subsequent calls will
+ * be used to retrieve the other information.
+ *)
+ List.map (fun { root_location = { mountable = m } } -> m) roots
+
+and root_of_fs =
+ function
+ | { fs_location = location; role = RoleRoot data } ->
+ { root_location = location; inspection_data = data }
+ | { role = (RoleUsr _ | RoleSwap | RoleOther) } ->
+ invalid_arg "root_of_fs"
+
+and inspect_get_mountpoints root_mountable =
+ let root = search_for_root root_mountable in
+ let fstab = root.inspection_data.fstab in
+
+ (* If no fstab information (Windows) return just the root. *)
+ if fstab = [] then
+ [ "/", root_mountable ]
+ else (
+ filter_map (
+ fun (mountable, mp) ->
+ if String.length mp > 0 && mp.[0] = '/' then
+ Some (mp, mountable)
+ else
+ None
+ ) fstab
+ )
+
+and inspect_get_filesystems root_mountable =
+ let root = search_for_root root_mountable in
+ let fstab = root.inspection_data.fstab in
+
+ (* If no fstab information (Windows) return just the root. *)
+ if fstab = [] then
+ [ root_mountable ]
+ else
+ List.map fst fstab
+
+and inspect_get_format root =
+ let root = search_for_root root in
+ match root.inspection_data.format with
+ | Some v -> string_of_format v
+ | None -> "unknown"
+
+and inspect_get_type root =
+ let root = search_for_root root in
+ match root.inspection_data.os_type with
+ | Some v -> string_of_os_type v
+ | None -> "unknown"
+
+and inspect_get_distro root =
+ let root = search_for_root root in
+ match root.inspection_data.distro with
+ | Some v -> string_of_distro v
+ | None -> "unknown"
+
+and inspect_get_package_format root =
+ let root = search_for_root root in
+ match root.inspection_data.package_format with
+ | Some v -> string_of_package_format v
+ | None -> "unknown"
+
+and inspect_get_package_management root =
+ let root = search_for_root root in
+ match root.inspection_data.package_management with
+ | Some v -> string_of_package_management v
+ | None -> "unknown"
+
+and inspect_get_product_name root =
+ let root = search_for_root root in
+ match root.inspection_data.product_name with
+ | Some v -> v
+ | None -> "unknown"
+
+and inspect_get_product_variant root =
+ let root = search_for_root root in
+ match root.inspection_data.product_variant with
+ | Some v -> v
+ | None -> "unknown"
+
+and inspect_get_major_version root =
+ let root = search_for_root root in
+ match root.inspection_data.version with
+ | Some (major, _) -> major
+ | None -> 0
+
+and inspect_get_minor_version root =
+ let root = search_for_root root in
+ match root.inspection_data.version with
+ | Some (_, minor) -> minor
+ | None -> 0
+
+and inspect_get_arch root =
+ let root = search_for_root root in
+ match root.inspection_data.arch with
+ | Some v -> v
+ | None -> "unknown"
+
+and inspect_get_hostname root =
+ let root = search_for_root root in
+ match root.inspection_data.hostname with
+ | Some v -> v
+ | None -> "unknown"
+
+and inspect_get_windows_systemroot root =
+ let root = search_for_root root in
+ match root.inspection_data.windows_systemroot with
+ | Some v -> v
+ | None ->
+ failwith "not a Windows guest, or systemroot could not be determined"
+
+and inspect_get_windows_system_hive root =
+ let root = search_for_root root in
+ match root.inspection_data.windows_system_hive with
+ | Some v -> v
+ | None ->
+ failwith "not a Windows guest, or system hive not found"
+
+and inspect_get_windows_software_hive root =
+ let root = search_for_root root in
+ match root.inspection_data.windows_software_hive with
+ | Some v -> v
+ | None ->
+ failwith "not a Windows guest, or software hive not found"
+
+and inspect_get_windows_current_control_set root =
+ let root = search_for_root root in
+ match root.inspection_data.windows_current_control_set with
+ | Some v -> v
+ | None ->
+ failwith "not a Windows guest, or CurrentControlSet could not be
determined"
+
+and inspect_is_live root =
+ let root = search_for_root root in
+ root.inspection_data.is_live_disk
+
+and inspect_is_netinst root =
+ let root = search_for_root root in
+ root.inspection_data.is_netinst_disk
+
+and inspect_is_multipart root =
+ let root = search_for_root root in
+ root.inspection_data.is_multipart_disk
+
+and search_for_root root =
+ let fses = !Inspect_types.inspect_fses in
+ if fses = [] then
+ failwith "no inspection data: call guestfs_inspect_os first";
+
+ let root =
+ try
+ List.find (
+ function
+ | { fs_location = { mountable = m }; role = RoleRoot _ } -> root = m
+ | _ -> false
+ ) fses
+ with
+ Not_found ->
+ failwithf "%s: root device not found: only call this function with a root
device previously returned by guestfs_inspect_os"
+ (Mountable.to_string root) in
+
+ root_of_fs root
diff --git a/daemon/inspect.mli b/daemon/inspect.mli
new file mode 100644
index 000000000..5bcdfe259
--- /dev/null
+++ b/daemon/inspect.mli
@@ -0,0 +1,40 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+val inspect_os : unit -> Mountable.t list
+val inspect_get_roots : unit -> Mountable.t list
+val inspect_get_mountpoints : Mountable.t -> (string * Mountable.t) list
+val inspect_get_filesystems : Mountable.t -> Mountable.t list
+val inspect_get_format : Mountable.t -> string
+val inspect_get_type : Mountable.t -> string
+val inspect_get_distro : Mountable.t -> string
+val inspect_get_package_format : Mountable.t -> string
+val inspect_get_package_management : Mountable.t -> string
+val inspect_get_product_name : Mountable.t -> string
+val inspect_get_product_variant : Mountable.t -> string
+val inspect_get_major_version : Mountable.t -> int
+val inspect_get_minor_version : Mountable.t -> int
+val inspect_get_arch : Mountable.t -> string
+val inspect_get_hostname : Mountable.t -> string
+val inspect_get_windows_systemroot : Mountable.t -> string
+val inspect_get_windows_software_hive : Mountable.t -> string
+val inspect_get_windows_system_hive : Mountable.t -> string
+val inspect_get_windows_current_control_set : Mountable.t -> string
+val inspect_is_live : Mountable.t -> bool
+val inspect_is_netinst : Mountable.t -> bool
+val inspect_is_multipart : Mountable.t -> bool
diff --git a/daemon/inspect_fs.ml b/daemon/inspect_fs.ml
new file mode 100644
index 000000000..1e3049aff
--- /dev/null
+++ b/daemon/inspect_fs.ml
@@ -0,0 +1,410 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Std_utils
+
+open Mountable
+open Inspect_types
+
+let rec check_for_filesystem_on mountable vfs_type =
+ if verbose () then
+ eprintf "check_for_filesystem_on: %s (%s)\n%!"
+ (Mountable.to_string mountable) vfs_type;
+
+ let role =
+ let is_swap = vfs_type = "swap" in
+ if is_swap then
+ Some RoleSwap
+ else (
+ (* If it's a whole device, see if it is an install ISO. *)
+ let is_whole_device = Devsparts.is_whole_device mountable.m_device in
+ let installer_role =
+ if is_whole_device then
+ Inspect_fs_cd.check_installer_iso mountable.m_device
+ else
+ None in
+ match installer_role with
+ | Some _ as role -> role
+ | None ->
+ (* Try mounting the device. Ignore errors if we can't do this. *)
+ let mounted =
+ if vfs_type = "ufs" then ( (* Hack for the *BSDs. *)
+ (* FreeBSD fs is a variant of ufs called ufs2 ... *)
+ try
+ Mount.mount_vfs (Some "ro,ufstype=ufs2") (Some "ufs")
+ mountable "/";
+ true
+ with _ ->
+ (* while NetBSD and OpenBSD use another variant labeled 44bsd *)
+ try
+ Mount.mount_vfs (Some "ro,ufstype=44bsd") (Some
"ufs")
+ mountable "/";
+ true
+ with _ -> false
+ ) else (
+ try Mount.mount_ro mountable "/";
+ true
+ with _ -> false
+ ) in
+ if not mounted then None
+ else (
+ let role = check_filesystem mountable is_whole_device in
+ Mount.umount_all ();
+ role
+ )
+ ) in
+
+ match role with
+ | None -> None
+ | Some role ->
+ Some { fs_location = { mountable = mountable; vfs_type = vfs_type };
+ role = role }
+
+(* When this function is called, the filesystem is mounted on sysroot (). *)
+and check_filesystem mountable is_whole_device =
+ let is_only_partition =
+ if is_whole_device then false
+ else is_only_partition mountable in
+
+ let role = ref `Other in
+ let data = ref null_inspection_data in
+
+ (* Grub /boot? *)
+ if Is.is_file "/grub/menu.lst" ||
+ Is.is_file "/grub/grub.conf" ||
+ Is.is_file "/grub2/grub.cfg" then
+ ()
+ (* FreeBSD root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_file "/etc/freebsd-update.conf" &&
+ Is.is_file "/etc/fstab" then (
+ role := `Root;
+ data := { !data with format = Some FORMAT_INSTALLED };
+ data := Inspect_fs_unix.check_freebsd_root !data
+ )
+ (* NetBSD root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_file "/netbsd" &&
+ Is.is_file "/etc/fstab" &&
+ Is.is_file "/etc/release" then (
+ role := `Root;
+ data := { !data with format = Some FORMAT_INSTALLED };
+ data := Inspect_fs_unix.check_netbsd_root !data;
+ )
+ (* OpenBSD root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_file "/bsd" &&
+ Is.is_file "/etc/fstab" &&
+ Is.is_file "/etc/motd" then (
+ role := `Root;
+ data := { !data with format = Some FORMAT_INSTALLED };
+ data := Inspect_fs_unix.check_openbsd_root !data;
+ )
+ (* Hurd root? *)
+ else if Is.is_file "/hurd/console" &&
+ Is.is_file "/hurd/hello" &&
+ Is.is_file "/hurd/null" then (
+ role := `Root;
+ data := { !data with format = Some FORMAT_INSTALLED };
+ data := Inspect_fs_unix.check_hurd_root !data;
+ )
+ (* Minix root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_file "/service/vm" &&
+ Is.is_file "/etc/fstab" &&
+ Is.is_file "/etc/version" then (
+ role := `Root;
+ data := { !data with format = Some FORMAT_INSTALLED };
+ data := Inspect_fs_unix.check_minix_root !data;
+ )
+ (* Linux root? *)
+ else if Is.is_dir "/etc" &&
+ (Is.is_dir "/bin" ||
+ is_symlink_to "/bin" "usr/bin") &&
+ (Is.is_file "/etc/fstab" ||
+ Is.is_file "/etc/hosts") then (
+ role := `Root;
+ data := { !data with format = Some FORMAT_INSTALLED };
+ data := Inspect_fs_unix.check_linux_root mountable !data;
+ )
+ (* CoreOS root? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/root" &&
+ Is.is_dir "/home" &&
+ Is.is_dir "/usr" &&
+ Is.is_file "/etc/coreos/update.conf" then (
+ role := `Root;
+ data := { !data with format = Some FORMAT_INSTALLED };
+ data := Inspect_fs_unix.check_coreos_root !data;
+ )
+ (* Linux /usr/local? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_dir "/share" &&
+ not (Is.is_dir "/local") &&
+ not (Is.is_file "/etc/fstab") then
+ ()
+ (* Linux /usr? *)
+ else if Is.is_dir "/etc" &&
+ Is.is_dir "/bin" &&
+ Is.is_dir "/share" &&
+ Is.is_dir "/local" &&
+ not (Is.is_file "/etc/fstab") then (
+ data := Inspect_fs_unix.check_linux_usr !data;
+ )
+ (* CoreOS /usr? *)
+ else if Is.is_dir "/bin" &&
+ Is.is_dir "/share" &&
+ Is.is_dir "/local" &&
+ Is.is_dir "/share/coreos" then (
+ data := Inspect_fs_unix.check_coreos_usr !data;
+ )
+ (* Linux /var? *)
+ else if Is.is_dir "/log" &&
+ Is.is_dir "/run" &&
+ Is.is_dir "/spool" then
+ ()
+ (* Windows root? *)
+ else if Inspect_fs_windows.is_windows_systemroot () then (
+ role := `Root;
+ data := { !data with format = Some FORMAT_INSTALLED };
+ data := Inspect_fs_windows.check_windows_root !data;
+ )
+ (* Windows volume with installed applications (but not root)? *)
+ else if is_dir_nocase "/System Volume Information" &&
+ is_dir_nocase "/Program Files" then
+ ()
+ (* Windows volume (but not root)? *)
+ else if is_dir_nocase "/System Volume Information" then
+ ()
+ (* FreeDOS? *)
+ else if is_dir_nocase "/FDOS" &&
+ is_file_nocase "/FDOS/FREEDOS.BSS" then (
+ role := `Root;
+ data := { !data with
+ format = Some FORMAT_INSTALLED;
+ os_type = Some OS_TYPE_DOS;
+ distro = Some DISTRO_FREEDOS;
+ (* FreeDOS is a mix of 16 and 32 bit, but
+ * assume it requires a 32 bit i386 processor.
+ *)
+ arch = Some "i386" }
+ )
+ (* Install CD/disk?
+ *
+ * Note that we checked (above) for an install ISO, but there are
+ * other types of install image (eg. USB keys) which that check
+ * wouldn't have picked up.
+ *
+ * Skip these checks if it's not a whole device (eg. CD) or the
+ * first partition (eg. bootable USB key).
+ *)
+ else if (is_whole_device || is_only_partition) &&
+ Is.is_file "/isolinux/isolinux.cfg" ||
+ Is.is_dir "/EFI/BOOT" ||
+ Is.is_file "/images/install.img" ||
+ Is.is_dir "/.disk" ||
+ Is.is_file "/.discinfo" ||
+ Is.is_file "/i386/txtsetup.sif" ||
+ Is.is_file "/amd64/txtsetup.sif" ||
+ Is.is_file "/freedos/freedos.ico" ||
+ Is.is_file "/boot/loader.rc" then (
+ role := `Root;
+ data := { !data with format = Some FORMAT_INSTALLER };
+ data := Inspect_fs_cd.check_installer_root !data;
+ );
+
+ (* The above code should have set [data.os_type] and [data.distro]
+ * fields, so we can now guess the package management system.
+ *)
+ let data = !data in
+ let data = { data with
+ package_format = check_package_format data;
+ package_management = check_package_management data } in
+ match !role with
+ | `Root -> Some (RoleRoot data)
+ | `Usr -> Some (RoleUsr data)
+ | `Other -> Some RoleOther
+
+(* The mountable is the first and only partition on a device
+ * with a single device.
+ *)
+and is_only_partition = function
+ | { m_type = MountablePath | MountableBtrfsVol _ } -> false
+ | { m_type = MountableDevice; m_device = device } ->
+ let partnum, nr_partitions = get_partition_context device in
+ partnum = 1 && nr_partitions = 1
+
+and get_partition_context partition =
+ let partnum = Devsparts.part_to_partnum partition in
+ let device = Devsparts.part_to_dev partition in
+ let nr_partitions = List.length (Parted.part_list device) in
+ partnum, nr_partitions
+
+and is_symlink_to file wanted_target =
+ if not (Is.is_symlink file) then false
+ else Link.readlink file = wanted_target
+
+and is_file_nocase path =
+ let path =
+ try Some (Realpath.case_sensitive_path path)
+ with _ -> None in
+ match path with
+ | None -> false
+ | Some path -> Is.is_file path
+
+and is_dir_nocase path =
+ let path =
+ try Some (Realpath.case_sensitive_path path)
+ with _ -> None in
+ match path with
+ | None -> false
+ | Some path -> Is.is_dir path
+
+(* At the moment, package format and package management are just a
+ * simple function of the [distro] and [version[0]] fields, so these
+ * can never return an error. We might be cleverer in future.
+ *)
+and check_package_format { distro = distro } =
+ match distro with
+ | None -> None
+ | Some DISTRO_FEDORA
+ | Some DISTRO_MEEGO
+ | Some DISTRO_REDHAT_BASED
+ | Some DISTRO_RHEL
+ | Some DISTRO_MAGEIA
+ | Some DISTRO_MANDRIVA
+ | Some DISTRO_SUSE_BASED
+ | Some DISTRO_OPENSUSE
+ | Some DISTRO_SLES
+ | Some DISTRO_CENTOS
+ | Some DISTRO_SCIENTIFIC_LINUX
+ | Some DISTRO_ORACLE_LINUX
+ | Some DISTRO_ALTLINUX ->
+ Some PACKAGE_FORMAT_RPM
+ | Some DISTRO_DEBIAN
+ | Some DISTRO_UBUNTU
+ | Some DISTRO_LINUX_MINT ->
+ Some PACKAGE_FORMAT_DEB
+ | Some DISTRO_ARCHLINUX ->
+ Some PACKAGE_FORMAT_PACMAN
+ | Some DISTRO_GENTOO ->
+ Some PACKAGE_FORMAT_EBUILD
+ | Some DISTRO_PARDUS ->
+ Some PACKAGE_FORMAT_PISI
+ | Some DISTRO_ALPINE_LINUX ->
+ Some PACKAGE_FORMAT_APK
+ | Some DISTRO_VOID_LINUX ->
+ Some PACKAGE_FORMAT_XBPS
+ | Some DISTRO_SLACKWARE
+ | Some DISTRO_TTYLINUX
+ | Some DISTRO_COREOS
+ | Some DISTRO_WINDOWS
+ | Some DISTRO_BUILDROOT
+ | Some DISTRO_CIRROS
+ | Some DISTRO_FREEDOS
+ | Some DISTRO_FREEBSD
+ | Some DISTRO_NETBSD
+ | Some DISTRO_OPENBSD
+ | Some DISTRO_FRUGALWARE
+ | Some DISTRO_PLD_LINUX ->
+ None
+
+and check_package_management { distro = distro; version = version } =
+ let major = match version with None -> 0 | Some (major, _) -> major in
+ match distro with
+ | None -> None
+
+ | Some DISTRO_MEEGO ->
+ Some PACKAGE_MANAGEMENT_YUM
+
+ | Some DISTRO_FEDORA ->
+ (* If Fedora >= 22 and dnf is installed, say "dnf". *)
+ if major >= 22 && Is.is_file ~followsymlinks:true
"/usr/bin/dnf" then
+ Some PACKAGE_MANAGEMENT_DNF
+ else if major >= 1 then
+ Some PACKAGE_MANAGEMENT_YUM
+ else
+ (* Probably parsing the release file failed, see RHBZ#1332025. *)
+ None
+
+ | Some DISTRO_REDHAT_BASED
+ | Some DISTRO_RHEL
+ | Some DISTRO_CENTOS
+ | Some DISTRO_SCIENTIFIC_LINUX
+ | Some DISTRO_ORACLE_LINUX ->
+ if major >= 8 then
+ Some PACKAGE_MANAGEMENT_DNF
+ else if major >= 5 then
+ Some PACKAGE_MANAGEMENT_YUM
+ else if major >= 2 then
+ Some PACKAGE_MANAGEMENT_UP2DATE
+ else
+ (* Probably parsing the release file failed, see RHBZ#1332025. *)
+ None
+
+ | Some DISTRO_DEBIAN
+ | Some DISTRO_UBUNTU
+ | Some DISTRO_LINUX_MINT
+ | Some DISTRO_ALTLINUX ->
+ Some PACKAGE_MANAGEMENT_APT
+
+ | Some DISTRO_ARCHLINUX ->
+ Some PACKAGE_MANAGEMENT_PACMAN
+
+ | Some DISTRO_GENTOO ->
+ Some PACKAGE_MANAGEMENT_PORTAGE
+
+ | Some DISTRO_PARDUS ->
+ Some PACKAGE_MANAGEMENT_PISI
+
+ | Some DISTRO_MAGEIA
+ | Some DISTRO_MANDRIVA ->
+ Some PACKAGE_MANAGEMENT_URPMI
+
+ | Some DISTRO_SUSE_BASED
+ | Some DISTRO_OPENSUSE
+ | Some DISTRO_SLES ->
+ Some PACKAGE_MANAGEMENT_ZYPPER
+
+ | Some DISTRO_ALPINE_LINUX ->
+ Some PACKAGE_MANAGEMENT_APK
+
+ | Some DISTRO_VOID_LINUX ->
+ Some PACKAGE_MANAGEMENT_XBPS;
+
+ | Some DISTRO_SLACKWARE
+ | Some DISTRO_TTYLINUX
+ | Some DISTRO_COREOS
+ | Some DISTRO_WINDOWS
+ | Some DISTRO_BUILDROOT
+ | Some DISTRO_CIRROS
+ | Some DISTRO_FREEDOS
+ | Some DISTRO_FREEBSD
+ | Some DISTRO_NETBSD
+ | Some DISTRO_OPENBSD
+ | Some DISTRO_FRUGALWARE
+ | Some DISTRO_PLD_LINUX ->
+ None
+
diff --git a/daemon/inspect_fs.mli b/daemon/inspect_fs.mli
new file mode 100644
index 000000000..53ea01587
--- /dev/null
+++ b/daemon/inspect_fs.mli
@@ -0,0 +1,23 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+val check_for_filesystem_on : Mountable.t -> string ->
+ Inspect_types.fs option
+(** [check_for_filesystem_on cmdline mountable vfs_type] inspects
+ [mountable] looking for a single mountpoint from an operating
+ system. *)
diff --git a/daemon/inspect_fs_cd.ml b/daemon/inspect_fs_cd.ml
new file mode 100644
index 000000000..d16ee8095
--- /dev/null
+++ b/daemon/inspect_fs_cd.ml
@@ -0,0 +1,23 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+let check_installer_iso device =
+ None (* XXX *)
+
+let check_installer_root data =
+ data (* XXX *)
diff --git a/daemon/inspect_fs_cd.mli b/daemon/inspect_fs_cd.mli
new file mode 100644
index 000000000..052753336
--- /dev/null
+++ b/daemon/inspect_fs_cd.mli
@@ -0,0 +1,25 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+val check_installer_iso : string -> Inspect_types.role option
+(** Check the named device to see if it could be an install ISO image.
+ If so, returns [Some (RoleRoot ...)]. *)
+
+val check_installer_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the install CD filesystem mounted on sysroot. *)
diff --git a/daemon/inspect_fs_unix.ml b/daemon/inspect_fs_unix.ml
new file mode 100644
index 000000000..87dd4bd9e
--- /dev/null
+++ b/daemon/inspect_fs_unix.ml
@@ -0,0 +1,642 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open C_utils
+open Std_utils
+
+open Utils
+open Inspect_types
+
+let re_major_minor = Str.regexp "\\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_major_no_minor = Str.regexp "\\([0-9]+\\)"
+
+let re_fedora = Str.regexp "Fedora release \\([0-9]+\\)"
+let re_rhel_old = Str.regexp "Red Hat.*release \\([0-9]+\\).*Update
\\([0-9]+\\)"
+let re_rhel = Str.regexp "Red Hat.*release \\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_rhel_no_minor = Str.regexp "Red Hat.*release \\([0-9]+\\)"
+let re_centos_old = Str.regexp "CentOS.*release \\([0-9]+\\).*Update
\\([0-9]+\\)"
+let re_centos = Str.regexp "CentOS.*release \\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_centos_no_minor = Str.regexp "CentOS.*release \\([0-9]+\\)"
+let re_scientific_linux_old =
+ Str.regexp "Scientific Linux.*release \\([0-9]+\\).*Update \\([0-9]+\\)"
+let re_scientific_linux =
+ Str.regexp "Scientific Linux.*release \\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_scientific_linux_no_minor =
+ Str.regexp "Scientific Linux.*release \\([0-9]+\\)"
+let re_oracle_linux_old =
+ Str.regexp "Oracle Linux.*release \\([0-9]+\\).*Update \\([0-9]+\\)"
+let re_oracle_linux =
+ Str.regexp "Oracle Linux.*release \\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_oracle_linux_no_minor = Str.regexp "Oracle Linux.*release \\([0-9]+\\)"
+let re_netbsd = Str.regexp "^NetBSD \\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_opensuse = Str.regexp "^\\(openSUSE|SuSE Linux|SUSE LINUX\\) "
+let re_sles = Str.regexp "^SUSE \\(Linux|LINUX\\) Enterprise "
+let re_nld = Str.regexp "^Novell Linux Desktop "
+let re_sles_version = Str.regexp "^VERSION = \\([0-9]+\\)"
+let re_sles_patchlevel = Str.regexp "^PATCHLEVEL = \\([0-9]+\\)"
+let re_minix = Str.regexp "^\\([0-9]+\\)\\.\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?"
+let re_openbsd = Str.regexp "^OpenBSD \\([0-9]+|\\?\\)\\.\\([0-9]+|\\?\\)"
+let re_frugalware = Str.regexp "Frugalware \\([0-9]+\\)\\.\\([0-9]+\\)"
+let re_pldlinux = Str.regexp "\\([0-9]+\\)\\.\\([0-9]+\\) PLD Linux"
+
+let re_openbsd_duid = Str.regexp
"^[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\.\\([a-z]\\)"
+
+let rec check_linux_root mountable data =
+ let os_type = OS_TYPE_LINUX in
+ let data = { data with os_type = Some os_type } in
+
+ let tests = [
+ (* systemd distros include /etc/os-release which is reasonably
+ * standardized. This entry should be first.
+ *)
+ "/etc/os-release", parse_os_release;
+ (* LSB is also a reasonable standard. This entry should be second. *)
+ "/etc/lsb-release", parse_lsb_release;
+
+ (* Now we enter the Wild West ... *)
+
+ (* RHEL-based distros include a [/etc/redhat-release] file, hence their
+ * checks need to be performed before the Red-Hat one.
+ *)
+ "/etc/oracle-release", parse_generic ~rex:re_oracle_linux_old
+ DISTRO_ORACLE_LINUX;
+ "/etc/oracle-release", parse_generic ~rex:re_oracle_linux
+ DISTRO_ORACLE_LINUX;
+ "/etc/oracle-release", parse_generic ~rex:re_oracle_linux_no_minor
+ DISTRO_ORACLE_LINUX;
+ "/etc/centos-release", parse_generic ~rex:re_centos_old
+ DISTRO_CENTOS;
+ "/etc/centos-release", parse_generic ~rex:re_centos
+ DISTRO_CENTOS;
+ "/etc/centos-release", parse_generic ~rex:re_centos_no_minor
+ DISTRO_CENTOS;
+ "/etc/altlinux-release", parse_generic DISTRO_ALTLINUX;
+ "/etc/redhat-release", parse_generic ~rex:re_fedora
+ DISTRO_FEDORA;
+ "/etc/redhat-release", parse_generic ~rex:re_rhel_old
+ DISTRO_RHEL;
+ "/etc/redhat-release", parse_generic ~rex:re_rhel
+ DISTRO_RHEL;
+ "/etc/redhat-release", parse_generic ~rex:re_rhel_no_minor
+ DISTRO_RHEL;
+ "/etc/redhat-release", parse_generic ~rex:re_centos_old
+ DISTRO_CENTOS;
+ "/etc/redhat-release", parse_generic ~rex:re_centos
+ DISTRO_CENTOS;
+ "/etc/redhat-release", parse_generic ~rex:re_centos_no_minor
+ DISTRO_CENTOS;
+ "/etc/redhat-release", parse_generic ~rex:re_scientific_linux_old
+ DISTRO_SCIENTIFIC_LINUX;
+ "/etc/redhat-release", parse_generic ~rex:re_scientific_linux
+ DISTRO_SCIENTIFIC_LINUX;
+ "/etc/redhat-release", parse_generic ~rex:re_scientific_linux_no_minor
+ DISTRO_SCIENTIFIC_LINUX;
+
+ (* If there's an /etc/redhat-release file, but nothing above
+ * matches, then it is a generic Red Hat-based distro.
+ *)
+ "/etc/redhat-release", parse_generic DISTRO_REDHAT_BASED;
+ "/etc/redhat-release",
+ (fun _ data -> { data with distro = Some DISTRO_REDHAT_BASED });
+
+ "/etc/debian_version", parse_generic DISTRO_DEBIAN;
+ "/etc/pardus-release", parse_generic DISTRO_PARDUS;
+
+ (* /etc/arch-release file is empty and I can't see a way to
+ * determine the actual release or product string.
+ *)
+ "/etc/arch-release",
+ (fun _ data -> { data with distro = Some DISTRO_ARCHLINUX });
+
+ "/etc/gentoo-release", parse_generic DISTRO_GENTOO;
+ "/etc/meego-release", parse_generic DISTRO_MEEGO;
+ "/etc/slackware-version", parse_generic DISTRO_SLACKWARE;
+ "/etc/ttylinux-target", parse_generic DISTRO_TTYLINUX;
+
+ "/etc/SuSE-release", parse_suse_release;
+ "/etc/SuSE-release",
+ (fun _ data -> { data with distro = Some DISTRO_SUSE_BASED });
+
+ "/etc/cirros/version", parse_generic DISTRO_CIRROS;
+ "/etc/br-version",
+ (fun release_file data ->
+ let distro =
+ if Is.is_file ~followsymlinks:true "/usr/share/cirros/logo" then
+ DISTRO_CIRROS
+ else
+ DISTRO_BUILDROOT in
+ (* /etc/br-version has the format YYYY.MM[-git/hg/svn release] *)
+ parse_generic distro release_file data);
+
+ "/etc/alpine-release", parse_generic DISTRO_ALPINE_LINUX;
+ "/etc/frugalware-release", parse_generic ~rex:re_frugalware
+ DISTRO_FRUGALWARE;
+ "/etc/pld-release", parse_generic ~rex:re_pldlinux
+ DISTRO_PLD_LINUX;
+ ] in
+
+ let rec loop = function
+ | (release_file, parse_fun) :: tests ->
+ if verbose () then
+ eprintf "check_linux_root: checking %s\n%!" release_file;
+ (try
+ if not (Is.is_file ~followsymlinks:true release_file) then
+ raise Not_found;
+ parse_fun release_file data
+ with
+ Not_found -> loop tests)
+ | [] -> data
+ in
+ let data = loop tests in
+
+ let data = {
+ data with
+ arch = check_architecture ();
+ fstab = check_fstab ~mdadm:true mountable os_type ();
+ hostname = check_hostname_linux ();
+ } in
+
+ data
+
+(* Parse a os-release file.
+ *
+ * Only few fields are parsed, falling back to the usual detection if we
+ * cannot read all of them.
+ *
+ * For the format of os-release, see also:
+ *
http://www.freedesktop.org/software/systemd/man/os-release.html
+ *)
+and parse_os_release release_file data =
+ let chroot = Chroot.create (Sysroot.sysroot ()) in
+ let lines =
+ Chroot.f chroot (
+ fun () ->
+ if not (is_small_file release_file) then (
+ eprintf "%s: not a regular file or too large\n" release_file;
+ raise Not_found
+ );
+ read_whole_file release_file
+ ) () in
+ let lines = String.nsplit "\n" lines in
+
+ let data = List.fold_left (
+ fun data line ->
+ let line = String.trim line in
+ if line = "" || line.[0] = '#' then
+ data
+ else (
+ let key, value = String.split "=" line in
+ let value =
+ let n = String.length value in
+ if n >= 2 && value.[0] = '"' && value.[n-1] =
'"' then
+ String.sub value 1 (n-2)
+ else
+ value in
+ if key = "ID" then (
+ let distro = distro_of_os_release_id value in
+ match distro with
+ | Some _ as distro -> { data with distro = distro }
+ | None -> data
+ )
+ else if key = "PRETTY_NAME" then
+ { data with product_name = Some value }
+ else if key = "VERSION_ID" then
+ parse_version_from_major_minor value data
+ else
+ data
+ )
+ ) data lines in
+
+ (* os-release in Debian and CentOS does not provide the full
+ * version number (VERSION_ID), just the major part of it. If
+ * we detect that situation then bail out and use the release
+ * files instead.
+ *)
+ (match data with
+ | { distro = Some (DISTRO_DEBIAN|DISTRO_CENTOS); version = Some (_, 0) } ->
+ raise Not_found
+ | _ -> ()
+ );
+
+ data
+
+(* ID="fedora" => Some DISTRO_FEDORA *)
+and distro_of_os_release_id = function
+ | "alpine" -> Some DISTRO_ALPINE_LINUX
+ | "altlinux" -> Some DISTRO_ALTLINUX
+ | "arch" -> Some DISTRO_ARCHLINUX
+ | "centos" -> Some DISTRO_CENTOS
+ | "coreos" -> Some DISTRO_COREOS
+ | "debian" -> Some DISTRO_DEBIAN
+ | "fedora" -> Some DISTRO_FEDORA
+ | "frugalware" -> Some DISTRO_FRUGALWARE
+ | "mageia" -> Some DISTRO_MAGEIA
+ | "opensuse" -> Some DISTRO_OPENSUSE
+ | "pld" -> Some DISTRO_PLD_LINUX
+ | "rhel" -> Some DISTRO_RHEL
+ | "sles" | "sled" -> Some DISTRO_SLES
+ | "ubuntu" -> Some DISTRO_UBUNTU
+ | "void" -> Some DISTRO_VOID_LINUX
+ | value ->
+ eprintf "/etc/os-release: unknown ID=%s\n" value;
+ None
+
+(* Ubuntu has /etc/lsb-release containing:
+ * DISTRIB_ID=Ubuntu # Distro
+ * DISTRIB_RELEASE=10.04 # Version
+ * DISTRIB_CODENAME=lucid
+ * DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS" # Product name
+ *
+ * [Ubuntu-derived ...] Linux Mint was found to have this:
+ * DISTRIB_ID=LinuxMint
+ * DISTRIB_RELEASE=10
+ * DISTRIB_CODENAME=julia
+ * DISTRIB_DESCRIPTION="Linux Mint 10 Julia"
+ * Linux Mint also has /etc/linuxmint/info with more information,
+ * but we can use the LSB file.
+ *
+ * Mandriva has:
+ * LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch
+ * DISTRIB_ID=MandrivaLinux
+ * DISTRIB_RELEASE=2010.1
+ * DISTRIB_CODENAME=Henry_Farman
+ * DISTRIB_DESCRIPTION="Mandriva Linux 2010.1"
+ * Mandriva also has a normal release file called /etc/mandriva-release.
+ *
+ * CoreOS has a /etc/lsb-release link to /usr/share/coreos/lsb-release containing:
+ * DISTRIB_ID=CoreOS
+ * DISTRIB_RELEASE=647.0.0
+ * DISTRIB_CODENAME="Red Dog"
+ * DISTRIB_DESCRIPTION="CoreOS 647.0.0"
+ *)
+and parse_lsb_release release_file data =
+ let chroot = Chroot.create (Sysroot.sysroot ()) in
+ let lines =
+ Chroot.f chroot (
+ fun () ->
+ if not (is_small_file release_file) then (
+ eprintf "%s: not a regular file or too large\n" release_file;
+ raise Not_found
+ );
+ read_whole_file release_file
+ ) () in
+ let lines = String.nsplit "\n" lines in
+
+ let data = List.fold_left (
+ fun data line ->
+ if data.distro = None && line = "DISTRIB_ID=Ubuntu" then
+ { data with distro = Some DISTRO_UBUNTU }
+ else if data.distro = None && line = "DISTRIB_ID=LinuxMint" then
+ { data with distro = Some DISTRO_LINUX_MINT }
+ else if data.distro = None && line =
"DISTRIB_ID=\"Mageia\"" then
+ { data with distro = Some DISTRO_MAGEIA }
+ else if data.distro = None && line = "DISTRIB_ID=CoreOS" then
+ { data with distro = Some DISTRO_COREOS }
+ else if String.is_prefix line "DISTRIB_RELEASE=" then
+ parse_version_from_major_minor line data
+ else if String.is_prefix line "DISTRIB_DESCRIPTION=\"" ||
+ String.is_prefix line "DISTRIB_DESCRIPTION='" then (
+ let n = String.length line in
+ let product_name = String.sub line 21 (n-20) in
+ { data with product_name = Some product_name }
+ )
+ else if String.is_prefix line "DISTRIB_DESCRIPTION=" then (
+ let n = String.length line in
+ let product_name = String.sub line 20 (n-20) in
+ { data with product_name = Some product_name }
+ )
+ else
+ data
+ ) data lines in
+
+ data
+
+and parse_suse_release release_file data =
+ let chroot = Chroot.create (Sysroot.sysroot ()) in
+ let lines =
+ Chroot.f chroot (
+ fun () ->
+ if not (is_small_file release_file) then (
+ eprintf "%s: not a regular file or too large\n" release_file;
+ raise Not_found
+ );
+ read_whole_file release_file
+ ) () in
+ let lines = String.nsplit "\n" lines in
+
+ if lines = [] then raise Not_found;
+
+ (* First line is dist release name. *)
+ let product_name = List.hd lines in
+ let data = {
+ data with
+ product_name = Some product_name
+ } in
+
+ (* Match SLES first because openSuSE regex overlaps some SLES
+ * release strings.
+ *)
+ if Str.string_match re_sles product_name 0 ||
+ Str.string_match re_nld product_name 0 then (
+ (* Second line contains version string. *)
+ let major =
+ if List.length lines >= 2 then (
+ let line = List.nth lines 1 in
+ if Str.string_match re_sles_version line 0 then
+ Some (int_of_string (Str.matched_group 1 line))
+ else None
+ )
+ else None in
+
+ (* Third line contains service pack string. *)
+ let minor =
+ if List.length lines >= 3 then (
+ let line = List.nth lines 2 in
+ if Str.string_match re_sles_patchlevel line 0 then
+ Some (int_of_string (Str.matched_group 1 line))
+ else None
+ )
+ else None in
+
+ let version =
+ match major, minor with
+ | Some major, Some minor -> Some (major, minor)
+ | Some major, None -> Some (major, 0)
+ | None, Some _ | None, None -> None in
+
+ { data with
+ distro = Some DISTRO_SLES;
+ version = version }
+ )
+ else if Str.string_match re_opensuse product_name 0 then (
+ (* Second line contains version string. *)
+ let data =
+ if List.length lines >= 2 then (
+ let line = List.nth lines 1 in
+ parse_version_from_major_minor line data
+ )
+ else data in
+
+ { data with distro = Some DISTRO_OPENSUSE }
+ )
+ else
+ data
+
+(* Parse any generic /etc/x-release file.
+ *
+ * The optional regular expression which may match 0, 1 or 2
+ * substrings, which are used as the major and minor numbers.
+ *
+ * The fixed distro is always set, and the product name is
+ * set to the first line of the release file.
+ *)
+and parse_generic ?rex distro release_file data =
+ let chroot = Chroot.create (Sysroot.sysroot ()) in
+ let product_name =
+ Chroot.f chroot (
+ fun () ->
+ if not (is_small_file release_file) then (
+ eprintf "%s: not a regular file or too large\n" release_file;
+ raise Not_found
+ );
+ read_first_line_from_file release_file
+ ) () in
+ if product_name = "" then
+ raise Not_found;
+
+ let data =
+ { data with product_name = Some product_name;
+ distro = Some distro } in
+
+ match rex with
+ | Some rex ->
+ (* If ~rex was supplied, then it must match the release file,
+ * else the parsing fails.
+ *)
+ if not (Str.string_match rex product_name 0) then
+ raise Not_found;
+
+ (* Although it's not documented, matched_group raises
+ * Invalid_argument if called with an unknown group number.
+ *)
+ let major =
+ try Some (int_of_string (Str.matched_group 1 product_name))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ let minor =
+ try Some (int_of_string (Str.matched_group 2 product_name))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ (match major, minor with
+ | None, None -> data
+ | None, Some _ -> data
+ | Some major, None -> { data with version = Some (major, 0) }
+ | Some major, Some minor -> { data with version = Some (major, minor) }
+ )
+
+ | None ->
+ (* However if no ~rex was supplied, then we make a best
+ * effort attempt to parse a version number, but don't
+ * fail if one cannot be found.
+ *)
+ parse_version_from_major_minor product_name data
+
+(* Make a best effort attempt to parse either X or X.Y from a string,
+ * usually the product_name string.
+ *)
+and parse_version_from_major_minor str data =
+ if Str.string_match re_major_minor str 0 ||
+ Str.string_match re_major_no_minor str 0 then (
+ let major =
+ try Some (int_of_string (Str.matched_group 1 str))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ let minor =
+ try Some (int_of_string (Str.matched_group 2 str))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ match major, minor with
+ | None, None -> data
+ | None, Some _ -> data
+ | Some major, None -> { data with version = Some (major, 0) }
+ | Some major, Some minor -> { data with version = Some (major, minor) }
+ )
+ else (
+ eprintf "parse_version_from_major_minor: cannot parse version from ‘%s’\n"
+ str;
+ data
+ )
+
+and check_architecture () =
+ (* XXX *) None
+
+and check_hostname_linux () =
+ (* XXX *) None
+
+and check_fstab ?(mdadm = false) (root_mountable : Mountable.t) os_type () =
+ let configfiles = "/etc/fstab" :: if mdadm then ["/etc/mdadm.conf"]
else [] in
+
+ with_augeas configfiles (
+ fun aug ->
+ let is_bsd =
+ os_type = OS_TYPE_FREEBSD ||
+ os_type = OS_TYPE_NETBSD ||
+ os_type = OS_TYPE_OPENBSD in
+
+ (* Generate a map of MD device paths listed in /etc/mdadm.conf
+ * to MD device paths in the guestfs appliance.
+ *)
+ let md_map = if mdadm then map_md_devices () else StringMap.empty in
+
+ let path = "/files/etc/fstab/*[label() != '#comment']" in
+ let entries = Augeas.matches aug path in
+ filter_map (
+ fun entry ->
+ let spec = Augeas.get aug (entry ^ "/spec") in
+ let mp = Augeas.get aug (entry ^ "/file") in
+ let vfstype = Augeas.get aug (entry ^ "/vfstype") in
+
+ match spec, mp, vfstype with
+ | None, _, _ | Some _, None, _ | Some _, Some _, None -> None
+ | Some spec, Some mp, Some vfstype ->
+ (* Ignore /dev/fd (floppy disks) (RHBZ#642929) and
+ * CD-ROM drives.
+ *
+ * /dev/iso9660/FREEBSD_INSTALL can be found in FreeBSD's
+ * installation discs.
+ *)
+ if (String.is_prefix spec "/dev/fd" &&
+ String.length spec >= 8 && Char.isdigit spec.[7]) ||
+ (String.is_prefix spec "/dev/cd" &&
+ String.length spec >= 8 && Char.isdigit spec.[7]) ||
+ spec = "/dev/floppy" ||
+ spec = "/dev/cdrom" ||
+ String.is_prefix spec "/dev/iso9660/" then
+ None
+ else (
+ (* Canonicalize the path, so "///usr//local//"
+ * -> "/usr/local"
+ *)
+ let mp = canonical_mountpoint mp in
+
+ (* Ignore certain mountpoints. *)
+ if String.is_prefix mp "/dev/" ||
+ mp = "/dev" ||
+ String.is_prefix mp "/media/" ||
+ String.is_prefix mp "/proc/" ||
+ mp = "/proc" ||
+ String.is_prefix mp "/selinux/" ||
+ mp = "/selinux" ||
+ String.is_prefix mp "/sys/" ||
+ mp = "/sys" then
+ None
+ else (
+ let mountable =
+ (* Resolve UUID= and LABEL= to the actual device. *)
+ if String.is_prefix spec "UUID=" then
+ Some (Mountable.of_device
+ (Findfs.findfs_uuid (shell_unquote spec)))
+ else if String.is_prefix spec "LABEL=" then
+ Some (Mountable.of_device
+ (Findfs.findfs_label (shell_unquote spec)))
+ (* Resolve /dev/root to the current device.
+ * Do the same for the / partition of the *BSD
+ * systems, since the BSD -> Linux device
+ * translation is not straight forward.
+ *)
+ else if spec = "/dev/root" || (is_bsd && mp =
"/") then
+ Some root_mountable
+ (* Resolve guest block device names. *)
+ else if String.is_prefix spec "/dev/" then
+ Some (resolve_fstab_device spec md_map os_type)
+ (* In OpenBSD's fstab you can specify partitions
+ * on a disk by appending a period and a partition
+ * letter to a Disklable Unique Identifier. The
+ * DUID is a 16 hex digit field found in the
+ * OpenBSD's altered BSD disklabel. For more info
+ * see here:
+ *
http://www.openbsd.org/faq/faq14.html#intro
+ *)
+ else if Str.string_match re_openbsd_duid spec 0 then (
+ let part = Str.matched_group 1 spec in
+ (* We cannot peep into disklabels, we can only
+ * assume that this is the first disk.
+ *)
+ let device = sprintf "/dev/sd0%s" part in
+ Some (resolve_fstab_device device md_map os_type)
+ )
+ (* Ignore "/.swap" (Pardus) and pseudo-devices
+ * like "tmpfs". If we haven't resolved the device
+ * successfully by this point, just ignore it.
+ *)
+ else
+ None in
+
+ match mountable with
+ | None -> None
+ | Some mountable ->
+ let mountable =
+ if vfstype = "btrfs" then (
+ (* XXX *) mountable
+ )
+ else mountable in
+
+ Some (mountable, mp)
+ )
+ )
+ ) entries (* filter_map *)
+ ) (* with_augeas *)
+
+and map_md_devices () =
+ (* XXX *) StringMap.empty
+
+and resolve_fstab_device spec md_map os_type =
+ (* XXX *) Mountable.of_device spec
+
+and canonical_mountpoint = (* XXX *) identity
+
+let check_linux_usr data =
+ (* XXX *) data
+
+let check_coreos_root data =
+ (* XXX *) data
+
+let check_coreos_usr data =
+ (* XXX *) data
+
+let check_freebsd_root data =
+ (* XXX *) data
+
+and check_hostname_freebsd () =
+ (* XXX *) None
+
+let check_netbsd_root data =
+ (* XXX *) data
+
+let rec check_openbsd_root data =
+ (* XXX *) data
+
+and check_hostname_openbsd () =
+ (* XXX *) None
+
+let check_hurd_root data =
+ (* XXX *) data
+
+let rec check_minix_root data =
+ (* XXX *) data
+
+and check_hostname_minix () =
+ (* XXX *) None
diff --git a/daemon/inspect_fs_unix.mli b/daemon/inspect_fs_unix.mli
new file mode 100644
index 000000000..7ca1e2fbb
--- /dev/null
+++ b/daemon/inspect_fs_unix.mli
@@ -0,0 +1,53 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+val check_coreos_usr : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the CoreOS [/usr] filesystem mounted on sysroot. *)
+
+val check_coreos_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the CoreOS filesystem mounted on sysroot. *)
+
+val check_freebsd_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the FreeBSD filesystem mounted on sysroot. *)
+
+val check_hurd_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Hurd filesystem mounted on sysroot. *)
+
+val check_linux_usr : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Linux [/usr] filesystem mounted on sysroot. *)
+
+val check_linux_root : Mountable.t -> Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Linux filesystem mounted on sysroot. *)
+
+val check_minix_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Minix filesystem mounted on sysroot. *)
+
+val check_netbsd_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the NetBSD filesystem mounted on sysroot. *)
+
+val check_openbsd_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the OpenBSD filesystem mounted on sysroot. *)
diff --git a/daemon/inspect_fs_windows.ml b/daemon/inspect_fs_windows.ml
new file mode 100644
index 000000000..9bc2afbc4
--- /dev/null
+++ b/daemon/inspect_fs_windows.ml
@@ -0,0 +1,23 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+let rec check_windows_root data =
+ (* XXX *) data
+
+and is_windows_systemroot () =
+ (* XXX *) false
diff --git a/daemon/inspect_fs_windows.mli b/daemon/inspect_fs_windows.mli
new file mode 100644
index 000000000..936d695c6
--- /dev/null
+++ b/daemon/inspect_fs_windows.mli
@@ -0,0 +1,25 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+val check_windows_root : Inspect_types.inspection_data ->
+ Inspect_types.inspection_data
+(** Inspect the Windows [C:] filesystem mounted on sysroot. *)
+
+val is_windows_systemroot : unit -> bool
+(** Decide if the filesystem mounted on sysroot looks like a
+ Windows [C:] filesystem. *)
diff --git a/daemon/inspect_types.ml b/daemon/inspect_types.ml
new file mode 100644
index 000000000..bdf22e50e
--- /dev/null
+++ b/daemon/inspect_types.ml
@@ -0,0 +1,325 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Std_utils
+
+type fs = {
+ fs_location : location;
+ role : role; (** Special cases: root filesystem or /usr *)
+}
+and root = {
+ root_location : location;
+ inspection_data : inspection_data;
+}
+and location = {
+ mountable : Mountable.t; (** The device name or other mountable object.*)
+ vfs_type : string; (** Returned from [vfs_type] API. *)
+}
+
+and role =
+ | RoleRoot of inspection_data
+ | RoleUsr of inspection_data
+ | RoleSwap
+ | RoleOther
+and inspection_data = {
+ format : format option;
+ os_type : os_type option;
+ distro : distro option;
+ package_format : package_format option;
+ package_management : package_management option;
+ product_name : string option;
+ product_variant : string option;
+ version : version option;
+ arch : string option;
+ hostname : string option;
+ fstab : fstab_entry list;
+ windows_systemroot : string option;
+ windows_software_hive : string option;
+ windows_system_hive : string option;
+ windows_current_control_set : string option;
+ drive_mappings : drive_mapping list;
+ is_live_disk : bool;
+ is_netinst_disk : bool;
+ is_multipart_disk : bool;
+}
+and format =
+ | FORMAT_INSTALLED
+ | FORMAT_INSTALLER
+ (* in future: supplemental install disks *)
+and os_type =
+ | OS_TYPE_DOS
+ | OS_TYPE_FREEBSD
+ | OS_TYPE_HURD
+ | OS_TYPE_LINUX
+ | OS_TYPE_MINIX
+ | OS_TYPE_NETBSD
+ | OS_TYPE_OPENBSD
+ | OS_TYPE_WINDOWS
+and distro =
+ | DISTRO_ALPINE_LINUX
+ | DISTRO_ALTLINUX
+ | DISTRO_ARCHLINUX
+ | DISTRO_BUILDROOT
+ | DISTRO_CENTOS
+ | DISTRO_CIRROS
+ | DISTRO_COREOS
+ | DISTRO_DEBIAN
+ | DISTRO_FEDORA
+ | DISTRO_FREEBSD
+ | DISTRO_FREEDOS
+ | DISTRO_FRUGALWARE
+ | DISTRO_GENTOO
+ | DISTRO_LINUX_MINT
+ | DISTRO_MAGEIA
+ | DISTRO_MANDRIVA
+ | DISTRO_MEEGO
+ | DISTRO_NETBSD
+ | DISTRO_OPENBSD
+ | DISTRO_OPENSUSE
+ | DISTRO_ORACLE_LINUX
+ | DISTRO_PARDUS
+ | DISTRO_PLD_LINUX
+ | DISTRO_REDHAT_BASED
+ | DISTRO_RHEL
+ | DISTRO_SCIENTIFIC_LINUX
+ | DISTRO_SLACKWARE
+ | DISTRO_SLES
+ | DISTRO_SUSE_BASED
+ | DISTRO_TTYLINUX
+ | DISTRO_UBUNTU
+ | DISTRO_VOID_LINUX
+ | DISTRO_WINDOWS
+and package_format =
+ | PACKAGE_FORMAT_APK
+ | PACKAGE_FORMAT_DEB
+ | PACKAGE_FORMAT_EBUILD
+ | PACKAGE_FORMAT_PACMAN
+ | PACKAGE_FORMAT_PISI
+ | PACKAGE_FORMAT_PKGSRC
+ | PACKAGE_FORMAT_RPM
+ | PACKAGE_FORMAT_XBPS
+and package_management =
+ | PACKAGE_MANAGEMENT_APK
+ | PACKAGE_MANAGEMENT_APT
+ | PACKAGE_MANAGEMENT_DNF
+ | PACKAGE_MANAGEMENT_PACMAN
+ | PACKAGE_MANAGEMENT_PISI
+ | PACKAGE_MANAGEMENT_PORTAGE
+ | PACKAGE_MANAGEMENT_UP2DATE
+ | PACKAGE_MANAGEMENT_URPMI
+ | PACKAGE_MANAGEMENT_XBPS
+ | PACKAGE_MANAGEMENT_YUM
+ | PACKAGE_MANAGEMENT_ZYPPER
+and version = int * int
+and fstab_entry = Mountable.t * string (* mountable, mountpoint *)
+and drive_mapping = string * string (* drive name, mountable *)
+
+let rec string_of_fs { fs_location = location; role = role } =
+ sprintf "fs: %s role: %s"
+ (string_of_location location)
+ (match role with
+ | RoleRoot _ -> "root"
+ | RoleUsr _ -> "usr"
+ | RoleSwap -> "swap"
+ | RoleOther -> "other")
+
+and string_of_location { mountable = mountable; vfs_type = vfs_type } =
+ sprintf "%s (%s)" (Mountable.to_string mountable) vfs_type
+
+and string_of_root { root_location = location;
+ inspection_data = inspection_data } =
+ sprintf "%s:\n%s"
+ (string_of_location location)
+ (string_of_inspection_data inspection_data)
+
+and string_of_inspection_data data =
+ let b = Buffer.create 1024 in
+ let bpf fs = bprintf b fs in
+ may (fun v -> bpf "\tformat: %s\n" (string_of_format v))
+ data.format;
+ may (fun v -> bpf "\ttype: %s\n" (string_of_os_type v))
+ data.os_type;
+ may (fun v -> bpf "\tdistro: %s\n" (string_of_distro v))
+ data.distro;
+ may (fun v -> bpf "\tpackage_format: %s\n" (string_of_package_format v))
+ data.package_format;
+ may (fun v -> bpf "\tpackage_management: %s\n"
(string_of_package_management v))
+ data.package_management;
+ may (fun v -> bpf "\tproduct_name: %s\n" v)
+ data.product_name;
+ may (fun v -> bpf "\tproduct_variant: %s\n" v)
+ data.product_variant;
+ may (fun (major, minor) -> bpf "\tversion: %d.%d\n" major minor)
+ data.version;
+ may (fun v -> bpf "\tarch: %s\n" v)
+ data.arch;
+ may (fun v -> bpf "\thostname: %s\n" v)
+ data.hostname;
+ if data.fstab <> [] then (
+ let v = List.map (
+ fun (a, b) -> sprintf "(%s, %s)" (Mountable.to_string a) b
+ ) data.fstab in
+ bpf "\tfstab: [%s]\n" (String.concat ", " v)
+ );
+ may (fun v -> bpf "\twindows_systemroot: %s\n" v)
+ data.windows_systemroot;
+ may (fun v -> bpf "\twindows_software_hive: %s\n" v)
+ data.windows_software_hive;
+ may (fun v -> bpf "\twindows_system_hive: %s\n" v)
+ data.windows_system_hive;
+ may (fun v -> bpf "\twindows_current_control_set: %s\n" v)
+ data.windows_current_control_set;
+ if data.drive_mappings <> [] then (
+ let v =
+ List.map (fun (a, b) -> sprintf "(%s, %s)" a b) data.drive_mappings
in
+ bpf "\tdrive_mappings: [%s]\n" (String.concat ", " v)
+ );
+ bpf "\tis_live_disk: %b\n" data.is_live_disk;
+ bpf "\tis_netinst_disk: %b\n" data.is_netinst_disk;
+ bpf "\tis_multipart_disk: %b\n" data.is_multipart_disk;
+ Buffer.contents b
+
+and string_of_format = function
+ | FORMAT_INSTALLED -> "installed"
+ | FORMAT_INSTALLER -> "installer"
+
+and string_of_os_type = function
+ | OS_TYPE_DOS -> "dos"
+ | OS_TYPE_FREEBSD -> "freebsd"
+ | OS_TYPE_HURD -> "hurd"
+ | OS_TYPE_LINUX -> "linux"
+ | OS_TYPE_MINIX -> "minix"
+ | OS_TYPE_NETBSD -> "netbsd"
+ | OS_TYPE_OPENBSD -> "openbsd"
+ | OS_TYPE_WINDOWS -> "windows"
+
+and string_of_distro = function
+ | DISTRO_ALPINE_LINUX -> "alpinelinux"
+ | DISTRO_ALTLINUX -> "altlinux"
+ | DISTRO_ARCHLINUX -> "archlinux"
+ | DISTRO_BUILDROOT -> "buildroot"
+ | DISTRO_CENTOS -> "centos"
+ | DISTRO_CIRROS -> "cirros"
+ | DISTRO_COREOS -> "coreos"
+ | DISTRO_DEBIAN -> "debian"
+ | DISTRO_FEDORA -> "fedora"
+ | DISTRO_FREEBSD -> "freebsd"
+ | DISTRO_FREEDOS -> "freedos"
+ | DISTRO_FRUGALWARE -> "frugalware"
+ | DISTRO_GENTOO -> "gentoo"
+ | DISTRO_LINUX_MINT -> "linuxmint"
+ | DISTRO_MAGEIA -> "mageia"
+ | DISTRO_MANDRIVA -> "mandriva"
+ | DISTRO_MEEGO -> "meego"
+ | DISTRO_NETBSD -> "netbsd"
+ | DISTRO_OPENBSD -> "openbsd"
+ | DISTRO_OPENSUSE -> "opensuse"
+ | DISTRO_ORACLE_LINUX -> "oraclelinux"
+ | DISTRO_PARDUS -> "pardus"
+ | DISTRO_PLD_LINUX -> "pldlinux"
+ | DISTRO_REDHAT_BASED -> "redhat-based"
+ | DISTRO_RHEL -> "rhel"
+ | DISTRO_SCIENTIFIC_LINUX -> "scientificlinux"
+ | DISTRO_SLACKWARE -> "slackware"
+ | DISTRO_SLES -> "sles"
+ | DISTRO_SUSE_BASED -> "suse-based"
+ | DISTRO_TTYLINUX -> "ttylinux"
+ | DISTRO_UBUNTU -> "ubuntu"
+ | DISTRO_VOID_LINUX -> "voidlinux"
+ | DISTRO_WINDOWS -> "windows"
+
+and string_of_package_format = function
+ | PACKAGE_FORMAT_APK -> "apk"
+ | PACKAGE_FORMAT_DEB -> "deb"
+ | PACKAGE_FORMAT_EBUILD -> "ebuild"
+ | PACKAGE_FORMAT_PACMAN -> "pacman"
+ | PACKAGE_FORMAT_PISI -> "pisi"
+ | PACKAGE_FORMAT_PKGSRC -> "pkgsrc"
+ | PACKAGE_FORMAT_RPM -> "rpm"
+ | PACKAGE_FORMAT_XBPS -> "xbps"
+
+and string_of_package_management = function
+ | PACKAGE_MANAGEMENT_APK -> "apk"
+ | PACKAGE_MANAGEMENT_APT -> "apt"
+ | PACKAGE_MANAGEMENT_DNF -> "dnf"
+ | PACKAGE_MANAGEMENT_PACMAN -> "pacman"
+ | PACKAGE_MANAGEMENT_PISI -> "pisi"
+ | PACKAGE_MANAGEMENT_PORTAGE -> "portage"
+ | PACKAGE_MANAGEMENT_UP2DATE -> "up2date"
+ | PACKAGE_MANAGEMENT_URPMI -> "urpmi"
+ | PACKAGE_MANAGEMENT_XBPS -> "xbps"
+ | PACKAGE_MANAGEMENT_YUM -> "yum"
+ | PACKAGE_MANAGEMENT_ZYPPER -> "zypper"
+
+let null_inspection_data = {
+ format = None;
+ os_type = None;
+ distro = None;
+ package_format = None;
+ package_management = None;
+ product_name = None;
+ product_variant = None;
+ version = None;
+ arch = None;
+ hostname = None;
+ fstab = [];
+ windows_systemroot = None;
+ windows_software_hive = None;
+ windows_system_hive = None;
+ windows_current_control_set = None;
+ drive_mappings = [];
+ is_live_disk = false;
+ is_netinst_disk = false;
+ is_multipart_disk = false;
+}
+
+let merge_inspection_data child parent =
+ let merge child parent = if parent = None then child else parent in
+
+ { format = merge child.format parent.format;
+ os_type = merge child.os_type parent.os_type;
+ distro = merge child.distro parent.distro;
+ package_format = merge child.package_format parent.package_format;
+ package_management =
+ merge child.package_management parent.package_management;
+ product_name = merge child.product_name parent.product_name;
+ product_variant = merge child.product_variant parent.product_variant;
+ version = merge child.version parent.version;
+ arch = merge child.arch parent.arch;
+ hostname = merge child.hostname parent.hostname;
+ fstab = child.fstab @ parent.fstab;
+ windows_systemroot =
+ merge child.windows_systemroot parent.windows_systemroot;
+ windows_software_hive =
+ merge child.windows_software_hive parent.windows_software_hive;
+ windows_system_hive =
+ merge child.windows_system_hive parent.windows_system_hive;
+ windows_current_control_set =
+ merge child.windows_current_control_set parent.windows_current_control_set;
+
+ (* This is what the old C code did, but I doubt that it's correct. *)
+ drive_mappings = child.drive_mappings @ parent.drive_mappings;
+
+ is_live_disk = child.is_live_disk || parent.is_live_disk;
+ is_netinst_disk = child.is_netinst_disk || parent.is_netinst_disk;
+ is_multipart_disk = child.is_multipart_disk || parent.is_multipart_disk;
+ }
+
+let inspect_fses = ref []
diff --git a/daemon/inspect_types.mli b/daemon/inspect_types.mli
new file mode 100644
index 000000000..99bffea6f
--- /dev/null
+++ b/daemon/inspect_types.mli
@@ -0,0 +1,175 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+type fs = {
+ fs_location : location;
+ role : role; (** Special cases: root filesystem or /usr *)
+}
+and root = {
+ root_location : location;
+ inspection_data : inspection_data;
+}
+and location = {
+ mountable : Mountable.t; (** The device name or other mountable object.*)
+ vfs_type : string; (** Returned from [vfs_type] API. *)
+}
+
+and role =
+ | RoleRoot of inspection_data
+ | RoleUsr of inspection_data
+ | RoleSwap
+ | RoleOther
+and inspection_data = {
+ format : format option;
+ os_type : os_type option;
+ distro : distro option;
+ package_format : package_format option;
+ package_management : package_management option;
+ product_name : string option;
+ product_variant : string option;
+ version : version option;
+ arch : string option;
+ hostname : string option;
+ fstab : fstab_entry list;
+ windows_systemroot : string option;
+ windows_software_hive : string option;
+ windows_system_hive : string option;
+ windows_current_control_set : string option;
+ drive_mappings : drive_mapping list;
+ is_live_disk : bool;
+ is_netinst_disk : bool;
+ is_multipart_disk : bool;
+}
+and format =
+ | FORMAT_INSTALLED
+ | FORMAT_INSTALLER
+ (* in future: supplemental install disks *)
+and os_type =
+ | OS_TYPE_DOS
+ | OS_TYPE_FREEBSD
+ | OS_TYPE_HURD
+ | OS_TYPE_LINUX
+ | OS_TYPE_MINIX
+ | OS_TYPE_NETBSD
+ | OS_TYPE_OPENBSD
+ | OS_TYPE_WINDOWS
+and distro =
+ | DISTRO_ALPINE_LINUX
+ | DISTRO_ALTLINUX
+ | DISTRO_ARCHLINUX
+ | DISTRO_BUILDROOT
+ | DISTRO_CENTOS
+ | DISTRO_CIRROS
+ | DISTRO_COREOS
+ | DISTRO_DEBIAN
+ | DISTRO_FEDORA
+ | DISTRO_FREEBSD
+ | DISTRO_FREEDOS
+ | DISTRO_FRUGALWARE
+ | DISTRO_GENTOO
+ | DISTRO_LINUX_MINT
+ | DISTRO_MAGEIA
+ | DISTRO_MANDRIVA
+ | DISTRO_MEEGO
+ | DISTRO_NETBSD
+ | DISTRO_OPENBSD
+ | DISTRO_OPENSUSE
+ | DISTRO_ORACLE_LINUX
+ | DISTRO_PARDUS
+ | DISTRO_PLD_LINUX
+ | DISTRO_REDHAT_BASED
+ | DISTRO_RHEL
+ | DISTRO_SCIENTIFIC_LINUX
+ | DISTRO_SLACKWARE
+ | DISTRO_SLES
+ | DISTRO_SUSE_BASED
+ | DISTRO_TTYLINUX
+ | DISTRO_UBUNTU
+ | DISTRO_VOID_LINUX
+ | DISTRO_WINDOWS
+and package_format =
+ | PACKAGE_FORMAT_APK
+ | PACKAGE_FORMAT_DEB
+ | PACKAGE_FORMAT_EBUILD
+ | PACKAGE_FORMAT_PACMAN
+ | PACKAGE_FORMAT_PISI
+ | PACKAGE_FORMAT_PKGSRC
+ | PACKAGE_FORMAT_RPM
+ | PACKAGE_FORMAT_XBPS
+and package_management =
+ | PACKAGE_MANAGEMENT_APK
+ | PACKAGE_MANAGEMENT_APT
+ | PACKAGE_MANAGEMENT_DNF
+ | PACKAGE_MANAGEMENT_PACMAN
+ | PACKAGE_MANAGEMENT_PISI
+ | PACKAGE_MANAGEMENT_PORTAGE
+ | PACKAGE_MANAGEMENT_UP2DATE
+ | PACKAGE_MANAGEMENT_URPMI
+ | PACKAGE_MANAGEMENT_XBPS
+ | PACKAGE_MANAGEMENT_YUM
+ | PACKAGE_MANAGEMENT_ZYPPER
+and version = int * int
+and fstab_entry = Mountable.t * string (* mountable, mountpoint *)
+and drive_mapping = string * string (* drive name, device *)
+
+val merge_inspection_data : inspection_data -> inspection_data -> inspection_data
+(** [merge_inspection_data child parent] merges two sets of inspection
+ data into a single set. The parent inspection data fields, if
+ present, take precedence over the child inspection data fields.
+
+ It's intended that you merge upwards, ie.
+ [merge_inspection_data usr root] *)
+
+val string_of_fs : fs -> string
+(** Convert [fs] into a single line string, for debugging only. *)
+
+val string_of_root : root -> string
+(** Convert [root] into a multi-line string, for debugging only. *)
+
+val string_of_location : location -> string
+(** Convert [location] into a string, for debugging only. *)
+
+val string_of_inspection_data : inspection_data -> string
+(** Convert [inspection_data] into a multi-line string, for debugging only. *)
+
+val string_of_format : format -> string
+(** Convert [format] to a string.
+ The string is part of the public API. *)
+
+val string_of_os_type : os_type -> string
+(** Convert [os_type] to a string.
+ The string is part of the public API. *)
+
+val string_of_distro : distro -> string
+(** Convert [distro] to a string.
+ The string is part of the public API. *)
+
+val string_of_package_format : package_format -> string
+(** Convert [package_format] to a string.
+ The string is part of the public API. *)
+
+val string_of_package_management : package_management -> string
+(** Convert [package_management] to a string.
+ The string is part of the public API. *)
+
+val null_inspection_data : inspection_data
+(** {!inspection_data} structure with all fields set to [None]. *)
+
+val inspect_fses : fs list ref
+(** The global list of filesystems found by the previous call to
+ inspect_os. *)
diff --git a/daemon/mount.ml b/daemon/mount.ml
index 4bb74fb82..40c81be0e 100644
--- a/daemon/mount.ml
+++ b/daemon/mount.ml
@@ -60,3 +60,64 @@ let mount_vfs options vfs mountable mountpoint =
let mount = mount_vfs None None
let mount_ro = mount_vfs (Some "ro") None
let mount_options options = mount_vfs (Some options) None
+
+(* Unmount everything mounted under /sysroot.
+ *
+ * We have to unmount in the correct order, so we sort the paths by
+ * longest first to ensure that child paths are unmounted by parent
+ * paths.
+ *
+ * This call is more important than it appears at first, because it
+ * is widely used by both test and production code in order to
+ * get back to a known state (nothing mounted, everything synchronized).
+ *)
+let rec umount_all () =
+ (* This is called from internal_autosync and generally as a cleanup
+ * function, and since the umount will definitely fail if any
+ * handles are open, we may as well close them.
+ *)
+ (* XXX
+ aug_finalize ();
+ hivex_finalize ();
+ journal_finalize ();
+ *)
+
+ let sysroot = Sysroot.sysroot () in
+ let sysroot_len = String.length sysroot in
+
+ let info = read_whole_file "/proc/self/mountinfo" in
+ let info = String.nsplit "\n" info in
+
+ let mps = ref [] in
+ List.iter (
+ fun line ->
+ let line = String.nsplit " " line in
+ (* The field of interest is the 5th field. Whitespace is escaped
+ * with octal sequences like \040 (for space).
+ * See fs/seq_file.c:mangle_path.
+ *)
+ if List.length line >= 5 then (
+ let mp = List.nth line 4 in
+ let mp = proc_unmangle_path mp in
+
+ (* Allow a mount directory like "/sysroot" or "/sysroot/..."
*)
+ if (sysroot_len > 0 && String.is_prefix mp sysroot) ||
+ (String.is_prefix mp sysroot &&
+ String.length mp > sysroot_len &&
+ mp.[sysroot_len] = '/') then
+ push_front mp mps
+ )
+ ) info;
+
+ let mps = !mps in
+ let mps = List.sort compare_longest_first mps in
+
+ (* Unmount them. *)
+ List.iter (
+ fun mp -> ignore (command "umount" [mp])
+ ) mps
+
+and compare_longest_first s1 s2 =
+ let n1 = String.length s1 in
+ let n2 = String.length s2 in
+ n2 - n1
diff --git a/daemon/mount.mli b/daemon/mount.mli
index e43d97c42..abf538521 100644
--- a/daemon/mount.mli
+++ b/daemon/mount.mli
@@ -20,3 +20,5 @@ val mount : Mountable.t -> string -> unit
val mount_ro : Mountable.t -> string -> unit
val mount_options : string -> Mountable.t -> string -> unit
val mount_vfs : string option -> string option -> Mountable.t -> string ->
unit
+
+val umount_all : unit -> unit
diff --git a/daemon/utils.ml b/daemon/utils.ml
index 48f6b9c5c..a8944a8a6 100644
--- a/daemon/utils.ml
+++ b/daemon/utils.ml
@@ -238,3 +238,78 @@ let proc_unmangle_path path =
let is_small_file path =
is_regular_file path &&
(stat path).st_size <= 2 * 1048 * 1024
+
+let max_augeas_file_size = 100 * 1000
+
+let rec with_augeas configfiles f =
+ let chroot = Chroot.create (Sysroot.sysroot ()) in
+
+ (* Security: Refuse to do this if a config file is too large. *)
+ List.iter (
+ fun file ->
+ let size = (Chroot.f chroot Unix.stat file).Unix.st_size in
+ if size >= max_augeas_file_size then
+ failwithf "size of %s is unreasonably large (%d bytes)"
+ file size
+ ) configfiles;
+
+ let aug = Augeas.create "/" None [Augeas.AugSaveNoop; Augeas.AugNoLoad] in
+
+ protect
+ ~f:(fun () ->
+ (* Tell Augeas to only load configfiles and no other files. This
+ * prevents a rogue guest from performing a denial of service attack
+ * by having large, over-complicated configuration files which are
+ * unrelated to the task at hand. (Thanks Dominic Cleal).
+ * Note this requires Augeas >= 1.0.0 because of RHBZ#975412.
+ *)
+ let pathexpr = make_augeas_path_expression configfiles in
+ ignore (Augeas.rm aug pathexpr);
+ Augeas.load aug;
+
+ (* Check that augeas did not get a parse error for any of the
+ * configfiles, otherwise we are silently missing information.
+ *)
+ let matches = Augeas.matches aug "/augeas/files//error" in
+ List.iter (
+ fun match_ ->
+ List.iter (
+ fun file ->
+ let errorpath = sprintf "/augeas/files%s/error" file in
+ if match_ = errorpath then (
+ (* There's been an error - get the error details. *)
+ let get path =
+ match Augeas.get aug (errorpath ^ path) with
+ | None -> "<missing>"
+ | Some v -> v
+ in
+ let message = get "message" in
+ let line = get "line" in
+ let charp = get "char" in
+ failwithf "%s:%s:%s: augeas parse failure: %s"
+ file line charp message
+ )
+ ) configfiles
+ ) matches;
+
+ f aug
+ )
+ ~finally:(
+ fun () -> Augeas.close aug
+ )
+
+(* Explained here:
https://bugzilla.redhat.com/show_bug.cgi?id=975412#c0 *)
+and make_augeas_path_expression files =
+ let subexprs =
+ List.map (
+ fun file ->
+ (* v NB trailing '/' after filename *)
+ sprintf "\"%s/\" !~ regexp('^') + glob(incl) +
regexp('/.*')" file
+ ) files in
+ let subexprs = String.concat " and " subexprs in
+
+ let ret = sprintf "/augeas/load/*[ %s ]" subexprs in
+ if verbose () then
+ eprintf "augeas pathexpr = %s\n%!" ret;
+
+ ret
diff --git a/daemon/utils.mli b/daemon/utils.mli
index a1f956be3..fb79582c4 100644
--- a/daemon/utils.mli
+++ b/daemon/utils.mli
@@ -78,3 +78,11 @@ val commandr : string -> string list -> (int * string * string)
val is_small_file : string -> bool
(** Return true if the path is a small regular file. *)
+
+val with_augeas : string list -> (Augeas.t -> 'a) -> 'a
+(** Open an Augeas handle, parse only 'configfiles' (these
+ files must exist), and then call 'f' with the Augeas handle.
+
+ As a security measure, this bails if any file is too large for
+ a reasonable configuration file. After the call to 'f' the
+ Augeas handle is closed. *)
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 7a3907ce2..b3c208f29 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -65,6 +65,7 @@ customize/perl_edit-c.c
daemon/9p.c
daemon/acl.c
daemon/actions.h
+daemon/augeas-c.c
daemon/augeas.c
daemon/available.c
daemon/base64.c
diff --git a/generator/actions.ml b/generator/actions.ml
index 75742397a..a745f6244 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -51,6 +51,7 @@ let daemon_functions =
Actions_core_deprecated.daemon_functions @
Actions_debug.daemon_functions @
Actions_hivex.daemon_functions @
+ Actions_inspection.daemon_functions @
Actions_tsk.daemon_functions @
Actions_yara.daemon_functions
diff --git a/generator/actions_inspection.ml b/generator/actions_inspection.ml
index b7ea5a4de..e7ac6a2c9 100644
--- a/generator/actions_inspection.ml
+++ b/generator/actions_inspection.ml
@@ -22,10 +22,11 @@ open Types
(* Inspection APIs. *)
-let non_daemon_functions = [
+let daemon_functions = [
{ defaults with
name = "inspect_os"; added = (1, 5, 3);
style = RStringList (RMountable, "roots"), [], [];
+ impl = OCaml "Inspect.inspect_os";
shortdesc = "inspect disk and return list of operating systems found";
longdesc = "\
This function uses other libguestfs functions and certain
@@ -61,8 +62,24 @@ Please read L<guestfs(3)/INSPECTION> for more details.
See also C<guestfs_list_filesystems>." };
{ defaults with
+ name = "inspect_get_roots"; added = (1, 7, 3);
+ style = RStringList (RMountable, "roots"), [], [];
+ impl = OCaml "Inspect.inspect_get_roots";
+ shortdesc = "return list of operating systems found by last inspection";
+ longdesc = "\
+This function is a convenient way to get the list of root
+devices, as returned from a previous call to C<guestfs_inspect_os>,
+but without redoing the whole inspection process.
+
+This returns an empty list if either no root devices were
+found or the caller has not called C<guestfs_inspect_os>.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
name = "inspect_get_type"; added = (1, 5, 3);
style = RString (RPlainString, "name"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_type";
shortdesc = "get type of inspected operating system";
longdesc = "\
This returns the type of the inspected operating system.
@@ -116,6 +133,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
{ defaults with
name = "inspect_get_arch"; added = (1, 5, 3);
style = RString (RPlainString, "arch"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_arch";
shortdesc = "get architecture of inspected operating system";
longdesc = "\
This returns the architecture of the inspected operating system.
@@ -130,6 +148,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
{ defaults with
name = "inspect_get_distro"; added = (1, 5, 3);
style = RString (RPlainString, "distro"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_distro";
shortdesc = "get distro of inspected operating system";
longdesc = "\
This returns the distro (distribution) of the inspected operating
@@ -286,6 +305,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
{ defaults with
name = "inspect_get_major_version"; added = (1, 5, 3);
style = RInt "major", [String (Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_major_version";
shortdesc = "get major version of inspected operating system";
longdesc = "\
This returns the major version number of the inspected operating
@@ -305,6 +325,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." };
{ defaults with
name = "inspect_get_minor_version"; added = (1, 5, 3);
style = RInt "minor", [String (Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_minor_version";
shortdesc = "get minor version of inspected operating system";
longdesc = "\
This returns the minor version number of the inspected operating
@@ -318,6 +339,7 @@ See also C<guestfs_inspect_get_major_version>." };
{ defaults with
name = "inspect_get_product_name"; added = (1, 5, 3);
style = RString (RPlainString, "product"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_product_name";
shortdesc = "get product name of inspected operating system";
longdesc = "\
This returns the product name of the inspected operating
@@ -331,8 +353,235 @@ string C<unknown> is returned.
Please read L<guestfs(3)/INSPECTION> for more details." };
{ defaults with
+ name = "inspect_get_windows_systemroot"; added = (1, 5, 25);
+ style = RString (RPlainString, "systemroot"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_windows_systemroot";
+ shortdesc = "get Windows systemroot of inspected operating system";
+ longdesc = "\
+This returns the Windows systemroot of the inspected guest.
+The systemroot is a directory path such as F</WINDOWS>.
+
+This call assumes that the guest is Windows and that the
+systemroot could be determined by inspection. If this is not
+the case then an error is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_package_format"; added = (1, 7, 5);
+ style = RString (RPlainString, "packageformat"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_package_format";
+ shortdesc = "get package format used by the operating system";
+ longdesc = "\
+This function and C<guestfs_inspect_get_package_management> return
+the package format and package management tool used by the
+inspected operating system. For example for Fedora these
+functions would return C<rpm> (package format), and
+C<yum> or C<dnf> (package management).
+
+This returns the string C<unknown> if we could not determine the
+package format I<or> if the operating system does not have
+a real packaging system (eg. Windows).
+
+Possible strings include:
+C<rpm>, C<deb>, C<ebuild>, C<pisi>, C<pacman>,
C<pkgsrc>, C<apk>,
+C<xbps>.
+Future versions of libguestfs may return other strings.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_package_management"; added = (1, 7, 5);
+ style = RString (RPlainString, "packagemanagement"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_package_management";
+ shortdesc = "get package management tool used by the operating system";
+ longdesc = "\
+C<guestfs_inspect_get_package_format> and this function return
+the package format and package management tool used by the
+inspected operating system. For example for Fedora these
+functions would return C<rpm> (package format), and
+C<yum> or C<dnf> (package management).
+
+This returns the string C<unknown> if we could not determine the
+package management tool I<or> if the operating system does not have
+a real packaging system (eg. Windows).
+
+Possible strings include: C<yum>, C<dnf>, C<up2date>,
+C<apt> (for all Debian derivatives),
+C<portage>, C<pisi>, C<pacman>, C<urpmi>, C<zypper>,
C<apk>, C<xbps>.
+Future versions of libguestfs may return other strings.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_hostname"; added = (1, 7, 9);
+ style = RString (RPlainString, "hostname"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_hostname";
+ shortdesc = "get hostname of the operating system";
+ longdesc = "\
+This function returns the hostname of the operating system
+as found by inspection of the guest’s configuration files.
+
+If the hostname could not be determined, then the
+string C<unknown> is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_format"; added = (1, 9, 4);
+ style = RString (RPlainString, "format"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_format";
+ shortdesc = "get format of inspected operating system";
+ longdesc = "\
+This returns the format of the inspected operating system. You
+can use it to detect install images, live CDs and similar.
+
+Currently defined formats are:
+
+=over 4
+
+=item \"installed\"
+
+This is an installed operating system.
+
+=item \"installer\"
+
+The disk image being inspected is not an installed operating system,
+but a I<bootable> install disk, live CD, or similar.
+
+=item \"unknown\"
+
+The format of this disk image is not known.
+
+=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." };
+
+ { defaults with
+ name = "inspect_is_live"; added = (1, 9, 4);
+ style = RBool "live", [String (Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_is_live";
+ shortdesc = "get live flag for install disk";
+ longdesc = "\
+If C<guestfs_inspect_get_format> returns C<installer> (this
+is an install disk), then this returns true if a live image
+was detected on the disk.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_is_netinst"; added = (1, 9, 4);
+ style = RBool "netinst", [String (Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_is_netinst";
+ shortdesc = "get netinst (network installer) flag for install disk";
+ longdesc = "\
+If C<guestfs_inspect_get_format> returns C<installer> (this
+is an install disk), then this returns true if the disk is
+a network installer, ie. not a self-contained install CD but
+one which is likely to require network access to complete
+the install.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_is_multipart"; added = (1, 9, 4);
+ style = RBool "multipart", [String (Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_is_multipart";
+ shortdesc = "get multipart flag for install disk";
+ longdesc = "\
+If C<guestfs_inspect_get_format> returns C<installer> (this
+is an install disk), then this returns true if the disk is
+part of a set.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_product_variant"; added = (1, 9, 13);
+ style = RString (RPlainString, "variant"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_product_variant";
+ shortdesc = "get product variant of inspected operating system";
+ longdesc = "\
+This returns the product variant of the inspected operating
+system.
+
+For Windows guests, this returns the contents of the Registry key
+C<HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion>
+C<InstallationType> which is usually a string such as
+C<Client> or C<Server> (other values are possible). This
+can be used to distinguish consumer and enterprise versions
+of Windows that have the same version number (for example,
+Windows 7 and Windows 2008 Server are both version 6.1,
+but the former is C<Client> and the latter is C<Server>).
+
+For enterprise Linux guests, in future we intend this to return
+the product variant such as C<Desktop>, C<Server> and so on. But
+this is not implemented at present.
+
+If the product variant could not be determined, then the
+string C<unknown> is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details.
+See also C<guestfs_inspect_get_product_name>,
+C<guestfs_inspect_get_major_version>." };
+
+ { defaults with
+ name = "inspect_get_windows_current_control_set"; added = (1, 9, 17);
+ style = RString (RPlainString, "controlset"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_windows_current_control_set";
+ shortdesc = "get Windows CurrentControlSet of inspected operating system";
+ longdesc = "\
+This returns the Windows CurrentControlSet of the inspected guest.
+The CurrentControlSet is a registry key name such as C<ControlSet001>.
+
+This call assumes that the guest is Windows and that the
+Registry could be examined by inspection. If this is not
+the case then an error is returned.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_windows_software_hive"; added = (1, 35, 26);
+ style = RString (RPlainString, "path"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_windows_software_hive";
+ shortdesc = "get the path of the Windows software hive";
+ longdesc = "\
+This returns the path to the hive (binary Windows Registry file)
+corresponding to HKLM\\SOFTWARE.
+
+This call assumes that the guest is Windows and that the guest
+has a software hive file with the right name. If this is not the
+case then an error is returned. This call does not check that the
+hive is a valid Windows Registry hive.
+
+You can use C<guestfs_hivex_open> to read or write to the hive.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
+ name = "inspect_get_windows_system_hive"; added = (1, 35, 26);
+ style = RString (RPlainString, "path"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_windows_system_hive";
+ shortdesc = "get the path of the Windows system hive";
+ longdesc = "\
+This returns the path to the hive (binary Windows Registry file)
+corresponding to HKLM\\SYSTEM.
+
+This call assumes that the guest is Windows and that the guest
+has a system hive file with the right name. If this is not the
+case then an error is returned. This call does not check that the
+hive is a valid Windows Registry hive.
+
+You can use C<guestfs_hivex_open> to read or write to the hive.
+
+Please read L<guestfs(3)/INSPECTION> for more details." };
+
+ { defaults with
name = "inspect_get_mountpoints"; added = (1, 5, 3);
style = RHashtable (RPlainString, RMountable, "mountpoints"), [String
(Mountable, "root")], [];
+ impl = OCaml "Inspect.inspect_get_mountpoints";
shortdesc = "get mountpoints of inspected operating system";
longdesc = "\
This returns a hash of where we think the filesystems
@@ -364,6 +613,7 @@ See also C<guestfs_inspect_get_filesystems>." };
{ defaults with
name = "inspect_get_filesystems"; added = (1, 5, 3);
style = RStringList (RMountable, "filesystems"), [String (Mountable,
"root")], [];
+ impl = OCaml "Inspect.inspect_get_filesystems";
shortdesc = "get filesystems associated with inspected operating system";
longdesc = "\
This returns a list of all the filesystems that we think
@@ -377,78 +627,9 @@ 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>." };
- { defaults with
- name = "inspect_get_windows_systemroot"; added = (1, 5, 25);
- style = RString (RPlainString, "systemroot"), [String (Mountable,
"root")], [];
- shortdesc = "get Windows systemroot of inspected operating system";
- longdesc = "\
-This returns the Windows systemroot of the inspected guest.
-The systemroot is a directory path such as F</WINDOWS>.
-
-This call assumes that the guest is Windows and that the
-systemroot could be determined by inspection. If this is not
-the case then an error is returned.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_roots"; added = (1, 7, 3);
- style = RStringList (RMountable, "roots"), [], [];
- shortdesc = "return list of operating systems found by last inspection";
- longdesc = "\
-This function is a convenient way to get the list of root
-devices, as returned from a previous call to C<guestfs_inspect_os>,
-but without redoing the whole inspection process.
-
-This returns an empty list if either no root devices were
-found or the caller has not called C<guestfs_inspect_os>.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_package_format"; added = (1, 7, 5);
- style = RString (RPlainString, "packageformat"), [String (Mountable,
"root")], [];
- shortdesc = "get package format used by the operating system";
- longdesc = "\
-This function and C<guestfs_inspect_get_package_management> return
-the package format and package management tool used by the
-inspected operating system. For example for Fedora these
-functions would return C<rpm> (package format), and
-C<yum> or C<dnf> (package management).
-
-This returns the string C<unknown> if we could not determine the
-package format I<or> if the operating system does not have
-a real packaging system (eg. Windows).
-
-Possible strings include:
-C<rpm>, C<deb>, C<ebuild>, C<pisi>, C<pacman>,
C<pkgsrc>, C<apk>,
-C<xbps>.
-Future versions of libguestfs may return other strings.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_package_management"; added = (1, 7, 5);
- style = RString (RPlainString, "packagemanagement"), [String (Mountable,
"root")], [];
- shortdesc = "get package management tool used by the operating system";
- longdesc = "\
-C<guestfs_inspect_get_package_format> and this function return
-the package format and package management tool used by the
-inspected operating system. For example for Fedora these
-functions would return C<rpm> (package format), and
-C<yum> or C<dnf> (package management).
-
-This returns the string C<unknown> if we could not determine the
-package management tool I<or> if the operating system does not have
-a real packaging system (eg. Windows).
-
-Possible strings include: C<yum>, C<dnf>, C<up2date>,
-C<apt> (for all Debian derivatives),
-C<portage>, C<pisi>, C<pacman>, C<urpmi>, C<zypper>,
C<apk>, C<xbps>.
-Future versions of libguestfs may return other strings.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
+]
+let non_daemon_functions = [
{ defaults with
name = "inspect_list_applications2"; added = (1, 19, 56);
style = RStructList ("applications2", "application2"), [String
(Mountable, "root")], [];
@@ -553,128 +734,6 @@ If unavailable this is returned as an empty string
C<\"\">.
Please read L<guestfs(3)/INSPECTION> for more details." };
{ defaults with
- name = "inspect_get_hostname"; added = (1, 7, 9);
- style = RString (RPlainString, "hostname"), [String (Mountable,
"root")], [];
- shortdesc = "get hostname of the operating system";
- longdesc = "\
-This function returns the hostname of the operating system
-as found by inspection of the guest’s configuration files.
-
-If the hostname could not be determined, then the
-string C<unknown> is returned.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_format"; added = (1, 9, 4);
- style = RString (RPlainString, "format"), [String (Mountable,
"root")], [];
- shortdesc = "get format of inspected operating system";
- longdesc = "\
-This returns the format of the inspected operating system. You
-can use it to detect install images, live CDs and similar.
-
-Currently defined formats are:
-
-=over 4
-
-=item \"installed\"
-
-This is an installed operating system.
-
-=item \"installer\"
-
-The disk image being inspected is not an installed operating system,
-but a I<bootable> install disk, live CD, or similar.
-
-=item \"unknown\"
-
-The format of this disk image is not known.
-
-=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." };
-
- { defaults with
- name = "inspect_is_live"; added = (1, 9, 4);
- style = RBool "live", [String (Mountable, "root")], [];
- shortdesc = "get live flag for install disk";
- longdesc = "\
-If C<guestfs_inspect_get_format> returns C<installer> (this
-is an install disk), then this returns true if a live image
-was detected on the disk.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_is_netinst"; added = (1, 9, 4);
- style = RBool "netinst", [String (Mountable, "root")], [];
- shortdesc = "get netinst (network installer) flag for install disk";
- longdesc = "\
-If C<guestfs_inspect_get_format> returns C<installer> (this
-is an install disk), then this returns true if the disk is
-a network installer, ie. not a self-contained install CD but
-one which is likely to require network access to complete
-the install.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_is_multipart"; added = (1, 9, 4);
- style = RBool "multipart", [String (Mountable, "root")], [];
- shortdesc = "get multipart flag for install disk";
- longdesc = "\
-If C<guestfs_inspect_get_format> returns C<installer> (this
-is an install disk), then this returns true if the disk is
-part of a set.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_product_variant"; added = (1, 9, 13);
- style = RString (RPlainString, "variant"), [String (Mountable,
"root")], [];
- shortdesc = "get product variant of inspected operating system";
- longdesc = "\
-This returns the product variant of the inspected operating
-system.
-
-For Windows guests, this returns the contents of the Registry key
-C<HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion>
-C<InstallationType> which is usually a string such as
-C<Client> or C<Server> (other values are possible). This
-can be used to distinguish consumer and enterprise versions
-of Windows that have the same version number (for example,
-Windows 7 and Windows 2008 Server are both version 6.1,
-but the former is C<Client> and the latter is C<Server>).
-
-For enterprise Linux guests, in future we intend this to return
-the product variant such as C<Desktop>, C<Server> and so on. But
-this is not implemented at present.
-
-If the product variant could not be determined, then the
-string C<unknown> is returned.
-
-Please read L<guestfs(3)/INSPECTION> for more details.
-See also C<guestfs_inspect_get_product_name>,
-C<guestfs_inspect_get_major_version>." };
-
- { defaults with
- name = "inspect_get_windows_current_control_set"; added = (1, 9, 17);
- style = RString (RPlainString, "controlset"), [String (Mountable,
"root")], [];
- shortdesc = "get Windows CurrentControlSet of inspected operating system";
- longdesc = "\
-This returns the Windows CurrentControlSet of the inspected guest.
-The CurrentControlSet is a registry key name such as C<ControlSet001>.
-
-This call assumes that the guest is Windows and that the
-Registry could be examined by inspection. If this is not
-the case then an error is returned.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
name = "inspect_get_drive_mappings"; added = (1, 9, 17);
style = RHashtable (RPlainString, RDevice, "drives"), [String (Mountable,
"root")], [];
shortdesc = "get drive letter mappings";
@@ -773,38 +832,4 @@ advice before using trademarks in applications.
=back" };
- { defaults with
- name = "inspect_get_windows_software_hive"; added = (1, 35, 26);
- style = RString (RPlainString, "path"), [String (Mountable,
"root")], [];
- shortdesc = "get the path of the Windows software hive";
- longdesc = "\
-This returns the path to the hive (binary Windows Registry file)
-corresponding to HKLM\\SOFTWARE.
-
-This call assumes that the guest is Windows and that the guest
-has a software hive file with the right name. If this is not the
-case then an error is returned. This call does not check that the
-hive is a valid Windows Registry hive.
-
-You can use C<guestfs_hivex_open> to read or write to the hive.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
- { defaults with
- name = "inspect_get_windows_system_hive"; added = (1, 35, 26);
- style = RString (RPlainString, "path"), [String (Mountable,
"root")], [];
- shortdesc = "get the path of the Windows system hive";
- longdesc = "\
-This returns the path to the hive (binary Windows Registry file)
-corresponding to HKLM\\SYSTEM.
-
-This call assumes that the guest is Windows and that the guest
-has a system hive file with the right name. If this is not the
-case then an error is returned. This call does not check that the
-hive is a valid Windows Registry hive.
-
-You can use C<guestfs_hivex_open> to read or write to the hive.
-
-Please read L<guestfs(3)/INSPECTION> for more details." };
-
]
diff --git a/generator/actions_inspection.mli b/generator/actions_inspection.mli
index 327f7aa4f..06b8116c4 100644
--- a/generator/actions_inspection.mli
+++ b/generator/actions_inspection.mli
@@ -19,3 +19,4 @@
(* Please read generator/README first. *)
val non_daemon_functions : Types.action list
+val daemon_functions : Types.action list
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 66b625388..2d1f57aff 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -597,6 +597,30 @@ return_string_mountable (value retv)
}
}
+/* Implement RStringList (RMountable, _). */
+static char **
+return_string_mountable_list (value retv)
+{
+ CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
+ value v;
+ char *m;
+
+ while (retv != Val_int (0)) {
+ v = Field (retv, 0);
+ m = return_string_mountable (v);
+ if (m == NULL)
+ return NULL;
+ if (add_string_nodup (&ret, m) == -1)
+ return NULL;
+ retv = Field (retv, 1);
+ }
+
+ if (end_stringsbuf (&ret) == -1)
+ return NULL;
+
+ return take_stringsbuf (&ret); /* caller frees */
+}
+
/* Implement RHashtable (RMountable, RPlainString, _). */
static char **
return_hashtable_mountable_string (value retv)
@@ -625,6 +649,34 @@ return_hashtable_mountable_string (value retv)
return take_stringsbuf (&ret); /* caller frees */
}
+/* Implement RHashtable (RPlainString, RMountable, _). */
+static char **
+return_hashtable_string_mountable (value retv)
+{
+ CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
+ value sv, v, mv;
+ char *m;
+
+ while (retv != Val_int (0)) {
+ v = Field (retv, 0); /* (string, Mountable.t) */
+ sv = Field (v, 0); /* string */
+ if (add_string (&ret, String_val (sv)) == -1)
+ return NULL;
+ mv = Field (v, 1); /* Mountable.t */
+ m = return_string_mountable (mv);
+ if (m == NULL)
+ return NULL;
+ if (add_string_nodup (&ret, m) == -1)
+ return NULL;
+ retv = Field (retv, 1);
+ }
+
+ if (end_stringsbuf (&ret) == -1)
+ return NULL;
+
+ return take_stringsbuf (&ret); /* caller frees */
+}
+
";
(* Implement code for returning structs and struct lists. *)
@@ -865,9 +917,12 @@ return_hashtable_mountable_string (value retv)
| RString (RMountable, _) ->
pr " char *ret = return_string_mountable (retv);\n";
pr " CAMLreturnT (char *, ret); /* caller frees */\n"
- | RStringList _ ->
+ | RStringList ((RPlainString|RDevice), _) ->
pr " char **ret = return_string_list (retv);\n";
pr " CAMLreturnT (char **, ret); /* caller frees */\n"
+ | RStringList (RMountable, _) ->
+ pr " char **ret = return_string_mountable_list (retv);\n";
+ pr " CAMLreturnT (char **, ret); /* caller frees */\n"
| RStruct (_, typ) ->
pr " guestfs_int_%s *ret =\n" typ;
pr " return_%s (retv);\n" typ;
@@ -881,6 +936,9 @@ return_hashtable_mountable_string (value retv)
| RHashtable (RMountable, RPlainString, _) ->
pr " char **ret = return_hashtable_mountable_string (retv);\n";
pr " CAMLreturnT (char **, ret); /* caller frees */\n"
+ | RHashtable (RPlainString, RMountable, _) ->
+ pr " char **ret = return_hashtable_string_mountable (retv);\n";
+ pr " CAMLreturnT (char **, ret); /* caller frees */\n"
| RHashtable _ -> assert false
| RBufferOut _ -> assert false
);
diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
index dec02f5fa..d830281a0 100644
--- a/generator/proc_nr.ml
+++ b/generator/proc_nr.ml
@@ -484,6 +484,28 @@ let proc_nr = [
474, "internal_yara_scan";
475, "file_architecture";
476, "list_filesystems";
+477, "inspect_os";
+478, "inspect_get_roots";
+479, "inspect_get_format";
+480, "inspect_get_type";
+481, "inspect_get_distro";
+482, "inspect_get_package_format";
+483, "inspect_get_package_management";
+484, "inspect_get_product_name";
+485, "inspect_get_product_variant";
+486, "inspect_get_major_version";
+487, "inspect_get_minor_version";
+488, "inspect_get_arch";
+489, "inspect_get_hostname";
+490, "inspect_get_windows_systemroot";
+491, "inspect_get_windows_software_hive";
+492, "inspect_get_windows_system_hive";
+493, "inspect_get_windows_current_control_set";
+494, "inspect_is_live";
+495, "inspect_is_netinst";
+496, "inspect_is_multipart";
+497, "inspect_get_mountpoints";
+498, "inspect_get_filesystems";
]
(* End of list. If adding a new entry, add it at the end of the list
diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
index b86395733..f9aaa4d56 100644
--- a/lib/MAX_PROC_NR
+++ b/lib/MAX_PROC_NR
@@ -1 +1 @@
-476
+498
diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h
index 7a61295b9..e694a8718 100644
--- a/lib/guestfs-internal.h
+++ b/lib/guestfs-internal.h
@@ -129,7 +129,7 @@
* those.
*/
#define MAX_SMALL_FILE_SIZE (2 * 1000 * 1000)
-#define MAX_AUGEAS_FILE_SIZE (100 * 1000)
+#define MAX_AUGEAS_FILE_SIZE (100 * 1000) /* XXX REMOVE */
/* Maximum RPM or dpkg database we will download to /tmp. RPM
* 'Packages' database can get very large: 70 MB is roughly the
diff --git a/lib/inspect.c b/lib/inspect.c
index 1cc0942f1..2aaf30344 100644
--- a/lib/inspect.c
+++ b/lib/inspect.c
@@ -45,569 +45,6 @@
COMPILE_REGEXP (re_primary_partition, "^/dev/(?:h|s|v)d.[1234]$", 0)
-static void check_for_duplicated_bsd_root (guestfs_h *g);
-static void collect_coreos_inspection_info (guestfs_h *g);
-static void collect_linux_inspection_info (guestfs_h *g);
-static void collect_linux_inspection_info_for (guestfs_h *g, struct inspect_fs *root);
-
-/**
- * The main inspection API.
- */
-char **
-guestfs_impl_inspect_os (guestfs_h *g)
-{
- CLEANUP_FREE_STRING_LIST char **fses = NULL;
- char **fs, **ret;
-
- /* Remove any information previously stored in the handle. */
- guestfs_int_free_inspect_info (g);
-
- if (guestfs_umount_all (g) == -1)
- return NULL;
-
- /* Iterate over all detected filesystems. Inspect each one in turn
- * and add that information to the handle.
- */
-
- fses = guestfs_list_filesystems (g);
- if (fses == NULL) return NULL;
-
- for (fs = fses; *fs; fs += 2) {
- if (guestfs_int_check_for_filesystem_on (g, *fs)) {
- guestfs_int_free_inspect_info (g);
- return NULL;
- }
- }
-
- /* The OS inspection information for CoreOS are gathered by inspecting
- * multiple filesystems. Gather all the inspected information in the
- * inspect_fs struct of the root filesystem.
- */
- collect_coreos_inspection_info (g);
-
- /* Check if the same filesystem was listed twice as root in g->fses.
- * This may happen for the *BSD root partition where an MBR partition
- * is a shadow of the real root partition probably /dev/sda5
- */
- check_for_duplicated_bsd_root (g);
-
- /* For Linux guests with a separate /usr filesyste, merge some of the
- * inspected information in that partition to the inspect_fs struct
- * of the root filesystem.
- */
- collect_linux_inspection_info (g);
-
- /* At this point we have, in the handle, a list of all filesystems
- * found and data about each one. Now we assemble the list of
- * filesystems which are root devices and return that to the user.
- * Fall through to guestfs_inspect_get_roots to do that.
- */
- ret = guestfs_inspect_get_roots (g);
- if (ret == NULL)
- guestfs_int_free_inspect_info (g);
- return ret;
-}
-
-/**
- * Traverse through the filesystem list and find out if it contains
- * the C</> and C</usr> filesystems of a CoreOS image. If this is the
- * case, sum up all the collected information on the root fs.
- */
-static void
-collect_coreos_inspection_info (guestfs_h *g)
-{
- size_t i;
- struct inspect_fs *root = NULL, *usr = NULL;
-
- for (i = 0; i < g->nr_fses; ++i) {
- struct inspect_fs *fs = &g->fses[i];
-
- if (fs->distro == OS_DISTRO_COREOS && fs->role == OS_ROLE_ROOT)
- root = fs;
- }
-
- if (root == NULL)
- return;
-
- for (i = 0; i < g->nr_fses; ++i) {
- struct inspect_fs *fs = &g->fses[i];
-
- if (fs->distro != OS_DISTRO_COREOS || fs->role != OS_ROLE_USR)
- continue;
-
- /* CoreOS is designed to contain 2 /usr partitions (USR-A, USR-B):
- *
https://coreos.com/docs/sdk-distributors/sdk/disk-partitions/
- * One is active and one passive. During the initial boot, the passive
- * partition is empty and it gets filled up when an update is performed.
- * Then, when the system reboots, the boot loader is instructed to boot
- * from the passive partition. If both partitions are valid, we cannot
- * determine which the active and which the passive is, unless we peep into
- * the boot loader. As a workaround, we check the OS versions and pick the
- * one with the higher version as active.
- */
- if (usr && guestfs_int_version_cmp_ge (&usr->version,
&fs->version))
- continue;
-
- usr = fs;
- }
-
- if (usr == NULL)
- return;
-
- guestfs_int_merge_fs_inspections (g, root, usr);
-}
-
-/**
- * Traverse through the filesystems and find the /usr filesystem for
- * the specified C<root>: if found, merge its basic inspection details
- * to the root when they were set (i.e. because the /usr had os-release
- * or other ways to identify the OS).
- */
-static void
-collect_linux_inspection_info_for (guestfs_h *g, struct inspect_fs *root)
-{
- size_t i;
- struct inspect_fs *usr = NULL;
-
- for (i = 0; i < g->nr_fses; ++i) {
- struct inspect_fs *fs = &g->fses[i];
- size_t j;
-
- if (!(fs->distro == root->distro || fs->distro == OS_DISTRO_UNKNOWN) ||
- fs->role != OS_ROLE_USR)
- continue;
-
- for (j = 0; j < root->nr_fstab; ++j) {
- if (STREQ (fs->mountable, root->fstab[j].mountable)) {
- usr = fs;
- goto got_usr;
- }
- }
- }
-
- assert (usr == NULL);
- return;
-
- got_usr:
- /* If the version information in /usr is not null, then most probably
- * there was an os-release file there, so reset what is in root
- * and pick the results from /usr.
- */
- if (!version_is_null (&usr->version)) {
- root->distro = OS_DISTRO_UNKNOWN;
- free (root->product_name);
- root->product_name = NULL;
- }
-
- guestfs_int_merge_fs_inspections (g, root, usr);
-}
-
-/**
- * Traverse through the filesystem list and find out if it contains
- * the C</> and C</usr> filesystems of a Linux image (but not CoreOS,
- * for which there is a separate C<collect_coreos_inspection_info>).
- * If this is the case, sum up all the collected information on each
- * root fs from the respective /usr filesystems.
- */
-static void
-collect_linux_inspection_info (guestfs_h *g)
-{
- size_t i;
-
- for (i = 0; i < g->nr_fses; ++i) {
- struct inspect_fs *fs = &g->fses[i];
-
- if (fs->distro != OS_DISTRO_COREOS && fs->role == OS_ROLE_ROOT)
- collect_linux_inspection_info_for (g, fs);
- }
-}
-
-/**
- * On *BSD systems, sometimes F</dev/sda[1234]> is a shadow of the
- * real root filesystem that is probably F</dev/sda5> (see:
- *
L<http://www.freebsd.org/doc/handbook/disk-organization.html>)
- */
-static void
-check_for_duplicated_bsd_root (guestfs_h *g)
-{
- size_t i;
- struct inspect_fs *bsd_primary = NULL;
-
- for (i = 0; i < g->nr_fses; ++i) {
- bool is_bsd;
- struct inspect_fs *fs = &g->fses[i];
-
- is_bsd =
- fs->type == OS_TYPE_FREEBSD ||
- fs->type == OS_TYPE_NETBSD ||
- fs->type == OS_TYPE_OPENBSD;
-
- if (fs->role == OS_ROLE_ROOT && is_bsd &&
- match (g, fs->mountable, re_primary_partition)) {
- bsd_primary = fs;
- continue;
- }
-
- if (fs->role == OS_ROLE_ROOT && bsd_primary &&
- bsd_primary->type == fs->type) {
- /* remove the root role from the bsd_primary */
- bsd_primary->role = OS_ROLE_UNKNOWN;
- bsd_primary->format = OS_FORMAT_UNKNOWN;
- return;
- }
- }
-}
-
-static int
-compare_strings (const void *vp1, const void *vp2)
-{
- const char *s1 = * (char * const *) vp1;
- const char *s2 = * (char * const *) vp2;
-
- return strcmp (s1, s2);
-}
-
-char **
-guestfs_impl_inspect_get_roots (guestfs_h *g)
-{
- size_t i;
- DECLARE_STRINGSBUF (ret);
-
- /* NB. Doesn't matter if g->nr_fses == 0. We just return an empty
- * list in this case.
- */
- for (i = 0; i < g->nr_fses; ++i) {
- if (g->fses[i].role == OS_ROLE_ROOT)
- guestfs_int_add_string (g, &ret, g->fses[i].mountable);
- }
- guestfs_int_end_stringsbuf (g, &ret);
-
- qsort (ret.argv, ret.size-1, sizeof (char *), compare_strings);
-
- return ret.argv;
-}
-
-char *
-guestfs_impl_inspect_get_type (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- char *ret = NULL;
-
- if (!fs)
- return NULL;
-
- switch (fs->type) {
- case OS_TYPE_DOS: ret = safe_strdup (g, "dos"); break;
- case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
- case OS_TYPE_HURD: ret = safe_strdup (g, "hurd"); break;
- case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break;
- case OS_TYPE_MINIX: ret = safe_strdup (g, "minix"); break;
- case OS_TYPE_NETBSD: ret = safe_strdup (g, "netbsd"); break;
- case OS_TYPE_OPENBSD: ret = safe_strdup (g, "openbsd"); break;
- case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break;
- case OS_TYPE_UNKNOWN: ret = safe_strdup (g, "unknown"); break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-char *
-guestfs_impl_inspect_get_arch (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- return safe_strdup (g, fs->arch ? : "unknown");
-}
-
-char *
-guestfs_impl_inspect_get_distro (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- char *ret = NULL;
-
- if (!fs)
- return NULL;
-
- switch (fs->distro) {
- case OS_DISTRO_ALPINE_LINUX: ret = safe_strdup (g, "alpinelinux"); break;
- case OS_DISTRO_ALTLINUX: ret = safe_strdup (g, "altlinux"); break;
- case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break;
- case OS_DISTRO_BUILDROOT: ret = safe_strdup (g, "buildroot"); break;
- case OS_DISTRO_CENTOS: ret = safe_strdup (g, "centos"); break;
- case OS_DISTRO_CIRROS: ret = safe_strdup (g, "cirros"); break;
- case OS_DISTRO_COREOS: ret = safe_strdup (g, "coreos"); break;
- case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break;
- case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break;
- case OS_DISTRO_FREEBSD: ret = safe_strdup (g, "freebsd"); break;
- case OS_DISTRO_FREEDOS: ret = safe_strdup (g, "freedos"); break;
- case OS_DISTRO_FRUGALWARE: ret = safe_strdup (g, "frugalware"); break;
- case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break;
- case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint"); break;
- case OS_DISTRO_MAGEIA: ret = safe_strdup (g, "mageia"); break;
- case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break;
- case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break;
- case OS_DISTRO_NETBSD: ret = safe_strdup (g, "netbsd"); break;
- case OS_DISTRO_OPENBSD: ret = safe_strdup (g, "openbsd"); break;
- case OS_DISTRO_OPENSUSE: ret = safe_strdup (g, "opensuse"); break;
- case OS_DISTRO_ORACLE_LINUX: ret = safe_strdup (g, "oraclelinux"); break;
- case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break;
- case OS_DISTRO_PLD_LINUX: ret = safe_strdup (g, "pldlinux"); break;
- case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break;
- case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break;
- case OS_DISTRO_SCIENTIFIC_LINUX: ret = safe_strdup (g, "scientificlinux");
break;
- case OS_DISTRO_SLACKWARE: ret = safe_strdup (g, "slackware"); break;
- case OS_DISTRO_SLES: ret = safe_strdup (g, "sles"); break;
- case OS_DISTRO_SUSE_BASED: ret = safe_strdup (g, "suse-based"); break;
- case OS_DISTRO_TTYLINUX: ret = safe_strdup (g, "ttylinux"); break;
- case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break;
- case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break;
- case OS_DISTRO_VOID_LINUX: ret = safe_strdup (g, "voidlinux"); break;
- case OS_DISTRO_UNKNOWN: ret = safe_strdup (g, "unknown"); break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-int
-guestfs_impl_inspect_get_major_version (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->version.v_major;
-}
-
-int
-guestfs_impl_inspect_get_minor_version (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->version.v_minor;
-}
-
-char *
-guestfs_impl_inspect_get_product_name (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- return safe_strdup (g, fs->product_name ? : "unknown");
-}
-
-char *
-guestfs_impl_inspect_get_product_variant (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- return safe_strdup (g, fs->product_variant ? : "unknown");
-}
-
-char *
-guestfs_impl_inspect_get_windows_systemroot (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- if (!fs->windows_systemroot) {
- error (g, _("not a Windows guest, or systemroot could not be
determined"));
- return NULL;
- }
-
- return safe_strdup (g, fs->windows_systemroot);
-}
-
-char *
-guestfs_impl_inspect_get_windows_software_hive (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- if (!fs->windows_software_hive) {
- error (g, _("not a Windows guest, or software hive not found"));
- return NULL;
- }
-
- return safe_strdup (g, fs->windows_software_hive);
-}
-
-char *
-guestfs_impl_inspect_get_windows_system_hive (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- if (!fs->windows_system_hive) {
- error (g, _("not a Windows guest, or system hive not found"));
- return NULL;
- }
-
- return safe_strdup (g, fs->windows_system_hive);
-}
-
-char *
-guestfs_impl_inspect_get_windows_current_control_set (guestfs_h *g,
- const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- if (!fs->windows_current_control_set) {
- error (g, _("not a Windows guest, or CurrentControlSet could not be
determined"));
- return NULL;
- }
-
- return safe_strdup (g, fs->windows_current_control_set);
-}
-
-char *
-guestfs_impl_inspect_get_format (guestfs_h *g, const char *root)
-{
- char *ret = NULL;
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- switch (fs->format) {
- case OS_FORMAT_INSTALLED: ret = safe_strdup (g, "installed"); break;
- case OS_FORMAT_INSTALLER: ret = safe_strdup (g, "installer"); break;
- case OS_FORMAT_UNKNOWN: ret = safe_strdup (g, "unknown"); break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-int
-guestfs_impl_inspect_is_live (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->is_live_disk;
-}
-
-int
-guestfs_impl_inspect_is_netinst (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->is_netinst_disk;
-}
-
-int
-guestfs_impl_inspect_is_multipart (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return -1;
-
- return fs->is_multipart_disk;
-}
-
-char **
-guestfs_impl_inspect_get_mountpoints (guestfs_h *g, const char *root)
-{
- char **ret;
- size_t i, count, nr;
- struct inspect_fs *fs;
-
- fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
-#define CRITERION(fs, i) fs->fstab[i].mountpoint[0] == '/'
-
- nr = fs->nr_fstab;
-
- if (nr == 0)
- count = 1;
- else {
- count = 0;
- for (i = 0; i < nr; ++i)
- if (CRITERION (fs, i))
- count++;
- }
-
- /* Hashtables have 2N+1 entries. */
- ret = calloc (2*count+1, sizeof (char *));
- if (ret == NULL) {
- perrorf (g, "calloc");
- return NULL;
- }
-
- /* If no fstab information (Windows) return just the root. */
- if (nr == 0) {
- ret[0] = safe_strdup (g, "/");
- ret[1] = safe_strdup (g, root);
- ret[2] = NULL;
- return ret;
- }
-
- count = 0;
- for (i = 0; i < nr; ++i)
- if (CRITERION (fs, i)) {
- ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint);
- ret[2*count+1] = safe_strdup (g, fs->fstab[i].mountable);
- count++;
- }
-#undef CRITERION
-
- return ret;
-}
-
-char **
-guestfs_impl_inspect_get_filesystems (guestfs_h *g, const char *root)
-{
- char **ret;
- size_t i, nr;
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
-
- if (!fs)
- return NULL;
-
- nr = fs->nr_fstab;
- ret = calloc (nr == 0 ? 2 : nr+1, sizeof (char *));
- if (ret == NULL) {
- perrorf (g, "calloc");
- return NULL;
- }
-
- /* If no fstab information (Windows) return just the root. */
- if (nr == 0) {
- ret[0] = safe_strdup (g, root);
- ret[1] = NULL;
- return ret;
- }
-
- for (i = 0; i < nr; ++i)
- ret[i] = safe_strdup (g, fs->fstab[i].mountable);
-
- return ret;
-}
-
char **
guestfs_impl_inspect_get_drive_mappings (guestfs_h *g, const char *root)
{
@@ -628,75 +65,6 @@ guestfs_impl_inspect_get_drive_mappings (guestfs_h *g, const char
*root)
return ret.argv;
}
-char *
-guestfs_impl_inspect_get_package_format (guestfs_h *g, const char *root)
-{
- char *ret = NULL;
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- switch (fs->package_format) {
- case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break;
- case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break;
- case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman"); break;
- case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild"); break;
- case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break;
- case OS_PACKAGE_FORMAT_PKGSRC: ret = safe_strdup (g, "pkgsrc"); break;
- case OS_PACKAGE_FORMAT_APK: ret = safe_strdup (g, "apk"); break;
- case OS_PACKAGE_FORMAT_XBPS: ret = safe_strdup (g, "xbps"); break;
- case OS_PACKAGE_FORMAT_UNKNOWN:
- ret = safe_strdup (g, "unknown");
- break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-char *
-guestfs_impl_inspect_get_package_management (guestfs_h *g, const char *root)
-{
- char *ret = NULL;
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- switch (fs->package_management) {
- case OS_PACKAGE_MANAGEMENT_APK: ret = safe_strdup (g, "apk"); break;
- case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt"); break;
- case OS_PACKAGE_MANAGEMENT_DNF: ret = safe_strdup (g, "dnf"); break;
- case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman"); break;
- case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi"); break;
- case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g, "portage"); break;
- case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g, "up2date"); break;
- case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi"); break;
- case OS_PACKAGE_MANAGEMENT_XBPS: ret = safe_strdup (g, "xbps"); break;
- case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum"); break;
- case OS_PACKAGE_MANAGEMENT_ZYPPER: ret = safe_strdup (g, "zypper"); break;
- case OS_PACKAGE_MANAGEMENT_UNKNOWN:
- ret = safe_strdup (g, "unknown");
- break;
- }
-
- if (ret == NULL)
- abort ();
-
- return ret;
-}
-
-char *
-guestfs_impl_inspect_get_hostname (guestfs_h *g, const char *root)
-{
- struct inspect_fs *fs = guestfs_int_search_for_root (g, root);
- if (!fs)
- return NULL;
-
- return safe_strdup (g, fs->hostname ? : "unknown");
-}
-
void
guestfs_int_free_inspect_info (guestfs_h *g)
{
--
2.13.0