333bd1583d3cdfedba4517b5147d079df4aca2a7
[libguestfs.git] / tools / virt-win-reg
1 #!/usr/bin/perl -w
2 # virt-win-reg
3 # Copyright (C) 2010 Red Hat Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 use warnings;
20 use strict;
21
22 use Sys::Guestfs;
23 use Sys::Guestfs::Lib qw(open_guest);
24 use Win::Hivex;
25 use Win::Hivex::Regedit qw(reg_import reg_export);
26
27 use Pod::Usage;
28 use Getopt::Long;
29 use File::Temp qw/tempdir/;
30 use File::Basename;
31 use Locale::TextDomain 'libguestfs';
32
33 =encoding utf8
34
35 =head1 NAME
36
37 virt-win-reg - Export and merge Windows Registry entries from a Windows guest
38
39 =head1 SYNOPSIS
40
41  virt-win-reg domname 'HKLM\Path\To\Subkey'
42
43  virt-win-reg domname 'HKLM\Path\To\Subkey' name
44
45  virt-win-reg domname 'HKLM\Path\To\Subkey' @
46
47  virt-win-reg --merge domname [input.reg ...]
48
49  virt-win-reg [--options] disk.img ... # instead of domname
50
51 =head1 WARNING
52
53 You must I<not> use C<virt-win-reg> with the I<--merge> option on live
54 virtual machines.  If you do this, you I<will> get irreversible disk
55 corruption in the VM.  C<virt-win-reg> tries to stop you from doing
56 this, but doesn't catch all cases.
57
58 Modifying the Windows Registry is an inherently risky operation.  The format
59 is deliberately obscure and undocumented, and Registry changes
60 can leave the system unbootable.  Therefore when using the I<--merge>
61 option, make sure you have a reliable backup first.
62
63 =head1 DESCRIPTION
64
65 This program can export and merge Windows Registry entries from a
66 Windows guest.
67
68 The first parameter is the libvirt guest name or the raw disk image of
69 a Windows guest.
70
71 If I<--merge> is I<not> specified, then the chosen registry
72 key is displayed/exported (recursively).  For example:
73
74  $ virt-win-reg Windows7 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft'
75
76 You can also display single values from within registry keys,
77 for example:
78
79  $ cvkey='HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
80  $ virt-win-reg Windows7 $cvkey ProductName
81  Windows 7 Enterprise
82
83 With I<--merge>, you can merge a textual regedit file into
84 the Windows Registry:
85
86  $ virt-win-reg --merge Windows7 changes.reg
87
88 =head2 NOTE
89
90 This program is only meant for simple access to the registry.  If you
91 want to do complicated things with the registry, we suggest you
92 download the Registry hive files from the guest using L<libguestfs(3)>
93 or L<guestfish(1)> and access them locally, eg. using L<hivex(3)>,
94 L<hivexsh(1)> or L<hivexregedit(1)>.
95
96 =head1 OPTIONS
97
98 =over 4
99
100 =cut
101
102 my $help;
103
104 =item B<--help>
105
106 Display brief help.
107
108 =cut
109
110 my $version;
111
112 =item B<--version>
113
114 Display version number and exit.
115
116 =cut
117
118 my $debug;
119
120 =item B<--debug>
121
122 Enable debugging messages.
123
124 =cut
125
126 my $uri;
127
128 =item B<--connect URI> | B<-c URI>
129
130 If using libvirt, connect to the given I<URI>.  If omitted, then we
131 connect to the default libvirt hypervisor.
132
133 If you specify guest block devices directly, then libvirt is not used
134 at all.
135
136 =cut
137
138 my $format;
139
140 =item B<--format> raw
141
142 Specify the format of disk images given on the command line.  If this
143 is omitted then the format is autodetected from the content of the
144 disk image.
145
146 If disk images are requested from libvirt, then this program asks
147 libvirt for this information.  In this case, the value of the format
148 parameter is ignored.
149
150 If working with untrusted raw-format guest disk images, you should
151 ensure the format is always specified.
152
153 =cut
154
155 my $merge;
156
157 =item B<--merge>
158
159 In merge mode, this merges a textual regedit file into the Windows
160 Registry of the virtual machine.  If this flag is I<not> given then
161 virt-win-reg displays or exports Registry entries instead.
162
163 Note that I<--merge> is I<unsafe> to use on live virtual machines, and
164 will result in disk corruption.  However exporting (without this flag)
165 is always safe.
166
167 =cut
168
169 my $encoding;
170
171 =item B<--encoding> UTF-16LE|ASCII
172
173 When merging (only), you may need to specify the encoding for strings
174 to be used in the hive file.  This is explained in detail in
175 L<Win::Hivex::Regedit(3)/ENCODING STRINGS>.
176
177 The default is to use UTF-16LE, which should work with recent versions
178 of Windows.
179
180 =cut
181
182 my $unsafe_printable_strings;
183
184 =item B<--unsafe-printable-strings>
185
186 When exporting (only), assume strings are UTF-16LE and print them as
187 strings instead of hex sequences.  Remove the final zero codepoint
188 from strings if present.
189
190 This is unsafe and does not preserve the fidelity of strings in the
191 original Registry for various reasons:
192
193 =over 4
194
195 =item *
196
197 Assumes the original encoding is UTF-16LE.  ASCII strings and strings
198 in other encodings will be corrupted by this transformation.
199
200 =item *
201
202 Assumes that everything which has type 1 or 2 is really a string
203 and that everything else is not a string, but the type field in
204 real Registries is not reliable.
205
206 =item *
207
208 Loses information about whether a zero codepoint followed the string
209 in the Registry or not.
210
211 =back
212
213 This all happens because the Registry itself contains no information
214 about how strings are encoded (see
215 L<Win::Hivex::Regedit(3)/ENCODING STRINGS>).
216
217 You should only use this option for quick hacking and debugging of the
218 Registry contents, and I<never> use it if the output is going to be
219 passed into another program or stored in another Registry.
220
221 =back
222
223 =cut
224
225 GetOptions ("help|?" => \$help,
226             "version" => \$version,
227             "connect|c=s" => \$uri,
228             "debug|d" => \$debug,
229             "format=s" => \$format,
230             "merge" => \$merge,
231             "encoding=s" => \$encoding,
232             "unsafe-printable-strings" => \$unsafe_printable_strings,
233     ) or pod2usage (2);
234 pod2usage (1) if $help;
235 if ($version) {
236     my $g = Sys::Guestfs->new ();
237     my %h = $g->version ();
238     print "$h{major}.$h{minor}.$h{release}$h{extra}\n";
239     exit
240 }
241
242 # virt-win-reg only takes a single disk image ...
243 die __"no libvirt domain name or disk image given\n" if @ARGV == 0;
244 my $domname_or_image = shift @ARGV;
245
246 warn "launching libguestfs ..." if $debug;
247
248 my @lib_args = ([$domname_or_image]);
249 push @lib_args, address => $uri if $uri;
250 push @lib_args, rw => 1 if $merge;
251 push @lib_args, format => $format if defined $format;
252 my $g = open_guest (@lib_args);
253 $g->launch ();
254
255 warn "inspecting guest ..." if $debug;
256
257 my @roots = $g->inspect_os ();
258 if (@roots == 0) {
259     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",
260             prog => basename ($0));
261 }
262 if (@roots > 1) {
263     die __x("{prog}: multiboot operating systems are not supported.\n",
264             prog => basename ($0))
265 }
266 my %fses = $g->inspect_get_mountpoints ($roots[0]);
267 my @fses = sort { length $a <=> length $b } keys %fses;
268 my $mountopts = $merge ? "" : "ro";
269 foreach (@fses) {
270     $g->mount_options ($mountopts, $fses{$_}, $_);
271 }
272
273 my $systemroot = $g->inspect_get_windows_systemroot ($roots[0]);
274
275 # Create a working directory to store the downloaded registry files.
276 my $tmpdir = tempdir (CLEANUP => 1);
277
278 # Used when merging (only) to map from the downloaded hiveshortname to
279 # various properties about the hive.  The key is hiveshortname.  The
280 # value is a hashref containing {h} (hive handle) and {hivefile} (full
281 # hive path on the Windows side).
282 my %hives;
283
284 if (!$merge) {                  # Export mode.
285     die __"expecting 1 or 2 more parameters, subkey path and optionally the value to export\n"
286         if @ARGV < 1 || @ARGV > 2;
287
288     my $path = shift @ARGV;
289     my $name = shift @ARGV; # or undef
290
291     # Map this to the hive name.  This function dies on failure.
292     my ($hiveshortname, $hivefile, $prefix);
293     ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($path);
294
295     # Download the chosen hive.
296     download_hive ($hivefile, $hiveshortname);
297
298     # Open it.
299     my $h = Win::Hivex->open ("$tmpdir/$hiveshortname", debug => $debug);
300
301     unless ($name) {
302         # Export it.
303         warn "exporting $path from $hiveshortname with prefix $prefix ..."
304             if $debug;
305         reg_export ($h, $path, \*STDOUT,
306                     prefix => $prefix,
307                     unsafe_printable_strings => $unsafe_printable_strings);
308     } else {
309         # Export a single key using hivexget.
310         my @args = ("hivexget", "$tmpdir/$hiveshortname", $path, $name);
311         warn "running ", join (" ", @args), " ..." if $debug;
312         system (@args) == 0 or die "hivexget failed: $?"
313     }
314 }
315 else {                          # Import mode.
316     if (@ARGV == 0) {
317         reg_import (\*STDIN, \&import_mapper, encoding => $encoding);
318     } else {
319         foreach (@ARGV) {
320             open my $fh, $_ or die "open: $_: $!";
321             reg_import ($fh, \&import_mapper, encoding => $encoding);
322         }
323     }
324
325     # Now we've done importing, commit all the hive handles and
326     # close them all.
327     foreach (values %hives) {
328         my $h = $_->{h};
329         delete $_->{h};
330         $h->commit (undef);
331     }
332
333     # Upload all the downloaded hives.
334     foreach my $hiveshortname (keys %hives) {
335         upload_hive ($hiveshortname, $hives{$hiveshortname}->{hivefile})
336     }
337
338     # Sync everything.
339     $g->umount_all ();
340     $g->sync ();
341 }
342
343 exit 0;
344
345 # map function passed to reg_import.
346 sub import_mapper
347 {
348     local $_ = shift;
349
350     my ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($_);
351
352     # Need to download this hive?
353     unless (-f "$tmpdir/$hiveshortname") {
354         download_hive ($hivefile, $hiveshortname);
355
356         my $h = Win::Hivex->open ("$tmpdir/$hiveshortname",
357                                   write => 1, debug => $debug);
358         my %hash = ( h => $h, hivefile => $hivefile );
359         $hives{$hiveshortname} = \%hash;
360     }
361
362     return ($hives{$hiveshortname}->{h}, $path);
363 }
364
365 # Given a path, map that to the name of the hive and the true path
366 # within that hive.
367 sub map_path_to_hive
368 {
369     local $_ = shift;
370     my ($hiveshortname, $hivefile, $path, $prefix);
371
372     if (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SAM(\\.*)?$/i) {
373         $hiveshortname = "sam";
374         $hivefile = "$systemroot/system32/config/$hiveshortname";
375         $path = defined $1 ? $1 : "\\";
376         $prefix = "HKEY_LOCAL_MACHINE\\SAM";
377     }
378     elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SECURITY(\\.*)?$/i) {
379         $hiveshortname = "security";
380         $hivefile = "$systemroot/system32/config/$hiveshortname";
381         $path = defined $1 ? $1 : "\\";
382         $prefix = "HKEY_LOCAL_MACHINE\\SECURITY";
383     }
384     elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SOFTWARE(\\.*)?$/i) {
385         $hiveshortname = "software";
386         $hivefile = "$systemroot/system32/config/$hiveshortname";
387         $path = defined $1 ? $1 : "\\";
388         $prefix = "HKEY_LOCAL_MACHINE\\SOFTWARE";
389     }
390     elsif (/^\\?(?:HKEY_LOCAL_MACHINE|HKLM)\\SYSTEM(\\.*)?$/i) {
391         $hiveshortname = "system";
392         $hivefile = "$systemroot/system32/config/$hiveshortname";
393         $path = defined $1 ? $1 : "\\";
394         $prefix = "HKEY_LOCAL_MACHINE\\SYSTEM";
395     }
396     elsif (/^\\?(?:HKEY_USERS|HKU)\\.DEFAULT(\\.*)?$/i) {
397         $hiveshortname = "default";
398         $hivefile = "$systemroot/system32/config/$hiveshortname";
399         $path = defined $1 ? $1 : "\\";
400         $prefix = "HKEY_LOCAL_MACHINE\\.DEFAULT";
401     }
402     elsif (/^\\?(?:HKEY_USERS|HKU)\\(S-1-5-[-\d]+)(\\.*)?$/i) {
403         my $sid = $1;
404         $hiveshortname = $sid;
405         $prefix = "HKEY_USERS\\$sid";
406         $path = defined $2 ? $2 : "\\";
407         # This requires a recursive call to download the SOFTWARE hive.
408         $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
409     }
410     elsif (/^\\?(?:HKEY_USERS|HKU)\\LocalSystem(\\.*)?$/i) {
411         my $sid = "S-1-5-18";
412         $hiveshortname = $sid;
413         $prefix = "HKEY_USERS\\$sid";
414         $path = defined $1 ? $1 : "\\";
415         # This requires a recursive call to download the SOFTWARE hive.
416         $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
417     }
418     elsif (/^\\?(?:HKEY_USERS|HKU)\\LocalService(\\.*)?$/i) {
419         my $sid = "S-1-5-19";
420         $hiveshortname = $sid;
421         $prefix = "HKEY_USERS\\$sid";
422         $path = defined $1 ? $1 : "\\";
423         # This requires a recursive call to download the SOFTWARE hive.
424         $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
425     }
426     elsif (/^\\?(?:HKEY_USERS|HKU)\\NetworkService(\\.*)?$/i) {
427         my $sid = "S-1-5-20";
428         $hiveshortname = $sid;
429         $prefix = "HKEY_USERS\\$sid";
430         $path = defined $1 ? $1 : "\\";
431         # This requires a recursive call to download the SOFTWARE hive.
432         $hivefile = lookup_pip_of_user_sid ($sid) . "/ntuser.dat";
433     }
434     elsif (/^\\?(?:HKEY_USERS|HKU)\\(.*?)(\\.*)?$/i) {
435         $hiveshortname = "user_$1";
436         $prefix = "HKEY_USERS\\$1";
437         $path = defined $2 ? $2 : "\\";
438         # XXX We should probably look this up properly.
439         if (is_dir_nocase ("/Users/$1")) {
440             $hivefile = "/Users/$1/ntuser.dat"
441         } elsif (is_dir_nocase ("/Documents and Settings/$1")) {
442             $hivefile = "/Documents and Settings/$1/ntuser.dat"
443         } else {
444             die __x("virt-win-reg: {p}: cannot find user directory\n",
445                     p => $1)
446         }
447     }
448     else {
449         die __x("virt-win-reg: {p}: not a supported Windows Registry path\n",
450                 p => $_)
451     }
452
453     return ($hiveshortname, $hivefile, $path, $prefix);
454 }
455
456 # Given a User SID, consult
457 # HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid
458 # and return the ProfileImagePath value.
459 sub lookup_pip_of_user_sid
460 {
461     local $_;
462     my $sid = shift;
463
464     my $path =
465         "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\".
466         $sid;
467
468     my ($hiveshortname, $hivefile, $prefix);
469     ($hiveshortname, $hivefile, $path, $prefix) = map_path_to_hive ($path);
470
471     download_hive ($hivefile, $hiveshortname)
472         unless -f "$tmpdir/$hiveshortname";
473
474     my @args = ("$tmpdir/$hiveshortname", $path, "ProfileImagePath");
475     warn "running hivexget ", join (" ", @args), " ..." if $debug;
476
477     my $fh;
478     open $fh, "-|", "hivexget", @args
479         or die "hivexget: see earlier errors: $!";
480     $_ = <$fh>;
481     close $fh or die "hivexget: see earlier errors: $!";
482
483     chomp;
484
485     # The contents of the registry are a windows path, possibly
486     # containing %systemroot%.  Expand it and remove some other
487     # windows-isms.  The caller will do case_sensitive_path for us, so
488     # we don't need to do that.
489     s/%systemroot%/$systemroot/;
490     s/^c://i;
491     s,\\,/,g;
492
493     $_;
494 }
495
496 sub is_dir_nocase
497 {
498     local $_;
499     my $dir = shift;
500
501     my $windir;
502     eval { $windir = $g->case_sensitive_path ($dir); };
503     if ($@) {
504         return 0;
505     }
506     return $g->is_dir ($windir);
507 }
508
509 # Download a named hive file.  Die on failure.
510 sub download_hive
511 {
512     local $_;
513     my $hivefile = shift;
514     my $hiveshortname = shift;
515
516     my $winfile;
517     eval { $winfile = $g->case_sensitive_path ($hivefile); };
518     if ($@) {
519         die __x("virt-win-reg: {p}: file not found in guest: {err}\n",
520                 p => $hivefile, err => $@);
521     }
522
523     warn "downloading $winfile ..." if $debug;
524     eval { $g->download ($winfile, "$tmpdir/$hiveshortname"); };
525     if ($@) {
526         die __x("virt-win-reg: {p}: could not download registry file: {err}\n",
527                 p => $winfile, err => $@);
528     }
529 }
530
531 # Upload a named hive file.  Die on failure.
532 sub upload_hive
533 {
534     local $_;
535     my $hiveshortname = shift;
536     my $hivefile = shift;
537
538     my $winfile;
539     eval { $winfile = $g->case_sensitive_path ($hivefile); };
540     if ($@) {
541         die __x("virt-win-reg: {p}: file not found in guest: {err}\n",
542                 p => $hivefile, err => $@);
543     }
544
545     warn "uploading $winfile ..." if $debug;
546     eval { $g->upload ("$tmpdir/$hiveshortname", $winfile); };
547     if ($@) {
548         die __x("virt-win-reg: {p}: could not upload registry file: {err}\n",
549                 p => $winfile, err => $@);
550     }
551 }
552
553 =head1 SUPPORTED SYSTEMS
554
555 The program currently supports Windows NT-derived guests starting with
556 Windows XP through to at least Windows 7.
557
558 The following Registry keys are supported:
559
560 =over 4
561
562 =item C<HKEY_LOCAL_MACHINE\SAM>
563
564 =item C<HKEY_LOCAL_MACHINE\SECURITY>
565
566 =item C<HKEY_LOCAL_MACHINE\SOFTWARE>
567
568 =item C<HKEY_LOCAL_MACHINE\SYSTEM>
569
570 =item C<HKEY_USERS\.DEFAULT>
571
572 =item C<HKEY_USERS\I<SID>>
573
574 where I<SID> is a Windows User SID (eg. C<S-1-5-18>).
575
576 =item C<HKEY_USERS\I<username>>
577
578 where I<username> is a local user name (this is a libguestfs extension).
579
580 =back
581
582 You can use C<HKLM> as a shorthand for C<HKEY_LOCAL_MACHINE>, and
583 C<HKU> for C<HKEY_USERS>.
584
585 The literal keys C<HKEY_USERS\$SID> and C<HKEY_CURRENT_USER> are not
586 supported (there is no "current user").
587
588 =head1 ENCODING
589
590 C<virt-win-reg> expects that regedit files have already been reencoded
591 in the local encoding.  Usually on Linux hosts, this means UTF-8 with
592 Unix-style line endings.  Since Windows regedit files are often in
593 UTF-16LE with Windows-style line endings, you may need to reencode the
594 whole file before or after processing.
595
596 To reencode a file from Windows format to Linux (before processing it
597 with the I<--merge> option), you would do something like this:
598
599  iconv -f utf-16le -t utf-8 < win.reg | dos2unix > linux.reg
600
601 To go in the opposite direction, after exporting and before sending
602 the file to a Windows user, do something like this:
603
604  unix2dos linux.reg | iconv -f utf-8 -t utf-16le > win.reg
605
606 For more information about encoding, see L<Win::Hivex::Regedit(3)>.
607
608 If you are unsure about the current encoding, use the L<file(1)>
609 command.  Recent versions of Windows regedit.exe produce a UTF-16LE
610 file with Windows-style (CRLF) line endings, like this:
611
612  $ file software.reg
613  software.reg: Little-endian UTF-16 Unicode text, with very long lines,
614  with CRLF line terminators
615
616 This file would need conversion before you could I<--merge> it.
617
618 =head1 CurrentControlSet etc.
619
620 Registry keys like C<CurrentControlSet> don't really exist in the
621 Windows Registry at the level of the hive file, and therefore you
622 cannot modify these.
623
624 C<CurrentControlSet> is usually an alias for C<ControlSet001>.  In
625 some circumstances it might refer to another control set.  The way
626 to find out is to look at the C<HKLM\SYSTEM\Select> key:
627
628  # virt-win-reg WindowsGuest 'HKLM\SYSTEM\Select'
629  [HKEY_LOCAL_MACHINE\SYSTEM\Select]
630  "Current"=dword:00000001
631  "Default"=dword:00000001
632  "Failed"=dword:00000000
633  "LastKnownGood"=dword:00000002
634
635 "Current" is the one which Windows will choose when it boots.
636
637 Similarly, other C<Current...> keys in the path may need to
638 be replaced.
639
640 =head1 WINDOWS TIPS
641
642 Note that some of these tips modify the guest disk image.  The guest
643 I<must> be shut off, else you will get disk corruption.
644
645 =head2 RUNNING A BATCH SCRIPT WHEN A USER LOGS IN
646
647 Prepare a DOS batch script, VBScript or executable.  Upload this using
648 L<guestfish(1)>.  For this example the script is called C<test.bat>
649 and it is uploaded into C<C:\>:
650
651  guestfish -i -d WindowsGuest upload test.bat /test.bat
652
653 Prepare a regedit file containing the registry change:
654
655  cat > test.reg <<'EOF'
656  [HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce]
657  "Test"="c:\\test.bat"
658  EOF
659
660 In this example we use the key C<RunOnce> which means that the script
661 will run precisely once when the first user logs in.  If you want it
662 to run every time a user logs in, replace C<RunOnce> with C<Run>.
663
664 Now update the registry:
665
666  virt-win-reg --merge WindowsGuest test.reg
667
668 =head2 INSTALLING A SERVICE
669
670 This section assumes you are familiar with Windows services, and you
671 either have a program which handles the Windows Service Control
672 Protocol directly or you want to run any program using a service
673 wrapper like SrvAny or the free RHSrvAny.
674
675 First upload the program and optionally the service wrapper.  In this
676 case the test program is called C<test.exe> and we are using the
677 RHSrvAny wrapper:
678
679  guestfish -i -d WindowsGuest <<EOF
680    upload rhsrvany.exe /rhsrvany.exe
681    upload test.exe /test.exe
682  EOF
683
684 Prepare a regedit file containing the registry changes.  In this
685 example, the first registry change is needed for the service itself or
686 the service wrapper (if used).  The second registry change is only
687 needed because I am using the RHSrvAny service wrapper.
688
689  cat > service.reg <<'EOF'
690  [HKLM\SYSTEM\ControlSet001\services\RHSrvAny]
691  "Type"=dword:00000010
692  "Start"=dword:00000002
693  "ErrorControl"=dword:00000001
694  "ImagePath"="c:\\rhsrvany.exe"
695  "DisplayName"="RHSrvAny"
696  "ObjectName"="NetworkService"
697  
698  [HKLM\SYSTEM\ControlSet001\services\RHSrvAny\Parameters]
699  "CommandLine"="c:\\test.exe"
700  "PWD"="c:\\Temp"
701  EOF
702
703 Notes:
704
705 =over 4
706
707 =item *
708
709 For use of C<ControlSet001> see the section above in this manual page.
710 You may need to adjust this according to the control set that is in
711 use by the guest.
712
713 =item *
714
715 C<"ObjectName"> controls the privileges that the service will have.
716 An alternative is C<"ObjectName"="LocalSystem"> which would be the
717 most privileged account.
718
719 =item *
720
721 For the meaning of the magic numbers, see this Microsoft KB article:
722 L<http://support.microsoft.com/kb/103000>.
723
724 =back
725
726 Update the registry:
727
728  virt-win-reg --merge WindowsGuest service.reg
729
730 =head1 SHELL QUOTING
731
732 Be careful when passing parameters containing C<\> (backslash) in the
733 shell.  Usually you will have to use 'single quotes' or double
734 backslashes (but not both) to protect them from the shell.
735
736 Paths and value names are case-insensitive.
737
738 Libvirt guest names can contain arbitrary characters, some of which
739 have meaning to the shell such as C<#> and space.  You may need to
740 quote or escape these characters on the command line.  See the shell
741 manual page L<sh(1)> for details.
742
743 =head1 SEE ALSO
744
745 L<hivex(3)>,
746 L<hivexsh(1)>,
747 L<hivexregedit(1)>,
748 L<guestfs(3)>,
749 L<guestfish(1)>,
750 L<virt-cat(1)>,
751 L<Sys::Guestfs(3)>,
752 L<Sys::Guestfs::Lib(3)>,
753 L<Win::Hivex(3)>,
754 L<Win::Hivex::Regedit(3)>,
755 L<Sys::Virt(3)>,
756 L<http://libguestfs.org/>.
757
758 =head1 BUGS
759
760 When reporting bugs, please enable debugging and capture the
761 I<complete> output:
762
763  export LIBGUESTFS_DEBUG=1
764  virt-win-reg --debug [... rest ...] > /tmp/virt-win-reg.log 2>&1
765
766 Attach /tmp/virt-win-reg.log to a new bug report at
767 L<https://bugzilla.redhat.com/>
768
769 =head1 AUTHOR
770
771 Richard W.M. Jones L<http://people.redhat.com/~rjones/>
772
773 =head1 COPYRIGHT
774
775 Copyright (C) 2010 Red Hat Inc.
776
777 This program is free software; you can redistribute it and/or modify
778 it under the terms of the GNU General Public License as published by
779 the Free Software Foundation; either version 2 of the License, or
780 (at your option) any later version.
781
782 This program is distributed in the hope that it will be useful,
783 but WITHOUT ANY WARRANTY; without even the implied warranty of
784 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
785 GNU General Public License for more details.
786
787 You should have received a copy of the GNU General Public License
788 along with this program; if not, write to the Free Software
789 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.