System() returning ECHILD error on FreeBSD 7.2

Naveen Gujje gujjenaveen at
Wed Feb 10 07:40:05 UTC 2010


We have a web proxy (running on FreeBSD 7.2-RELEASE-p2), where we register
the SIGCHLD handler as follows:

signal(SIGCHLD, SigChildHandler);

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

    const char *command;
    pid_t pid, savedpid;
    int pstat;
    struct sigaction ign, intact, quitact;
    sigset_t newsigblock, oldsigblock;

    if (!command)        /* just checking... */

     * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
     * existing signal dispositions.
    ign.sa_handler = SIG_IGN;
    ign.sa_flags = 0;
    (void)_sigaction(SIGINT, &ign, &intact);
    (void)_sigaction(SIGQUIT, &ign, &quitact);
    (void)sigaddset(&newsigblock, SIGCHLD);
    (void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
    switch(pid = fork()) {
    case -1:            /* error */
    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);
    default:            /* parent */
        savedpid = pid;
        do {
            pid = _wait4(savedpid, &pstat, 0, (struct rusage *)0);
        } while (pid == -1 && errno == EINTR);
    (void)_sigaction(SIGINT, &intact, NULL);
    (void)_sigaction(SIGQUIT,  &quitact, NULL);
    (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
    return(pid == -1 ? -1 : pstat);


More information about the freebsd-hackers mailing list