fp@1619: #!/usr/bin/perl -w fp@1619: fp@1619: #------------------------------------------------------------------------------ fp@1619: # fp@1619: # Copyright (C) 2006 Ingenieurgemeinschaft IgH fp@1619: # fp@1619: # This file is part of the IgH EtherCAT Master. fp@1619: # fp@1619: # The IgH EtherCAT Master is free software; you can redistribute it fp@1619: # and/or modify it under the terms of the GNU General Public License fp@1619: # as published by the Free Software Foundation; either version 2 of the fp@1619: # License, or (at your option) any later version. fp@1619: # fp@1619: # The IgH EtherCAT Master is distributed in the hope that it will be fp@1619: # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1619: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fp@1619: # GNU General Public License for more details. fp@1619: # fp@1619: # You should have received a copy of the GNU General Public License fp@1619: # along with the IgH EtherCAT Master; if not, write to the Free Software fp@1619: # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@1619: # fp@1619: # The right to use EtherCAT Technology is granted and comes free of fp@1619: # charge under condition of compatibility of product made by fp@1619: # Licensee. People intending to distribute/sell products based on the fp@1619: # code, have to sign an agreement to guarantee that products using fp@1619: # software based on IgH EtherCAT master stay compatible with the actual fp@1619: # EtherCAT specification (which are released themselves as an open fp@1619: # standard) as the (only) precondition to have the right to use EtherCAT fp@1619: # Technology, IP and trade marks. fp@1619: # fp@1619: #------------------------------------------------------------------------------ fp@1619: # fp@1619: # Multithreaded Server fp@1619: # according to the example from "Programming Perl" fp@1619: # this code is improved according to the example from fp@1619: # perldoc perlipc, so now safely being usable under Perl 5.8 fp@1619: # (see note (*)) fp@1619: # fp@1619: # works with read/write on a device-file fp@1619: # fp@1619: #------------------------------------------------------------------------------ fp@1619: fp@1619: require 5.002; fp@1619: use strict; fp@1619: BEGIN { $ENV{PATH} = '/opt/msr/bin:/usr/bin:/bin' } fp@1619: use Socket; fp@1619: use Carp; fp@1619: use FileHandle; fp@1619: use Getopt::Std; fp@1619: fp@1619: use Sys::Syslog qw(:DEFAULT setlogsock); fp@1619: fp@1619: use vars qw ( fp@1619: $self $pid $dolog $port $dev %opts $selfbase fp@1619: $len $offset $stream $written $read $log $blksize fp@1619: $instdir fp@1619: $authfile %authhosts fp@1619: ); fp@1619: fp@1619: fp@1619: # Do logging to local syslogd by unix-domain socket instead of inetd fp@1619: setlogsock("unix"); fp@1619: fp@1619: # Prototypes and some little Tools fp@1619: sub spawn; fp@1619: sub logmsg { fp@1619: my ($level, $debug, @text) = @_; fp@1619: syslog("daemon|$level", @text) if $debug > $dolog; fp@1619: # print STDERR "daemon|$level", @text, "\n" if $dolog; fp@1619: } fp@1619: sub out { fp@1619: my $waitpid = wait; fp@1619: logmsg("notice", 2, "$waitpid exited"); fp@1619: unlink "$selfbase.pid"; fp@1619: exit 0; fp@1619: } fp@1619: fp@1619: sub help { fp@1619: print "\n usage: $0 [-l og] [-h elp] [-p port] [-d device]\n"; fp@1619: exit; fp@1619: } fp@1619: fp@1619: # Process Options fp@1619: %opts = ( fp@1619: "l" => 1, fp@1619: "h" => 0, fp@1619: "p" => 2345, fp@1619: "d" => "/dev/msr" fp@1619: ); fp@1619: fp@1619: getopts("lhp:d:", \%opts); fp@1619: fp@1619: help if $opts{"h"}; fp@1619: fp@1619: ( $self = $0 ) =~ s+.*/++ ; fp@1619: ( $selfbase = $self ) =~ s/\..*//; fp@1619: $log = "$selfbase.log"; fp@1619: $dolog = $opts{"l"}; fp@1619: $port = $opts{"p"}; fp@1619: $dev = $opts{"d"}; fp@1619: $blksize = 1024; # try to write as much bytes fp@1619: $instdir = "/opt/msr"; fp@1619: $authfile = "$instdir/etc/hosts.auth"; fp@1619: fp@1619: # Start logging fp@1619: openlog($self, 'pid'); fp@1619: fp@1619: # Flush Output, dont buffer fp@1619: $| = 1; fp@1619: fp@1619: # first fork and run in background fp@1619: if ($pid = fork) { fp@1619: # open LOG, ">$log" if $dolog; fp@1619: # close LOG; fp@1619: logmsg("notice", 2, "forked process: $pid\n"); fp@1619: exit 0; fp@1619: } fp@1619: fp@1619: # Server tells about startup success fp@1619: open (PID, ">/$instdir/var/run/$selfbase.pid"); fp@1619: print PID "$$\n"; fp@1619: close PID; fp@1619: fp@1619: # Cleanup on exit (due to kill -TERM signal) fp@1619: $SIG{TERM} = \&out; fp@1619: fp@1619: # We use streams fp@1619: my $proto = getprotobyname('tcp'); fp@1619: fp@1619: # Open Server socket fp@1619: socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; fp@1619: setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) fp@1619: or die "setsocketopt: $!"; fp@1619: bind (Server, sockaddr_in($port, INADDR_ANY)) fp@1619: or die "bind: $!"; fp@1619: listen (Server, SOMAXCONN) fp@1619: or die "listen: $!"; fp@1619: fp@1619: %authhosts = (); fp@1619: # get authorized hosts fp@1619: open (AUTH, $authfile) fp@1619: or logmsg ("notice", 2, "Could not read allowed hosts file: $authfile"); fp@1619: while () { fp@1619: chomp; fp@1619: my $host = lc $_; fp@1619: if ($host =~ /^[\d\w]/) { fp@1619: $authhosts{$_} = 1; fp@1619: logmsg ("notice", 2, "Authorized host: >$host<"); fp@1619: } fp@1619: } fp@1619: close (AUTH); fp@1619: fp@1619: # tell about open server socket fp@1619: logmsg ("notice", 2, "Server started at port $port"); fp@1619: fp@1619: my $waitedpid = 0; fp@1619: my $paddr; fp@1619: fp@1619: # wait for children to return, thus avoiding zombies fp@1619: # improvement (*) fp@1619: use POSIX ":sys_wait_h"; fp@1619: sub REAPER { fp@1619: my $child; fp@1619: while (($waitedpid = waitpid(-1,WNOHANG)) > 0) { fp@1619: logmsg ("notice", 2, "reaped $waitedpid", ($? ? " with exit $?" : "")); fp@1619: } fp@1619: $SIG{CHLD} = \&REAPER; # loathe sysV fp@1619: } fp@1619: fp@1619: # also all sub-processes should wait for their children fp@1619: $SIG{CHLD} = \&REAPER; fp@1619: fp@1619: # start a new server for every incoming request fp@1619: # improvement (*) -- loop forever fp@1619: fp@1619: while ( 1 ) { fp@1619: for ( $waitedpid = 0; fp@1619: ($paddr = accept(Client,Server)) || $waitedpid; fp@1619: $waitedpid = 0, close Client ) { fp@1619: next if $waitedpid and not $paddr; fp@1619: my ($port, $iaddr) = sockaddr_in($paddr); fp@1619: my $name = lc gethostbyaddr($iaddr, AF_INET); fp@1619: my $ipaddr = inet_ntoa($iaddr); fp@1619: my $n = 0; fp@1619: fp@1619: # tell about the requesting client fp@1619: logmsg ("info", 2, "Connection from >$ipaddr< ($name) at port $port"); fp@1619: fp@1619: spawn sub { fp@1619: my ($head, $hlen, $pos, $pegel, $typ, $siz, $nch, $nrec, $dat, $i, $j, $n, $llen); fp@1619: my ($watchpegel, $shmpegel); fp@1619: my ($rin, $rout, $in, $line, $data_requested, $oversample); fp@1619: my (@channels); fp@1619: fp@1619: # to use stdio on writing to Client fp@1619: Client->autoflush(); fp@1619: fp@1619: # Open Device fp@1619: sysopen (DEV, "$dev", O_RDWR | O_NONBLOCK, 0666) or die("can't open $dev"); fp@1619: fp@1619: # Bitmask to check for input on stdin fp@1619: $rin = ""; fp@1619: vec($rin, fileno(Client), 1) = 1; fp@1619: fp@1619: # check for authorized hosts fp@1619: my $access = 'deny'; fp@1619: $access = 'allow' if $authhosts{$ipaddr}; fp@1619: $line = "\n"; fp@1619: logmsg ("info", 2, $line); fp@1619: $len = length $line; fp@1619: $offset = 0; fp@1619: while ($len) { fp@1619: $written = syswrite (DEV, $line, $len, $offset); fp@1619: $len -= $written; fp@1619: $offset += $written; fp@1619: } fp@1619: fp@1619: while ( 1 ) { fp@1619: $in = select ($rout=$rin, undef, undef, 0.0); # poll client fp@1619: # look for any Input from Client fp@1619: if ($in) { fp@1619: # exit on EOF fp@1619: $len = sysread (Client, $line, $blksize) or exit; fp@1619: logmsg("info", 0, "got $len bytes: \"$line\""); fp@1619: $offset = 0; fp@1619: # copy request to device fp@1619: while ($len) { fp@1619: $written = syswrite (DEV, $line, $len, $offset); fp@1619: $len -= $written; fp@1619: $offset += $written; fp@1619: } fp@1619: } fp@1619: # look for some output from device fp@1619: if ($len = sysread DEV, $stream, $blksize) { fp@1619: print Client $stream; fp@1619: } else { fp@1619: select undef, undef, undef, 0.1; # calm down if nothing on device fp@1619: } fp@1619: } fp@1619: }; fp@1619: logmsg("info", 2, "spawned\n"); fp@1619: } fp@1619: logmsg("info", 2, "server loop\n"); fp@1619: } fp@1619: fp@1619: sub spawn { fp@1619: my $coderef = shift; fp@1619: fp@1619: unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') { fp@1619: confess "usage: spawn CODEREF"; fp@1619: } fp@1619: my $pid; fp@1619: if (!defined($pid = fork)) { fp@1619: logmsg ("notice", 2, "fork failed: $!"); fp@1619: return; fp@1619: } elsif ($pid) { fp@1619: logmsg ("notice", 2, "Request $pid"); fp@1619: return; # Parent fp@1619: } fp@1619: fp@1619: # do not use fdup as in the original example fp@1619: # open (STDIN, "<&Client") or die "Can't dup client to stdin"; fp@1619: # open (STDOUT, ">&Client") or die "Can't dup client to stdout"; fp@1619: # STDOUT->autoflush(); fp@1619: exit &$coderef(); fp@1619: }