Annotation of loncom/lonsql, revision 1.7
1.1 harris41 1: #!/usr/bin/perl
1.2 harris41 2: # lonsql-based on the preforker:harsha jagasia:date:5/10/00
1.4 www 3: # 7/25 Gerd Kortemeyer
1.6 harris41 4: # many different dates Scott Harrison
1.7 ! harris41 5: # 03/22/2001 Scott Harrison
1.2 harris41 6: use IO::Socket;
7: use Symbol;
1.1 harris41 8: use POSIX;
9: use IO::Select;
10: use IO::File;
11: use Socket;
12: use Fcntl;
13: use Tie::RefHash;
14: use DBI;
15:
16:
17: $childmaxattempts=10;
1.2 harris41 18: $run =0;#running counter to generate the query-id
19:
1.1 harris41 20: # ------------------------------------ Read httpd access.conf and get variables
21: open (CONFIG,"/etc/httpd/conf/access.conf") || die "Can't read access.conf";
22:
23: while ($configline=<CONFIG>) {
24: if ($configline =~ /PerlSetVar/) {
25: my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
26: chomp($varvalue);
27: $perlvar{$varname}=$varvalue;
28: }
29: }
30: close(CONFIG);
1.4 www 31:
32: # --------------------------------------------- Check if other instance running
33:
34: my $pidfile="$perlvar{'lonDaemons'}/logs/lonsql.pid";
35:
36: if (-e $pidfile) {
37: my $lfh=IO::File->new("$pidfile");
38: my $pide=<$lfh>;
39: chomp($pide);
40: if (kill 0 => $pide) { die "already running"; }
41: }
1.1 harris41 42:
43: # ------------------------------------------------------------- Read hosts file
1.2 harris41 44: $PREFORK=4; # number of children to maintain, at least four spare
1.1 harris41 45:
46: open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";
47:
48: while ($configline=<CONFIG>) {
49: my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
50: chomp($ip);
51:
1.2 harris41 52: $hostip{$ip}=$id;
1.1 harris41 53:
54: if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; }
55:
1.2 harris41 56: $PREFORK++;
1.1 harris41 57: }
58: close(CONFIG);
59:
1.2 harris41 60: $unixsock = "mysqlsock";
61: my $localfile="$perlvar{'lonSockDir'}/$unixsock";
62: my $server;
63: unlink ($localfile);
64: unless ($server=IO::Socket::UNIX->new(Local =>"$localfile",
65: Type => SOCK_STREAM,
66: Listen => 10))
67: {
68: print "in socket error:$@\n";
69: }
1.1 harris41 70:
71: # -------------------------------------------------------- Routines for forking
72: # global variables
1.2 harris41 73: $MAX_CLIENTS_PER_CHILD = 5; # number of clients each child should process
1.1 harris41 74: %children = (); # keys are current child process IDs
1.2 harris41 75: $children = 0; # current number of children
1.1 harris41 76:
77: sub REAPER { # takes care of dead children
78: $SIG{CHLD} = \&REAPER;
79: my $pid = wait;
1.2 harris41 80: $children --;
81: &logthis("Child $pid died");
1.1 harris41 82: delete $children{$pid};
83: }
84:
85: sub HUNTSMAN { # signal handler for SIGINT
86: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
87: kill 'INT' => keys %children;
88: my $execdir=$perlvar{'lonDaemons'};
89: unlink("$execdir/logs/lonsql.pid");
90: &logthis("<font color=red>CRITICAL: Shutting down</font>");
1.2 harris41 91: $unixsock = "mysqlsock";
92: my $port="$perlvar{'lonSockDir'}/$unixsock";
93: unlink(port);
1.1 harris41 94: exit; # clean up with dignity
95: }
96:
97: sub HUPSMAN { # signal handler for SIGHUP
98: local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children
99: kill 'INT' => keys %children;
100: close($server); # free up socket
101: &logthis("<font color=red>CRITICAL: Restarting</font>");
102: my $execdir=$perlvar{'lonDaemons'};
1.2 harris41 103: $unixsock = "mysqlsock";
104: my $port="$perlvar{'lonSockDir'}/$unixsock";
105: unlink(port);
1.1 harris41 106: exec("$execdir/lonsql"); # here we go again
107: }
108:
109: sub logthis {
110: my $message=shift;
111: my $execdir=$perlvar{'lonDaemons'};
1.2 harris41 112: my $fh=IO::File->new(">>$execdir/logs/lonsqlfinal.log");
1.1 harris41 113: my $now=time;
114: my $local=localtime($now);
115: print $fh "$local ($$): $message\n";
116: }
117: # ---------------------------------------------------- Fork once and dissociate
118: $fpid=fork;
119: exit if $fpid;
120: die "Couldn't fork: $!" unless defined ($fpid);
121:
122: POSIX::setsid() or die "Can't start new session: $!";
123:
124: # ------------------------------------------------------- Write our PID on disk
125:
126: $execdir=$perlvar{'lonDaemons'};
127: open (PIDSAVE,">$execdir/logs/lonsql.pid");
128: print PIDSAVE "$$\n";
129: close(PIDSAVE);
130: &logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>");
131:
132: # ----------------------------- Ignore signals generated during initial startup
133: $SIG{HUP}=$SIG{USR1}='IGNORE';
1.2 harris41 134: # ------------------------------------------------------- Now we are on our own
135: # Fork off our children.
136: for (1 .. $PREFORK) {
137: make_new_child();
1.1 harris41 138: }
139:
1.2 harris41 140: # Install signal handlers.
1.1 harris41 141: $SIG{CHLD} = \&REAPER;
142: $SIG{INT} = $SIG{TERM} = \&HUNTSMAN;
143: $SIG{HUP} = \&HUPSMAN;
144:
145: # And maintain the population.
146: while (1) {
147: sleep; # wait for a signal (i.e., child's death)
1.2 harris41 148: for ($i = $children; $i < $PREFORK; $i++) {
149: make_new_child(); # top up the child pool
1.1 harris41 150: }
151: }
152:
1.2 harris41 153:
1.1 harris41 154: sub make_new_child {
155: my $pid;
156: my $sigset;
1.2 harris41 157:
1.1 harris41 158: # block signal for fork
159: $sigset = POSIX::SigSet->new(SIGINT);
160: sigprocmask(SIG_BLOCK, $sigset)
161: or die "Can't block SIGINT for fork: $!\n";
162:
1.2 harris41 163: die "fork: $!" unless defined ($pid = fork);
164:
1.1 harris41 165: if ($pid) {
166: # Parent records the child's birth and returns.
167: sigprocmask(SIG_UNBLOCK, $sigset)
168: or die "Can't unblock SIGINT for fork: $!\n";
169: $children{$pid} = 1;
170: $children++;
171: return;
172: } else {
1.2 harris41 173: # Child can *not* return from this subroutine.
1.1 harris41 174: $SIG{INT} = 'DEFAULT'; # make SIGINT kill us as it did before
175:
176: # unblock signals
177: sigprocmask(SIG_UNBLOCK, $sigset)
178: or die "Can't unblock SIGINT for fork: $!\n";
1.2 harris41 179:
180:
181: #open database handle
182: # making dbh global to avoid garbage collector
1.1 harris41 183: unless (
1.5 harris41 184: $dbh = DBI->connect("DBI:mysql:loncapa","www","123",{ RaiseError =>0,PrintError=>0})
1.1 harris41 185: ) {
186: my $st=120+int(rand(240));
187: &logthis("<font color=blue>WARNING: Couldn't connect to database ($st secs): $@</font>");
1.2 harris41 188: print "database handle error\n";
1.1 harris41 189: sleep($st);
1.2 harris41 190: exit;
191:
192: };
193: # make sure that a database disconnection occurs with ending kill signals
194: $SIG{TERM}=$SIG{INT}=$SIG{QUIT}=$SIG{__DIE__}=\&DISCONNECT;
195:
1.1 harris41 196: # handle connections until we've reached $MAX_CLIENTS_PER_CHILD
197: for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) {
198: $client = $server->accept() or last;
1.2 harris41 199:
200: # do something with the connection
1.1 harris41 201: $run = $run+1;
1.2 harris41 202: my $userinput = <$client>;
203: chomp($userinput);
204:
1.7 ! harris41 205: my ($conserver,$querytmp,$customtmp)=split(/&/,$userinput);
1.3 harris41 206: my $query=unescape($querytmp);
1.7 ! harris41 207: my $custom=unescape($customtmp);
1.2 harris41 208:
209: #send query id which is pid_unixdatetime_runningcounter
210: $queryid = $thisserver;
211: $queryid .="_".($$)."_";
212: $queryid .= time."_";
213: $queryid .= $run;
214: print $client "$queryid\n";
215:
216: #prepare and execute the query
1.3 harris41 217: my $sth = $dbh->prepare($query);
218: my $result;
219: unless ($sth->execute())
220: {
221: &logthis("<font color=blue>WARNING: Could not retrieve from database: $@</font>");
222: $result="";
223: }
224: else {
225: my $r1=$sth->fetchall_arrayref;
226: my @r2; map {my $a=$_; my @b=map {escape($_)} @$a; push @r2,join(",", @b)} (@$r1);
227: $result=join("&",@r2) . "\n";
228: }
1.7 ! harris41 229:
! 230: # do custom metadata searching here and build into result
! 231:
1.2 harris41 232: &reply("queryreply:$queryid:$result",$conserver);
233:
1.1 harris41 234: }
235:
236: # tidy up gracefully and finish
1.2 harris41 237:
238: #close the database handle
239: $dbh->disconnect
240: or &logthis("<font color=blue>WARNING: Couldn't disconnect from database $DBI::errstr ($st secs): $@</font>");
1.1 harris41 241:
242: # this exit is VERY important, otherwise the child will become
243: # a producer of more and more children, forking yourself into
244: # process death.
245: exit;
246: }
1.2 harris41 247: }
1.1 harris41 248:
1.2 harris41 249: sub DISCONNECT {
250: $dbh->disconnect or
251: &logthis("<font color=blue>WARNING: Couldn't disconnect from database $DBI::errstr ($st secs): $@</font>");
252: exit;
253: }
1.1 harris41 254:
1.2 harris41 255: # -------------------------------------------------- Non-critical communication
1.1 harris41 256:
1.2 harris41 257: sub subreply {
258: my ($cmd,$server)=@_;
259: my $peerfile="$perlvar{'lonSockDir'}/$server";
260: my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile",
261: Type => SOCK_STREAM,
262: Timeout => 10)
263: or return "con_lost";
264: print $sclient "$cmd\n";
265: my $answer=<$sclient>;
266: chomp($answer);
267: if (!$answer) { $answer="con_lost"; }
268: return $answer;
269: }
1.1 harris41 270:
1.2 harris41 271: sub reply {
272: my ($cmd,$server)=@_;
273: my $answer;
274: if ($server ne $perlvar{'lonHostID'}) {
275: $answer=subreply($cmd,$server);
276: if ($answer eq 'con_lost') {
277: $answer=subreply("ping",$server);
278: $answer=subreply($cmd,$server);
279: }
280: } else {
281: $answer='self_reply';
282: }
283: return $answer;
284: }
1.1 harris41 285:
1.3 harris41 286: # -------------------------------------------------------- Escape Special Chars
287:
288: sub escape {
289: my $str=shift;
290: $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
291: return $str;
292: }
293:
294: # ----------------------------------------------------- Un-Escape Special Chars
295:
296: sub unescape {
297: my $str=shift;
298: $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
299: return $str;
300: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>