#!/usr/bin/perl -w
# test_multiserver

use strict;
use IO::Socket;
use IO::Select;

$| = 1;

use POSIX ":sys_wait_h";

$SIG{CHLD} = sub { while (waitpid(-1,WNOHANG) > 0) {} };

my ($MSG_EOL, $kidpid, $listen_handle, $handle, $line);

my $listeners = 20;

if (($ARGV[0]||'') eq '-crlf') {
	shift;
	$MSG_EOL = "\015\012";
} else {
	$MSG_EOL = "\012";
}

if (@ARGV == 1 && $ARGV[0] =~ /^\d{1,5}$/) {
	my $port = shift;
	# serve tcp on the specified host and port
	$listen_handle = IO::Socket::INET->new(Proto     => "tcp",
					       LocalPort => $port,
					       Listen    => $listeners,
					       Reuse     => 1)
		or die "can't serve on TCP port $port: $!";
	print STDERR "[Serving on TCP port $port]\n";
} elsif (@ARGV == 1) {
	my $socket = shift;
			# serve unix domain on the specified named socket
	unlink $socket;
	$listen_handle = IO::Socket::UNIX->new( Local  => $socket,
						Listen => $listeners )
		or die "cannot serve on UNIX socket $socket: $!";
	chmod 0777, $socket;
	print STDERR "[Serving on UNIX socket $socket]\n";
} else {
	print "usage: $0 [-crlf] (UNIX-socket-name|TCP-port)\n";
	exit 1;
}

my $select = IO::Select->new;
$select->add(\*STDIN);
$select->add($listen_handle);

my %handle_by_fileno = ();

# this will accept clients, and fork to read from them, writing to STDOUT
print STDERR "[waiting for connections]\n";

while (1) {
	my @handles = $select->can_read();

	for my $handle (@handles) {
		if ($handle == \*STDIN) {
			my $line = <STDIN>; # please, don't block!!!
			exit 0 unless defined $line;
			my ($fileno, $command, $send) = $line =~ /(\d+)([->: ])(.*)/; # this strips newline also
			my $handle = $handle_by_fileno{$fileno};
			unless ($handle) { print "[$fileno no such connection]\n" }
			else {
				print $handle "$send$MSG_EOL" if length $send > 0 || $command ne '-';

				if ($command eq '-') { # close it
					shutdown $handle_by_fileno{$fileno}, 2;
					delete $handle_by_fileno{$fileno};
				}
			}
		} elsif ($handle == $listen_handle) {
			my $handle = $listen_handle->accept()
				or die "accept failed";

			my $fileno = $handle->fileno;
			print "[$fileno connected]\n";
			
			$handle->autoflush(1); # so output gets there right away
			
			$handle_by_fileno{$fileno} = $handle; # so we don't close it
			
			die "can't fork: $!" unless defined($kidpid = fork());
		
			if ($kidpid == 0) {
				undef %handle_by_fileno;
				# copy the socket to standard output
				while (defined ($line = <$handle>)) {
					print "$fileno<$line";
				}
				print "[$fileno closed]\n";
				exit 0;
			}
		}
	}
}
