N
none
Hi, I have some questions about perl codes
running as daemon which launch a child process.
I need to run a perl script as a daemon. The
script will monitor tcpdump's output. At first
my script was using a temp file to store the
tcpdump output, but I decided to use a pipe
instead (and did not want to run my script piped
on the command line: tcpdump | ./foo . I wanted
to include the pipe to tcpdump in the script).
The best way I found to do this is by creating
a child process for tcpdump, because running:
open LOGTMPFILE, "tcpdump -i ne1 2> /dev/null |";
do not return the control to the script until
tcpdump terminated. I'm doing it in starttcpdump()
and it works very well, but I'm not sure that
it's the best way to do this.
Another thing I wanted is to be able to call
my script like this to terminated the running
daemon:
~$ foo stop
Here I have a problem, what I planned to do
was catching signals in the script to make some
cleanup before terminating the script:
- kill the child process
- close the tcpdump open pipe (LOGTMPFILE)
if I don't do this, tcpdump continues to run
when the script ends. The problem I have is
that only the child process can close the
LOGTMPFILE file. So I tought I should kill the
child process first (which close the tcpdump
pipe) and then ask the child process to close
the parent process. I also tried to close the
parent process, thinking it would automatically
close the child and tcpdump pipe. but it does
not work. Everything I tried seems to hang at
close LOGTMPFILE and never give back the control
to the script.
The best I was able to get, is have both instances
of perl (parent and child) killed, but tcpdump
still running. By the way it seems to work well
if I press CTRL-C will running the script normally
(not as daemon). the tcpdump process is killed.
Here are my questions:
- I'm I doing tcpdump pipe launching the right
way? it's the best way I found to do this. But
it takes more memory (another instance of the
process). Is there a better way. I do not want
to use temp files.
- Is there anything wrong with my daemon mode
subroutine?
- How can I modify my code to be able to kill
everything: parent, child and tcpdump processes.
- Which signal should I use to stop the script?
HUP, INT, QUIT ?
Any suggestions and tips would be greatly appreciated.
Thanks in advance.
Here a very simplified version of my code. I didn't
test it and it may not run correctly. But it
demonstrate what I want to achieve, I have removed
a lot of things for code simplicity like every
"or die ..." tests and irrelevant code:
#!/usr/bin/perl
use FileHandle;
use POSIX;
if ($ARGV[0] eq "stop")
{
print "Terminating process ...\n";
sendterminatesig ();
exit 0;
}
createdaemon();
starttcpdump ();
$SIG{INT} = \&signalhandler;
while (1)
{
# do something
}
exit 0;
sub createdaemon ()
{
# for problems on mounted partitions
chdir '/';
umask 0;
# fork() a child process and have the parent process exit()
my $pid = fork;
exit if $pid;
die "\nError: Couldn't fork: $!\n" unless defined $pid;
open (PIDFILE, ">/var/run/foo.pid");
printf PIDFILE "%d\n", POSIX::getpid();
close (PIDFILE);
# so it doesn't have a controlling terminal.
POSIX::setsid();
open STDIN, '/dev/null';
open STDOUT, '>/dev/null';
open STDERR, '>/dev/null';
}
sub starttcpdump ()
{
if (!defined(my $kidpid = fork()))
{
# fork returned undef, so failed
die "\nError: Cannot fork: $!\n";
}
elsif ($kidpid == 0)
{
# fork returned 0, so this branch is the child
open LOGTMPFILE, "tcpdump -i ne1 2> /dev/null |";
open (PIDFILE, ">/var/run/foo.child.pid");
printf PIDFILE "%d\n", POSIX::getpid();
close (PIDFILE);
}
else
{
# so this branch is the parent
waitpid($kidpid, 0);
}
}
sub sendterminatesig ()
{
if (-e "/var/run/foo.pid")
{
# Open and read pid file
open (PIDFILE, "/var/run/foo.child.pid");
my $childpid = <PIDFILE>;
close (PIDFILE);
# sending message to process
if (kill 0 => $pid)
{
# Here I tried: calling the parent process
# first, so it close the tcpdump pipe, but
# it doesn't work. I also tried calling the
# child process then the parent process, but
# the tcpdump process is not closed. Then I
# tried to call the child process to close the
# pipe (in signalhandle) and ask the child process
# to kill the parent (still in signalhandle).
# None of them works.
kill INT => $childpid;
exit 0;
}
elsif ($! == EPERM)
{ # changed uid
print "\nError: foo (pid:$pid) has escaped my control!\n";
exit 1;
}
elsif ($! == ESRCH)
{ # process not found or zombied
print "\nError: foo (pid:$pid) is deceased.\n";
exit 1;
}
else
{
print "\nError: couldn't check on the status: $!\n";
exit 1;
}
}
else # pid file not found, quit
{
print "\nError: pid file not found!\n";
exit 1;
}
}
sub signalhandler
{
my $signame = shift;
# Child process
if (-e "/var/run/foo.child.pid")
{
open (PIDFILE, "/var/run/foo.pid");
my $pid = <PIDFILE>;
close (PIDFILE);
close (LOGTMPFILE); # Close the tcpdump pipe
kill INT => $pid; # Kill the parent process
unlink("/var/run/foo.child.pid");
}
else # Parent process
{
unlink("/var/run/foo.pid");
}
exit 0;
}
running as daemon which launch a child process.
I need to run a perl script as a daemon. The
script will monitor tcpdump's output. At first
my script was using a temp file to store the
tcpdump output, but I decided to use a pipe
instead (and did not want to run my script piped
on the command line: tcpdump | ./foo . I wanted
to include the pipe to tcpdump in the script).
The best way I found to do this is by creating
a child process for tcpdump, because running:
open LOGTMPFILE, "tcpdump -i ne1 2> /dev/null |";
do not return the control to the script until
tcpdump terminated. I'm doing it in starttcpdump()
and it works very well, but I'm not sure that
it's the best way to do this.
Another thing I wanted is to be able to call
my script like this to terminated the running
daemon:
~$ foo stop
Here I have a problem, what I planned to do
was catching signals in the script to make some
cleanup before terminating the script:
- kill the child process
- close the tcpdump open pipe (LOGTMPFILE)
if I don't do this, tcpdump continues to run
when the script ends. The problem I have is
that only the child process can close the
LOGTMPFILE file. So I tought I should kill the
child process first (which close the tcpdump
pipe) and then ask the child process to close
the parent process. I also tried to close the
parent process, thinking it would automatically
close the child and tcpdump pipe. but it does
not work. Everything I tried seems to hang at
close LOGTMPFILE and never give back the control
to the script.
The best I was able to get, is have both instances
of perl (parent and child) killed, but tcpdump
still running. By the way it seems to work well
if I press CTRL-C will running the script normally
(not as daemon). the tcpdump process is killed.
Here are my questions:
- I'm I doing tcpdump pipe launching the right
way? it's the best way I found to do this. But
it takes more memory (another instance of the
process). Is there a better way. I do not want
to use temp files.
- Is there anything wrong with my daemon mode
subroutine?
- How can I modify my code to be able to kill
everything: parent, child and tcpdump processes.
- Which signal should I use to stop the script?
HUP, INT, QUIT ?
Any suggestions and tips would be greatly appreciated.
Thanks in advance.
Here a very simplified version of my code. I didn't
test it and it may not run correctly. But it
demonstrate what I want to achieve, I have removed
a lot of things for code simplicity like every
"or die ..." tests and irrelevant code:
#!/usr/bin/perl
use FileHandle;
use POSIX;
if ($ARGV[0] eq "stop")
{
print "Terminating process ...\n";
sendterminatesig ();
exit 0;
}
createdaemon();
starttcpdump ();
$SIG{INT} = \&signalhandler;
while (1)
{
# do something
}
exit 0;
sub createdaemon ()
{
# for problems on mounted partitions
chdir '/';
umask 0;
# fork() a child process and have the parent process exit()
my $pid = fork;
exit if $pid;
die "\nError: Couldn't fork: $!\n" unless defined $pid;
open (PIDFILE, ">/var/run/foo.pid");
printf PIDFILE "%d\n", POSIX::getpid();
close (PIDFILE);
# so it doesn't have a controlling terminal.
POSIX::setsid();
open STDIN, '/dev/null';
open STDOUT, '>/dev/null';
open STDERR, '>/dev/null';
}
sub starttcpdump ()
{
if (!defined(my $kidpid = fork()))
{
# fork returned undef, so failed
die "\nError: Cannot fork: $!\n";
}
elsif ($kidpid == 0)
{
# fork returned 0, so this branch is the child
open LOGTMPFILE, "tcpdump -i ne1 2> /dev/null |";
open (PIDFILE, ">/var/run/foo.child.pid");
printf PIDFILE "%d\n", POSIX::getpid();
close (PIDFILE);
}
else
{
# so this branch is the parent
waitpid($kidpid, 0);
}
}
sub sendterminatesig ()
{
if (-e "/var/run/foo.pid")
{
# Open and read pid file
open (PIDFILE, "/var/run/foo.child.pid");
my $childpid = <PIDFILE>;
close (PIDFILE);
# sending message to process
if (kill 0 => $pid)
{
# Here I tried: calling the parent process
# first, so it close the tcpdump pipe, but
# it doesn't work. I also tried calling the
# child process then the parent process, but
# the tcpdump process is not closed. Then I
# tried to call the child process to close the
# pipe (in signalhandle) and ask the child process
# to kill the parent (still in signalhandle).
# None of them works.
kill INT => $childpid;
exit 0;
}
elsif ($! == EPERM)
{ # changed uid
print "\nError: foo (pid:$pid) has escaped my control!\n";
exit 1;
}
elsif ($! == ESRCH)
{ # process not found or zombied
print "\nError: foo (pid:$pid) is deceased.\n";
exit 1;
}
else
{
print "\nError: couldn't check on the status: $!\n";
exit 1;
}
}
else # pid file not found, quit
{
print "\nError: pid file not found!\n";
exit 1;
}
}
sub signalhandler
{
my $signame = shift;
# Child process
if (-e "/var/run/foo.child.pid")
{
open (PIDFILE, "/var/run/foo.pid");
my $pid = <PIDFILE>;
close (PIDFILE);
close (LOGTMPFILE); # Close the tcpdump pipe
kill INT => $pid; # Kill the parent process
unlink("/var/run/foo.child.pid");
}
else # Parent process
{
unlink("/var/run/foo.pid");
}
exit 0;
}