Wednesday, June 23, 2010

perlipc in CGI

Sometimes we need start a background process from CGI. When leave that CGI by closing page or window we wish to clean up what we have put in the background. This is perlipc get into role.

There is an example. I have a page displaying a list of network interfaces. I want to get a real time throughput(I/O) chart window when I click on one of these interfaces. Certainly it should be "UP". My solution is forking a poller in background. Poller keeps polling, calculating, and writing data to a shared memory segment. CGI reads from shared memory segment then draws chart for user.



+----------+ fork +------+
|chart page| ----------------> |poller|
+----------+ +------+
| ^
| |
|request |Shared Memory
| |
| +--------+ |
+------> | charter| <------+
+--------+


When click "Close" button on chart page, I need to clean up shared memory and kill the poller. This works fine.

This is the chart page:


...
...

defined (my $pid = fork) or die "Can't fork: $!";

if (!$pid) {
setsid;
open STDIN, '>/dev/null' or die "Can't read from /dev/null: $!";
open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!";
exec '/usr/local/NPMS/bin/if_poller.pl', $val->{ip}, $val->{commstring}, $val->{if};
}
...
...

print <<ENDOFFORM;
<script type="text/javascript">
function clean_up() {
document.getElementById('form1').submit();
window.close();
}
</script>
<form id="form1" method="post" action="clean_poller.cgi">
<input name="pid" value="$pid" type="hidden">
<input name="close" value="Close" onclick="clean_up();" type="button">
</form>
ENDOFFORM



This is the clean_poller.cgi:


...
...
kill 'INT' => $in{'pid'};
...
...


This is the poller which trap SIGINT to gracefully exit after clean up resources:


...
...
my $EXIT = 0;
$SIG{INT} = sub {
$EXIT = 1;
};
...
...
while (1) {
...
...
do polling stuff
do {
IPC::Shareable->clean_up_all;
exit;
} if $EXIT;
...
...
}



Let me explain this whole thing:
1. User click one of the active interfaces("UP" state)
2. Open a new window
3. Before display chart, fork a poller
4. poller start to work in background
5. poller write data sample to shared memory segment
6. chart page request charter to draw a chart
7. charter read data sample from shared memory segment and draw
8. User click "Close" button
9. chart page request clean_poller.cgi before closing
10. clean_poller.cgi get pid and send a INT to it
11. clean_poller.cgi close window
12. poller get INT signal, clean up shared memory segment he created
13. poller exit
14. all resources get cleaned up

That's it!

Detail information refer to perlipc, IPC::Shareable, signal, fork, ...

No comments:

Post a Comment