[Libguestfs] [PATCH 2/2] v2v:windows: prevent conflicts with PnP on firstboot

Richard W.M. Jones rjones at redhat.com
Tue Aug 23 16:33:30 UTC 2016


On Wed, Aug 17, 2016 at 08:59:44PM +0300, Roman Kagan wrote:
> When put on new virtual hardware Windows will start driver installation
> for newly discovered devices.
> 
> The problem is that it happens asynchronously and concurrently with
> other activities, in particular, the firstboot scripts.  This may result
> in conflicts (because a firstboot script may want to install or
> uninstall a driver, etc.) and eventually in the system left in
> inconsistent state.
> 
> In order to prevent it, add another firstboot script which calls a
> special utility, pnp_wait, whose sole purpose is to wait until all
> PnP-related activities are finished.  (It does so via a WinAPI call
> which isn't available from a script so it has to be a compiled .exe.
> The source code for it can be found in the corresponding directory in
> https://github.com/rwmjones/rhsrvany).  This firstboot script is put
> first, so that other firstboot scripts do not have to worry about
> interactions with PnP manager any more.
> 
> One caveat is that on Windows XP and Windows 2003 the PnP manager always
> fires the "Found New Hardware" wizard, which doesn't allow PnP to make
> any progress until the user closes it.  As a result, this firstboot
> script would never finish.
> 
> To work it around, follow the Microsoft KB
> (https://support.microsoft.com/en-us/kb/938596) and set up a registry
> key to make the the PnP manager not fire the wizard; the key is restored
> to its initial state upon PnP completion.  Unfortunately this only works
> on systems having the mentioned update installed; otherwise the user
> will have to interact with the UI.
> 
> Signed-off-by: Roman Kagan <rkagan at virtuozzo.com>
> ---
>  v2v/convert_windows.ml | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 92 insertions(+)
> 
> diff --git a/v2v/convert_windows.ml b/v2v/convert_windows.ml
> index 751a6ab..10a6d31 100644
> --- a/v2v/convert_windows.ml
> +++ b/v2v/convert_windows.ml
> @@ -43,6 +43,18 @@ let convert ~keep_serial_console (g : G.guestfs) inspect source rcaps =
>      try Sys.getenv "VIRT_TOOLS_DATA_DIR"
>      with Not_found -> Guestfs_config.datadir // "virt-tools" in
>  
> +  let pnp_wait_exe = virt_tools_data_dir // "pnp_wait.exe" in
> +  let pnp_wait_exe =
> +    try
> +      let chan = open_in pnp_wait_exe in
> +      close_in chan;
> +      Some pnp_wait_exe
> +    with
> +      Sys_error msg ->
> +        warning (f_"'%s' is missing.  Firstboot scripts may conflict with PnP.  Original error: %s")
> +          pnp_wait_exe msg;
> +        None in
> +
>    (* Check if either RHEV-APT or VMDP exists.  This is optional. *)
>    let tools = [`RhevApt, "rhev-apt.exe"; `VmdpExe, "vmdp.exe"] in
>    let installer =
> @@ -218,6 +230,7 @@ let convert ~keep_serial_console (g : G.guestfs) inspect source rcaps =
>      sprintf "ControlSet%03Ld" value in
>  
>    let rec configure_firstboot () =
> +    wait_pnp ();
>      (match installer with
>       | None -> ()
>       | Some (`RhevApt, tool_path) -> configure_rhev_apt tool_path
> @@ -226,6 +239,85 @@ let convert ~keep_serial_console (g : G.guestfs) inspect source rcaps =
>      unconfigure_xenpv ();
>      unconfigure_prltools ()
>  
> +  and set_reg_val_dword_1 root key_path name =
> +    (* set reg value to REG_DWORD 1, creating intermediate keys if needed *)
> +    let node =
> +      let rec loop parent = function
> +        | [] -> parent
> +        | x :: xs ->
> +          let node =
> +            match g#hivex_node_get_child parent x with
> +            | 0L -> g#hivex_node_add_child parent x (* not found, create *)
> +            | node -> node in
> +          loop node xs
> +      in
> +      loop root key_path in
> +    let valueh = g#hivex_node_get_value node name in
> +    let value =
> +      match valueh with
> +      | 0L -> None
> +      | _ -> Some (int_of_le32 (g#hivex_value_value valueh)) in
> +    g#hivex_node_set_value node name 4_L (le32_of_int 1_L);
> +    value
> +
> +  and reg_restore key name value =
> +    let strkey = String.concat "\\" key in
> +    match value with
> +    | Some value -> sprintf "\
> +reg add \"%s\" /v %s /t REG_DWORD /d %Ld /f" strkey name value
> +    | None -> sprintf "\
> +reg delete \"%s\" /v %s /f" strkey name
> +
> +  and wait_pnp () =
> +    (* prevent destructive interactions of firstboot with PnP *)
> +    match pnp_wait_exe with
> +    | None -> ()
> +    | Some pnp_wait_exe ->
> +      let pnp_wait_path = [""; "Program Files"; "Guestfs"; "Firstboot";
> +                           "pnp_wait.exe"] in
> +      (* suppress "New Hardware Wizard" until PnP settles (see
> +       * https://support.microsoft.com/en-us/kb/938596) and restore it
> +       * afterwards *)
> +      let reg_restore_str =
> +        match inspect.i_major_version, inspect.i_minor_version with
> +        (* WinXP 32bit *)
> +        | 5, 1 ->
> +          let key_path = ["Policies"; "Microsoft"; "Windows"; "DeviceInstall";
> +                          "Settings"] in
> +          let name = "SuppressNewHWUI" in
> +          let value = Windows.with_hive_write g software_hive_filename (
> +            fun root ->
> +              set_reg_val_dword_1 root key_path name
> +          ) in
> +          reg_restore ("HKLM\\Software" :: key_path) name value
> +
> +        (* WinXP 64bit / Win2k3 *)
> +        | 5, 2 ->
> +          let key_path = ["Services"; "PlugPlay"; "Parameters"] in
> +          let name = "SuppressUI" in
> +          let value = Windows.with_hive_write g system_hive_filename (
> +            fun root ->
> +              let current_cs = get_current_cs root in
> +              set_reg_val_dword_1 root (current_cs :: key_path) name
> +          ) in
> +          reg_restore ("HKLM\\SYSTEM\\CurrentControlSet" :: key_path) name
> +                      value
> +
> +        (* any later Windows *)
> +        | _ -> "" in
> +
> +      let fb_script = sprintf "\
> + at echo off
> +
> +echo Wait for PnP to complete
> +\"%s\" >\"%%~dpn0.log\" 2>&1
> +%s" (String.concat "\\" pnp_wait_path) reg_restore_str in
> +
> +      Firstboot.add_firstboot_script g inspect.i_root "wait pnp" fb_script;
> +      (* add_firstboot_script has created the path already *)
> +      g#upload pnp_wait_exe (g#case_sensitive_path
> +                              (String.concat "/" pnp_wait_path))
> +
>    and configure_rhev_apt tool_path =
>      (* Configure RHEV-APT (the RHEV guest agent).  However if it doesn't
>       * exist just warn about it and continue.

This one looks OK, ACK.

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html




More information about the Libguestfs mailing list