blob: 1f7a41718ca6afdf2e897751e78a12ce2f8d72c8 [file] [log] [blame]
apw3812c032006-12-07 21:01:14 +00001#!/usr/bin/perl
2#
3# conmux-registry -- console name registry server
4#
5# Main registry server. This server holds host/port assignments for
6# conmux daemons registering with it. This allows users to specify
7# human names for their consoles and find the relevant conmux daemon.
8#
9# (C) Copyright IBM Corp. 2004, 2005, 2006
10# Author: Andy Whitcroft <andyw@uk.ibm.com>
11#
12# The Console Multiplexor is released under the GNU Public License V2
13#
14use strict;
15
mblighf5180482008-02-20 16:09:08 +000016use FindBin;
apw3812c032006-12-07 21:01:14 +000017use Symbol qw(gensym);
18
19use IO::Socket;
20use IO::Multiplex;
21use URI::Escape;
22
23# Find our internal libraries.
mblighf5180482008-02-20 16:09:08 +000024use lib $FindBin::Bin;
apw3812c032006-12-07 21:01:14 +000025use Conmux;
26
27our $P = 'conmux-registry';
28our $debug = 0;
29
30#
31# LISTENER SOCKET: creates an intenet listener for new clients and
32# connects them to the junction provided.
33#
34package ListenerSocket;
35
36sub new {
37 my ($class, $mux, $port, $registry) = @_;
38 my $self = bless { 'mux' => $mux, 'registry' => $registry }, $class;
39
40 print "ListenerSocket::new [$self] mux<$mux> port<$port> " .
41 "registry<$registry>\n" if ($main::debug);
42
43 $self->initialise($mux, $port, $registry);
44
45 $self;
46}
47
48sub initialise {
49 my ($self, $mux, $port, $registry) = @_;
50 my ($sock);
51
52 print "ListenerSocket::initialise [$self] mux<$mux> port<$port> " .
53 "registry<$registry>\n" if ($main::debug);
54
55 # Create a listening socket and add it to the multiplexor.
56 my $sock = new IO::Socket::INET(Proto => 'tcp',
57 LocalPort => $port,
58 Listen => 4,
59 ReuseAddr => 1)
60 or die "socket: $@";
61
62 print " adding $self $sock\n" if ($main::debug);
63 $mux->listen($sock);
64 $mux->set_callback_object($self, $sock);
65 $self->{'listener'} = $sock;
66}
67
68# Handle new connections by instantiating a new client class.
69sub mux_connection {
70 my ($self, $mux, $fh) = @_;
71 my ($client);
72
73 print "ListenerSocket::mux_connection [$self] mux<$mux> fh<$fh>\n"
74 if ($main::debug);
75
76 # Make a new client connection.
77 $client = Client->new($mux, $fh, $self->{'registry'});
78 print " new connection $self $client\n" if ($main::debug);
79}
80
81sub DESTROY {
82 my ($self) = @_;
83
84 print "ListenerSocket::DESTROY [$self]\n" if ($main::debug);
85
86 close($self->{'listener'});
87}
88
89#
90# CLIENT: general client object, represents a remote client channel
91#
92package Client;
93
94sub new {
95 my ($class, $mux, $fh, $registry) = @_;
96 my $self = bless { 'mux' => $mux,
97 'fh' => $fh,
98 'registry' => $registry }, $class;
99
100 print "Client::new [$self] mux<$mux> fh<$fh> registry<$registry>\n"
101 if ($main::debug);
102
103 $self->initialise($mux, $fh, $registry);
104
105 $self;
106}
107
108sub initialise {
109 my ($self, $mux, $fh, $registry) = @_;
110
111 print "Client::initialise [$self] mux<$mux> fh<$fh> " .
112 "registry<$registry>\n" if ($main::debug);
113
114 $mux->set_callback_object($self, $fh);
115}
116
117sub mux_input {
118 my ($self, $mux, $fh, $input) = @_;
119
120 print "Client::mux_input [$self] mux<$mux> fh<$fh> input<$$input>\n"
121 if ($main::debug);
122
123 while ($$input =~ s/^(.*?)\n//) {
124 my ($cmd, $args) = split(' ', $1, 2);
125 my (%args) = Conmux::decodeArgs($args);
126
127 my $reply = {
128 'status' => 'ENOSYS',
129 };
130
131 # Fill in the common results.
132 $reply->{'title'} = 'registry';
133
134 # Handle this command.
135 if ($cmd eq "LOOKUP") {
136 my $r = $self->{'registry'}->lookup($args{'service'});
137
138 if (defined $r) {
139 $reply->{'result'} = $r;
140 $reply->{'status'} = 'OK';
141
142 } else {
143 $reply->{'status'} = 'ENOENT entry not found';
144 }
145
146 } elsif ($cmd eq "ADD") {
147 $self->{'registry'}->add($args{'service'},
148 $args{'location'});
149 $reply->{'status'} = 'OK';
150
151 } elsif ($cmd eq "LIST") {
152 $reply->{'result'} = $self->{'registry'}->list();
153 $reply->{'status'} = 'OK';
154 }
155
156 $fh->write(Conmux::encodeArgs($reply) . "\n");
157 }
158}
159sub mux_eof {
160 my ($self, $mux, $fh, $input) = @_;
161
162 print "Client::mux_eof [$self] mux<$mux> fh<$fh> input<$input>\n"
163 if ($main::debug);
164
165 # Handle any pending input, then remove myself.
166 $self->mux_input($mux, $fh, $input);
167
168 # Tell the multiplexor we no longer are using this channel.
169 $mux->shutdown($fh, 1);
170}
171sub mux_close {
172 my ($self, $mux, $fn) = @_;
173
174 print "Client::close [$self]\n" if ($main::debug);
175}
176
177sub DESTROY {
178 my ($self) = @_;
179
180 print "Client::DESTROY [$self]\n" if ($main::debug);
181}
182
183#
184# REGISTRY: registry elements.
185#
186package Registry;
187
188sub new {
189 my ($class, $store) = @_;
190 my $self = bless { 'store' => $store }, $class;
191
192 my ($key, $val);
193
194 print "Registry::new [$self] store<$store>\n" if ($main::debug);
195
196 # Open the store and populate the keys.
197 open(S, '<', $store) || die "Registry::new: $store: open failed - $!\n";
198 while (<S>) {
199 chomp;
200
201 ($key, $val) = split(' ', $_);
202
203 $self->{'key'}->{$key} = $val;
204 }
205 close(S);
206
207 $self;
208}
209
210sub add {
211 my ($self, $what, $where) = @_;
212
213 my ($key);
214
215 print "Registry::add [$self] what<$what> where<$where>\n"
216 if ($main::debug);
217
218 $self->{'key'}->{$what} = $where;
219
220 print "$what at $where\n";
221
222 if (open(S, '>', $self->{'store'} . '.new')) {
223 foreach $key (sort keys %{$self->{'key'}}) {
224 print S "$key $self->{'key'}->{$key}\n";
225 }
226 close(S);
227 rename $self->{'store'} . '.new', $self->{'store'};
228
229 } else {
230 warn "$P: $self->{'store'}.new: open failed - $!";
231 }
232}
233
234sub lookup {
235 my ($self, $what) = @_;
236
237 print "Registry::lookup [$self] what<$what>\n" if ($main::debug);
238
239 $self->{'key'}->{$what};
240}
241
242sub list {
243 my ($self) = @_;
244 my ($r, $key);
245
246 print "Registry::list [$self]\n" if ($main::debug);
247
248 foreach $key (sort keys %{$self->{'key'}}) {
249 $r .= "$key $self->{'key'}->{$key}\n";
250 }
251
252 $r;
253}
254
255#
256# MAIN: makes the IO multiplexor, listener and registry and stitches
257# them all together.
258#
259package main;
260
261# Usage checks.
262if ($#ARGV != 1) {
263 print STDERR "Usage: $P <local port> <store>\n";
264 exit 1
265}
266my ($lport, $store) = @ARGV;
267
268# Make a new multiplexer.
269my $mux = new IO::Multiplex;
270
271# Make the registry object.
272my $registry = Registry->new($store);
273
274# Create the client listener socket.
275my $listener = ListenerSocket->new($mux, $lport, $registry);
276
277# Hand over to the multiplexor.
278$mux->loop;