news.utdallas.edu!wupost!howland.reston.ans.net!gatech!concert!duke!khera Thu Feb 25 18:31:14 CST 1993 Article: 1231 of comp.lang.perl Xref: feenix.metronet.com comp.lang.perl:1231 Path: feenix.metronet.com!news.utdallas.edu!wupost!howland.reston.ans.net!gatech!concert!duke!khera From: khera@cs.duke.edu (Vivek Khera) Newsgroups: comp.lang.perl #Subject: Re: socket-based server (not from inetd) Message-ID: Date: 25 Feb 93 19:18:28 GMT References: <1mimbd$ac3@milk.Warren.MENTORG.COM> Sender: news@duke.cs.duke.edu Organization: Duke University CS Dept., Durham, NC Lines: 182 Nntp-Posting-Host: thneed.cs.duke.edu To: tal@Warren.MENTORG.COM (Tom Limoncelli) In-reply-to: tal@Warren.MENTORG.COM's message of 25 Feb 93 14:50:21 GMT X-Md4-Signature: 876f378184b5a49ff0bd7a8dbbfea73b In article <1mimbd$ac3@milk.Warren.MENTORG.COM> tal@Warren.MENTORG.COM (Tom Limoncelli) writes: I would like to develop a long-running daemon that accepts connections via sockets but never spawns. (i.e. all connections are handled by one program, a lot like INN's innd). here's a piece of the Internet Programming Contest judging software I wrote last October. This particular server keeps track of which jobs are currently active in the system (it also does the job number assignment). this is the raw code, before being run through my configure script to fill in the things like the host names and such. all such substitutions are done to things of the form @@VAR@@ in the code. these should be obvious as to what they should be. but then, you probably won't want to run this code anyway... --cut here-- #@@PERL@@ # # TaskMaster program. This program should only run on one machine. # assigns task numbers and tracks completed problems. if log file exists, # then assumes it needs to restart any outstanding jobs. # V. Khera 5-OCT-1992 # $Id: TaskMaster.perl,v 1.6 1992/10/19 02:17:44 khera Exp $ require 'ctime.pl'; # # assigned port for ScoreServer # $port = 6616; $machine = '@@TMHOST@@'; # must run on specified machine $imach = '@@INHOST@@'; # machine where `incoming' runs # other private info $logfile = '@@TMLOGFILE@@'; $savedir = '@@TMSTASH@@'; # # some networking constants (values from header files) # $SecretCode = "42"; # used in private communication $AF_INET = 2; $SOCK_STREAM = 1; $sockaddr = 'S n a4 x8'; # used for pack() $LOCK_EX = 2; $LOCK_NB = 4; $SIG{'PIPE'} = 'handlepipe'; sub handlepipe { print "Got a SIGPIPE! Client must have died.\nContinuing...\n"; } $SIG{'INT'} = 'cleanup'; sub cleanup { print "\nExiting...\n"; shutdown(S,2); exit(0); # should close/flush all pipes/files } # # first check to see if another TaskMaster is running (has a lock on the # log file). this only checks that there are no other servers running # on *this* machine. # umask(077); # make sure file is secure chop($hostname = `hostname`); die "Must run on $machine\n" unless $hostname eq $machine; open(LOGFILE,">>$logfile") || die "Cannot open $logfile: $!\n"; flock(LOGFILE,$LOCK_EX | $LOCK_NB) || die "Could not lock $logfile, must be another TaskMaster running\n"; # # create a socket on which to await connections # ($name, $aliases, $proto) = getprotobyname('tcp'); $myconn = pack($sockaddr, $AF_INET, $port, "\0\0\0\0"); socket(S, $AF_INET, $SOCK_STREAM, $proto) || die "socket: $!\n"; bind(S,$myconn) || die "bind: $!\n"; listen(S,5) || die "listen: $!\n"; # force flush on every write select(NS); $| = 1; select(S); $| = 1; select(LOGFILE); $| = 1; select(STDOUT); $| = 1; print "TaskMaster running...\n"; $tasknumber = 0; &restartjobs() unless -z $logfile; # now loop forever until we get some requests for(;;) { accept(NS,S) || die "accept: $!\n"; chop($_ = ); if ($_ eq $SecretCode) { print NS "OK\n"; } else { print NS "GO AWAY\n"; close(NS); next; } chop($cmd = ); if ($cmd eq "NEW") { $tasknumber++; $scoreboard{$tasknumber} = time; # mark the time assigned print "assigned $tasknumber\n"; print NS "$tasknumber\n"; print LOGFILE "$tasknumber a\n"; # job assigned } elsif ($cmd eq "PRINT") { &showjobs(); } elsif (($jobnum) = $cmd =~ m/DONE\s+(\d+)/) { delete $scoreboard{$jobnum}; print "job $jobnum completed\n"; print LOGFILE "$jobnum d\n"; # job done unlink("$savedir/$jobnum"); # no longer needed } close (NS); } # in case TaskMaster somhow died, we can restart it using the log file. # TaskMaster assumes that all pending processes are also terminated, and # restarts them. this is not a problem as we ignore duplicate score entries # for the same job number. sub restartjobs { local($jn,$status); open(F,$logfile) || die "Cannot read $logfile to restart jobs.\n"; $tasknumber = 0; while () { ($jn,$status) = m/(\d+)\s([ad])/; if ($status eq 'a') { $tasknumber++; $scoreboard{$jn} = time; } elsif ($status eq 'd') { delete $scoreboard{$jn}; } } close(F); foreach (keys %scoreboard) { print "***restarting job $_\n"; # feed stored file to `incoming' program. system "rsh $imach incoming $_"; } } # scan list of jobs and print the time that it was assigned. sub showjobs { local($jn); &boldtext(); print "Pending jobs: ",&ctime(time); &normtext(); print "Job\tTime assigned\n"; foreach $jn (sort { $a <=> $b; } (keys %scoreboard)) { print "$jn\t",&ctime($scoreboard{$jn}); } &boldtext(); print "End of list.\n"; &normtext(); } # set xterm character attributes sub setxattr { local($val) = @_; local($|) = 1; print "\033[${val}m"; } sub normtext { &setxattr(0); } sub boldtext { &setxattr(1); } --cut here-- -- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Vivek Khera, Gradual Student/Systems Guy Department of Computer Science Internet: khera@cs.duke.edu Box 90129 (MIME mail accepted) Durham, NC 27708-0129 (919)660-6528 .