2 * Copyright (C) 2011 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 (*module C = Libvirt.Connect*)
25 module Cond = Condition
26 (*module D = Libvirt.Domain*)
32 type 'a callback = 'a -> unit
33 let no_callback _ = ()
36 let failure_hook = ref (fun _ -> ())
37 let busy_hook = ref (fun _ -> ())
38 let idle_hook = ref (fun _ -> ())
39 let status_hook = ref (fun _ -> ())
40 let progress_hook = ref (fun _ -> ())
42 let set_failure_hook cb = failure_hook := cb
43 let set_busy_hook cb = busy_hook := cb
44 let set_idle_hook cb = idle_hook := cb
45 let set_status_hook cb = status_hook := cb
46 let set_progress_hook cb = progress_hook := cb
51 | Open_images of (string * string option) list * inspection_data callback
53 let rec string_of_message = function
54 | Exit_thread -> "Exit_thread"
55 | Open_images (images, _) ->
56 sprintf "Open_images %s" (string_of_images images)
58 and string_of_images images =
64 | fn, Some format -> sprintf "%s (%s)" fn format
68 (* Execute a function, while holding a mutex. If the function
69 * fails, ensure we release the mutex before rethrowing the
74 let r = try Left (f ()) with exn -> Right exn in
78 | Right exn -> raise exn
80 (* The queue of commands, and a lock and condition to protect it. *)
82 let q_discard = ref false
83 let q_lock = M.create ()
84 let q_cond = Cond.create ()
86 (* Send a command message to the slave thread. *)
87 let send_message ?fail msg =
88 debug "sending message %s to slave thread ..." (string_of_message msg);
95 let discard_command_queue () =
99 (* Discard the currently running command too. *)
103 (*----------------------------------------------------------------------*)
104 (* This is the slave thread. *)
106 (* Run the callback unless someone set the q_discard flag while we
107 were running the command. This allows discard_command_queue () to
108 discard both the command queue and the running command (although
109 the running command is not interrupted). *)
110 let callback_if_not_discarded (cb : 'a callback) (arg : 'a) =
111 let discard = with_lock q_lock (fun () -> !q_discard) in
113 GtkThread.async cb arg
115 (* Set this to true to exit the thread. *)
118 (* Handles. These are not protected by locks because only the slave
119 * thread has access to them.
121 (*let conn = ref None*)
124 (* Update the status bar. *)
126 let f str = GtkThread.async !status_hook str in
130 debug "top of slave loop";
132 (* Get the next command. *)
136 while Q.is_empty q do Cond.wait q_cond q_lock done;
141 debug "slave processing command %s ..." (string_of_message cmd);
144 GtkThread.async !busy_hook ();
147 (* If the user provided an override ?fail parameter to the
148 original call, call that, else call the global hook. *)
150 | Some cb -> GtkThread.async cb exn
151 | None -> GtkThread.async !failure_hook exn
154 (* If there are no more commands in the queue, run the idle hook. *)
155 let empty = with_lock q_lock (fun () -> Q.is_empty q) in
156 if empty then GtkThread.async !idle_hook ();
158 if !quit then Thread.exit ();
161 and execute_command = function
166 | Open_images (images, cb) as cmd ->
167 status "Opening disk images ...";
169 open_disk_images images cb cmd
171 (* Close all handles. *)
174 (match !conn with Some conn -> C.close conn | None -> ());
180 (match !g with Some g -> g#close () | None -> ());
183 (* The common code for Open_domain and Open_images which opens the
184 * libguestfs handle, adds the disks, and launches the appliance.
186 and open_disk_images images cb cmd =
187 debug "opening disk image %s" (string_of_images images);
190 let g' = new G.guestfs () in
194 g#set_trace (trace ());
196 (* Attach progress bar callback. *)
198 g#set_event_callback (
199 fun g event handle buf array ->
200 if event == G.EVENT_PROGRESS && Array.length array >= 4 then (
201 let proc_nr = array.(0)
202 and serial = array.(1)
203 and position = array.(2)
204 and total = array.(3) in
205 debug "progress callback proc_nr=%Ld serial=%Ld posn=%Ld total=%Ld"
206 proc_nr serial position total;
207 GtkThread.async !progress_hook (position, total)
209 ) [ G.EVENT_PROGRESS ]
215 g#add_drive_opts ~readonly:true filename
216 | filename, Some format ->
217 g#add_drive_opts ~readonly:true ~format filename
222 status "Listing filesystems ...";
224 (* Get list of filesystems. *)
225 let fses = g#list_filesystems () in
227 status "Looking for operating systems ...";
229 (* Perform inspection. This can fail, ignore errors. *)
231 try Array.to_list (g#inspect_os ())
234 debug "inspection failed (error ignored): %s" msg;
237 let oses = List.map (
239 let typ = g#inspect_get_type root in
240 let windows_current_control_set =
241 if typ <> "windows" then None
243 try Some (g#inspect_get_windows_current_control_set root)
244 with G.Error _ -> None
246 let windows_systemroot =
247 if typ <> "windows" then None
249 try Some (g#inspect_get_windows_systemroot root)
250 with G.Error _ -> None
255 insp_arch = g#inspect_get_arch root;
256 insp_distro = g#inspect_get_distro root;
257 insp_drive_mappings = g#inspect_get_drive_mappings root;
258 insp_filesystems = g#inspect_get_filesystems root;
259 insp_hostname = g#inspect_get_hostname root;
260 insp_major_version = g#inspect_get_major_version root;
261 insp_minor_version = g#inspect_get_minor_version root;
262 insp_mountpoints = g#inspect_get_mountpoints root;
263 insp_package_format = g#inspect_get_package_format root;
264 insp_package_management = g#inspect_get_package_management root;
265 insp_product_name = g#inspect_get_product_name root;
266 insp_product_variant = g#inspect_get_product_variant root;
268 insp_windows_current_control_set = windows_current_control_set;
269 insp_windows_systemroot = windows_systemroot;
274 insp_all_filesystems = fses;
278 status "Finished opening disk";
280 callback_if_not_discarded cb data
282 (*----------------------------------------------------------------------*)
283 (* Start up one slave thread when the program starts. *)
284 let slave_thread = Thread.create loop ()
287 discard_command_queue ();
288 send_message Exit_thread;
289 Thread.join slave_thread