1 # Copyright (c) 2010-2012 Zmanda, Inc. All Rights Reserved.
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License version 2 as published
5 # by the Free Software Foundation.
7 # This program is distributed in the hope that it will be useful, but
8 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 # You should have received a copy of the GNU General Public License along
13 # with this program; if not, write to the Free Software Foundation, Inc.,
14 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 # Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19 use Test::More tests => 30;
25 use lib "@amperldir@";
26 use Amanda::ClientService;
27 use Amanda::Constants;
30 use Amanda::Config qw( :init );
34 config_init(0, undef);
35 Amanda::Debug::dbopen('installcheck');
37 # test connect_streams
39 # that these tests assume that DATA_FD_OFFSET and DATA_FD_COUNT have these values
40 is($Amanda::Constants::DATA_FD_OFFSET, 50, "DATA_FD_OFFSET is what I think it is");
41 is($Amanda::Constants::DATA_FD_COUNT, 3, "DATA_FD_COUNT is what I think it is");
43 sub test_connect_streams {
44 my ($args, $exp_line, $exp_closed_fds, $exp_streams, $msg) = @_;
46 my $cs = Amanda::ClientService->new();
47 $cs->{'_dont_use_real_fds'} = 1;
48 $cs->{'_argv'} = [ 'amandad' ];
49 my $got_line = $cs->connect_streams(@$args);
51 is($got_line, $exp_line, "$msg (CONNECT line)");
53 is_deeply([ sort @{$cs->{'_would_have_closed_fds'}} ],
54 [ sort @$exp_closed_fds ],
57 # get the named streams and their fd's
60 my $name = shift @$args;
61 my $dirs = shift @$args;
62 $streams{$name} = [ $cs->rfd($name), $cs->wfd($name) ];
65 is_deeply(\%streams, $exp_streams, "$msg (streams)");
72 { 'DATA' => [ 51, 50 ] },
73 "simple read/write DATA stream");
78 [ 50, 52, 53, 54, 55 ],
79 { 'DATA' => [ 51, -1 ] },
85 [ 51, 52, 53, 54, 55 ],
86 { 'DATA' => [ -1, 50 ] },
90 [ 'DATA' => 'rw', 'RD' => 'r', 'WR' => 'w' ],
91 'CONNECT DATA 50 RD 51 WR 52',
93 { 'DATA' => [ 51, 50 ],
99 # test from_inetd and friends
103 $cs = Amanda::ClientService->new();
105 ok($cs->from_inetd, "no argv[0] interpreted as a run from inetd");
107 $cs = Amanda::ClientService->new();
108 $cs->{'_argv'} = [ 'installcheck' ];
109 ok($cs->from_inetd, "argv[0] = 'installcheck' also interpreted as a run from inetd");
111 $cs = Amanda::ClientService->new();
112 $cs->{'_argv'} = [ 'amandad' ];
113 ok(!$cs->from_inetd, "argv[0] = 'amandad' interpreted as a run from amandad");
115 $cs = Amanda::ClientService->new();
116 $cs->{'_argv'} = [ 'amandad', 'bsdgre' ];
117 is($cs->amandad_auth, "bsdgre",
118 "argv[1] = 'bsdgre' interpreted as auth");
120 $cs = Amanda::ClientService->new();
121 $cs->{'_argv'} = [ 'amandad' ];
122 is($cs->amandad_auth, undef,
123 "amandad_auth interpreted as undef if missing");
126 # test add_connection and half-close operations
127 sub test_connections {
128 my ($finished_cb) = @_;
131 my $cs = Amanda::ClientService->new();
132 $cs->{'_argv'} = [ ];
134 my $steps = define_steps
135 cb_ref => \$finished_cb;
138 $port = $cs->connection_listen('FOO', 0);
140 $steps->{'fork'}->();
144 # fork off a child to connect to and write to that port
146 socket(my $foo, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
147 or die "error creating connect socket: $!";
148 connect($foo, sockaddr_in($port, inet_aton("127.0.0.1")))
149 or die "error connecting: $!";
151 print $foo "GOT[$info]";
155 $steps->{'accept'}->();
160 $cs->connection_accept('FOO', 90, $steps->{'accept_finished'});
163 step accept_finished => sub {
164 # write a message to the fd and read back the result; this is
166 my $msg = "HELLO WORLD";
167 Amanda::Util::full_write($cs->wfd('FOO'), $msg, length($msg))
168 or die "full write: $!";
169 $cs->close('FOO', 'w');
170 is($cs->wfd('FOO'), -1,
171 "FOO is closed for writing");
173 my $input = Amanda::Util::full_read($cs->rfd('FOO'), 1024);
174 $cs->close('FOO', 'r');
175 is_deeply([ keys %{$cs->{'_streams'}} ], [ 'main' ],
176 "FOO stream is deleted when completely closed");
178 is($input, "GOT[HELLO WORLD]",
179 "both directions of the FOO stream work");
184 test_connections(\&Amanda::MainLoop::quit);
185 Amanda::MainLoop::run();
189 my $cs = Amanda::ClientService->new();
190 is($cs->rfd('main'), 0,
191 "main rfd is stdin");
192 is($cs->wfd('main'), 1,
193 "main wfd is stdout");
194 is($cs->wfd('none'), -1,
195 "wfd returns -1 for invalid stream");
196 is($cs->rfd('none'), -1,
197 "rfd returns -1 for invalid stream");
200 # check check_bsd_security
202 # note that we can't completely test this, because BSD security entails checking
203 # DNS and privileged ports, neither of which are controllable from the installcheck
204 # environment. However, we can at least call the method.
206 my $cs = Amanda::ClientService->new();
207 $cs->{'_argv'} = [ 'installcheck' ]; # basically neuters check_bsd_security
209 ok(!$cs->check_bsd_security('main', "USER bart"),
210 "check_bsd_security returns undef");
215 my $cs = Amanda::ClientService->new();
218 # is_deeply doesn't like objects very much
223 return $x unless defined $x->{'features'};
224 $x->{'features'} = "featureset";
229 OPTIONS auth=passport;features=f0039;
232 is_deeply(strip_features($cs->parse_req($req_str)), {
233 lines => [ 'OPTIONS auth=passport;features=f0039;', 'FOO' ],
239 features => "featureset",
240 }, "parse_req parses a request properly");
243 OPTIONS auth=bsd;no-features;yes=no;
245 is_deeply(strip_features($cs->parse_req($req_str)), {
246 lines => [ 'OPTIONS auth=bsd;no-features;yes=no;' ],
254 }, "parse_req parses a request with boolean options");
260 is_deeply(strip_features($cs->parse_req($req_str)), {
261 lines => [ 'OPTIONS turn=left;', 'OPTIONS turn=right;' ],
265 errors => [ 'got multiple OPTIONS lines' ],
267 }, "parse_req detects multiple OPTIONS lines as an error");