System() returning ECHILD error on FreeBSD 7.2
Naveen Gujje
gujjenaveen at gmail.com
Wed Feb 10 07:40:05 UTC 2010
Hi,
We have a web proxy (running on FreeBSD 7.2-RELEASE-p2), where we register
the SIGCHLD handler as follows:
signal(SIGCHLD, SigChildHandler);
void
SigChildHandler(int sig)
{
pid_t pid;
/* get status of all dead procs */
do {
int procstat;
pid = waitpid(-1, &procstat, WNOHANG);
if (pid < 0) {
if (errno == EINTR)
continue; /* ignore it */
else {
if (errno != ECHILD)
perror("getting waitpid");
pid = 0; /* break out */
}
}
else if (pid != 0)
syslog(LOG_INFO, "child process %d completed", (int) pid);
} while (pid);
signal(SIGCHLD, SigChildHandler);
}
And, in some other part of the code, we call system() to add an ethernet
interface. This system() call is returning -1 with errno set to ECHILD,
though the passed command is executed successfully. I have noticed that,
the problem is observed only after we register SigChildHandler. If I have a
simple statement like system("ls") before and after the call to
signal(SIGCHLD, SigChildHandler), the call before setting signal handler
succeeds without errors and the call after setting signal handler returns -1
with errno set to ECHILD.
Here, I believe that within the system() call, the child exited before the
parent got a chance to call _wait4 and thus resulted in ECHILD error. But,
for the child to exit without notifying the parent, SIGCHLD has to be set to
SIG_IGN in the parent and this is not the case, because we are already
setting it to SigChildHandler. If I set SIGCHLD to SIG_DFL before calling
system() then i don't see this problem.
I would like to know how setting SIGCHLD to SIG_DFL or SigChildHanlder is
making the difference.
Can someone please help me out?
[Note: Replacing signal() with sigaction() doesn't help either.]
Following is the code for system() call
cat /usr/src/lib/libc/stdlib/system.c
int
__system(command)
const char *command;
{
pid_t pid, savedpid;
int pstat;
struct sigaction ign, intact, quitact;
sigset_t newsigblock, oldsigblock;
if (!command) /* just checking... */
return(1);
/*
* Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
* existing signal dispositions.
*/
ign.sa_handler = SIG_IGN;
(void)sigemptyset(&ign.sa_mask);
ign.sa_flags = 0;
(void)_sigaction(SIGINT, &ign, &intact);
(void)_sigaction(SIGQUIT, &ign, &quitact);
(void)sigemptyset(&newsigblock);
(void)sigaddset(&newsigblock, SIGCHLD);
(void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
switch(pid = fork()) {
case -1: /* error */
break;
case 0: /* child */
/*
* Restore original signal dispositions and exec the command.
*/
(void)_sigaction(SIGINT, &intact, NULL);
(void)_sigaction(SIGQUIT, &quitact, NULL);
(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL);
_exit(127);
default: /* parent */
savedpid = pid;
do {
pid = _wait4(savedpid, &pstat, 0, (struct rusage *)0);
} while (pid == -1 && errno == EINTR);
break;
}
(void)_sigaction(SIGINT, &intact, NULL);
(void)_sigaction(SIGQUIT, &quitact, NULL);
(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
return(pid == -1 ? -1 : pstat);
}
Thanks,
Naveen
More information about the freebsd-hackers
mailing list