doc: Use I<-...> for cross-references to command line options.
[libguestfs.git] / tools / virt-win-reg
index 16688f4..695e619 100755 (executable)
@@ -20,15 +20,14 @@ 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
@@ -51,14 +50,14 @@ virt-win-reg - Export and merge Windows Registry entries from a Windows guest
 
 =head1 WARNING
 
-You must I<not> use C<virt-win-reg> with the C<--merge> option on live
+You must I<not> use C<virt-win-reg> with the I<--merge> option on live
 virtual machines.  If you do this, you I<will> get irreversible disk
 corruption in the VM.  C<virt-win-reg> tries to stop you from doing
 this, but doesn't catch all cases.
 
 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 C<--merge>
+can leave the system unbootable.  Therefore when using the I<--merge>
 option, make sure you have a reliable backup first.
 
 =head1 DESCRIPTION
@@ -69,7 +68,7 @@ Windows guest.
 The first parameter is the libvirt guest name or the raw disk image of
 a Windows guest.
 
-If C<--merge> is I<not> specified, then the chosen registry
+If I<--merge> is I<not> specified, then the chosen registry
 key is displayed/exported (recursively).  For example:
 
  $ virt-win-reg Windows7 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft'
@@ -81,26 +80,11 @@ for example:
  $ virt-win-reg Windows7 $cvkey ProductName
  Windows 7 Enterprise
 
-With C<--merge>, you can merge a textual regedit file into
+With I<--merge>, you can merge a textual regedit file into
 the Windows Registry:
 
  $ virt-win-reg --merge Windows7 changes.reg
 
-=head2 SUPPORTED SYSTEMS
-
-The program currently supports Windows NT-derived guests starting with
-Windows XP through to at least Windows 7.
-
-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>.
-
-You can use C<HKLM> as a shorthand for C<HKEY_LOCAL_MACHINE>, and
-C<HKU> for C<HKEY_USERS>.
-
-C<HKEY_USERS\$SID> and C<HKEY_CURRENT_USER> are B<not> supported at
-this time.
-
 =head2 NOTE
 
 This program is only meant for simple access to the registry.  If you
@@ -109,66 +93,6 @@ download the Registry hive files from the guest using L<libguestfs(3)>
 or L<guestfish(1)> and access them locally, eg. using L<hivex(3)>,
 L<hivexsh(1)> or L<hivexregedit(1)>.
 
-=head2 ENCODING
-
-C<virt-win-reg> 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 C<--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<Win::Hivex::Regedit(3)>.
-
-If you are unsure about the current encoding, use the L<file(1)>
-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 C<--merge> it.
-
-=head2 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.
-
-=head2 CurrentControlSet etc.
-
-Registry keys like C<CurrentControlSet> don't really exist in the
-Windows Registry at the level of the hive file, and therefore you
-cannot modify these.
-
-C<CurrentControlSet> is usually an alias for C<ControlSet001>.  In
-some circumstances it might refer to another control set.  The way
-to find out is to look at the C<HKLM\SYSTEM\Select> 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
-
-"Default" is the one which Windows will choose when it boots.
-
-Similarly, other C<Current...> keys in the path may need to
-be replaced.
-
 =head1 OPTIONS
 
 =over 4
@@ -211,6 +135,23 @@ 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>
@@ -219,7 +160,7 @@ In merge mode, this merges a textual regedit file into the Windows
 Registry of the virtual machine.  If this flag is I<not> given then
 virt-win-reg displays or exports Registry entries instead.
 
-Note that C<--merge> is I<unsafe> to use on live virtual machines, and
+Note that I<--merge> is I<unsafe> to use on live virtual machines, and
 will result in disk corruption.  However exporting (without this flag)
 is always safe.
 
@@ -244,6 +185,7 @@ GetOptions ("help|?" => \$help,
             "version" => \$version,
             "connect|c=s" => \$uri,
             "debug|d" => \$debug,
+            "format=s" => \$format,
             "merge" => \$merge,
             "encoding=s" => \$encoding,
     ) or pod2usage (2);
@@ -264,28 +206,29 @@ warn "launching libguestfs ..." if $debug;
 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;
 
-# List of possible filesystems.
-my @partitions = get_partitions ($g);
-
-# 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);
-
-my $oses = inspect_operating_systems ($g, \%fses);
-
-my @roots = keys %$oses;
-die __"multiboot operating systems are not supported by virt-win-reg" if @roots > 1;
-my $root_dev = $roots[0];
+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 $os = $oses->{$root_dev};
-my $ro = $merge ? 0 : 1;
-mount_operating_system ($g, $os, $ro);
+my $systemroot = $g->inspect_get_windows_systemroot ($roots[0]);
 
 # Create a working directory to store the downloaded registry files.
 my $tmpdir = tempdir (CLEANUP => 1);
@@ -418,7 +361,6 @@ sub download_hive
     local $_;
     my $hivename = shift;
 
-    my $systemroot = $os->{root}->{systemroot} || "/windows";
     my $winfile_before = "$systemroot/system32/config/$hivename";
     my $winfile;
     eval { $winfile = $g->case_sensitive_path ($winfile_before); };
@@ -441,7 +383,6 @@ sub upload_hive
     local $_;
     my $hivename = shift;
 
-    my $systemroot = $os->{root}->{systemroot} || "/windows";
     my $winfile_before = "$systemroot/system32/config/$hivename";
     my $winfile;
     eval { $winfile = $g->case_sensitive_path ($winfile_before); };
@@ -458,8 +399,171 @@ sub upload_hive
     }
 }
 
+=head1 SUPPORTED SYSTEMS
+
+The program currently supports Windows NT-derived guests starting with
+Windows XP through to at least Windows 7.
+
+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>.
+
+You can use C<HKLM> as a shorthand for C<HKEY_LOCAL_MACHINE>, and
+C<HKU> for C<HKEY_USERS>.
+
+C<HKEY_USERS\$SID> and C<HKEY_CURRENT_USER> are B<not> supported at
+this time.
+
+=head1 ENCODING
+
+C<virt-win-reg> 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<Win::Hivex::Regedit(3)>.
+
+If you are unsure about the current encoding, use the L<file(1)>
+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<CurrentControlSet> don't really exist in the
+Windows Registry at the level of the hive file, and therefore you
+cannot modify these.
+
+C<CurrentControlSet> is usually an alias for C<ControlSet001>.  In
+some circumstances it might refer to another control set.  The way
+to find out is to look at the C<HKLM\SYSTEM\Select> 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<Current...> 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<must> 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<guestfish(1)>.  For this example the script is called C<test.bat>
+and it is uploaded into C<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<RunOnce> 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<RunOnce> with C<Run>.
+
+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<test.exe> and we are using the
+RHSrvAny wrapper:
+
+ guestfish -i -d WindowsGuest <<EOF
+   upload rhsrvany.exe /rhsrvany.exe
+   upload test.exe /test.exe
+ EOF
+
+Prepare a regedit file containing the registry changes.  In this
+example, the first registry change is needed for the service itself or
+the service wrapper (if used).  The second registry change is only
+needed because I am using the RHSrvAny service wrapper.
+
+ cat > 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<ControlSet001> 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<http://support.microsoft.com/kb/103000>.
+
+=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