Annotation of telnetbbs/telnetbbs.pl, revision 1.1.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>