Richard W.M. Jones
2017-Jun-12 22:06 UTC
[Libguestfs] [PATCH] UNFINISHED daemon: Reimplement most inspection APIs in the daemon.
This is the (incomplete) patch which reimplements inspection APIs in the daemon. All ‘XXX’s in this patch indicate areas which are not yet implemented or need further work. Rich.
Richard W.M. Jones
2017-Jun-12 22:06 UTC
[Libguestfs] [PATCH] UNFINISHED daemon: Reimplement most inspection APIs in the daemon, in OCaml.
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: 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 # 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: + + 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: + * 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: + * 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: 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): - * 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<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
Seemingly Similar Threads
- [PATCH v10 00/10] Reimplement inspection in the daemon.
- [PATCH v9 00/11] Reimplement inspection in the daemon.
- [PATCH v11 00/10] Reimplement inspection in the daemon.
- [PATCH v12 00/11] Reimplement inspection in the daemon.
- [PATCH v7 00/29] Reimplement inspection in the daemon.