Richard W.M. Jones
2018-Oct-01 19:16 UTC
[Libguestfs] [PATCH v2 API PROPOSAL 0/5] inspection Add network interfaces to inspection data.
The proposed API is the same as v1, but this includes an implementation (for /etc/sysconfig/network-scripts/ifcfg-*) and modifications to virt-inspector. This compiles and works. If you look in patch 5 you can see proposed output as virt-inspector XML for a guest (although this guest has not been booted, so a real guest would hopefully have a hwaddr="MAC" attribute too). Rich.
Richard W.M. Jones
2018-Oct-01 19:16 UTC
[Libguestfs] [PATCH v2 API PROPOSAL 1/5] daemon: Allow OCaml code to raise Not_supported exception.
It would be possible to implement this as: raise (Unix_error (ENOTSUP, fn, arg)) except that Unix.ENOTSUP is not defined (OCaml 4.07). Instead of working around this let's have a special exception, and anyway this works like the C code. The only tricky part of this patch is that I wanted to use the ‘Daemon’ module name for daemonic things, such as this exception. So I had to rename the existing Daemon module (which contains initialization code) to ‘Daemon_init’. --- daemon/Makefile.am | 4 +++- daemon/daemon-c.c | 2 ++ daemon/daemon.ml | 21 ++++----------------- daemon/daemon.mli | 3 ++- daemon/daemon_init.ml | 38 ++++++++++++++++++++++++++++++++++++++ daemon/daemon_init.mli | 19 +++++++++++++++++++ 6 files changed, 68 insertions(+), 19 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 5d1c222db..d3adcb53e 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -270,6 +270,7 @@ SOURCES_MLI = \ chroot.mli \ daemon.mli \ daemon_config.mli \ + daemon_init.mli \ devsparts.mli \ file.mli \ filearch.mli \ @@ -301,6 +302,7 @@ SOURCES_MLI = \ SOURCES_ML = \ daemon_config.ml \ + daemon.ml \ utils.ml \ structs.ml \ optgroups.ml \ @@ -333,7 +335,7 @@ SOURCES_ML = \ inspect_fs.ml \ inspect.ml \ callbacks.ml \ - daemon.ml + daemon_init.ml BOBJECTS = $(SOURCES_ML:.ml=.cmo) XOBJECTS = $(BOBJECTS:.cmo=.cmx) diff --git a/daemon/daemon-c.c b/daemon/daemon-c.c index 533bf7ce7..b347ade81 100644 --- a/daemon/daemon-c.c +++ b/daemon/daemon-c.c @@ -59,6 +59,8 @@ guestfs_int_daemon_exn_to_reply_with_error (const char *func, value exn) String_val (Field (exn, 2)), String_val (Field (exn, 3))); } + else if (STREQ (exn_name, "Not_supported")) + reply_with_error_errno (ENOTSUP, "%s", String_val (Field (exn, 1))); else if (STREQ (exn_name, "Failure")) reply_with_error ("%s", String_val (Field (exn, 1))); else if (STREQ (exn_name, "Sys_error")) diff --git a/daemon/daemon.ml b/daemon/daemon.ml index f24d64a13..132bc6a21 100644 --- a/daemon/daemon.ml +++ b/daemon/daemon.ml @@ -18,21 +18,8 @@ open Printf -(* When guestfsd starts up, early on (after parsing the command line - * but not much else), it calls 'caml_startup' which runs all - * initialization code in the OCaml modules, including this one. - * - * Therefore this is where we can place OCaml initialization code - * for the daemon. - *) +exception Not_supported of string + let () - (* Connect the guestfsd [-v] (verbose) flag into 'verbose ()' - * used in OCaml code to print debugging messages. - *) - if Utils.get_verbose_flag () then ( - Std_utils.set_verbose (); - eprintf "OCaml daemon loaded\n%!" - ); - - (* Register the callbacks which are used to call OCaml code from C. *) - Callbacks.init_callbacks () + (* Register exceptions. *) + Callback.register_exception "Not_supported" (Not_supported "") diff --git a/daemon/daemon.mli b/daemon/daemon.mli index c893b4a1e..856364a78 100644 --- a/daemon/daemon.mli +++ b/daemon/daemon.mli @@ -16,4 +16,5 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(* Nothing is exported. *) +exception Not_supported of string +(** Like the [NOT_SUPPORTED] macro in [daemon.h]. *) diff --git a/daemon/daemon_init.ml b/daemon/daemon_init.ml new file mode 100644 index 000000000..f24d64a13 --- /dev/null +++ b/daemon/daemon_init.ml @@ -0,0 +1,38 @@ +(* guestfs-inspection + * Copyright (C) 2009-2018 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 + +(* When guestfsd starts up, early on (after parsing the command line + * but not much else), it calls 'caml_startup' which runs all + * initialization code in the OCaml modules, including this one. + * + * Therefore this is where we can place OCaml initialization code + * for the daemon. + *) +let () + (* Connect the guestfsd [-v] (verbose) flag into 'verbose ()' + * used in OCaml code to print debugging messages. + *) + if Utils.get_verbose_flag () then ( + Std_utils.set_verbose (); + eprintf "OCaml daemon loaded\n%!" + ); + + (* Register the callbacks which are used to call OCaml code from C. *) + Callbacks.init_callbacks () diff --git a/daemon/daemon_init.mli b/daemon/daemon_init.mli new file mode 100644 index 000000000..c893b4a1e --- /dev/null +++ b/daemon/daemon_init.mli @@ -0,0 +1,19 @@ +(* guestfsd + * Copyright (C) 2009-2018 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. + *) + +(* Nothing is exported. *) -- 2.19.0.rc0
Richard W.M. Jones
2018-Oct-01 19:16 UTC
[Libguestfs] [PATCH v2 API PROPOSAL 2/5] common/mlutils: Add wrapper around guestfs_int_is_true.
Plus two small helper functions. --- common/mlutils/c_utils-c.c | 13 +++++++++++++ common/mlutils/c_utils.ml | 4 ++++ common/mlutils/c_utils.mli | 11 +++++++++++ 3 files changed, 28 insertions(+) diff --git a/common/mlutils/c_utils-c.c b/common/mlutils/c_utils-c.c index 83cf76398..a25bf3d16 100644 --- a/common/mlutils/c_utils-c.c +++ b/common/mlutils/c_utils-c.c @@ -60,6 +60,19 @@ guestfs_int_mlutils_drive_index (value strv) CAMLreturn (Val_int (r)); } +value +guestfs_int_mlutils_is_true (value strv) +{ + CAMLparam1 (strv); + ssize_t r; + + r = guestfs_int_is_true (String_val (strv)); + if (r == -1) + caml_invalid_argument ("is_true"); + + CAMLreturn (Val_bool (r)); +} + value guestfs_int_mlutils_shell_unquote (value strv) { diff --git a/common/mlutils/c_utils.ml b/common/mlutils/c_utils.ml index 0f9d1943d..33a1db313 100644 --- a/common/mlutils/c_utils.ml +++ b/common/mlutils/c_utils.ml @@ -23,6 +23,10 @@ open Printf external drive_name : int -> string = "guestfs_int_mlutils_drive_name" external drive_index : string -> int = "guestfs_int_mlutils_drive_index" +external is_true : string -> bool = "guestfs_int_mlutils_is_true" +let is_true_noraise s = try is_true s with Invalid_argument _ -> false +let is_false_noraise s = try not (is_true s) with Invalid_argument _ -> false + external shell_unquote : string -> string = "guestfs_int_mlutils_shell_unquote" external is_reg : int64 -> bool = "guestfs_int_mlutils_is_reg" "noalloc" diff --git a/common/mlutils/c_utils.mli b/common/mlutils/c_utils.mli index d2ad6fc1d..bb2f43017 100644 --- a/common/mlutils/c_utils.mli +++ b/common/mlutils/c_utils.mli @@ -21,6 +21,17 @@ val drive_name : int -> string val drive_index : string -> int +val is_true : string -> bool +(** Converts strings like ["true"], ["yes"] etc into [true], and + strings like ["false"], ["no"] etc into [false]. Other strings + will raise [Invalid_argument "is_true"]. *) + +val is_true_noraise : string -> bool +val is_false_noraise : string -> bool +(** Wrappers around {!is_true} which do not raise an error. They + simply return a boolean indicating if the string is a true-like + or false-like string. *) + val shell_unquote : string -> string (** If the string looks like a shell quoted string, then attempt to unquote it. -- 2.19.0.rc0
Richard W.M. Jones
2018-Oct-01 19:16 UTC
[Libguestfs] [PATCH v2 API PROPOSAL 3/5] inspection: Add network interfaces to inspection data.
This adds network interfaces to inspection data. The data is returned as a flat list of structs containing a general overview of the interfaces, and a separate call returning a fairly arbitrary list of key/value parameters for each interface. The list of parameters is roughly modelled on the ifcfg-* files, although with some mapping of the data to make it easier to consume. Note this doesn't attempt a detailed model of all possible guest network configurations, which in particular cases could be extremely complex. This commits just throws ENOTSUP error for all guests. Following commits add this information for Red Hat-derived, Debian-derived and Windows guests. --- daemon/inspect.ml | 18 +++++ daemon/inspect_types.ml | 19 +++++- daemon/inspect_types.mli | 3 + generator/actions_inspection.ml | 86 ++++++++++++++++++++++++ generator/proc_nr.ml | 2 + generator/structs.ml | 10 +++ gobject/Makefile.inc | 2 + java/Makefile.inc | 1 + java/com/redhat/et/libguestfs/.gitignore | 1 + lib/MAX_PROC_NR | 2 +- 10 files changed, 142 insertions(+), 2 deletions(-) diff --git a/daemon/inspect.ml b/daemon/inspect.ml index ce62c17f2..0cb1aa5d8 100644 --- a/daemon/inspect.ml +++ b/daemon/inspect.ml @@ -376,6 +376,24 @@ and inspect_get_drive_mappings root let root = search_for_root root in root.inspection_data.drive_mappings +and inspect_get_interfaces root + let root = search_for_root root in + match root.inspection_data.interfaces with + | None -> + raise (Daemon.Not_supported "guest type not supported") + | Some interfaces -> List.map fst interfaces + +and inspect_get_interface_parameters root i + let root = search_for_root root in + match root.inspection_data.interfaces with + | None -> + raise (Daemon.Not_supported "guest type not supported") + | Some interfaces -> + let interfaces = List.map snd interfaces in + try List.nth interfaces i + with Failure _ | Invalid_argument _ -> + failwith "index out of range" + and search_for_root root let fses = !Inspect_types.inspect_fses in if fses = [] then diff --git a/daemon/inspect_types.ml b/daemon/inspect_types.ml index 70b34c51c..37592275b 100644 --- a/daemon/inspect_types.ml +++ b/daemon/inspect_types.ml @@ -54,6 +54,7 @@ and inspection_data = { mutable windows_system_hive : string option; mutable windows_current_control_set : string option; mutable drive_mappings : drive_mapping list; + mutable interfaces : interfaces option; } and os_type | OS_TYPE_DOS @@ -125,6 +126,8 @@ and package_management and version = int * int and fstab_entry = Mountable.t * string (* mountable, mountpoint *) and drive_mapping = string * string (* drive name, device *) +and interfaces = interface list +and interface = Structs.interface * (string * string) list let rec string_of_fs { fs_location = location; role } sprintf "fs: %s role: %s\n" @@ -183,6 +186,17 @@ and string_of_inspection_data data List.map (fun (a, b) -> sprintf "(%s, %s)" a b) data.drive_mappings in bpf " drive_mappings: [%s]\n" (String.concat ", " v) ); + Option.may ( + fun interfaces -> + bpf " interfaces:\n"; + List.iteri ( + fun i ({ Structs.if_type; if_hwaddr; if_name }, params) -> + bpf " [%d]: type: %s mac: %s name: %s" + i if_type if_hwaddr if_name; + List.iter (fun (k, v) -> bpf " %s: %s" k v) params; + bpf "\n" + ) interfaces + ) data.interfaces; Buffer.contents b and string_of_os_type = function @@ -272,6 +286,7 @@ let null_inspection_data = { windows_system_hive = None; windows_current_control_set = None; drive_mappings = []; + interfaces = None; } let null_inspection_data () = { null_inspection_data with os_type = None } @@ -299,7 +314,9 @@ let merge_inspection_data child parent 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. *) - parent.drive_mappings <- child.drive_mappings @ parent.drive_mappings + parent.drive_mappings <- child.drive_mappings @ parent.drive_mappings; + + parent.interfaces <- merge child.interfaces parent.interfaces let merge child_fs parent_fs let inspection_data_of_fs = function diff --git a/daemon/inspect_types.mli b/daemon/inspect_types.mli index 7493aa3a6..e82b2a554 100644 --- a/daemon/inspect_types.mli +++ b/daemon/inspect_types.mli @@ -57,6 +57,7 @@ and inspection_data = { mutable windows_system_hive : string option; mutable windows_current_control_set : string option; mutable drive_mappings : drive_mapping list; + mutable interfaces : interfaces option; } (** During inspection, this data is collected incrementally for each filesystem. At the end of inspection, inspection data is merged @@ -132,6 +133,8 @@ and package_management and version = int * int and fstab_entry = Mountable.t * string (* mountable, mountpoint *) and drive_mapping = string * string (* drive name, device *) +and interfaces = interface list +and interface = Structs.interface * (string * string) list val merge_inspection_data : inspection_data -> inspection_data -> unit (** [merge_inspection_data child parent] merges two sets of inspection diff --git a/generator/actions_inspection.ml b/generator/actions_inspection.ml index d913f53e3..a4ca437ff 100644 --- a/generator/actions_inspection.ml +++ b/generator/actions_inspection.ml @@ -603,6 +603,92 @@ Please read L<guestfs(3)/INSPECTION> for more details. See also C<guestfs_inspect_get_mountpoints>, C<guestfs_inspect_get_filesystems>." }; + { defaults with + name = "inspect_get_interfaces"; added = (1, 39, 11); + style = RStructList ("interfaces", "interface"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_interfaces"; + shortdesc = "get the list of network interfaces"; + longdesc = "\ +Return the list of network interfaces seen by this guest. This returns +a list of structs, one struct per network interface. + +The returned C<interface> struct contains only a few fields common +to all interfaces. For detailed parameters for each interface +you also need to call C<guestfs_inspect_get_interface_parameters> +with the index of the interface you are interested in. + +The returned C<interface> struct contains these fields: + +=over 4 + +=item C<if_type> + +Interface type, including C<ethernet> (wired ethernet), C<wireless> +(WiFi). Other interface types are possible. + +=item C<if_hwaddr> + +The hardware address (MAC address) of the interface if one is known, +otherwise the empty string. + +=item C<if_name> + +The name of the interface as it is known to the guest (eg. C<eth0>). +Note that network interface names might not be unique. + +=back + +Depending on the guest type this call may return more network interfaces +than the guest has physical devices. In particular it may return all +network interfaces ever seen by the guest across multiple boots. To +work out which interfaces correspond to currently available devices +you will have to use MAC address information derived from another +source (eg. hypervisor metadata) and match that to C<if_hwaddr> from +the struct. + +If inspection of network interfaces is not implemented for this +particular guest type then this will fail with an error and set +C<errno> to C<ENOTSUP>, so callers should be careful to check for that +case." }; + + { defaults with + name = "inspect_get_interface_parameters"; added = (1, 39, 11); + style = RHashtable (RPlainString, RPlainString, "parameters"), [String (Mountable, "root"); Int "index"], []; + impl = OCaml "Inspect.inspect_get_interface_parameters"; + shortdesc = "get detailed configuration parameters for a network interface"; + longdesc = "\ +Return the detailed configuration parameters of the C<index> network +interface. Indexes refer to the list returned by +C<guestfs_inspect_get_interfaces> and count from zero. + +The returned parameters can contain a variety of keys. Some notable +ones are described below: + +=over 4 + +=item C<bootproto> + +C<dhcp> if the interface was last configured to find an IP address +through DHCP, else C<none> if it was configured to use a static +IP address. Other values may be possible. + +=item C<essid> + +For wireless interfaces, the ESSID. + +=item C<ipaddr>, C<network>, C<netmask>, C<gateway>, C<broadcast> + +IPv4 address if configured statically. For some (but not all) +guest types if the interface uses DHCP then this may record the +previous IPv4 address which was acquired through DHCP. + +=item C<onboot> + +Key is present and value is C<1> if the network interface is configured +to come up at boot. + +=back" }; + ] let non_daemon_functions = [ diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml index 2adf8a32f..4781f0a24 100644 --- a/generator/proc_nr.ml +++ b/generator/proc_nr.ml @@ -514,6 +514,8 @@ let proc_nr = [ 504, "part_get_gpt_attributes"; 505, "f2fs_expand"; 506, "lvm_scan"; +507, "inspect_get_interfaces"; +508, "inspect_get_interface_parameters"; ] (* End of list. If adding a new entry, add it at the end of the list diff --git a/generator/structs.ml b/generator/structs.ml index 14ee2813f..09e90bd0b 100644 --- a/generator/structs.ml +++ b/generator/structs.ml @@ -478,6 +478,16 @@ let structs = [ ]; s_camel_name = "YaraDetection" }; + (* Network interfaces. *) + { defaults with + s_name = "interface"; + s_cols = [ + "if_type", FString; + "if_hwaddr", FString; + "if_name", FString; + ]; + s_camel_name = "Interface" }; + ] (* end of structs *) let lookup_struct name diff --git a/gobject/Makefile.inc b/gobject/Makefile.inc index 5aa2dcafe..42839a85f 100644 --- a/gobject/Makefile.inc +++ b/gobject/Makefile.inc @@ -35,6 +35,7 @@ guestfs_gobject_headers= \ include/guestfs-gobject/struct-hivex_value.h \ include/guestfs-gobject/struct-inotify_event.h \ include/guestfs-gobject/struct-int_bool.h \ + include/guestfs-gobject/struct-interface.h \ include/guestfs-gobject/struct-isoinfo.h \ include/guestfs-gobject/struct-lvm_lv.h \ include/guestfs-gobject/struct-lvm_pv.h \ @@ -128,6 +129,7 @@ guestfs_gobject_sources= \ src/struct-hivex_value.c \ src/struct-inotify_event.c \ src/struct-int_bool.c \ + src/struct-interface.c \ src/struct-isoinfo.c \ src/struct-lvm_lv.c \ src/struct-lvm_pv.c \ diff --git a/java/Makefile.inc b/java/Makefile.inc index 93ec5e129..54b9da971 100644 --- a/java/Makefile.inc +++ b/java/Makefile.inc @@ -33,6 +33,7 @@ java_built_sources = \ com/redhat/et/libguestfs/INotifyEvent.java \ com/redhat/et/libguestfs/ISOInfo.java \ com/redhat/et/libguestfs/IntBool.java \ + com/redhat/et/libguestfs/Interface.java \ com/redhat/et/libguestfs/LV.java \ com/redhat/et/libguestfs/MDStat.java \ com/redhat/et/libguestfs/PV.java \ diff --git a/java/com/redhat/et/libguestfs/.gitignore b/java/com/redhat/et/libguestfs/.gitignore index bc03cb965..b5843c8f1 100644 --- a/java/com/redhat/et/libguestfs/.gitignore +++ b/java/com/redhat/et/libguestfs/.gitignore @@ -10,6 +10,7 @@ HivexValue.java INotifyEvent.java ISOInfo.java IntBool.java +Interface.java LV.java MDStat.java PV.java diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR index 80e3e6eab..1eccde110 100644 --- a/lib/MAX_PROC_NR +++ b/lib/MAX_PROC_NR @@ -1 +1 @@ -506 +508 -- 2.19.0.rc0
Richard W.M. Jones
2018-Oct-01 19:16 UTC
[Libguestfs] [PATCH v2 API PROPOSAL 4/5] inspection: Derive interface information from old Red Hat network scripts.
For Red Hat-derived guests using the old network scripts, derive the list of network interfaces from the files ‘/etc/sysconfig/network-scripts/ifcfg-*’. This also works for Red Hat-derived guests using NetworkManager, although perhaps in future we should write a specific parser for them. --- daemon/inspect_fs_unix.ml | 85 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/daemon/inspect_fs_unix.ml b/daemon/inspect_fs_unix.ml index 4dd89fa8f..3f1f026d5 100644 --- a/daemon/inspect_fs_unix.ml +++ b/daemon/inspect_fs_unix.ml @@ -474,7 +474,8 @@ let rec check_linux_root mountable data data.arch <- check_architecture (); data.fstab <- Inspect_fs_unix_fstab.check_fstab ~mdadm_conf:true mountable os_type; - data.hostname <- check_hostname_linux () + data.hostname <- check_hostname_linux (); + data.interfaces <- check_network_interfaces_linux () and check_architecture () let rec loop = function @@ -546,6 +547,88 @@ and check_hostname_from_sysconfig_network aug *) aug_get_noerrors aug "/files/etc/sysconfig/network/HOSTNAME" +and check_network_interfaces_linux () + (* If there are any /etc/sysconfig/network-scripts/ifcfg-* files + * then we assume old Red Hat network scripts style. Note if + * we ever support NetworkManager explicitly, or systemd-networkd, + * then those should be checked _before_ this line. + *) + match check_old_redhat_network_scripts () with + | Some interfaces -> Some interfaces + | None -> None + +and check_old_redhat_network_scripts () + with_return (fun {return} -> + let conf_dir = "/etc/sysconfig/network-scripts" in + if not (Is.is_dir conf_dir) then return None; + + (* Get the list of all files and filter for "ifcfg-*". *) + let chroot = Chroot.create ~name:"check_old_redhat_network_scripts" () in + let files = Chroot.f chroot Sys.readdir conf_dir in + let files = Array.to_list files in + let files = List.sort compare files in + let files = List.filter (fun n -> String.is_prefix n "ifcfg-") files in + let files = List.filter (fun n -> not (is_editor_backup n)) files in + let files + List.filter_map ( + fun filename -> + match Chroot.f chroot read_small_file (conf_dir // filename) with + | None -> None + | Some lines -> Some (filename, lines) + ) files in + if files = [] then return None; + + (* At this point we are going to return one interface per file. *) + let interfaces = List.map check_old_redhat_ifcfg_file files in + Some interfaces + ) + +(* Load the file and map the key=value pairs found into params. + * For ifcfg-* files this mapping is largely an identity map + * except: + * - keys "TYPE", "HWADDR", "NAME" and "DEVICE" get special treatment + * - key is lowercased + * - comments are ignored and values must be unquoted + * - values like "yes", "no", etc are translated to "1" and "0" + * + * The if_name comes from: "DEVICE", "NAME" or the ifcfg- filename + * with the first taking priority. + *) +and check_old_redhat_ifcfg_file (filename, lines) + let default_name + assert (String.is_prefix filename "ifcfg-"); + String.sub filename 6 (String.length filename - 6) in + + let lines = parse_key_value_strings ~unquote:shell_unquote lines in + let lines = List.map (fun (k, v) -> String.lowercase_ascii k, v) lines in + + let if_type = ref "" in + let if_hwaddr = ref "" in + let device = ref "" in + let name = ref "" in + let params = ref [] in + List.iter ( + function + | "type", v -> if_type := String.lowercase_ascii v + | "hwaddr", v -> if_hwaddr := String.lowercase_ascii v + | "device", v -> device := v + | "name", v -> name := v + | k, v when is_true_noraise v -> List.push_front (k, "1") params + | k, v when is_false_noraise v -> List.push_front (k, "0") params + | kv -> List.push_front kv params + ) lines; + + let if_name + if !device <> "" then !device + else if !name <> "" then !name + else default_name in + + { Structs.if_type = !if_type; if_hwaddr = !if_hwaddr; if_name }, !params + +and is_editor_backup filename + String.is_suffix filename "~" || + String.is_suffix filename "*.bak" + (* The currently mounted device looks like a Linux /usr. *) let check_linux_usr data data.os_type <- Some OS_TYPE_LINUX; -- 2.19.0.rc0
Richard W.M. Jones
2018-Oct-01 19:16 UTC
[Libguestfs] [PATCH v2 API PROPOSAL 5/5] inspector: Add support for interfaces.
Creates XML output which looks like this: <interfaces> <interface name="ens2" type="ethernet"> <parameters> <parameter name="bootproto">dhcp</parameter> <parameter name="browser_only">0</parameter> <parameter name="defroute">1</parameter> <parameter name="ipv4_failure_fatal">0</parameter> <parameter name="ipv6_autoconf">1</parameter> <parameter name="ipv6_defroute">1</parameter> <parameter name="ipv6_failure_fatal">0</parameter> <parameter name="ipv6init">1</parameter> <parameter name="netboot">1</parameter> <parameter name="onboot">1</parameter> <parameter name="proxy_method">none</parameter> <parameter name="uuid">b90eea1d-36df-4196-862c-8f10432bcbc7</parameter> </parameters> </interface> <interface name="lo"> <parameters> <parameter name="broadcast">127.255.255.255</parameter> <parameter name="ipaddr">127.0.0.1</parameter> <parameter name="netmask">255.0.0.0</parameter> <parameter name="network">127.0.0.0</parameter> <parameter name="onboot">1</parameter> </parameters> </interface> </interfaces> --- inspector/inspector.c | 81 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/inspector/inspector.c b/inspector/inspector.c index 9be8f5110..066ed61bc 100644 --- a/inspector/inspector.c +++ b/inspector/inspector.c @@ -66,6 +66,7 @@ static void output_root (xmlTextWriterPtr xo, char *root); static void output_mountpoints (xmlTextWriterPtr xo, char *root); static void output_filesystems (xmlTextWriterPtr xo, char *root); static void output_drive_mappings (xmlTextWriterPtr xo, char *root); +static void output_interfaces (xmlTextWriterPtr xo, char *root); static void output_applications (xmlTextWriterPtr xo, char *root); static void do_xpath (const char *query); @@ -463,6 +464,8 @@ output_root (xmlTextWriterPtr xo, char *root) output_drive_mappings (xo, root); + output_interfaces (xo, root); + /* We need to mount everything up in order to read out the list of * applications and the icon, ie. everything below this point. */ @@ -668,6 +671,84 @@ output_drive_mappings (xmlTextWriterPtr xo, char *root) XMLERROR (-1, xmlTextWriterEndElement (xo)); } +static int +compare_if_name (const void *p1, const void *p2) +{ + const struct guestfs_interface *if1 = p1; + const struct guestfs_interface *if2 = p2; + + return strcmp (if1->if_name, if2->if_name); +} + +static void +output_interfaces (xmlTextWriterPtr xo, char *root) +{ + CLEANUP_FREE_INTERFACE_LIST struct guestfs_interface_list *interfaces = NULL; + size_t i; + + guestfs_push_error_handler (g, NULL, NULL); + interfaces = guestfs_inspect_get_interfaces (g, root); + guestfs_pop_error_handler (g); + if (interfaces == NULL || interfaces->len == 0) + return; + + /* Sort by if_name. */ + qsort (interfaces->val, interfaces->len, sizeof (struct guestfs_interface), + compare_if_name); + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "interfaces")); + + for (i = 0; i < interfaces->len; ++i) { + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "interface")); + if (STRNEQ (interfaces->val[i].if_name, "")) + XMLERROR (-1, + xmlTextWriterWriteAttribute (xo, BAD_CAST "name", + BAD_CAST + interfaces->val[i].if_name)); + if (STRNEQ (interfaces->val[i].if_type, "")) + XMLERROR (-1, + xmlTextWriterWriteAttribute (xo, BAD_CAST "type", + BAD_CAST + interfaces->val[i].if_type)); + if (STRNEQ (interfaces->val[i].if_hwaddr, "")) + XMLERROR (-1, + xmlTextWriterWriteAttribute (xo, BAD_CAST "hwaddr", + BAD_CAST + interfaces->val[i].if_hwaddr)); + + /* Parameters. */ + CLEANUP_FREE_STRING_LIST char **params + guestfs_inspect_get_interface_parameters (g, root, (int) i); + size_t j; + + if (params == NULL || params[0] == NULL) + goto noparams; + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "parameters")); + + /* Sort by key. */ + qsort (params, + guestfs_int_count_strings (params) / 2, 2 * sizeof (char *), + compare_keys_nocase); + + for (j = 0; params[j] != NULL; j += 2) { + XMLERROR (-1, + xmlTextWriterStartElement (xo, BAD_CAST "parameter")); + XMLERROR (-1, + xmlTextWriterWriteAttribute (xo, BAD_CAST "name", + BAD_CAST params[j])); + XMLERROR (-1, xmlTextWriterWriteString (xo, BAD_CAST params[j+1])); + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + + XMLERROR (-1, xmlTextWriterEndElement (xo)); + noparams: + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + + XMLERROR (-1, xmlTextWriterEndElement (xo)); +} + static void output_applications (xmlTextWriterPtr xo, char *root) { -- 2.19.0.rc0
Richard W.M. Jones
2018-Oct-02 14:15 UTC
Re: [Libguestfs] [PATCH v2 API PROPOSAL 0/5] inspection Add network interfaces to inspection data.
I wrote some code which can get this kind of information from the Windows Registry: <interfaces> <interface name="Ethernet" type="unknown"> <parameters> <parameter name="bootproto">dhcp</parameter> <parameter name="gateway">10.0.2.2</parameter> <parameter name="ipaddr">10.0.2.15</parameter> <parameter name="win:AddressType">0</parameter> <parameter name="win:DhcpConnForceBroadcastFlag">0</parameter> <parameter name="win:DhcpGatewayHardware"> </parameter> <parameter name="win:DhcpGatewayHardwareCount">1</parameter> <parameter name="win:DhcpNameServer">10.0.2.3</parameter> <parameter name="win:DhcpServer">10.0.2.2</parameter> <parameter name="win:DhcpSubnetMask">255.255.255.0</parameter> <parameter name="win:DhcpSubnetMaskOpt">255.255.255.0</parameter> <parameter name="win:Domain"></parameter> <parameter name="win:EnableDeadGWDetect">1</parameter> <parameter name="win:IsServerNapAware">0</parameter> <parameter name="win:Lease">86400</parameter> <parameter name="win:LeaseObtainedTime">1536802270</parameter> <parameter name="win:LeaseTerminatesTime">1536888670</parameter> <parameter name="win:NameServer"></parameter> <parameter name="win:RegisterAdapterName">0</parameter> <parameter name="win:RegistrationEnabled">1</parameter> <parameter name="win:T1">1536845470</parameter> <parameter name="win:T2">1536877870</parameter> <parameter name="win:UseZeroBroadcast">0</parameter> </parameters> </interface> <interface name="Local Area Connection* 9" type="unknown"> <parameters> <parameter name="bootproto">dhcp</parameter> <parameter name="win:Domain"></parameter> <parameter name="win:EnableDeadGWDetect">1</parameter> <parameter name="win:NameServer"></parameter> <parameter name="win:RegisterAdapterName">0</parameter> <parameter name="win:RegistrationEnabled">1</parameter> <parameter name="win:UseZeroBroadcast">0</parameter> </parameters> </interface> </interfaces> Unfortunately - no MAC address! That screws up the plan greatly, since without the MAC address there is no way to associate network adapters with hardware. Well, that's not quite true. There is some PCI address information available. For example for the above guest we have: [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{307052F7-EE0A-4FD2-8CE6-212EFDD711B0}\Connection] "DefaultNameResourceId"=dword:0000076c "DefaultNameTargetsPort"=dword:00000000 "Name"=str(1):"Ethernet" "PnPInstanceId"=str(1):"PCI\VEN_10EC&DEV_8139&SUBSYS_11001AF4&REV_20\3&13C0B0C5&0&18" It's not clear if we could go from this to a hypervisor network interface however. Also the MAC address of the guest is visible in some very obscure places in the registry in binary blobs. I don't think I've yet got a full picture of how network adapters are stored in Windows. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
Brett Thurber
2018-Oct-03 03:09 UTC
Re: [Libguestfs] [PATCH v2 API PROPOSAL 0/5] inspection Add network interfaces to inspection data.
On Tue, Oct 2, 2018 at 9:15 AM Richard W.M. Jones <rjones@redhat.com> wrote:> I wrote some code which can get this kind of information from the > Windows Registry: > > <interfaces> > <interface name="Ethernet" type="unknown"> > <parameters> > <parameter name="bootproto">dhcp</parameter> > <parameter name="gateway">10.0.2.2</parameter> > <parameter name="ipaddr">10.0.2.15</parameter> > <parameter name="win:AddressType">0</parameter> > <parameter name="win:DhcpConnForceBroadcastFlag">0</parameter> > <parameter name="win:DhcpGatewayHardware"> > </parameter> > <parameter name="win:DhcpGatewayHardwareCount">1</parameter> > <parameter name="win:DhcpNameServer">10.0.2.3</parameter> > <parameter name="win:DhcpServer">10.0.2.2</parameter> > <parameter name="win:DhcpSubnetMask">255.255.255.0</parameter> > <parameter name="win:DhcpSubnetMaskOpt">255.255.255.0</parameter> > <parameter name="win:Domain"></parameter> > <parameter name="win:EnableDeadGWDetect">1</parameter> > <parameter name="win:IsServerNapAware">0</parameter> > <parameter name="win:Lease">86400</parameter> > <parameter name="win:LeaseObtainedTime">1536802270</parameter> > <parameter name="win:LeaseTerminatesTime">1536888670</parameter> > <parameter name="win:NameServer"></parameter> > <parameter name="win:RegisterAdapterName">0</parameter> > <parameter name="win:RegistrationEnabled">1</parameter> > <parameter name="win:T1">1536845470</parameter> > <parameter name="win:T2">1536877870</parameter> > <parameter name="win:UseZeroBroadcast">0</parameter> > </parameters> > </interface> > <interface name="Local Area Connection* 9" type="unknown"> > <parameters> > <parameter name="bootproto">dhcp</parameter> > <parameter name="win:Domain"></parameter> > <parameter name="win:EnableDeadGWDetect">1</parameter> > <parameter name="win:NameServer"></parameter> > <parameter name="win:RegisterAdapterName">0</parameter> > <parameter name="win:RegistrationEnabled">1</parameter> > <parameter name="win:UseZeroBroadcast">0</parameter> > </parameters> > </interface> > </interfaces> > > Unfortunately - no MAC address! > > That screws up the plan greatly, since without the MAC address there > is no way to associate network adapters with hardware. > > Well, that's not quite true. There is some PCI address information > available. For example for the above guest we have: > > > [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{307052F7-EE0A-4FD2-8CE6-212EFDD711B0}\Connection] > "DefaultNameResourceId"=dword:0000076c > "DefaultNameTargetsPort"=dword:00000000 > "Name"=str(1):"Ethernet" > > "PnPInstanceId"=str(1):"PCI\VEN_10EC&DEV_8139&SUBSYS_11001AF4&REV_20\3&13C0B0C5&0&18" > > It's not clear if we could go from this to a hypervisor network > interface however. > > Also the MAC address of the guest is visible in some very obscure > places in the registry in binary blobs. > > I don't think I've yet got a full picture of how network adapters are > stored in Windows. > > Rich. >It's a bit of a mess based on some of my investigation today. From what I have read s it even may differ from 2008/2012 to 2016. We are maintaining the MAC address today as part of migration, for the new virtio NIC. Instead of trying to find the MAC address is there a way to move the IP config over based on vmxnet or IE1000 enumeration? Thinking of other options outside of MAC linkage.> > -- > Richard Jones, Virtualization Group, Red Hat > http://people.redhat.com/~rjones > Read my programming and virtualization blog: http://rwmj.wordpress.com > libguestfs lets you edit virtual machines. Supports shell scripting, > bindings from many languages. http://libguestfs.org >-- Brett Thurber - RHCA, RHCVA Engineering Manager and Sr. Principal Software Engineer, Solutions Engineering Products & Technologies Group, Red Hat Mobile: +1 (512) 547-9282