type command =
| Exit_thread
| Connect of string option * domain list callback
+ | Disk_usage of source * string * int64 callback
| Download_dir_find0 of source * string * string * unit callback
| Download_dir_tarball of source * string * download_dir_tarball_format * string * unit callback
| Download_file of source * string * string * unit callback
insp_product_name : string;
insp_type : string;
insp_windows_systemroot : string option;
+ insp_winreg_DEFAULT : string option;
+ insp_winreg_SAM : string option;
+ insp_winreg_SECURITY : string option;
+ insp_winreg_SOFTWARE : string option;
+ insp_winreg_SYSTEM : string option;
}
and source = OS of inspection_os | Volume of string
| Exit_thread -> "Exit_thread"
| Connect (Some name, _) -> sprintf "Connect %s" name
| Connect (None, _) -> "Connect NULL"
+ | Disk_usage (src, remotedir, _) ->
+ sprintf "Disk_usage (%s, %s)" (string_of_source src) remotedir
| Download_dir_find0 (src, remotedir, localfile, _) ->
sprintf "Download_dir_find0 (%s, %s, %s)"
(string_of_source src) remotedir localfile
let failure_hook = ref (fun _ -> ())
let busy_hook = ref (fun _ -> ())
let idle_hook = ref (fun _ -> ())
+let status_hook = ref (fun _ -> ())
let progress_hook = ref (fun _ -> ())
let set_failure_hook cb = failure_hook := cb
let set_busy_hook cb = busy_hook := cb
let set_idle_hook cb = idle_hook := cb
+let set_status_hook cb = status_hook := cb
let set_progress_hook cb = progress_hook := cb
(* Execute a function, while holding a mutex. If the function
)
let connect ?fail uri cb = send_to_slave ?fail (Connect (uri, cb))
+let disk_usage ?fail src remotedir cb =
+ send_to_slave ?fail (Disk_usage (src, remotedir, cb))
let download_dir_find0 ?fail src remotedir localfile cb =
send_to_slave ?fail (Download_dir_find0 (src, remotedir, localfile, cb))
let download_dir_tarball ?fail src remotedir format localfile cb =
f ()
) ()
+(* Update the status bar. *)
+let status fs =
+ let f str = GtkThread.async !status_hook str in
+ ksprintf f fs
+
let rec loop () =
debug "top of slave loop";
close_all ()
| Connect (name, cb) ->
+ let printable_name =
+ match name with None -> "default hypervisor" | Some uri -> uri in
+ status "Connecting to %s ..." printable_name;
+
close_all ();
conn := Some (C.connect_readonly ?name ());
) doms in
let cmp { dom_name = n1 } { dom_name = n2 } = compare n1 n2 in
let doms = List.sort ~cmp doms in
+
+ status "Connected to %s" printable_name;
callback_if_not_discarded cb doms
+ | Disk_usage (src, remotedir, cb) ->
+ status "Calculating disk usage of %s ..." remotedir;
+
+ let g = get_g () in
+ let r =
+ with_mount_ro g src (
+ fun () ->
+ g#du remotedir
+ ) in
+
+ status "Finished calculating disk usage of %s" remotedir;
+ callback_if_not_discarded cb r
+
| Download_dir_find0 (src, remotedir, localfile, cb) ->
+ status "Downloading %s filenames to %s ..." remotedir localfile;
+
let g = get_g () in
with_mount_ro g src (
fun () ->
g#find0 remotedir localfile
);
+
+ status "Finished downloading %s" localfile;
callback_if_not_discarded cb ()
| Download_dir_tarball (src, remotedir, format, localfile, cb) ->
+ status "Downloading %s to %s ..." remotedir localfile;
+
let g = get_g () in
let f = match format with
| Tar -> g#tar_out
fun () ->
f remotedir localfile
);
+
+ status "Finished downloading %s" localfile;
callback_if_not_discarded cb ()
| Download_file (src, remotefile, localfile, cb) ->
+ status "Downloading %s to %s ..." remotefile localfile;
+
let g = get_g () in
with_mount_ro g src (
fun () ->
g#download remotefile localfile
);
+
+ status "Finished downloading %s" localfile;
callback_if_not_discarded cb ()
| Open_domain (name, cb) ->
+ status "Opening %s ..." name;
+
let conn = get_conn () in
let dom = D.lookup_by_name conn name in
let xml = D.get_xml_desc dom in
open_disk_images images cb
| Open_images (images, cb) ->
+ status "Opening disk images ...";
+
open_disk_images images cb
| Read_directory (src, dir, cb) ->
+ status "Reading directory %s ..." dir;
+
let g = get_g () in
let names, stats, links =
with_mount_ro g src (
fun ((name, stat), link) ->
{ dent_name = name; dent_stat = stat; dent_link = link }
) entries in
+
+ status "Finished reading directory %s" dir;
callback_if_not_discarded cb entries
(* Expect to be connected, and return the current libvirt connection. *)
g#launch ();
+ status "Listing filesystems ...";
+
(* Get list of filesystems. *)
let fses = g#list_filesystems () in
+ status "Looking for operating systems ...";
+
(* Perform inspection. This can fail, ignore errors. *)
let roots =
try Array.to_list (g#inspect_os ())
[] in
let oses = List.map (
- fun root -> {
- insp_root = root;
- insp_arch = g#inspect_get_arch root;
- insp_distro = g#inspect_get_distro root;
- insp_filesystems = g#inspect_get_filesystems root;
- insp_hostname = g#inspect_get_hostname root;
- insp_major_version = g#inspect_get_major_version root;
- insp_minor_version = g#inspect_get_minor_version root;
- insp_mountpoints = g#inspect_get_mountpoints root;
- insp_package_format = g#inspect_get_package_format root;
- insp_package_management = g#inspect_get_package_management root;
- insp_product_name = g#inspect_get_product_name root;
- insp_type = g#inspect_get_type root;
- insp_windows_systemroot =
- try Some (g#inspect_get_windows_systemroot root)
- with Guestfs.Error _ -> None
- }
+ fun root ->
+ let typ = g#inspect_get_type root in
+ let windows_systemroot =
+ if typ <> "windows" then None
+ else (
+ try Some (g#inspect_get_windows_systemroot root)
+ with Guestfs.Error _ -> None
+ ) in
+
+ (* Create most of the OS object that we're going to return. We
+ * have to pass this to with_mount_ro below which is why we need
+ * to partially create it here.
+ *)
+ let os = {
+ insp_root = root;
+ insp_arch = g#inspect_get_arch root;
+ insp_distro = g#inspect_get_distro root;
+ insp_filesystems = g#inspect_get_filesystems root;
+ insp_hostname = g#inspect_get_hostname root;
+ insp_major_version = g#inspect_get_major_version root;
+ insp_minor_version = g#inspect_get_minor_version root;
+ insp_mountpoints = g#inspect_get_mountpoints root;
+ insp_package_format = g#inspect_get_package_format root;
+ insp_package_management = g#inspect_get_package_management root;
+ insp_product_name = g#inspect_get_product_name root;
+ insp_type = typ;
+ insp_windows_systemroot = windows_systemroot;
+ insp_winreg_DEFAULT = None; (* incomplete, see below *)
+ insp_winreg_SAM = None;
+ insp_winreg_SECURITY = None;
+ insp_winreg_SOFTWARE = None;
+ insp_winreg_SYSTEM = None;
+ } in
+
+ (* We need to mount the root in order to look for Registry hives. *)
+ let winreg_DEFAULT, winreg_SAM, winreg_SECURITY, winreg_SOFTWARE,
+ winreg_SYSTEM =
+ match windows_systemroot with
+ | None -> None, None, None, None, None
+ | Some sysroot ->
+ with_mount_ro g (OS os) (
+ fun () ->
+ let check_for_hive filename =
+ let path =
+ sprintf "%s/system32/config/%s" sysroot filename in
+ try Some (g#case_sensitive_path path)
+ with Guestfs.Error _ -> None
+ in
+ check_for_hive "default",
+ check_for_hive "sam",
+ check_for_hive "security",
+ check_for_hive "software",
+ check_for_hive "system"
+ ) in
+
+ (* Fill in the remaining struct fields. *)
+ let os = { os with
+ insp_winreg_DEFAULT = winreg_DEFAULT;
+ insp_winreg_SAM = winreg_SAM;
+ insp_winreg_SECURITY = winreg_SECURITY;
+ insp_winreg_SOFTWARE = winreg_SOFTWARE;
+ insp_winreg_SYSTEM = winreg_SYSTEM
+ } in
+ os
) roots in
+
let data = {
insp_all_filesystems = fses;
insp_oses = oses;
} in
+
+ status "Finished opening disk";
callback_if_not_discarded cb data
(* guestfs_lstatlist has a "hidden" limit of the protocol message size.