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