fp@0: #!/usr/bin/perl -w hm@28: #------------------------------------------------------------ hm@28: # hm@28: # (C) Copyright hm@28: # Diese Software ist geistiges Eigentum der hm@28: # Ingenieurgemeinschaft IgH. Sie darf von hm@28: # Toyota Motorsport GmbH hm@28: # beliebig kopiert und veraendert werden. hm@28: # Die Weitergabe an Dritte ist untersagt. hm@28: # Dieser Urhebrrechtshinweis muss erhalten hm@28: # bleiben. hm@28: # hm@28: # Ingenieurgemeinschaft IgH hm@28: # Heinz-Baecker-Strasse 34 hm@28: # D-45356 Essen hm@28: # Tel.: +49-201/61 99 31 hm@28: # Fax.: +49-201/61 98 36 hm@28: # WWW: http://www.igh-essen.com hm@28: # Email: msr@igh-essen.com hm@28: # hm@28: #------------------------------------------------------------ hm@28: # fp@0: # Multithreaded Server fp@0: # according to the example from "Programming Perl" hm@28: # this code is improved according to the example from hm@28: # perldoc perlipc, so now safely being usable under Perl 5.8 hm@28: # (see note (*)) fp@0: # fp@0: # works with read/write on a device-file fp@0: # fp@0: # $Revision: 1.1 $ hm@28: # $Date: 2004/10/01 16:00:42 $ fp@0: # $RCSfile: msrserv.pl,v $ fp@0: # hm@28: #------------------------------------------------------------ fp@0: fp@0: require 5.002; fp@0: use strict; hm@28: BEGIN { $ENV{PATH} = '/opt/msr/bin:/usr/bin:/bin' } fp@0: use Socket; fp@0: use Carp; fp@0: use FileHandle; fp@0: use Getopt::Std; fp@0: fp@0: use Sys::Syslog qw(:DEFAULT setlogsock); fp@0: fp@0: use vars qw ( fp@0: $self $pid $dolog $port $dev %opts $selfbase fp@0: $len $offset $stream $written $read $log $blksize hm@28: $instdir fp@0: $authfile %authhosts fp@0: ); fp@0: fp@0: fp@0: # Do logging to local syslogd by unix-domain socket instead of inetd fp@0: setlogsock("unix"); fp@0: fp@0: # Prototypes and some little Tools fp@0: sub spawn; fp@0: sub logmsg { hm@28: my ($level, $debug, @text) = @_; hm@28: syslog("daemon|$level", @text) if $debug > $dolog; fp@0: # print STDERR "daemon|$level", @text, "\n" if $dolog; fp@0: } fp@0: sub out { fp@0: my $waitpid = wait; hm@28: logmsg("notice", 2, "$waitpid exited"); fp@0: unlink "$selfbase.pid"; fp@0: exit 0; fp@0: } fp@0: fp@0: sub help { fp@0: print "\n usage: $0 [-l og] [-h elp] [-p port] [-d device]\n"; fp@0: exit; fp@0: } fp@0: fp@0: # Process Options fp@0: %opts = ( fp@0: "l" => 1, fp@0: "h" => 0, fp@0: "p" => 2345, fp@0: "d" => "/dev/msr" fp@0: ); fp@0: fp@0: getopts("lhp:d:", \%opts); fp@0: fp@0: help if $opts{"h"}; fp@0: fp@0: ( $self = $0 ) =~ s+.*/++ ; fp@0: ( $selfbase = $self ) =~ s/\..*//; fp@0: $log = "$selfbase.log"; fp@0: $dolog = $opts{"l"}; fp@0: $port = $opts{"p"}; fp@0: $dev = $opts{"d"}; fp@0: $blksize = 1024; # try to write as much bytes hm@28: $instdir = "/opt/msr"; hm@28: $authfile = "$instdir/etc/hosts.auth"; fp@0: fp@0: # Start logging fp@0: openlog($self, 'pid'); fp@0: fp@0: # Flush Output, dont buffer fp@0: $| = 1; fp@0: fp@0: # first fork and run in background fp@0: if ($pid = fork) { fp@0: # open LOG, ">$log" if $dolog; fp@0: # close LOG; hm@28: logmsg("notice", 2, "forked process: $pid\n"); fp@0: exit 0; fp@0: } fp@0: fp@0: # Server tells about startup success hm@28: open (PID, ">/$instdir/var/run/$selfbase.pid"); fp@0: print PID "$$\n"; fp@0: close PID; fp@0: fp@0: # Cleanup on exit (due to kill -TERM signal) fp@0: $SIG{TERM} = \&out; fp@0: fp@0: # We use streams fp@0: my $proto = getprotobyname('tcp'); fp@0: fp@0: # Open Server socket fp@0: socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; fp@0: setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) fp@0: or die "setsocketopt: $!"; fp@0: bind (Server, sockaddr_in($port, INADDR_ANY)) fp@0: or die "bind: $!"; fp@0: listen (Server, SOMAXCONN) fp@0: or die "listen: $!"; fp@0: fp@0: %authhosts = (); fp@0: # get authorized hosts fp@0: open (AUTH, $authfile) hm@28: or logmsg ("notice", 2, "Could not read allowed hosts file: $authfile"); fp@0: while () { fp@0: chomp; fp@0: my $host = lc $_; hm@28: if ($host =~ /^[\d\w]/) { hm@28: $authhosts{$_} = 1; hm@28: logmsg ("notice", 2, "Authorized host: >$host<"); hm@28: } fp@0: } fp@0: close (AUTH); fp@0: fp@0: # tell about open server socket hm@28: logmsg ("notice", 2, "Server started at port $port"); hm@28: hm@28: my $waitedpid = 0; fp@0: my $paddr; fp@0: fp@0: # wait for children to return, thus avoiding zombies hm@28: # improvement (*) hm@28: use POSIX ":sys_wait_h"; fp@0: sub REAPER { hm@28: my $child; hm@28: while (($waitedpid = waitpid(-1,WNOHANG)) > 0) { hm@28: logmsg ("notice", 2, "reaped $waitedpid", ($? ? " with exit $?" : "")); hm@28: } hm@28: $SIG{CHLD} = \&REAPER; # loathe sysV fp@0: } fp@0: fp@0: # also all sub-processes should wait for their children fp@0: $SIG{CHLD} = \&REAPER; fp@0: fp@0: # start a new server for every incoming request hm@28: # improvement (*) -- loop forever hm@28: hm@28: while ( 1 ) { hm@28: for ( $waitedpid = 0; hm@28: ($paddr = accept(Client,Server)) || $waitedpid; hm@28: $waitedpid = 0, close Client ) { hm@28: next if $waitedpid and not $paddr; hm@28: my ($port, $iaddr) = sockaddr_in($paddr); hm@28: my $name = lc gethostbyaddr($iaddr, AF_INET); hm@28: my $ipaddr = inet_ntoa($iaddr); hm@28: my $n = 0; hm@28: fp@0: # tell about the requesting client hm@28: logmsg ("info", 2, "Connection from >$ipaddr< ($name) at port $port"); fp@0: hm@28: spawn sub { hm@28: my ($head, $hlen, $pos, $pegel, $typ, $siz, $nch, $nrec, $dat, $i, $j, $n, $llen); hm@28: my ($watchpegel, $shmpegel); hm@28: my ($rin, $rout, $in, $line, $data_requested, $oversample); hm@28: my (@channels); hm@28: fp@0: # to use stdio on writing to Client hm@28: Client->autoflush(); hm@28: fp@0: # Open Device hm@28: sysopen (DEV, "$dev", O_RDWR | O_NONBLOCK, 0666) or die("can't open $dev"); hm@28: fp@0: # Bitmask to check for input on stdin hm@28: $rin = ""; hm@28: vec($rin, fileno(Client), 1) = 1; hm@28: fp@0: # check for authorized hosts hm@28: my $access = 'deny'; hm@28: $access = 'allow' if $authhosts{$ipaddr}; hm@28: $line = "\n"; hm@28: logmsg ("info", 2, $line); hm@28: $len = length $line; hm@28: $offset = 0; hm@28: while ($len) { fp@0: $written = syswrite (DEV, $line, $len, $offset); fp@0: $len -= $written; fp@0: $offset += $written; hm@28: } hm@28: hm@28: while ( 1 ) { hm@28: $in = select ($rout=$rin, undef, undef, 0.0); # poll client fp@0: # look for any Input from Client hm@28: if ($in) { fp@0: # exit on EOF hm@28: $len = sysread (Client, $line, $blksize) or exit; hm@28: logmsg("info", 0, "got $len bytes: \"$line\""); hm@28: $offset = 0; fp@0: # copy request to device hm@28: while ($len) { hm@28: $written = syswrite (DEV, $line, $len, $offset); hm@28: $len -= $written; hm@28: $offset += $written; hm@28: } hm@28: } hm@28: # look for some output from device hm@28: if ($len = sysread DEV, $stream, $blksize) { hm@28: print Client $stream; hm@28: } else { hm@28: select undef, undef, undef, 0.1; # calm down if nothing on device fp@0: } fp@0: } hm@28: }; hm@28: logmsg("info", 2, "spawned\n"); hm@28: } hm@28: logmsg("info", 2, "server loop\n"); fp@0: } fp@0: fp@0: sub spawn { fp@0: my $coderef = shift; hm@28: fp@0: unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') { fp@0: confess "usage: spawn CODEREF"; fp@0: } fp@0: my $pid; fp@0: if (!defined($pid = fork)) { hm@28: logmsg ("notice", 2, "fork failed: $!"); fp@0: return; fp@0: } elsif ($pid) { hm@28: logmsg ("notice", 2, "Request $pid"); fp@0: return; # Parent fp@0: } fp@0: fp@0: # do not use fdup as in the original example fp@0: # open (STDIN, "<&Client") or die "Can't dup client to stdin"; fp@0: # open (STDOUT, ">&Client") or die "Can't dup client to stdout"; fp@0: # STDOUT->autoflush(); fp@0: exit &$coderef(); fp@0: } fp@0: fp@0: fp@0: fp@0: fp@0: fp@0: fp@0: fp@0: fp@0: fp@0: fp@0: fp@0: