1: #!/usr/bin/perl
2:
3: # The LearningOnline Network
4: # lonsql
5: # provides unix domain sockets to receive queries from lond and send replies to lonc
6: #
7: # PID in subdir logs/lonc.pid
8: # kill kills
9: # HUP restarts
10: # USR1 tries to open connections again
11:
12: # 6/4/99,6/5,6/7,6/8,6/9,6/10,6/11,6/12,7/14,7/19,
13: # 10/8,10/9,10/15,11/18,12/22,
14: # 2/8 Gerd Kortemeyer
15: # based on nonforker from Perl Cookbook
16: # - server who multiplexes without forking
17:
18: use POSIX;
19: use IO::Socket;
20: use IO::Select;
21: use IO::File;
22: use Socket;
23: use Fcntl;
24: use Tie::RefHash;
25: use Crypt::IDEA;
26: use DBI;
27:
28:
29: $childmaxattempts=10;
30: $run =0;
31: # ------------------------------------ Read httpd access.conf and get variables
32:
33: open (CONFIG,"/etc/httpd/conf/access.conf") || die "Can't read access.conf";
34:
35: while ($configline=<CONFIG>) {
36: if ($configline =~ /PerlSetVar/) {
37: my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
38: chomp($varvalue);
39: $perlvar{$varname}=$varvalue;
40: }
41: }
42: close(CONFIG);
43:
44: # ------------------------------------------------------------- Read hosts file
45: #$PREFORK=4; # number of children to maintain, at least four spare
46:
47: open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";
48:
49: while ($configline=<CONFIG>) {
50: my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
51: chomp($ip);
52:
53: #$hostip{$ip}=$id;
54: $hostip{$id}=$ip;
55:
56: if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; }
57:
58: #$PREFORK++;
59: }
60: close(CONFIG);
61:
62:
63: # -------------------------------------------------------- Routines for forking
64: # global variables
65: #$MAX_CLIENTS_PER_CHILD = 5; # number of clients each child should process
66: %children = (); # keys are current child process IDs
67: #$children = 0; # current number of children
68: %childpid = (); # the other way around
69:
70: %childatt = (); # number of attempts to start server
71: # for ID
72:
73:
74: sub REAPER { # takes care of dead children
75: $SIG{CHLD} = \&REAPER;
76: my $pid = wait;
77:
78: #$children --;
79: #&logthis("Child $pid died");
80: #delete $children{$pid};
81:
82: my $wasserver=$children{$pid};
83: &logthis("<font color=red>CRITICAL: "
84: ."Child $pid for server $wasserver died ($childatt{$wasserver})</font>");
85: delete $children{$pid};
86: delete $childpid{$wasserver};
87: my $port = "$perlvar{'lonSockDir'}/$wasserver";
88: unlink($port);
89:
90:
91: }
92:
93: sub HUNTSMAN { # signal handler for SIGINT
94: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
95: kill 'INT' => keys %children;
96: my $execdir=$perlvar{'lonDaemons'};
97: unlink("$execdir/logs/lonsql.pid");
98: &logthis("<font color=red>CRITICAL: Shutting down</font>");
99: exit; # clean up with dignity
100: }
101:
102: sub HUPSMAN { # signal handler for SIGHUP
103: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
104: kill 'INT' => keys %children;
105: close($server); # free up socket
106: &logthis("<font color=red>CRITICAL: Restarting</font>");
107: my $execdir=$perlvar{'lonDaemons'};
108: exec("$execdir/lonsql"); # here we go again
109: }
110:
111: sub logthis {
112: my $message=shift;
113: my $execdir=$perlvar{'lonDaemons'};
114: my $fh=IO::File->new(">>$execdir/logs/lonsql.log");
115: my $now=time;
116: my $local=localtime($now);
117: print $fh "$local ($$): $message\n";
118: }
119:
120: # ----------------------------------------------------------- Send USR1 to lonc
121: sub reconlonc {
122: my $peerfile=shift;
123: &logthis("Trying to reconnect for $peerfile");
124: my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
125: if (my $fh=IO::File->new("$loncfile")) {
126: my $loncpid=<$fh>;
127: chomp($loncpid);
128: if (kill 0 => $loncpid) {
129: &logthis("lonc at pid $loncpid responding, sending USR1");
130: kill USR1 => $loncpid;
131: sleep 1;
132: if (-e "$peerfile") { return; }
133: &logthis("$peerfile still not there, give it another try");
134: sleep 5;
135: if (-e "$peerfile") { return; }
136: &logthis(
137: "<font color=blue>WARNING: $peerfile still not there, giving up</font>");
138: } else {
139: &logthis(
140: "<font color=red>CRITICAL: "
141: ."lonc at pid $loncpid not responding, giving up</font>");
142: }
143: } else {
144: &logthis('<font color=red>CRITICAL: lonc not running, giving up</font>');
145: }
146: }
147:
148: # -------------------------------------------------- Non-critical communication
149: sub subreply {
150: my ($cmd,$server)=@_;
151: my $peerfile="$perlvar{'lonSockDir'}/$server";
152: my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile",
153: Type => SOCK_STREAM,
154: Timeout => 10)
155: or return "con_lost";
156: print $sclient "$cmd\n";
157: my $answer=<$sclient>;
158: chomp($answer);
159: if (!$answer) { $answer="con_lost"; }
160: return $answer;
161: }
162:
163: sub reply {
164: my ($cmd,$server)=@_;
165: my $answer;
166: if ($server ne $perlvar{'lonHostID'}) {
167: $answer=subreply($cmd,$server);
168: if ($answer eq 'con_lost') {
169: $answer=subreply("ping",$server);
170: if ($answer ne $server) {
171: &reconlonc("$perlvar{'lonSockDir'}/$server");
172: }
173: $answer=subreply($cmd,$server);
174: }
175: } else {
176: $answer='self_reply';
177: }
178: return $answer;
179: }
180:
181: $unixsock = "msua1_sql";
182: my $localfile="$perlvar{'lonSockDir'}/$unixsock";
183: my $server=IO::Socket::UNIX->new(LocalAddr =>"$localfile",
184: Type => SOCK_STREAM,
185: Timeout => 10);
186:
187: # ---------------------------------------------------- Fork once and dissociate
188: $fpid=fork;
189: exit if $fpid;
190: die "Couldn't fork: $!" unless defined ($fpid);
191:
192: POSIX::setsid() or die "Can't start new session: $!";
193:
194: # ------------------------------------------------------- Write our PID on disk
195:
196: $execdir=$perlvar{'lonDaemons'};
197: open (PIDSAVE,">$execdir/logs/lonsql.pid");
198: print PIDSAVE "$$\n";
199: close(PIDSAVE);
200: &logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>");
201:
202: # ----------------------------- Ignore signals generated during initial startup
203: $SIG{HUP}=$SIG{USR1}='IGNORE';
204:
205: # ------------------------------------------------------- Now we are on our own
206: #Fork of children one for every server
207:
208: #for (1 .. $PREFORK) {
209: # make_new_child($thisserver);
210: #}
211:
212: foreach $thisserver (keys %hostip) {
213: make_new_child($thisserver);
214: }
215:
216: &logthis("Done starting initial servers");
217: # ----------------------------------------------------- Install signal handlers
218:
219: $SIG{CHLD} = \&REAPER;
220: $SIG{INT} = $SIG{TERM} = \&HUNTSMAN;
221: $SIG{HUP} = \&HUPSMAN;
222:
223: # And maintain the population.
224: while (1) {
225: sleep; # wait for a signal (i.e., child's death)
226:
227: #for ($i = $children; $i < $PREFORK; $i++) {
228: # make_new_child(); # top up the child pool
229: #}
230:
231: foreach $thisserver (keys %hostip) {
232: if (!$childpid{$thisserver}) {
233: if ($childatt{$thisserver}<=$childmaxattempts) {
234: $childatt{$thisserver}++;
235: &logthis(
236: "<font color=yellow>INFO: Trying to reconnect for $thisserver "
237: ."($childatt{$thisserver} of $childmaxattempts attempts)</font>");
238: make_new_child($thisserver);
239: }
240: }
241: }
242: }
243:
244: sub make_new_child {
245: my $conserver=shift;
246: my $pid;
247: my $sigset;
248: my $queryid;
249:
250: &logthis("Attempting to start child");
251: # block signal for fork
252: $sigset = POSIX::SigSet->new(SIGINT);
253: sigprocmask(SIG_BLOCK, $sigset)
254: or die "Can't block SIGINT for fork: $!\n";
255:
256: die "fork: $!" unless defined ($pid = fork);#do the forking of children
257:
258: if ($pid) {
259: # Parent records the child's birth and returns.
260: sigprocmask(SIG_UNBLOCK, $sigset)
261: or die "Can't unblock SIGINT for fork: $!\n";
262: $children{$pid} = 1;
263: $children++;
264: return;
265: } else {
266: # Child can *not* return from this subroutine.
267: $SIG{INT} = 'DEFAULT'; # make SIGINT kill us as it did before
268:
269: # unblock signals
270: sigprocmask(SIG_UNBLOCK, $sigset)
271: or die "Can't unblock SIGINT for fork: $!\n";
272:
273: #connect to the database
274: unless (
275: my $dbh = DBI->connect("DBI:mysql:loncapa","root","mysql",{ RaiseError =>1,})
276: ) {
277: my $st=120+int(rand(240));
278: &logthis("<font color=blue>WARNING: Couldn't connect to database ($st secs): $@</font>");
279: sleep($st);
280: exit;#do I need to cleanup before exit if can't connect to database
281: };
282:
283: # handle connections until we've reached $MAX_CLIENTS_PER_CHILD
284: for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) {
285: $client = $server->accept() or last;
286: $run = $run+1;
287: # =============================================================================
288: # do something with the connection
289: # -----------------------------------------------------------------------------
290: my $userinput = "1";
291: #while (my $userinput=<$client>) {
292: while (my $userinput="1") {
293: print ("here we go\n");
294: chomp($userinput);
295:
296: #send query id which is pid_unixdatetime_runningcounter
297: $queryid = $conserver;
298: $queryid .=($$)."_";
299: $queryid .= time."_";
300: $queryid .= run;
301: print $client "$queryid\n";
302:
303: #prepare and execute the query
304:
305: my $sth = $dbh->prepare("select * into outfile \"$queryid\" from resource");#can't use $userinput directly since we the query to write to a file which depends on the query id generated
306:
307: $sth->execute();
308: if (-e "$queryid") { print "Oops ,file is already there!\n";}
309: else
310: {
311: print "error reading into file\n";
312: }
313:
314: #connect to lonc and send the query results
315: $reply = reply($queryid,$conserver);
316:
317: }
318: # =============================================================================
319: }
320:
321: # tidy up gracefully and finish
322:
323: # this exit is VERY important, otherwise the child will become
324: # a producer of more and more children, forking yourself into
325: # process death.
326: exit;
327: }
328: }
329:
330:
331:
332:
333:
334:
335:
336:
337:
338:
339:
340:
341:
342:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>