waitid() fails with EINVAL for SA_RESTART signals

From: Michael Kerrisk
Date: Wed May 18 2005 - 03:22:40 EST


Hello Roland,

I'm seeing a strange behaviour with waitid() in 2.6.12-rc3
(I'm fairly sure the behaviour also appears in other 2.6.x).

If we establish a handler for a signal with SA_RESTART, and that
signal interrupts a blocked waitid(), then waitid() fails with
the error EINVAL. I would expect the call to be restarted
(like wait(), waitpid(), etc.) or if you are choosing to design
waitid() to ignore SA_RESTART, then fail with EINTR.

I haven't been able to spot the source of the EINVAL in kernel
or glibc sources.

The program below can be used to demonstrate the problem.

Cheers,

Michael

/* waitid_intr.c

Michael Kerrisk, May 2005

Demonstration of a problem with waitid() on Linux (Linux 2.6.12-rc3).

If S_A_RESTART was specified for a signal handler, and that signal
is delivered during a blocked waitid(), then the waitid() fails
with EINVAL, when it should either be restarted or (if the intent
is to ignore SA_RESTART in the waitid() implementation) fail
with EINTR. This program demonstrates the problem.

The program creates two children. The first is a child that sleeps
for argv[2] seconds and is then reaped by waitid() in the parent.
The second child sends a signal (SIG) to the parent after argv[3]
seconds.

argv[1] specifies options for the waitid() call (see the code
below).

If the optional argv[4] is present (as any string), then the
handler for the signal sent by the second child is established
with SA_RESTART.

To demonstrate the waitid() problem, use the following command:

./waitid_intr esc 10 2 restart
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <syscall.h>
#include <unistd.h>
#include <errno.h>

#define errExit(msg) { perror(msg); exit(EXIT_FAILURE); }

#define fatalErr(msg) { fprintf(stderr, "%s\n", msg); \
exit(EXIT_FAILURE); }

static void
handler(int sig)
{
int savedErrno;

savedErrno = errno;
printf("Caught signal\n");
errno = savedErrno;
} /* handler */

#define SIG SIGUSR1

int
main(int argc, char *argv[])
{
siginfo_t info;
int options, s;
char *p;
pid_t pid;
struct rusage ru;
struct sigaction sa;

setbuf(stdout, NULL);

printf("Parent's PID is %ld\n", (long) getpid());

if (argc < 4) {
fprintf(stderr, "%s {escHW} child-secs sig-secs [sa_restart]\n",
argv[0]);
exit(EXIT_FAILURE);
}

options = 0;

for (p = argv[1]; *p != '\0'; p++) {
if (*p == 'e') options |= WEXITED;
else if (*p == 's') options |= WSTOPPED;
else if (*p == 'c') options |= WCONTINUED;
else if (*p == 'H') options |= WNOHANG;
else if (*p == 'W') options |= WNOWAIT;

else fatalErr("Bad option letter");
}

/* Create first child to waitid() for */

pid = fork();
if (pid == -1) errExit("fork");

if (pid == 0) {
printf("child (PID = %ld) started\n", (long) getpid());
sleep(atoi(argv[2]));
printf("child (PID = %ld) finished\n", (long) getpid());
exit(EXIT_SUCCESS);
}

/* Parent falls through */

/* Set up handler for signal sent by child 2 */

sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = (argc > 4) ? SA_RESTART : 0;
if (sigaction(SIG, &sa, NULL) == -1) errExit("sigaction");

/* Create a second child that will send parent a signal */

switch (fork()) {
case -1:
errExit("fork-2");

case 0:
sleep(atoi(argv[3]));
printf("Sending signal to parent\n");
if (kill(getppid(), SIG) == -1) errExit("kill");
exit(EXIT_SUCCESS);

default:
break;
}

/* Parent now waits for the first child */

memset(&info, 0, sizeof(siginfo_t));

s = waitid(P_PID, pid, &info, options);
//s = syscall(SYS_waitid, P_ALL, 0, &info, options, &ru);
//s = syscall(SYS_waitpid, -1, NULL, 0);

if (s == -1) errExit("waitid");

printf("waitid() returned %d\n", s);
printf(" ");
printf("si_pid=%ld; ", (long) info.si_pid);
printf("si_uid=%ld; ", (long) info.si_uid);
printf("si_signo=%ld; ", (long) info.si_signo);
printf("\n");
printf(" ");
printf("si_status=%ld; ", (long) info.si_status);
printf("si_code=%ld ", (long) info.si_code);
printf("(%s); ",
(info.si_code == CLD_EXITED) ? "CLD_EXITED" :
(info.si_code == CLD_KILLED) ? "CLD_KILLED" :
(info.si_code == CLD_DUMPED) ? "CLD_DUMPED" :
(info.si_code == CLD_TRAPPED) ? "CLD_TRAPPED" :
(info.si_code == CLD_STOPPED) ? "CLD_STOPPED" :
(info.si_code == CLD_CONTINUED) ? "CLD_CONTINUED" :
"????");
printf("\n");
} /* main */


--
Weitersagen: GMX DSL-Flatrates mit Tempo-Garantie!
Ab 4,99 Euro/Monat: http://www.gmx.net/de/go/dsl
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/