Annotation of telnetbbs/telnetbbs.pl, revision 1.1

1.1     ! nick        1: #!/usr/bin/perl -wT
        !             2: ################################################################################
        !             3: ##
        !             4: ## See end of script for comments and 'pod2man telnetbbs | nroff -man' to
        !             5: ## view man page or pod2text telnetbbs for plain text.
        !             6: ##
        !             7: ##   Nicholas DeClario <nick@declario.com>
        !             8: ##   October 2009
        !             9: ##     $Id$
        !            10: ##
        !            11: ################################################################################
        !            12: BEGIN {
        !            13:         delete @ENV{qw(IFS CDPATH ENV BASH_ENV PATH)};
        !            14:         $ENV{PATH} = "/bin:/usr/bin";
        !            15:         $|++;
        !            16: # Flip this back on for more detailed error reporting
        !            17: #        $SIG{__DIE__} = sub { require Carp; Carp::confess(@_); }
        !            18:       }
        !            19: 
        !            20: use strict;
        !            21: use Getopt::Long;
        !            22: use Pod::Usage;
        !            23: use Data::Dumper;
        !            24: use POSIX qw/ mkfifo /;
        !            25: use IO::File;
        !            26: use Socket;
        !            27: use IO::Socket::INET;
        !            28: use Time::HiRes qw/ sleep setitimer ITIMER_REAL time /;
        !            29: use threads;
        !            30: use threads::shared;
        !            31: 
        !            32: ##
        !            33: ## Fetch our command line options
        !            34: ##
        !            35: my %opts    = &fetchOptions( );
        !            36: my $pidFile = "/var/run/telnetbbs.pid";
        !            37: my $EOL     = "\015\012";
        !            38: 
        !            39: ##
        !            40: ## Check that we are 'root' 
        !            41: ##
        !            42: die( "Must be root user to run this.\n" )
        !            43:        if ( getpwuid( $> ) ne "root" );
        !            44: 
        !            45: ##
        !            46: ## Check for a PID
        !            47: ##
        !            48: exit( 255 ) if ( ! &checkPID( $pidFile ) );
        !            49: 
        !            50: ##
        !            51: ## Lets keep an eye on the forked children our network socket will be using
        !            52: ##
        !            53: $SIG{CHLD} = 'IGNORE';
        !            54: 
        !            55: ##
        !            56: ## Catch any type of kill signals so that we may cleanly shutdown.  These
        !            57: ## include 'kill', 'kill -HUP' and a 'CTRL-C' from the keyboard.
        !            58: ##
        !            59: local $SIG{HUP}  = $SIG{INT} = $SIG{TERM} = \&shutdown;
        !            60: 
        !            61: ##
        !            62: ## Start the network server
        !            63: ##
        !            64: my $netThread = threads->create( \&startNetServer );
        !            65: 
        !            66: while( 1 ) { sleep 1; }
        !            67: 
        !            68: ##
        !            69: ## If we made it here, the main loop died, lets shutdown
        !            70: ##
        !            71: &shutdown( );
        !            72: 
        !            73: ###############################################################################
        !            74: ###############################################################################
        !            75: ##
        !            76: ## Sub-routines begin here
        !            77: ##
        !            78: ###############################################################################
        !            79: ###############################################################################
        !            80: 
        !            81: sub logmsg { print "$0 $$: @_ at ", scalar localtime, "\n" }
        !            82: 
        !            83: ###############################################################################
        !            84: ## startNetServer( );
        !            85: ##
        !            86: ##  We want to open the next free port when an incoming connection to the
        !            87: ##  main port is made.  We need to set that up here.
        !            88: ##
        !            89: ###############################################################################
        !            90: sub startNetServer 
        !            91: {
        !            92:        my $hostConnection;
        !            93:        my $childPID;
        !            94:        my $port = $opts{'port'};
        !            95:        my $node = 1;
        !            96: 
        !            97:        my $server = IO::Socket::INET->new( 
        !            98:                        LocalPort => $port,
        !            99:                        Type      => SOCK_STREAM,
        !           100:                        Proto     => 'tcp',
        !           101:                        Reuse     => 1,
        !           102:                        Listen    => 1,
        !           103:                ) or die "Couldn't create socket on port $port: $!\n";
        !           104:        
        !           105:        &logmsg( "Listening on port $port" );
        !           106: 
        !           107:         ##
        !           108:         ## We want to fork our connection when it's made so that we are ready
        !           109:         ## to accept other incoming connections.
        !           110:         ##
        !           111:        REQUEST: while( $hostConnection = $server->accept( ) )
        !           112:        {
        !           113:                my $kidpid;
        !           114:                my $line;
        !           115: 
        !           116:                if( $childPID = fork( ) ) {
        !           117:                        close( $hostConnection );
        !           118:                        next REQUEST;
        !           119:                }
        !           120:                defined( $childPID ) || die( "Cannot fork: $!\n" );
        !           121: 
        !           122:                ##
        !           123:                ## Default file descriptor to the client and turn on autoflush
        !           124:                ##
        !           125:                $hostConnection->autoflush( 1 );
        !           126: 
        !           127:                print $hostConnection "Welcome to Hell's Dominion BBS!" . $EOL;
        !           128:                print $hostConnection "Starting BBS on node $node...$EOL";
        !           129: 
        !           130:                ##
        !           131:                ## Launch BBS via dosbox
        !           132:                ##
        !           133:                my $bbsPID = fork( );
        !           134:                if ( $bbsPID ) 
        !           135:                {
        !           136:                        exec( "dosbox" );
        !           137:                        exit;
        !           138:                }
        !           139:                sleep 10;
        !           140: 
        !           141:                ##
        !           142:                ## Create connection to BBS
        !           143:                ##
        !           144:                my $bbs = IO::Socket::INET->new (
        !           145:                                PeerAddr        => 'localhost',
        !           146:                                Type      => SOCK_STREAM,
        !           147:                                PeerPort        => 5000,
        !           148:                                Proto           => 'tcp',
        !           149:                        ) || die "Could not open BBS socket: $!\n";
        !           150:                $bbs->autoflush( 1 );
        !           151:                die "Can't fork BBS connection: $!\n" 
        !           152:                        unless defined( $kidpid = fork( ) );
        !           153: 
        !           154:                if ( $kidpid ) 
        !           155:                {
        !           156:                        my $byte;
        !           157:                        while ( sysread( $bbs, $byte, 1 ) == 1 ) 
        !           158:                        {
        !           159:                                print $hostConnection $byte;
        !           160:                        }
        !           161:                        kill( "TERM" => $childPID );
        !           162:                }
        !           163:                else 
        !           164:                {
        !           165:                        my $byte;
        !           166:                        while( sysread( $hostConnection, $byte, 1 ) == 1 )
        !           167:                        {
        !           168:                                print $bbs $byte;
        !           169:                        }
        !           170:                }
        !           171:        }
        !           172:        close( $hostConnection );
        !           173:        exit;
        !           174: 
        !           175:        close( $server );
        !           176: }
        !           177: 
        !           178: ###############################################################################
        !           179: ##
        !           180: ## shutdown( $signame );
        !           181: ##
        !           182: ##  Call our shutdown routine which cleanly kills all the child processes 
        !           183: ##  and shuts down.  Optionally, if a kill signal was received, it will be
        !           184: ##  displayed.
        !           185: ##
        !           186: ###############################################################################
        !           187: sub shutdown 
        !           188: {
        !           189:        my $signame = shift || 0;
        !           190: 
        !           191:        print "$0: Shutdown (SIG$signame) received.\n"
        !           192:                if( $signame );
        !           193:        
        !           194:        ##      
        !           195:        ## Remove the PID
        !           196:        ##
        !           197:        unlink( $pidFile );
        !           198: 
        !           199:        ##
        !           200:        ## Wait for the thread to shutdown
        !           201:        ##
        !           202: #      $netThread->detach( );  
        !           203: 
        !           204:        ##
        !           205:        ## And time to exit
        !           206:        ##
        !           207:        &POSIX::_exit( 0 );
        !           208: }
        !           209: 
        !           210: ###############################################################################
        !           211: ##
        !           212: ## my $result = checkPID( $pidFile );
        !           213: ##
        !           214: ##   We need to see if there is a PID file, if so if the PID inside is still
        !           215: ## actually running, if not, re-create the PID file with the new PID.  
        !           216: ## If there is no PID file to begin with, we create one.
        !           217: ##
        !           218: ## If this process is successfull a non-zero value is returned.
        !           219: ##
        !           220: ###############################################################################
        !           221: sub checkPID
        !           222: {
        !           223:        my $pidFile = shift || return 0;
        !           224:        my $pid     = 0;
        !           225: 
        !           226:         ##
        !           227:         ## If there is no PID file, create it.
        !           228:         ##
        !           229:         if ( ! stat( $pidFile ) ) 
        !           230:        {
        !           231:             open PF, ">$pidFile" || return 0;
        !           232:                 print PF $$;
        !           233:             close( PF );
        !           234: 
        !           235:             return 1;
        !           236:         }
        !           237:         ##
        !           238:         ## We have a PID file.  If the process does not actually exist, then
        !           239:         ## delete the PID file.
        !           240:         ##
        !           241:         else 
        !           242:        {
        !           243:                open PIDFILE, "<$pidFile" || 
        !           244:                        die( "Failed ot open PID file: $!\n" );
        !           245:                        $pid = <PIDFILE>;
        !           246:                close( PIDFILE );
        !           247: 
        !           248:                ##
        !           249:                ## Unlink the file if the process doesn't exist 
        !           250:                ## and continue with execution
        !           251:                ##
        !           252:                if ( &processExists( $pid, $0 ) ) 
        !           253:                {
        !           254:                        unlink( $pidFile );
        !           255:                        open PF, ">$pidFile" || return 0;
        !           256:                                print PF $$ . "\n";
        !           257:                        close( PF );
        !           258: 
        !           259:                        return 2;
        !           260:                }
        !           261:                return 0;
        !           262:         }
        !           263: }
        !           264: 
        !           265: ###############################################################################
        !           266: ##
        !           267: ## sub processExists( );
        !           268: ##
        !           269: ##     Check the '/proc' file system for the process ID number listed in the
        !           270: ##  PID file.  '/proc/$PID/cmdline' will be compared to the running process
        !           271: ##  name.  If the process ID does exist but the name does not match we assume
        !           272: ##  it's a dead PID file.
        !           273: ##
        !           274: ###############################################################################
        !           275: sub processExists 
        !           276: {
        !           277:        my $pid   = shift || return 0;
        !           278:        my $pname = shift || return 0;
        !           279: 
        !           280:        ##
        !           281:        ## If the directory doesn't exist, there is no way the 
        !           282:        ## process is running
        !           283:        ##
        !           284:        return 0 if ( ! -f "/proc/$pid" );
        !           285: 
        !           286:        ##
        !           287:        ## However, if the directory does exist, we need to confirm that it is
        !           288:        ## indeed the process we are looking.
        !           289:        ##
        !           290:        open CMD, "</proc/$pid/cmdline";
        !           291:                my $cmd = <CMD>;
        !           292:        close( CMD );
        !           293: 
        !           294:        ##
        !           295:        ## Filter out leading PATH information
        !           296:        ##
        !           297:        $pname =~ s/^\.\///;
        !           298:        $cmd   =~ s/.*\/(.*)/$1/;
        !           299: 
        !           300:        ##
        !           301:        ## if we found the process, return 1
        !           302:        ##
        !           303:        return 1 if ( $cmd =~ m/$pname/ );
        !           304: 
        !           305:        return 0;
        !           306: }
        !           307: 
        !           308: 
        !           309: ###############################################################################
        !           310: ##
        !           311: ## &fetchOptions( );
        !           312: ##
        !           313: ##      Grab our command line arguments and toss them in to a hash
        !           314: ##
        !           315: ###############################################################################
        !           316: sub fetchOptions {
        !           317:         my %opts;
        !           318: 
        !           319:         &GetOptions(
        !           320:                         "help|?"        => \$opts{'help'},
        !           321:                         "man"           => \$opts{'man'},
        !           322:                        "port:i"                => \$opts{'port'},
        !           323:                    ) || &pod2usage( );
        !           324:         &pod2usage( ) if defined $opts{'help'};
        !           325:         &pod2usage( { -verbose => 2, -input => \*DATA } ) if defined $opts{'man'};
        !           326: 
        !           327:         return %opts;
        !           328: }
        !           329: 
        !           330: __END__
        !           331: 
        !           332: =head1 NAME
        !           333: 
        !           334: masterbuild.pl - blurb
        !           335: 
        !           336: =head1 SYNOPSIS
        !           337: 
        !           338: masterbuild.pl [options]
        !           339: 
        !           340:  Options:
        !           341:         --help,?        Display the basic help menu
        !           342:         --man,m         Display the detailed man page
        !           343: 
        !           344: =head1 DESCRIPTION
        !           345: 
        !           346: =head1 HISTORY
        !           347: 
        !           348: =head1 AUTHOR
        !           349: 
        !           350: Nicholas DeClario <nick@declario.com>
        !           351: 
        !           352: =head1 BUGS
        !           353: 
        !           354: This is a work in progress.  Please report all bugs to the author.
        !           355: 
        !           356: =head1 SEE ALSO
        !           357: 
        !           358: =head1 COPYRIGHT
        !           359: 
        !           360: =cut

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>