X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=blobdiff_plain;f=tools%2Fvirt-win-reg;h=c9597e7fac9795a15d02879845e43e6f5d5b2bcd;hp=d4329d1c80d2af3b3f29c5f56aedc54f27ae0df3;hb=8cf06ddcddf1fda3a189667dbaaa4560ba49df3c;hpb=792c5283009ed6753239a14df9a6e9c71bea35fd diff --git a/tools/virt-win-reg b/tools/virt-win-reg index d4329d1..c9597e7 100755 --- a/tools/virt-win-reg +++ b/tools/virt-win-reg @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # virt-win-reg -# Copyright (C) 2009 Red Hat Inc. +# Copyright (C) 2010 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 @@ -20,96 +20,78 @@ use warnings; use strict; use Sys::Guestfs; -use Sys::Guestfs::Lib qw(open_guest get_partitions resolve_windows_path - inspect_all_partitions inspect_partition - inspect_operating_systems mount_operating_system); +use Sys::Guestfs::Lib qw(open_guest); +use Win::Hivex; +use Win::Hivex::Regedit qw(reg_import reg_export); + use Pod::Usage; use Getopt::Long; use File::Temp qw/tempdir/; +use File::Basename; use Locale::TextDomain 'libguestfs'; =encoding utf8 =head1 NAME -virt-win-reg - Display Windows Registry entries from a Windows guest +virt-win-reg - Export and merge Windows Registry entries from a Windows guest =head1 SYNOPSIS - virt-win-reg [--options] domname '\Path\To\Subkey' name ['\Path'...] + virt-win-reg domname 'HKLM\Path\To\Subkey' - virt-win-reg [--options] domname '\Path\To\Subkey' @ ['\Path'...] + virt-win-reg domname 'HKLM\Path\To\Subkey' name - virt-win-reg [--options] domname '\Path\To\Subkey' ['\Path'...] + virt-win-reg domname 'HKLM\Path\To\Subkey' @ - virt-win-reg [--options] disk.img [...] '\Path\To\Subkey' (name|@) + virt-win-reg --merge domname [input.reg ...] -=head1 DESCRIPTION + virt-win-reg [--options] disk.img ... # instead of domname -This program can display Windows Registry entries from a Windows -guest. +=head1 WARNING -The first parameter is the libvirt guest name or the raw disk image of -the Windows guest. +You must I use C with the I<--merge> option on live +virtual machines. If you do this, you I get irreversible disk +corruption in the VM. C tries to stop you from doing +this, but doesn't catch all cases. -Then follow one or more sets of path specifiers. The path must begin -with a C<\> (backslash) character, and may be something like -C<'\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion'>. +Modifying the Windows Registry is an inherently risky operation. The format +is deliberately obscure and undocumented, and Registry changes +can leave the system unbootable. Therefore when using the I<--merge> +option, make sure you have a reliable backup first. -The next parameter after that is either a value name, the single -at-character C<@>, or missing. +=head1 DESCRIPTION -If it's a value name, then we print the data associated with that -value. If it's C<@>, then we print the default data associated with -the subkey. If it's missing, then we print all the data associated -with the subkey. +This program can export and merge Windows Registry entries from a +Windows guest. -If this is confusing, look at the L section below. +The first parameter is the libvirt guest name or the raw disk image of +a Windows guest. -Usually you should use single quotes to protect backslashes in the -path from the shell. +If I<--merge> is I specified, then the chosen registry +key is displayed/exported (recursively). For example: -Paths and value names are case-insensitive. + $ virt-win-reg Windows7 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft' -=head2 SUPPORTED SYSTEMS +You can also display single values from within registry keys, +for example: -The program currently supports Windows NT-derived guests starting with -Windows XP through to at least Windows 7. + $ cvkey='HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion' + $ virt-win-reg Windows7 $cvkey ProductName + Windows 7 Enterprise -Registry support is done for C<\HKEY_LOCAL_MACHINE\SAM>, -C<\HKEY_LOCAL_MACHINE\SECURITY>, C<\HKEY_LOCAL_MACHINE\SOFTWARE>, -C<\HKEY_LOCAL_MACHINE\SYSTEM> and C<\HKEY_USERS\.DEFAULT>. +With I<--merge>, you can merge a textual regedit file into +the Windows Registry: -C<\HKEY_USERS\$SID> and C<\HKEY_CURRENT_USER> are B supported at -this time. + $ virt-win-reg --merge Windows7 changes.reg -=head2 NOTES +=head2 NOTE This program is only meant for simple access to the registry. If you want to do complicated things with the registry, we suggest you -download the Registry hive files from the guest using C -or C and access them locally, eg. using C, -C or C. - -=head1 EXAMPLES - - $ virt-win-reg MyWinGuest \ - '\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion' \ - ProductName - Microsoft Windows Server 2003 - - $ virt-win-reg MyWinGuest \ - '\HKEY_LOCAL_MACHINE\System\ControlSet001\Control' SystemBootDevice - multi(0)disk(0)rdisk(0)partition(1) - - $ virt-win-reg MyWinGuest \ - '\HKEY_LOCAL_MACHINE\System\ControlSet001\Control' - "CurrentUser"="USERNAME" - "WaitToKillServiceTimeout"="20000" - "SystemStartOptions"="NOEXECUTE=OPTOUT FASTDETECT" - "SystemBootDevice"="multi(0)disk(0)rdisk(0)partition(1)" - -(please suggest some more) +download the Registry hive files from the guest using L +or L and access them locally, eg. using L, +L or L. =head1 OPTIONS @@ -133,9 +115,19 @@ Display version number and exit. =cut +my $debug; + +=item B<--debug> + +Enable debugging messages. + +=cut + my $uri; -=item B<--connect URI> | B<-c URI> +=item B<-c URI> + +=item B<--connect URI> If using libvirt, connect to the given I. If omitted, then we connect to the default libvirt hypervisor. @@ -143,6 +135,91 @@ connect to the default libvirt hypervisor. If you specify guest block devices directly, then libvirt is not used at all. +=cut + +my $format; + +=item B<--format> raw + +Specify the format of disk images given on the command line. If this +is omitted then the format is autodetected from the content of the +disk image. + +If disk images are requested from libvirt, then this program asks +libvirt for this information. In this case, the value of the format +parameter is ignored. + +If working with untrusted raw-format guest disk images, you should +ensure the format is always specified. + +=cut + +my $merge; + +=item B<--merge> + +In merge mode, this merges a textual regedit file into the Windows +Registry of the virtual machine. If this flag is I given then +virt-win-reg displays or exports Registry entries instead. + +Note that I<--merge> is I to use on live virtual machines, and +will result in disk corruption. However exporting (without this flag) +is always safe. + +=cut + +my $encoding; + +=item B<--encoding> UTF-16LE|ASCII + +When merging (only), you may need to specify the encoding for strings +to be used in the hive file. This is explained in detail in +L. + +The default is to use UTF-16LE, which should work with recent versions +of Windows. + +=cut + +my $unsafe_printable_strings; + +=item B<--unsafe-printable-strings> + +When exporting (only), assume strings are UTF-16LE and print them as +strings instead of hex sequences. Remove the final zero codepoint +from strings if present. + +This is unsafe and does not preserve the fidelity of strings in the +original Registry for various reasons: + +=over 4 + +=item * + +Assumes the original encoding is UTF-16LE. ASCII strings and strings +in other encodings will be corrupted by this transformation. + +=item * + +Assumes that everything which has type 1 or 2 is really a string +and that everything else is not a string, but the type field in +real Registries is not reliable. + +=item * + +Loses information about whether a zero codepoint followed the string +in the Registry or not. + +=back + +This all happens because the Registry itself contains no information +about how strings are encoded (see +L). + +You should only use this option for quick hacking and debugging of the +Registry contents, and I use it if the output is going to be +passed into another program or stored in another Registry. + =back =cut @@ -150,6 +227,11 @@ at all. GetOptions ("help|?" => \$help, "version" => \$version, "connect|c=s" => \$uri, + "debug|d" => \$debug, + "format=s" => \$format, + "merge" => \$merge, + "encoding=s" => \$encoding, + "unsafe-printable-strings" => \$unsafe_printable_strings, ) or pod2usage (2); pod2usage (1) if $help; if ($version) { @@ -159,139 +241,541 @@ if ($version) { exit } -# Split the command line at the first path. Paths begin with -# backslash so this is predictable. +# virt-win-reg only takes a single disk image ... +die __"no libvirt domain name or disk image given\n" if @ARGV == 0; +my $domname_or_image = shift @ARGV; -my @lib_args; -my $i; +warn "launching libguestfs ..." if $debug; -for ($i = 0; $i < @ARGV; ++$i) { - if (substr ($ARGV[$i], 0, 1) eq "\\") { - @lib_args = @ARGV[0 .. ($i-1)]; - @ARGV = @ARGV[$i .. $#ARGV]; - last; - } +my @lib_args = ([$domname_or_image]); +push @lib_args, address => $uri if $uri; +push @lib_args, rw => 1 if $merge; +push @lib_args, format => $format if defined $format; +my $g = open_guest (@lib_args); +$g->launch (); + +warn "inspecting guest ..." if $debug; + +my @roots = $g->inspect_os (); +if (@roots == 0) { + die __x("{prog}: No operating system could be detected inside this disk image.\n\nThis may be because the file is not a disk image, or is not a virtual machine\nimage, or because the OS type is not understood by libguestfs.\n\nIf you feel this is an error, please file a bug report including as much\ninformation about the disk image as possible.\n", + prog => basename ($0)); +} +if (@roots > 1) { + die __x("{prog}: multiboot operating systems are not supported.\n", + prog => basename ($0)) } +my %fses = $g->inspect_get_mountpoints ($roots[0]); +my @fses = sort { length $a <=> length $b } keys %fses; +my $mountopts = $merge ? "" : "ro"; +foreach (@fses) { + $g->mount_options ($mountopts, $fses{$_}, $_); +} + +my $systemroot = $g->inspect_get_windows_systemroot ($roots[0]); + +# Create a working directory to store the downloaded registry files. +my $tmpdir = tempdir (CLEANUP => 1); -pod2usage (__"virt-win-reg: no VM name, disk images or Registry path given") if 0 == @lib_args; +# Used when merging (only) to map from the downloaded hiveshortname to +# various properties about the hive. The key is hiveshortname. The +# value is a hashref containing {h} (hive handle) and {hivefile} (full +# hive path on the Windows side). +my %hives; -my $g; -if ($uri) { - $g = open_guest (\@lib_args, address => $uri); -} else { - $g = open_guest (\@lib_args); +if (!$merge) { # Export mode. + die __"expecting 1 or 2 more parameters, subkey path and optionally the value to export\n" + if @ARGV < 1 || @ARGV > 2; + + my $path = shift @ARGV; + my $name = shift @ARGV; # or undef + + # Map this to the hive name. This function dies on failure. + my ($hiveshortname, $hivefile, $prefix); + ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($path); + + # Download the chosen hive. + download_hive ($hivefile, $hiveshortname); + + # Open it. + my $h = Win::Hivex->open ("$tmpdir/$hiveshortname", debug => $debug); + + unless ($name) { + # Export it. + warn "exporting $path from $hiveshortname with prefix $prefix ..." + if $debug; + reg_export ($h, $path, \*STDOUT, + prefix => $prefix, + unsafe_printable_strings => $unsafe_printable_strings); + } else { + # Export a single key using hivexget. + my @args = ("hivexget", "$tmpdir/$hiveshortname", $path, $name); + warn "running ", join (" ", @args), " ..." if $debug; + system (@args) == 0 or die "hivexget failed: $?" + } } +else { # Import mode. + if (@ARGV == 0) { + reg_import (\*STDIN, \&import_mapper, encoding => $encoding); + } else { + foreach (@ARGV) { + open my $fh, $_ or die "open: $_: $!"; + reg_import ($fh, \&import_mapper, encoding => $encoding); + } + } -$g->launch (); + # Now we've done importing, commit all the hive handles and + # close them all. + foreach (values %hives) { + my $h = $_->{h}; + delete $_->{h}; + $h->commit (undef); + } -# List of possible filesystems. -my @partitions = get_partitions ($g); + # Upload all the downloaded hives. + foreach my $hiveshortname (keys %hives) { + upload_hive ($hiveshortname, $hives{$hiveshortname}->{hivefile}) + } -# Now query each one to build up a picture of what's in it. -my %fses = - inspect_all_partitions ($g, \@partitions, - use_windows_registry => 0); + # Sync everything. + $g->umount_all (); + $g->sync (); +} -my $oses = inspect_operating_systems ($g, \%fses); +exit 0; -my @roots = keys %$oses; -die __"no root device found in this operating system image" if @roots == 0; -die __"multiboot operating systems are not supported by virt-win-reg" if @roots > 1; -my $root_dev = $roots[0]; +# map function passed to reg_import. +sub import_mapper +{ + local $_ = shift; -my $os = $oses->{$root_dev}; -mount_operating_system ($g, $os); + my ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($_); -# Create a working directory to store the downloaded registry files. -my $tmpdir = tempdir (CLEANUP => 1); + # Need to download this hive? + unless (-f "$tmpdir/$hiveshortname") { + download_hive ($hivefile, $hiveshortname); -# Now process each request in turn. -my $winfile; -my $localhive; -my $path; + my $h = Win::Hivex->open ("$tmpdir/$hiveshortname", + write => 1, debug => $debug); + my %hash = ( h => $h, hivefile => $hivefile ); + $hives{$hiveshortname} = \%hash; + } -for ($i = 0; $i < @ARGV; ++$i) { - $_ = $ARGV[$i]; + return ($hives{$hiveshortname}->{h}, $path); +} - if (/^\\HKEY_LOCAL_MACHINE\\SAM(\\.*)/i) { - $winfile = "/windows/system32/config/sam"; - $localhive = "$tmpdir/sam"; - $path = $1; +# Given a path, map that to the name of the hive and the true path +# within that hive. +sub map_path_to_hive +{ + local $_ = shift; + my ($hiveshortname, $hivefile, $path, $prefix); + + if (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SAM(\\.*)?$/i) { + $hiveshortname = "sam"; + $hivefile = "$systemroot/system32/config/$hiveshortname"; + $path = defined $1 ? $1 : "\\"; + $prefix = "HKEY_LOCAL_MACHINE\\SAM"; + } + elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SECURITY(\\.*)?$/i) { + $hiveshortname = "security"; + $hivefile = "$systemroot/system32/config/$hiveshortname"; + $path = defined $1 ? $1 : "\\"; + $prefix = "HKEY_LOCAL_MACHINE\\SECURITY"; + } + elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SOFTWARE(\\.*)?$/i) { + $hiveshortname = "software"; + $hivefile = "$systemroot/system32/config/$hiveshortname"; + $path = defined $1 ? $1 : "\\"; + $prefix = "HKEY_LOCAL_MACHINE\\SOFTWARE"; + } + elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SYSTEM(\\.*)?$/i) { + $hiveshortname = "system"; + $hivefile = "$systemroot/system32/config/$hiveshortname"; + $path = defined $1 ? $1 : "\\"; + $prefix = "HKEY_LOCAL_MACHINE\\SYSTEM"; + } + elsif (/^\\?(?:HKEY_USERS|HKU)\\.DEFAULT(\\.*)?$/i) { + $hiveshortname = "default"; + $hivefile = "$systemroot/system32/config/$hiveshortname"; + $path = defined $1 ? $1 : "\\"; + $prefix = "HKEY_LOCAL_MACHINE\\.DEFAULT"; } - elsif (/^\\HKEY_LOCAL_MACHINE\\SECURITY(\\.*)/i) { - $winfile = "/windows/system32/config/security"; - $localhive = "$tmpdir/security"; - $path = $1; + elsif (/^\\?(?:HKEY_USERS|HKU)\\(S-1-5-[-\d]+)(\\.*)?$/i) { + my $sid = $1; + $hiveshortname = $sid; + $prefix = "HKEY_USERS\\$sid"; + $path = defined $2 ? $2 : "\\"; + # This requires a recursive call to download the SOFTWARE hive. + $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat"; } - elsif (/^\\HKEY_LOCAL_MACHINE\\SOFTWARE(\\.*)/i) { - $winfile = "/windows/system32/config/software"; - $localhive = "$tmpdir/software"; - $path = $1; + elsif (/^\\?(?:HKEY_USERS|HKU)\\LocalSystem(\\.*)?$/i) { + my $sid = "S-1-5-18"; + $hiveshortname = $sid; + $prefix = "HKEY_USERS\\$sid"; + $path = defined $1 ? $1 : "\\"; + # This requires a recursive call to download the SOFTWARE hive. + $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat"; } - elsif (/^\\HKEY_LOCAL_MACHINE\\SYSTEM(\\.*)/i) { - $winfile = "/windows/system32/config/system"; - $localhive = "$tmpdir/system"; - $path = $1; + elsif (/^\\?(?:HKEY_USERS|HKU)\\LocalService(\\.*)?$/i) { + my $sid = "S-1-5-19"; + $hiveshortname = $sid; + $prefix = "HKEY_USERS\\$sid"; + $path = defined $1 ? $1 : "\\"; + # This requires a recursive call to download the SOFTWARE hive. + $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat"; } - elsif (/^\\HKEY_USERS\\.DEFAULT(\\.*)/i) { - $winfile = "/windows/system32/config/default"; - $localhive = "$tmpdir/default"; - $path = $1; + elsif (/^\\?(?:HKEY_USERS|HKU)\\NetworkService(\\.*)?$/i) { + my $sid = "S-1-5-20"; + $hiveshortname = $sid; + $prefix = "HKEY_USERS\\$sid"; + $path = defined $1 ? $1 : "\\"; + # This requires a recursive call to download the SOFTWARE hive. + $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat"; + } + elsif (/^\\?(?:HKEY_USERS|HKU)\\(.*?)(\\.*)?$/i) { + $hiveshortname = "user_$1"; + $prefix = "HKEY_USERS\\$1"; + $path = defined $2 ? $2 : "\\"; + # XXX We should probably look this up properly. + if (is_dir_nocase ("/Users/$1")) { + $hivefile = "/Users/$1/ntuser.dat" + } elsif (is_dir_nocase ("/Documents and Settings/$1")) { + $hivefile = "/Documents and Settings/$1/ntuser.dat" + } else { + die __x("virt-win-reg: {p}: cannot find user directory\n", + p => $1) + } } else { - die "virt-win-reg: $_: not a supported Windows Registry path\n" + die __x("virt-win-reg: {p}: not a supported Windows Registry path\n", + p => $_) } - unless (-f $localhive) { - # Check the hive file exists and get the real name. - eval { - $winfile = $g->case_sensitive_path ($winfile); - $g->download ($winfile, $localhive); - }; - if ($@) { - die "virt-win-reg: $winfile: could not download registry file: $@\n" - } + return ($hiveshortname, $hivefile, $path, $prefix); +} + +# Given a User SID, consult +# HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid +# and return the ProfileImagePath value. +sub lookup_pip_of_user_sid +{ + local $_; + my $sid = shift; + + my $path = + "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\". + $sid; + + my ($hiveshortname, $hivefile, $prefix); + ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($path); + + download_hive ($hivefile, $hiveshortname) + unless -f "$tmpdir/$hiveshortname"; + + my @args = ("$tmpdir/$hiveshortname", $path, "ProfileImagePath"); + warn "running hivexget ", join (" ", @args), " ..." if $debug; + + my $fh; + open $fh, "-|", "hivexget", @args + or die "hivexget: see earlier errors: $!"; + $_ = <$fh>; + close $fh or die "hivexget: see earlier errors: $!"; + + chomp; + + # The contents of the registry are a windows path, possibly + # containing %systemroot% and %systemdrive% (on Win XP). Expand + # it and remove some other windows-isms. The caller will do + # case_sensitive_path for us, so we don't need to do that. + s/%systemroot%/$systemroot/i; + s/%systemdrive%//i; + s/^c://i; + s,\\,/,g; + + $_; +} + +sub is_dir_nocase +{ + local $_; + my $dir = shift; + + my $windir; + eval { $windir = $g->case_sensitive_path ($dir); }; + if ($@) { + return 0; } + return $g->is_dir ($windir); +} - # What sort of request is it? Peek at the next arg. - my $name; # will be: undefined, @ or a name - if ($i+1 < @ARGV) { - if (substr ($ARGV[$i+1], 0, 1) ne "\\") { - $name = $ARGV[$i+1]; - $i++; - } +# Download a named hive file. Die on failure. +sub download_hive +{ + local $_; + my $hivefile = shift; + my $hiveshortname = shift; + + my $winfile; + eval { $winfile = $g->case_sensitive_path ($hivefile); }; + if ($@) { + die __x("virt-win-reg: {p}: file not found in guest: {err}\n", + p => $hivefile, err => $@); } - my @cmd; - if (defined $name) { - @cmd = ("hivexget", $localhive, $path, $name); - } else { - @cmd = ("hivexget", $localhive, $path); + warn "downloading $winfile ..." if $debug; + eval { $g->download ($winfile, "$tmpdir/$hiveshortname"); }; + if ($@) { + die __x("virt-win-reg: {p}: could not download registry file: {err}\n", + p => $winfile, err => $@); + } +} + +# Upload a named hive file. Die on failure. +sub upload_hive +{ + local $_; + my $hiveshortname = shift; + my $hivefile = shift; + + my $winfile; + eval { $winfile = $g->case_sensitive_path ($hivefile); }; + if ($@) { + die __x("virt-win-reg: {p}: file not found in guest: {err}\n", + p => $hivefile, err => $@); } - system (@cmd) == 0 - or die "hivexget command failed: $?\n"; + warn "uploading $winfile ..." if $debug; + eval { $g->upload ("$tmpdir/$hiveshortname", $winfile); }; + if ($@) { + die __x("virt-win-reg: {p}: could not upload registry file: {err}\n", + p => $winfile, err => $@); + } } +=head1 SUPPORTED SYSTEMS + +The program currently supports Windows NT-derived guests starting with +Windows XP through to at least Windows 7. + +The following Registry keys are supported: + +=over 4 + +=item C + +=item C + +=item C + +=item C + +=item C + +=item C> + +where I is a Windows User SID (eg. C). + +=item C> + +where I is a local user name (this is a libguestfs extension). + +=back + +You can use C as a shorthand for C, and +C for C. + +The literal keys C and C are not +supported (there is no "current user"). + +=head1 ENCODING + +C expects that regedit files have already been reencoded +in the local encoding. Usually on Linux hosts, this means UTF-8 with +Unix-style line endings. Since Windows regedit files are often in +UTF-16LE with Windows-style line endings, you may need to reencode the +whole file before or after processing. + +To reencode a file from Windows format to Linux (before processing it +with the I<--merge> option), you would do something like this: + + iconv -f utf-16le -t utf-8 < win.reg | dos2unix > linux.reg + +To go in the opposite direction, after exporting and before sending +the file to a Windows user, do something like this: + + unix2dos linux.reg | iconv -f utf-8 -t utf-16le > win.reg + +For more information about encoding, see L. + +If you are unsure about the current encoding, use the L +command. Recent versions of Windows regedit.exe produce a UTF-16LE +file with Windows-style (CRLF) line endings, like this: + + $ file software.reg + software.reg: Little-endian UTF-16 Unicode text, with very long lines, + with CRLF line terminators + +This file would need conversion before you could I<--merge> it. + +=head1 CurrentControlSet etc. + +Registry keys like C don't really exist in the +Windows Registry at the level of the hive file, and therefore you +cannot modify these. + +C is usually an alias for C. In +some circumstances it might refer to another control set. The way +to find out is to look at the C key: + + # virt-win-reg WindowsGuest 'HKLM\SYSTEM\Select' + [HKEY_LOCAL_MACHINE\SYSTEM\Select] + "Current"=dword:00000001 + "Default"=dword:00000001 + "Failed"=dword:00000000 + "LastKnownGood"=dword:00000002 + +"Current" is the one which Windows will choose when it boots. + +Similarly, other C keys in the path may need to +be replaced. + +=head1 WINDOWS TIPS + +Note that some of these tips modify the guest disk image. The guest +I be shut off, else you will get disk corruption. + +=head2 RUNNING A BATCH SCRIPT WHEN A USER LOGS IN + +Prepare a DOS batch script, VBScript or executable. Upload this using +L. For this example the script is called C +and it is uploaded into C: + + guestfish -i -d WindowsGuest upload test.bat /test.bat + +Prepare a regedit file containing the registry change: + + cat > test.reg <<'EOF' + [HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce] + "Test"="c:\\test.bat" + EOF + +In this example we use the key C which means that the script +will run precisely once when the first user logs in. If you want it +to run every time a user logs in, replace C with C. + +Now update the registry: + + virt-win-reg --merge WindowsGuest test.reg + +=head2 INSTALLING A SERVICE + +This section assumes you are familiar with Windows services, and you +either have a program which handles the Windows Service Control +Protocol directly or you want to run any program using a service +wrapper like SrvAny or the free RHSrvAny. + +First upload the program and optionally the service wrapper. In this +case the test program is called C and we are using the +RHSrvAny wrapper: + + guestfish -i -d WindowsGuest < service.reg <<'EOF' + [HKLM\SYSTEM\ControlSet001\services\RHSrvAny] + "Type"=dword:00000010 + "Start"=dword:00000002 + "ErrorControl"=dword:00000001 + "ImagePath"="c:\\rhsrvany.exe" + "DisplayName"="RHSrvAny" + "ObjectName"="NetworkService" + + [HKLM\SYSTEM\ControlSet001\services\RHSrvAny\Parameters] + "CommandLine"="c:\\test.exe" + "PWD"="c:\\Temp" + EOF + +Notes: + +=over 4 + +=item * + +For use of C see the section above in this manual page. +You may need to adjust this according to the control set that is in +use by the guest. + +=item * + +C<"ObjectName"> controls the privileges that the service will have. +An alternative is C<"ObjectName"="LocalSystem"> which would be the +most privileged account. + +=item * + +For the meaning of the magic numbers, see this Microsoft KB article: +L. + +=back + +Update the registry: + + virt-win-reg --merge WindowsGuest service.reg + +=head1 SHELL QUOTING + +Be careful when passing parameters containing C<\> (backslash) in the +shell. Usually you will have to use 'single quotes' or double +backslashes (but not both) to protect them from the shell. + +Paths and value names are case-insensitive. + +Libvirt guest names can contain arbitrary characters, some of which +have meaning to the shell such as C<#> and space. You may need to +quote or escape these characters on the command line. See the shell +manual page L for details. + =head1 SEE ALSO L, -L, +L, +L, L, L, L, L, L, +L, +L, L, L. +=head1 BUGS + +When reporting bugs, please enable debugging and capture the +I output: + + export LIBGUESTFS_DEBUG=1 + virt-win-reg --debug [... rest ...] > /tmp/virt-win-reg.log 2>&1 + +Attach /tmp/virt-win-reg.log to a new bug report at +L + =head1 AUTHOR -Richard W.M. Jones L +Richard W.M. Jones L =head1 COPYRIGHT -Copyright (C) 2009 Red Hat Inc. +Copyright (C) 2010 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