1#!/usr/bin/perl -w
2
3=head1 NAME
4
5    regress-win32.pl -- Helper for Windows regression tests
6
7=head2 DESCRIPTION
8
9    This perl script permits to run test Bareos Client Daemon on Windows.
10    It allows to:
11       - stop/start/upgrade the Bareos Client Daemon
12       - compare to subtree with checksums, attribs and ACL
13       - create test environments
14
15=head2 USAGE
16
17  X:\> regress-win32.pl [-b basedir] [-i ip_address] [-p c:/bareos]
18   or
19  X:\> perl regress-win32.pl ...
20
21    -b|--base=path      Where to find regress and bareos directories
22    -i|--ip=ip          Restrict access to this tool to this ip address
23    -p|--prefix=path    Path to the windows installation
24    -h|--help           Print this help
25
26=head2 EXAMPLE
27
28    regress-win32.pl -b z:/git         # will find z:/git/regress z:/git/bareos
29
30    regress-win32.pl -i 192.168.0.1 -b z:
31
32=head2 INSTALL
33
34    This perl script needs a Perl distribution on the Windows Client
35    (http://strawberryperl.com)
36
37    You need to have the following subtree on x:
38    x:/
39      bareos/
40      regress/
41
42   This script requires perl to work (http://strawberryperl.com), and by default
43   it assumes that Bareos is installed in the standard location. Once it's
44   started on the windows, you can do remote commands like:
45    - start the service
46    - stop the service
47    - edit the bareos-fd.conf to change the director and password setting
48    - install a new binary version (not tested, no plugin support)
49    - create weird files and directories
50    - create files with windows attributes
51    - compare two directories (with md5)
52
53
54   To test it, you can follow this procedure
55   On the windows box:
56    - install perl from http://strawberryperl.com on windows
57    - copy or export regress directory somewhere on your windows
58      You can use a network share to your regress directory on linux
59      Then, copy a link to this script to your desktop
60      And double-click on it, and always open .pl file with perl.exe
61
62    - If you export the regress directory to your windows box and you
63      make windows binaries available, this script can update bareos version.
64      You need to put your binaries on:
65        regress/release32 and regress/release64
66      or
67        regress/build/src/win32/release32 and regress/build/src/win32/release64
68
69    - start the regress/scripts/regress-win32.pl (open it with perl.exe)
70    - create $WIN32_FILE
71    - make sure that the firewall is well configured or just disabled (needs
72   bareos and 8091/tcp)
73
74   On Linux box:
75    - edit config file to fill the following variables
76
77   WIN32_CLIENT="win2008-fd"
78   # Client FQDN or IP address
79   WIN32_ADDR="192.168.0.6"
80   # File or Directory to backup.  This is put in the "File" directive
81   #   in the FileSet
82   WIN32_FILE="c:/tmp"
83   # Port of Win32 client
84   WIN32_PORT=9102
85   # Win32 Client password
86   WIN32_PASSWORD="xxx"
87   # will be the ip address of the linux box
88   WIN32_STORE_ADDR="192.168.0.1"
89   # set for autologon
90   WIN32_USER=Administrator
91   WIN32_PASS=password
92   # set for MSSQL
93   WIN32_MSSQL_USER=sa
94   WIN32_MSSQL_PASS=pass
95    - type make setup
96    - run ./tests/backup-bareos-test to be sure that everything is ok
97    - start ./tests/win32-fd-test
98
99   I'm not very happy with this script, but it works :)
100
101=cut
102
103use strict;
104use HTTP::Daemon;
105use HTTP::Status;
106use HTTP::Response;
107use HTTP::Headers;
108use File::Copy;
109use Pod::Usage;
110use Cwd 'chdir';
111use File::Find;
112use Digest::MD5;
113use Getopt::Long ;
114use POSIX;
115use File::Basename qw/dirname/;
116
117my $base = 'x:';
118my $src_ip = '';
119my $help;
120my $bareos_prefix="c:/Program Files/Bareos";
121my $conf = "C:/Documents and Settings/All Users/Application Data/Bareos";
122GetOptions("base=s"   => \$base,
123           "help"     => \$help,
124           "prefix=s" => \$bareos_prefix,
125           "ip=s"     => \$src_ip);
126
127if ($help) {
128    pod2usage(-verbose => 2,
129              -exitval => 0);
130}
131
132if (! -d $bareos_prefix) {
133    print "regress-win32.pl: Could not find Bareos installation dir $bareos_prefix\n";
134    print "regress-win32.pl: Won't be able to upgrade the version or modify the configuration\n";
135}
136
137if (-f "$bareos_prefix/bareos-fd.conf" and -f "$conf/bareos-fd.conf") {
138    print "regress-win32.pl: Unable to determine bareos-fd location $bareos_prefix or $conf ?\n";
139
140} elsif (-f "$bareos_prefix/bareos-fd.conf") {
141    $conf = $bareos_prefix;
142}
143
144#if (! -d "$base/bareos" || ! -d "$base/regress") {
145#    pod2usage(-verbose => 2,
146#              -exitval => 1,
147#              -message => "Can't find bareos or regress dir on $base\n");
148#}
149
150# stop the fd service
151sub stop_fd
152{
153    return `net stop bareos-fd`;
154}
155
156my $arch;
157my $bin_path;
158sub find_binaries
159{
160    if ($_ =~ /bareos-fd.exe/i) {
161        if ($File::Find::dir =~ /release$arch/) {
162            $bin_path = $File::Find::dir;
163        }
164    }
165}
166
167# copy binaries for a new fd
168# to work, you need to mount the regress directory
169sub install_fd
170{
171    my ($r) = shift;
172    if ($r->url !~ m!^/install$!) {
173        return "ERR\nIncorrect url: " . $r->url . "\n";
174    }
175
176    if (-d "c:/Program Files (x86)") {
177        $arch = "64";
178    } else {
179        $arch = "32";
180    }
181
182    # X:/regress/scripts/regress-win32.pl
183    # X:/scripts/regress-win32.pl
184    # perl script location
185
186    my $dir = dirname(dirname($0));
187    print "searching bareos-fd.exe in $dir\n";
188    find(\&find_binaries, ("$dir\\build\\src\\win32\\release$arch",
189                           "$dir\\release$arch"));
190
191    if (!$bin_path) {
192        return "ERR\nCan't find bareos-fd.exe in $dir\n";
193    }
194
195    print "Found binaries in $bin_path\n";
196
197    stop_fd();
198
199    system("del \"c:\\Program Files\\bareos\\bareos.dll\"");
200    system("del \"c:\\Program Files\\bareos\\bareos-fd.exe\"");
201    system("del \"c:\\Program Files\\bareos\\plugins\\vss-fd.dll\"");
202
203    my $ret="Ok\n";
204
205    copy("$bin_path/bareos-fd.exe",
206         "c:/Program Files/bareos/bareos-fd.exe") or $ret="ERR\n$!\n";
207
208    copy("$bin_path/bareos.dll",
209         "c:/Program Files/bareos/bareos.dll") or $ret="ERR\n$!\n";
210
211    copy("$bin_path/vss-fd.dll",
212         "c:/Program Files/bareos/plugins/vss-fd.dll") or $ret="ERR\n$!\n";
213
214    start_fd();
215    return "OK\n";
216}
217
218# start the fd service
219sub start_fd
220{
221    return `net start bareos-fd`;
222}
223
224# initialize the weird directory for runscript test
225sub init_weird_runscript_test
226{
227    my ($r) = shift;
228
229    if ($r->url !~ m!^/init_weird_runscript_test\?source=(\w:/[\w\d\-\./]+)$!) {
230        return "ERR\nIncorrect url: ". $r->url . "\n";
231    }
232    my $source = $1;
233
234    # Create $source if needed
235    my $tmp = $source;
236    $tmp =~ s:/:\\:g;
237    system("mkdir $tmp");
238
239    if (!chdir($source)) {
240        return "ERR\nCan't access to $source $!\n";
241    }
242
243    if (-d "weird_runscript") {
244        system("rmdir /Q /S weird_runscript");
245    }
246
247    mkdir("weird_runscript");
248    if (!chdir("weird_runscript")) {
249        return "ERR\nCan't access to $source $!\n";
250    }
251
252    open(FP, ">test.bat")                 or return "ERR\n";
253    print FP "\@echo off\n";
254    print FP "echo hello \%1\n";
255    close(FP);
256
257    copy("test.bat", "test space.bat")    or return "ERR\n";
258    copy("test.bat", "test2 space.bat")   or return "ERR\n";
259    copy("test.bat", "test�.bat")         or return "ERR\n";
260
261    mkdir("dir space")                    or return "ERR\n";
262    copy("test.bat", "dir space")         or return "ERR\n";
263    copy("test�.bat","dir space")         or return "ERR\n";
264    copy("test2 space.bat", "dir space")  or return "ERR\n";
265
266    mkdir("�voil�")                       or return "ERR\n";
267    copy("test.bat", "�voil�")            or return "ERR\n";
268    copy("test�.bat","�voil�")            or return "ERR\n";
269    copy("test2 space.bat", "�voil�")     or return "ERR\n";
270
271    mkdir("�with space")                  or return "ERR\n";
272    copy("test.bat", "�with space")       or return "ERR\n";
273    copy("test�.bat","�with space")       or return "ERR\n";
274    copy("test2 space.bat", "�with space") or return "ERR\n";
275    mkdir("a"x200);
276    copy("test.bat", "a"x200);
277    system("mklink /J junc " . "a"x200); # TODO: need something for win2003
278    link("test.bat", "link.bat");
279    return "OK\n";
280}
281
282# init the Attrib test by creating some files and settings attributes
283sub init_attrib_test
284{
285    my ($r) = shift;
286
287    if ($r->url !~ m!^/init_attrib_test\?source=(\w:/[\w/]+)$!) {
288        return "ERR\nIncorrect url: " . $r->url . "\n";
289    }
290
291    my $source = $1;
292    system("mkdir $source");
293
294    if (!chdir($source)) {
295        return "ERR\nCan't access to $source $!\n";
296    }
297
298    # cleanup the old directory if any
299    if (-d "attrib_test") {
300        system("rmdir /Q /S attrib_test");
301    }
302
303    mkdir("attrib_test");
304    chdir("attrib_test");
305
306    mkdir("hidden");
307    mkdir("hidden/something");
308    system("attrib +H hidden");
309
310    mkdir("readonly");
311    mkdir("readonly/something");
312    system("attrib +R readonly");
313
314    mkdir("normal");
315    mkdir("normal/something");
316    system("attrib -R -H -S normal");
317
318    mkdir("system");
319    mkdir("system/something");
320    system("attrib +S system");
321
322    mkdir("readonly_hidden");
323    mkdir("readonly_hidden/something");
324    system("attrib +R +H readonly_hidden");
325
326    my $ret = `attrib /S /D`;
327    $ret = strip_base($ret, $source);
328
329    return "OK\n$ret\n";
330}
331
332sub md5sum
333{
334    my $file = shift;
335    open(FILE, $file) or return "Can't open $file $!";
336    binmode(FILE);
337    return Digest::MD5->new->addfile(*FILE)->hexdigest;
338}
339
340# set $src and $dst before using Find call
341my ($src, $dst);
342my $error="";
343sub wanted
344{
345    my $f = $File::Find::name;
346    $f =~ s!^\Q$src\E/?!!i;
347
348    if (-f "$src/$f") {
349        if (! -f "$dst/$f") {
350            $error .= "$dst/$f is missing\n";
351        } else {
352            my $a = md5sum("$src/$f");
353            my $b = md5sum("$dst/$f");
354            if ($a ne $b) {
355                $error .= "$src/$f $a\n$dst/$f $b\n";
356            }
357        }
358    }
359}
360
361sub create_schedtask
362{
363    my ($r) = shift;
364    if ($r->url !~ m!^/create_schedtask\?name=([\w\d\-.]+)$!) {
365        return "ERR\nIncorrect url: " . $r->url . "\n";
366    }
367    my $ret='';
368    my ($task,$pass) = ($1, $2);
369    my (undef, undef, $version, undef) = POSIX::uname();
370    if ($version < 6) {         # win2003
371        $ret = `echo pass | SCHTASKS /Create /TN $task /SC ONLOGON  /TR C:\\windows\\system32\\calc.exe /F 2>&1`;
372    } else {
373        $ret=`SCHTASKS /Create /TN $task /SC ONLOGON /F /TR C:\\windows\\system32\\calc.exe`;
374    }
375
376    if ($ret =~ /SUCCESS|has been created/) {
377        return "OK\n$ret";
378    } else {
379        return "ERR\n$ret";
380    }
381#
382# SCHTASKS /Create [/S system [/U username [/P [password]]]]
383#     [/RU username [/RP password]] /SC schedule [/MO modifier] [/D day]
384#     [/M months] [/I idletime] /TN taskname /TR taskrun [/ST starttime]
385#     [/RI interval] [ {/ET endtime | /DU duration} [/K] [/XML xmlfile] [/V1]]
386#     [/SD startdate] [/ED enddate] [/IT | /NP] [/Z] [/F]
387}
388
389sub del_schedtask
390{
391    my ($r) = shift;
392    if ($r->url !~ m!^/del_schedtask\?name=([\w\d\-.]+)$!) {
393        return "ERR\nIncorrect url: " . $r->url . "\n";
394    }
395    my ($task) = ($1);
396    my $ret=`SCHTASKS /Delete /TN $task /F`;
397
398    if ($ret =~ /SUCCESS/) {
399        return "OK\n$ret";
400    } else {
401        return "ERR\n$ret";
402    }
403}
404
405sub check_schedtask
406{
407    my ($r) = shift;
408    if ($r->url !~ m!^/check_schedtask\?name=([\w\d\-.]+)$!) {
409        return "ERR\nIncorrect url: " . $r->url . "\n";
410    }
411
412    my ($task) = ($1);
413    my (undef, undef, $version, undef) = POSIX::uname();
414    my $ret;
415    if ($version < 6) {         # win2003
416        $ret=`SCHTASKS /Query`;
417    } else {
418        $ret=`SCHTASKS /Query /TN $task`;
419    }
420
421    if ($ret =~ /^($task .+)$/m) {
422        return "OK\n$1\n";
423    } else {
424        return "ERR\n$ret";
425    }
426}
427
428sub set_director_name
429{
430    my ($r) = shift;
431
432    if ($r->url !~ m!^/set_director_name\?name=([\w\d\.\-]+);pass=([\w\d+\-\.*]+)$!)
433    {
434        return "ERR\nIncorrect url: " . $r->url . "\n";
435    }
436
437    my ($name, $pass) = ($1, $2);
438
439    open(ORG, "$conf/bareos-fd.conf") or return "ERR\nORG $!\n";
440    open(NEW, ">$conf/bareos-fd.conf.new") or return "ERR\nNEW $!\n";
441
442    my $in_dir=0;               # don't use monitoring section
443    my $nb_dir="";
444    while (my $l = <ORG>)
445    {
446        if ($l =~ /^\s*Director\s+{/i) {
447            print NEW $l;
448            $in_dir = 1;
449        } elsif ($l =~ /^(\s*)Name\s*=/ and $in_dir) {
450            print NEW "${1}Name=$name$nb_dir\n";
451        } elsif ($l =~ /^(\s*)Password\s*=/ and $in_dir) {
452            print NEW "${1}Password=$pass\n";
453        } elsif ($l =~ /#(\s*Plugin.*)$/) {
454            print NEW $1;
455        } elsif ($l =~ /\s*}/ and $in_dir) {
456            print NEW $l;
457            $in_dir = 0;
458            $nb_dir++;
459        } else {
460            print NEW $l;
461        }
462    }
463
464    close(ORG);
465    close(NEW);
466    move("$conf/bareos-fd.conf.new", "$conf/bareos-fd.conf")
467        and return "OK\n";
468
469    return "ERR\nCan't set the director name\n";
470}
471
472# convert \ to / and strip the path
473sub strip_base
474{
475    my ($data, $path) = @_;
476    $data =~ s!\\!/!sg;
477    $data =~ s!\Q$path!!sig;
478    return $data;
479}
480
481# Compare two directories, make checksums, compare attribs and ACLs
482sub compare
483{
484    my ($r) = shift;
485
486    if ($r->url !~ m!^/compare\?source=(\w:/[\w/]+);dest=(\w:/[\w/]+)$!) {
487        return "ERR\nIncorrect url: " . $r->url . "\n";
488    }
489
490    my ($source, $dest) = ($1, $2);
491
492    if (!Cwd::chdir($source)) {
493        return "ERR\nCan't access to $source $!\n";
494    }
495
496    my $src_attrib = `attrib /D /S`;
497    $src_attrib = strip_base($src_attrib, $source);
498
499    if (!Cwd::chdir($dest)) {
500        return "ERR\nCan't access to $dest $!\n";
501    }
502
503    my $dest_attrib = `attrib /D /S`;
504    $dest_attrib = strip_base($dest_attrib, $dest);
505
506    if (lc($src_attrib) ne lc($dest_attrib)) {
507        print "ERR\n$src_attrib\n=========\n$dest_attrib\n";
508        return "ERR\n$src_attrib\n=========\n$dest_attrib\n";
509    }
510
511    ($src, $dst, $error) = ($source, $dest, '');
512    find(\&wanted, $source);
513    if ($error) {
514        return "ERR\n$error";
515    } else {
516        return "OK\n";
517    }
518}
519
520sub cleandir
521{
522    my ($r) = shift;
523
524    if ($r->url !~ m!^/cleandir\?source=(\w:/[\w/]+)/restore$!) {
525        return "ERR\nIncorrect url: " . $r->url . "\n";
526    }
527
528    my $source = $1;
529
530    if (! -d "$source/restore") {
531        return "ERR\nIncorrect path\n";
532    }
533
534    if (!chdir($source)) {
535        return "ERR\nCan't access to $source $!\n";
536    }
537
538    system("rmdir /Q /S restore");
539
540    return "OK\n";
541}
542
543sub reboot
544{
545    Win32::InitiateSystemShutdown('', "\nSystem will now Reboot\!", 2, 0, 1 );
546    exit 0;
547}
548
549# boot disabled auto
550sub set_service
551{
552    my ($r) = shift;
553
554    if ($r->url !~ m!^/set_service\?srv=([\w-]+);action=(\w+)$!) {
555        return "ERR\nIncorrect url: " . $r->url . "\n";
556    }
557    my $out = `sc config $1 start= $2`;
558    if ($out !~ /SUCCESS/) {
559        return "ERR\n$out";
560    }
561    return "OK\n";
562}
563
564# RUNNING, STOPPED
565sub get_service
566{
567    my ($r) = shift;
568
569    if ($r->url !~ m!^/get_service\?srv=([\w-]+);state=(\w+)$!) {
570        return "ERR\nIncorrect url: " . $r->url . "\n";
571    }
572    my $out = `sc query $1`;
573    if ($out !~ /$2/) {
574        return "ERR\n$out";
575    }
576    return "OK\n";
577}
578
579sub add_registry_key
580{
581    my ($r) = shift;
582    if ($r->url !~ m!^/add_registry_key\?key=(\w+);val=(\w+)$!) {
583        return "ERR\nIncorrect url: " . $r->url . "\n";
584    }
585    my ($k, $v) = ($1,$2);
586    my $ret = "ERR";
587    open(FP, ">tmp.reg")
588        or return "ERR\nCan't open tmp.reg $!\n";
589
590    print FP "Windows Registry Editor Version 5.00
591
592[HKEY_LOCAL_MACHINE\\SOFTWARE\\Bareos]
593\"$k\"=\"$v\"
594
595";
596    close(FP);
597    system("regedit /s tmp.reg");
598
599    unlink("tmp2.reg");
600    system("regedit /e tmp2.reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Bareos");
601
602    open(FP, "<:encoding(UTF-16LE)", "tmp2.reg")
603       or return "ERR\nCan't open tmp2.reg $!\n";
604    while (my $l = <FP>) {
605       if ($l =~ /"$k"="$v"/) {
606          $ret = "OK";
607       }
608    }
609    close(FP);
610    unlink("tmp.reg");
611    unlink("tmp2.reg");
612    return "$ret\n";
613}
614
615sub set_auto_logon
616{
617    my ($r) = shift;
618    my $self = $0;              # perl script location
619    $self =~ s/\\/\\\\/g;
620    my $p = $^X;                # perl.exe location
621    $p =~ s/\\/\\\\/g;
622    if ($r->url !~ m!^/set_auto_logon\?user=([\w\d\-+\.]+);pass=([\w\d\.\,:*+%\-]*)$!) {
623        return "ERR\nIncorrect url: " . $r->url . "\n";
624    }
625    my $k = $1;
626    my $v = $2 || '';           # password can be empty
627    my $ret = "ERR\nCan't find AutoAdminLogon key\n";
628    open(FP, ">c:/autologon.reg")
629        or return "ERR\nCan't open autologon.reg $!\n";
630    print FP "Windows Registry Editor Version 5.00
631
632[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon]
633\"DefaultUserName\"=\"$k\"
634\"DefaultPassword\"=\"$v\"
635\"AutoAdminLogon\"=\"1\"
636
637[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run]
638\"regress\"=\"$p $self\"
639
640[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Reliability]
641\"ShutdownReasonUI\"=dword:00000000
642
643[HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Windows NT\\Reliability]
644\"ShutdownReasonOn\"=dword:00000000
645";
646    close(FP);
647    system("regedit /s c:\\autologon.reg");
648
649    unlink("tmp2.reg");
650    system("regedit /e tmp2.reg \"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\"");
651
652    open(FP, "<:encoding(UTF-16LE)", "tmp2.reg")
653       or return "ERR\nCan't open tmp2.reg $!\n";
654    while (my $l = <FP>) {
655       if ($l =~ /"AutoAdminLogon"="1"/) {
656          $ret = "OK\n";
657       }
658    }
659    close(FP);
660    unlink("tmp2.reg");
661    return $ret;
662}
663
664sub del_registry_key
665{
666    my ($r) = shift;
667    if ($r->url !~ m!^/del_registry_key\?key=(\w+)$!) {
668        return "ERR\nIncorrect url: " . $r->url . "\n";
669    }
670    my $k = $1;
671    my $ret = "OK\n";
672
673    unlink("tmp2.reg");
674    open(FP, ">tmp.reg")
675        or return "ERR\nCan't open tmp.reg $!\n";
676    print FP "Windows Registry Editor Version 5.00
677
678[HKEY_LOCAL_MACHINE\\SOFTWARE\\Bareos]
679\"$k\"=-
680
681";
682    close(FP);
683    system("regedit /s tmp.reg");
684    system("regedit /e tmp2.reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Bareos");
685
686    open(FP, "<:encoding(UTF-16LE)", "tmp2.reg")
687       or return "ERR\nCan't open tmp2.reg $!\n";
688    while (my $l = <FP>) {
689       if ($l =~ /"$k"=/) {
690          $ret = "ERR\nThe key $k is still present\n";
691       }
692    }
693    close(FP);
694    unlink("tmp.reg");
695    unlink("tmp2.reg");
696    return $ret;
697}
698
699sub get_registry_key
700{
701    my ($r) = shift;
702    if ($r->url !~ m!^/get_registry_key\?key=(\w+);val=(\w+)$!) {
703        return "ERR\nIncorrect url: " . $r->url . "\n";
704    }
705    my ($k, $v) = ($1, $2);
706    my $ret = "ERR\nCan't get or verify registry key $k\n";
707
708    unlink("tmp2.reg");
709    system("regedit /e tmp2.reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Bareos");
710    open(FP, "<:encoding(UTF-16LE)", "tmp2.reg")
711       or return "ERR\nCan't open tmp2.reg $!\n";
712    while (my $l = <FP>) {
713       if ($l =~ /"$k"="$v"/) {
714          $ret = "OK\n";
715       }
716    }
717    close(FP);
718    unlink("tmp2.reg");
719
720    return $ret;
721}
722
723my $mssql_user;
724my $mssql_pass;
725my $mssql_cred;
726my $mssql_bin;
727sub find_mssql
728{
729    if ($_ =~ /sqlcmd.exe/i) {
730        $mssql_bin = $File::Find::name;
731    }
732}
733
734# Verify that we can use SQLCMD.exe
735sub check_mssql
736{
737    my ($r) = shift;
738    my $ret = "ERR";
739    if ($r->url !~ m!^/check_mssql\?user=(\w*);pass=(.*)$!) {
740        return "ERR\nIncorrect url: " . $r->url . "\n";
741    }
742    ($mssql_user, $mssql_pass) = ($1, $2);
743
744    unless ($mssql_bin) {
745        find(\&find_mssql, 'c:/program files/microsoft sql server/');
746    }
747    unless ($mssql_bin) {
748        find(\&find_mssql, 'c:/program files (x86)/microsoft sql server/');
749    }
750
751    if (!$mssql_bin) {
752        return "ERR\nCan't find SQLCMD.exe in c:/program files\n";
753    }
754
755    print $mssql_bin, "\n";
756    $mssql_cred = ($mssql_user)?"-U $mssql_user -P $mssql_pass":"";
757    my $res = `"$mssql_bin" $mssql_cred -Q "SELECT 'OK';"`;
758    if ($res !~ /OK/) {
759        return "ERR\nCan't verify the SQLCMD result\n" .
760            "Please verify that MSSQL is accepting connection:\n" .
761            "$mssql_bin $mssql_cred -Q \"SELECT 1;\"\n";
762    }
763    return "OK\n";
764}
765
766# Create simple DB, a table and some information in
767sub setup_mssql_db
768{
769    my ($r) = shift;
770    my $ret = "ERR";
771    if ($r->url !~ m!^/setup_mssql_db\?db=([\w\d]+)$!) {
772        return "ERR\nIncorrect url: " . $r->url . "\n";
773    }
774    my $db = $1;
775
776    unless ($mssql_bin) {
777        return "ERR\nCan't find mssql bin (run check_mssql first)\n";
778    }
779
780    my $res = `"$mssql_bin" $mssql_cred -Q "CREATE DATABASE $db;"`;
781    $res = `"$mssql_bin" $mssql_cred -d $db -Q "CREATE TABLE table1 (a int, b int);"`;
782    $res = `"$mssql_bin" $mssql_cred -d $db -Q "INSERT INTO table1 (a, b) VALUES (1,1);"`;
783    $res = `"$mssql_bin" $mssql_cred -d $db -Q "SELECT 'OK' FROM table1;"`;
784
785    if ($res !~ /OK/) {
786        return "ERR\nCan't verify the SQLCMD result\n" .
787            "Please verify that MSSQL is accepting connection:\n" .
788            "$mssql_bin $mssql_cred -Q \"SELECT 1;\"\n";
789    }
790    return "OK\n";
791}
792
793# drop database
794sub cleanup_mssql_db
795{
796    my ($r) = shift;
797    my $ret = "ERR";
798    if ($r->url !~ m!^/cleanup_mssql_db\?db=([\w\d]+)$!) {
799        return "ERR\nIncorrect url: " . $r->url . "\n";
800    }
801    my $db = $1;
802
803    unless ($mssql_bin) {
804        return "ERR\nCan't find mssql bin\n";
805    }
806
807    my $res = `"$mssql_bin" $mssql_cred -Q "DROP DATABASE $db;"`;
808
809    return "OK\n";
810}
811
812# truncate the table that is in database
813sub truncate_mssql_table
814{
815    my ($r) = shift;
816    my $ret = "ERR";
817    if ($r->url !~ m!^/truncate_mssql_table\?db=([\w\d]+)$!) {
818        return "ERR\nIncorrect url: " . $r->url . "\n";
819    }
820    my $db = $1;
821
822    unless ($mssql_bin) {
823        return "ERR\nCan't find mssql bin\n";
824    }
825
826    my $res = `"$mssql_bin" $mssql_cred -d $db -Q "TRUNCATE TABLE table1;"`;
827    $res = `"$mssql_bin" $mssql_cred -d $db -Q "SELECT 'OK' FROM table1;"`;
828
829    if ($res =~ /OK/) {
830        return "ERR\nCan't truncate $db.table1\n";
831    }
832    return "OK\n";
833}
834
835# test that table1 contains some rows
836sub test_mssql_content
837{
838    my ($r) = shift;
839    my $ret = "ERR";
840    if ($r->url !~ m!^/test_mssql_content\?db=([\w\d]+)$!) {
841        return "ERR\nIncorrect url: " . $r->url . "\n";
842    }
843    my $db = $1;
844
845    unless ($mssql_bin) {
846        return "ERR\nCan't find mssql bin\n";
847    }
848
849    my $res = `"$mssql_bin" $mssql_cred -d $db -Q "SELECT 'OK' FROM table1;"`;
850
851    if ($res !~ /OK/) {
852        return "ERR\nNo content from $mssql_bin\n$res\n";
853    }
854    return "OK\n";
855}
856
857my $mssql_mdf;
858my $mdf_to_find;
859sub find_mdf
860{
861    if ($_ =~ /$mdf_to_find/i) {
862        $mssql_mdf = $File::Find::dir;
863    }
864}
865
866# put a mdf online
867sub online_mssql_db
868{
869    my ($r) = shift;
870    if ($r->url !~ m!^/online_mssql_db\?mdf=([\w\d]+);db=([\w\d]+)$!) {
871        return "ERR\nIncorrect url: " . $r->url . "\n";
872    }
873    my ($mdf, $db) = ($1, $2);
874    $mdf_to_find = "$mdf.mdf";
875
876    find(\&find_mdf, 'c:/program files/microsoft sql server/');
877    unless ($mssql_mdf) {
878        find(\&find_mssql, 'c:/program files (x86)/microsoft sql server/');
879    }
880    unless ($mssql_mdf) {
881        return "ERR\nCan't find $mdf.mdf in c:/program files\n";
882    }
883    $mssql_mdf =~ s:/:\\:g;
884
885    open(FP, ">c:/mssql.sql");
886    print FP "
887USE [master]
888GO
889CREATE DATABASE [$db] ON
890( FILENAME = N'$mssql_mdf\\$mdf.mdf' ),
891( FILENAME = N'$mssql_mdf\\${mdf}_log.LDF' )
892 FOR ATTACH
893GO
894USE [$db]
895GO
896SELECT 'OK' FROM table1
897GO
898";
899    close(FP);
900    my $res = `"$mssql_bin" $mssql_cred -i c:\\mssql.sql`;
901    #unlink("c:/mssql.sql");
902    if ($res !~ /OK/) {
903        return "ERR\nNo content from $mssql_bin\n";
904    }
905    return "OK\n";
906}
907
908# create a script c:/del.cmd to delete protected files with runscript
909sub remove_dir
910{
911    my ($r) = shift;
912    if ($r->url !~ m!^/remove_dir\?file=([\w\d:\/\.\-+*]+);dest=([\w\d\.:\/]+)$!) {
913        return "ERR\nIncorrect url: " . $r->url . "\n";
914    }
915    my ($file, $cmd) = ($1, $2);
916    $file =~ s:/:\\:g;
917
918    open(FP, ">$cmd") or return "ERR\nCan't open $file $!\n";
919    print FP "DEL /F /S /Q $file\n";
920    close(FP);
921    return "OK\n";
922}
923
924sub get_traces
925{
926    my ($file) = <"c:/program files/bareos/working/*.trace">;
927    if (!$file || ! -f $file) {
928        return "ERR\n$!\n";
929    }
930    return $file;
931}
932
933sub truncate_traces
934{
935    my $f = get_traces();
936    unlink($f) or return "ERR\n$!\n";
937    return "OK\n";
938}
939
940# When adding an action, fill this hash with the right function
941my %action_list = (
942    nop     => sub { return "OK\n"; },
943    stop    => \&stop_fd,
944    start   => \&start_fd,
945    install => \&install_fd,
946    compare => \&compare,
947    init_attrib_test => \&init_attrib_test,
948    init_weird_runscript_test => \&init_weird_runscript_test,
949    set_director_name => \&set_director_name,
950    cleandir => \&cleandir,
951    add_registry_key => \&add_registry_key,
952    del_registry_key => \&del_registry_key,
953    get_registry_key => \&get_registry_key,
954    quit => sub {  exit 0; },
955    reboot => \&reboot,
956    set_service => \&set_service,
957    get_service => \&get_service,
958    set_auto_logon => \&set_auto_logon,
959    remove_dir => \&remove_dir,
960    reload => \&reload,
961    create_schedtask => \&create_schedtask,
962    del_schedtask => \&del_schedtask,
963    check_schedtask => \&check_schedtask,
964    get_traces => \&get_traces,
965    truncate_traces => \&truncate_traces,
966
967    check_mssql => \&check_mssql,
968    setup_mssql_db => \&setup_mssql_db,
969    cleanup_mssql_db => \&cleanup_mssql_db,
970    truncate_mssql_table => \&truncate_mssql_table,
971    test_mssql_content => \&test_mssql_content,
972    online_mssql_db => \&online_mssql_db,
973    );
974
975my $reload=0;
976sub reload
977{
978    $reload=1;
979    return "OK\n";
980}
981
982# handle client request
983sub handle_client
984{
985    my ($c, $ip) = @_ ;
986    my $action;
987    my $r = $c->get_request ;
988
989    if (!$r) {
990        $c->send_error(RC_FORBIDDEN) ;
991        return;
992    }
993    if ($r->url->path !~ m!^/(\w+)!) {
994        $c->send_error(RC_NOT_FOUND) ;
995        return;
996    }
997    $action = $1;
998
999    if (($r->method eq 'GET')
1000        and $action_list{$action})
1001    {
1002        print "Exec $action:\n";
1003
1004        my $ret = $action_list{$action}($r);
1005        if ($action eq 'get_traces' && $ret !~ /ERR/) {
1006            print "Sending $ret\n";
1007            $c->send_file_response($ret);
1008
1009        } else {
1010            my $h = HTTP::Headers->new('Content-Type' => 'text/plain') ;
1011            my $r = HTTP::Response->new(HTTP::Status::RC_OK,
1012                                        'OK', $h, $ret) ;
1013            print $ret;
1014            $c->send_response($r) ;
1015        }
1016    } else {
1017        print "$action not found, probably a version problem\n";
1018        $c->send_error(RC_NOT_FOUND) ;
1019    }
1020
1021    $c->close;
1022}
1023
1024print "Starting regress-win32.pl daemon...\n";
1025my $d = HTTP::Daemon->new ( LocalPort =>  8091,
1026                            ReuseAddr => 1)
1027    || die "Error: Can't bind $!" ;
1028
1029my $olddir = Cwd::cwd();
1030while (1) {
1031    my $c = $d->accept ;
1032    my $ip = $c->peerhost;
1033    if (!$ip) {
1034        $c->send_error(RC_FORBIDDEN) ;
1035    } elsif ($src_ip && $ip ne $src_ip) {
1036        $c->send_error(RC_FORBIDDEN) ;
1037    } elsif ($c) {
1038        handle_client($c, $ip) ;
1039    } else {
1040        $c->send_error(RC_FORBIDDEN) ;
1041    }
1042    close($c) ;
1043    undef $c;
1044    chdir($olddir);
1045
1046    # When we have the reload command, just close the http daemon
1047    # and exec ourself
1048    if ($reload) {
1049        $d->close();
1050        undef $d;
1051
1052        exec("$^X $0");
1053    }
1054}
1055