1 # Copyright (c) 2010-2012 Zmanda, Inc. All Rights Reserved.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful, but
9 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 # You should have received a copy of the GNU General Public License along
14 # with this program; if not, write to the Free Software Foundation, Inc.,
15 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
18 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
20 use Test::More tests => 30;
26 use lib "@amperldir@";
27 use Amanda::ClientService;
28 use Amanda::Constants;
31 use Amanda::Config qw( :init );
35 config_init(0, undef);
36 Amanda::Debug::dbopen('installcheck');
38 # test connect_streams
40 # that these tests assume that DATA_FD_OFFSET and DATA_FD_COUNT have these values
41 is($Amanda::Constants::DATA_FD_OFFSET, 50, "DATA_FD_OFFSET is what I think it is");
42 is($Amanda::Constants::DATA_FD_COUNT, 3, "DATA_FD_COUNT is what I think it is");
44 sub test_connect_streams {
45 my ($args, $exp_line, $exp_closed_fds, $exp_streams, $msg) = @_;
47 my $cs = Amanda::ClientService->new();
48 $cs->{'_dont_use_real_fds'} = 1;
49 $cs->{'_argv'} = [ 'amandad' ];
50 my $got_line = $cs->connect_streams(@$args);
52 is($got_line, $exp_line, "$msg (CONNECT line)");
54 is_deeply([ sort @{$cs->{'_would_have_closed_fds'}} ],
55 [ sort @$exp_closed_fds ],
58 # get the named streams and their fd's
61 my $name = shift @$args;
62 my $dirs = shift @$args;
63 $streams{$name} = [ $cs->rfd($name), $cs->wfd($name) ];
66 is_deeply(\%streams, $exp_streams, "$msg (streams)");
73 { 'DATA' => [ 51, 50 ] },
74 "simple read/write DATA stream");
79 [ 50, 52, 53, 54, 55 ],
80 { 'DATA' => [ 51, -1 ] },
86 [ 51, 52, 53, 54, 55 ],
87 { 'DATA' => [ -1, 50 ] },
91 [ 'DATA' => 'rw', 'RD' => 'r', 'WR' => 'w' ],
92 'CONNECT DATA 50 RD 51 WR 52',
94 { 'DATA' => [ 51, 50 ],
100 # test from_inetd and friends
104 $cs = Amanda::ClientService->new();
106 ok($cs->from_inetd, "no argv[0] interpreted as a run from inetd");
108 $cs = Amanda::ClientService->new();
109 $cs->{'_argv'} = [ 'installcheck' ];
110 ok($cs->from_inetd, "argv[0] = 'installcheck' also interpreted as a run from inetd");
112 $cs = Amanda::ClientService->new();
113 $cs->{'_argv'} = [ 'amandad' ];
114 ok(!$cs->from_inetd, "argv[0] = 'amandad' interpreted as a run from amandad");
116 $cs = Amanda::ClientService->new();
117 $cs->{'_argv'} = [ 'amandad', 'bsdgre' ];
118 is($cs->amandad_auth, "bsdgre",
119 "argv[1] = 'bsdgre' interpreted as auth");
121 $cs = Amanda::ClientService->new();
122 $cs->{'_argv'} = [ 'amandad' ];
123 is($cs->amandad_auth, undef,
124 "amandad_auth interpreted as undef if missing");
127 # test add_connection and half-close operations
128 sub test_connections {
129 my ($finished_cb) = @_;
132 my $cs = Amanda::ClientService->new();
133 $cs->{'_argv'} = [ ];
135 my $steps = define_steps
136 cb_ref => \$finished_cb;
139 $port = $cs->connection_listen('FOO', 0);
141 $steps->{'fork'}->();
145 # fork off a child to connect to and write to that port
147 socket(my $foo, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
148 or die "error creating connect socket: $!";
149 connect($foo, sockaddr_in($port, inet_aton("127.0.0.1")))
150 or die "error connecting: $!";
152 print $foo "GOT[$info]";
156 $steps->{'accept'}->();
161 $cs->connection_accept('FOO', 90, $steps->{'accept_finished'});
164 step accept_finished => sub {
165 # write a message to the fd and read back the result; this is
167 my $msg = "HELLO WORLD";
168 Amanda::Util::full_write($cs->wfd('FOO'), $msg, length($msg))
169 or die "full write: $!";
170 $cs->close('FOO', 'w');
171 is($cs->wfd('FOO'), -1,
172 "FOO is closed for writing");
174 my $input = Amanda::Util::full_read($cs->rfd('FOO'), 1024);
175 $cs->close('FOO', 'r');
176 is_deeply([ keys %{$cs->{'_streams'}} ], [ 'main' ],
177 "FOO stream is deleted when completely closed");
179 is($input, "GOT[HELLO WORLD]",
180 "both directions of the FOO stream work");
185 test_connections(\&Amanda::MainLoop::quit);
186 Amanda::MainLoop::run();
190 my $cs = Amanda::ClientService->new();
191 is($cs->rfd('main'), 0,
192 "main rfd is stdin");
193 is($cs->wfd('main'), 1,
194 "main wfd is stdout");
195 is($cs->wfd('none'), -1,
196 "wfd returns -1 for invalid stream");
197 is($cs->rfd('none'), -1,
198 "rfd returns -1 for invalid stream");
201 # check check_bsd_security
203 # note that we can't completely test this, because BSD security entails checking
204 # DNS and privileged ports, neither of which are controllable from the installcheck
205 # environment. However, we can at least call the method.
207 my $cs = Amanda::ClientService->new();
208 $cs->{'_argv'} = [ 'installcheck' ]; # basically neuters check_bsd_security
210 ok(!$cs->check_bsd_security('main', "USER bart"),
211 "check_bsd_security returns undef");
216 my $cs = Amanda::ClientService->new();
219 # is_deeply doesn't like objects very much
224 return $x unless defined $x->{'features'};
225 $x->{'features'} = "featureset";
230 OPTIONS auth=passport;features=f0039;
233 is_deeply(strip_features($cs->parse_req($req_str)), {
234 lines => [ 'OPTIONS auth=passport;features=f0039;', 'FOO' ],
240 features => "featureset",
241 }, "parse_req parses a request properly");
244 OPTIONS auth=bsd;no-features;yes=no;
246 is_deeply(strip_features($cs->parse_req($req_str)), {
247 lines => [ 'OPTIONS auth=bsd;no-features;yes=no;' ],
255 }, "parse_req parses a request with boolean options");
261 is_deeply(strip_features($cs->parse_req($req_str)), {
262 lines => [ 'OPTIONS turn=left;', 'OPTIONS turn=right;' ],
266 errors => [ 'got multiple OPTIONS lines' ],
268 }, "parse_req detects multiple OPTIONS lines as an error");