Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 10 Feb 2010 12:44:57 +0530
From:      Naveen Gujje <gujjenaveen@gmail.com>
To:        freebsd-hackers@freebsd.org
Subject:   System() returning ECHILD error on FreeBSD 7.2
Message-ID:  <39c945731002092314u4a8fd100q69c0735a11e9063a@mail.gmail.com>

next in thread | raw e-mail | index | archive | help
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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?39c945731002092314u4a8fd100q69c0735a11e9063a>