File Coverage

blib/lib/CWB/CQI/Server.pm
Criterion Covered Total %
statement 20 43 46.5
branch 1 14 7.1
condition 0 6 0.0
subroutine 8 9 88.8
pod 2 2 100.0
total 31 74 41.8


line stmt bran cond sub pod time code
1             package CWB::CQI::Server;
2             # -*-cperl-*-
3             $VERSION = 'v3.5.0';
4              
5 3     3   2856 use strict;
  3         6  
  3         85  
6 3     3   14 use warnings;
  3         5  
  3         71  
7              
8 3     3   13 use CWB::CQI;
  3         5  
  3         40  
9 3     3   13 use Carp;
  3         4  
  3         400  
10 3     3   17 use FileHandle;
  3         4  
  3         15  
11              
12             # export CQi server startup functions
13 3     3   895 use base qw(Exporter);
  3         6  
  3         1610  
14             our @EXPORT = qw(cqi_server cqi_server_available);
15              
16             =head1 NAME
17              
18             CWB::CQI::Server - launch private CQPserver on local machine
19              
20              
21             =head1 SYNOPSIS
22              
23             use CWB::CQI::Server;
24             use CWB::CQI::Client;
25              
26             if (cqi_server_available()) {
27             my @details = cqi_server();
28             cqi_connect(@details);
29             ...
30             }
31              
32             =head1 DESCRIPTION
33              
34             The B module can be used to launch a private CQPserver
35             on the local machine, which B can then connect to.
36              
37             Note that this is only possible if a suitable version of the IMS Open Corpus Workbench
38             and the B Perl module have been installed. Availability must therefore be
39             checked with the B function before calling B.
40              
41              
42             =head1 FUNCTIONS
43              
44             =over 4
45              
46             =cut
47              
48             our $CQPserver = undef;
49 3     3   1784 if (eval 'use CWB 3.005_000; 1') {
  0         0  
  0         0  
50             $CQPserver = $CWB::CQPserver
51             if -x $CWB::CQPserver;
52             }
53              
54             =item I<$ok> = B();
55              
56             Returns a B value if a suitable CQPserver binary is installed on the local machine and
57             can be started with the B function.
58              
59             =cut
60              
61             sub cqi_server_available {
62 2 50   2 1 133 return (defined $CQPserver) ? 1 : 0;
63             }
64              
65             =item (I<$user>, I<$passwd>, I<$host>, I<$port>) = B();
66              
67             =item I<@details> = B(I<$flags>);
68              
69             C searches for a free port on the local machine, then
70             launches a single-user B process and returns the connection details
71             required by the B function from B (in the appropriate order).
72             The simplest way to establish a connection with a private, local CQPserver is
73              
74             cqi_connect(cqi_server());
75              
76             Be sure to check with B whether the required C
77             command-line program is available first.
78              
79             An optional argument to B is appended to the C command-line flags
80             and can be used to specify further start-up options (e.g. to read a macro definition file).
81             Keep in mind that arguments containing shell metacharacters need to be quoted appropriately.
82              
83             B Since B runs as a separate process in the background, it is
84             important to establish a connection B. If the user's
85             program aborts before B is called and contacts the new CQPserver,
86             this process will accept further connections from other users (on the local machine),
87             which might compromise confidential data.
88              
89             =cut
90              
91             #
92             #
93             # Start CQPserver in the background and return (host, port, user, passwd) list for cqi_connect()
94             # An init file is generated which adds a random user/passwd to the server's user list,
95             # so you can connect to the newly created server with the user/passwd combination returned
96             # by cqi_server() only. (NB uses '-I' at the moment, so .cqprc won't be read)
97             #
98             #
99             sub cqi_server {
100 0     0 1   my $user = "cqi_server_$$";
101 0           my $passwd = "pass" . int rand(42000);
102 0           my $flags = "-1 -L -q "; # single-client server, localhost only (for security reasons)
103 0 0         $flags .= "@_" if @_; # append optional command-line flags
104              
105 0 0         croak "CQPserver is not installed on this machine"
106             unless cqi_server_available();
107              
108             # generate temporary user list file for CQPserver
109 0           my $passfile = "/tmp/CQI::Server.$$";
110 0           my $fh = new FileHandle "> $passfile";
111 0 0         croak "Can't create temporary user list file. Aborting."
112             unless defined $fh;
113 0           print $fh "user $user \"$passwd\";\n";
114 0           $fh->close;
115 0           chmod 0600, $passfile; # so no one can spy on us
116              
117             # scan for free port (using rand() so two servers invoked at the same time won't collide)
118 0           my $port = 10000 + int rand(2000);
119             my %in_use =
120 0           map {$_ => 1}
121 0 0         map {(/\*\.([0-9]+)/) ? $1 : 0}
  0            
122             `netstat -a -n | grep LISTEN`;
123 0   0       while ($port < 60000 and $in_use{$port}) {
124 0           $port += rand(20); # jump randomly to avoid collisions
125             }
126 0 0         croak "Can't find free port for CQPserver. Abort."
127             unless $port < 60000;
128              
129             # now start CQPserver on this port
130 0 0 0       croak "CQPserver failed to launch: $!\n"
131             if system "$CQPserver $flags -P $port -I $passfile >/dev/null 2>&1" or $? != 0;
132              
133             # delete user list file
134 0           unlink $passfile;
135              
136             # return connection information suitable for cqi_connect()
137 0           return $user, $passwd, "localhost", $port;
138             }
139              
140              
141             1;
142              
143             __END__