Hi,
I just noticed a wired case where poll returns an signalled descriptor
event with revents == 0.
This can happen regularly with pipes if you create two separate pipes
for stdout and stderr for redirected process.
E.g. redirecting '/bin/sh ls' with two set of pipes and doing poll on
both stderr and stdout will cause the poll to return 1 but the returned
pollfd will have revents == 0 for stdout
(because sh cannot execute binary file).
I suppose this should actually be POLLHUP cause the next read will
return zero for that descriptor (meaning EOF).
Looking at our poll code I see that we just skip descriptors
for which poll returned as signaled but with revents == 0.
I wonder weather this is a correct approach, cause
1. I cannot find any reference in poll man pages
what the revents == 0 means.
2. This is potential infinite loop because one will end up calling
poll with SUCCESS and zero descriptors returned.
I have a small test case that proves this issue
(just a plain code, no APR)
$ gcc testpoll.c -o testpoll
$ ./testpoll /bin/sh ls
... and watch for the result
It's wired that it doesn't return revents == 0 all the times.
Tested on Linux (Fedora 12) and Mac OSX 10.6
Same happens if you modify the code to use select instead poll.
Anyone knows what might be the technical background
for such a behaviour?
Also is just skipping those descriptors and never reporting
that situation back in our code correct approach?
Regards
--
^TM
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
static int _nonblock(int fd, int on)
{
/* Use non-blocking I/O
*/
long mode, forg;
if ((mode = fcntl(fd, F_GETFL, 0)) == -1)
return -1;
forg = mode;
if (on)
mode |= O_NONBLOCK;
else
mode &= ~O_NONBLOCK;
if (forg != mode && fcntl(fd, F_SETFL, mode) == -1)
return -1;
return 0;
}
/*
* Test with '/bin/sh ls'
*/
int main(int argc, char *const argv[])
{
char *const *args = argv;
pid_t pid;
int cnt;
int status;
int stdinp_pipe[2];
int stdout_pipe[2];
int stderr_pipe[2];
if (argc < 3) {
fputs("Usage: testpoll program cmdline\n", stderr);
exit(EINVAL);
}
++args;
if(pipe(stdout_pipe) == -1)
return errno;
if(pipe(stderr_pipe) == -1)
return errno;
if (_nonblock(stdout_pipe[0], 1))
return errno;
if (_nonblock(stderr_pipe[0], 1))
return errno;
/* Disables POLHUP ? */
signal(SIGPIPE, SIG_IGN);
if ((pid = fork()) == -1)
exit(errno);
if (pid == 0) {
close(stdout_pipe[0]);
close(stderr_pipe[0]);
dup2(stdout_pipe[1], 1);
dup2(stderr_pipe[1], 2);
execv(*args, args);
exit(-1);
}
close(stdout_pipe[1]);
close(stderr_pipe[1]);
for (cnt = 0; cnt < 25; cnt++) {
int i = 0, nsig;
struct pollfd pout = { stdout_pipe[0], POLLIN, 0 };
struct pollfd perr = { stderr_pipe[0], POLLIN, 0 };
struct pollfd pfd[2];
if (stdout_pipe[0] != -1) {
// memset(&pfd[i], 0, sizeof(struct pollfd));
pfd[i].fd = stdout_pipe[0];
pfd[i].events = POLLIN;
pfd[i].revents = 0;
i++;
}
if (stderr_pipe[0] != -1) {
// memset(&pfd[i], 0, sizeof(struct pollfd));
pfd[i].fd = stderr_pipe[0];
pfd[i].events = POLLIN;
pfd[i].revents = 0;
// pfd[i++] = perr;
i++;
}
if (!i) {
printf("No more descriptors left.\nQuitting.\n");
break;
}
nsig = poll(pfd, i, -1);
if (nsig < 1)
break;
for (i = 0; i < nsig; i++) {
int zeropoll = 0;
char b[128];
size_t rd;
if (pfd[i].revents == 0) {
printf("poll with %d descriptor and fd[%d].revents == 0\n",
nsig, i);
if (pfd[i].fd == stdout_pipe[0])
printf("signaled on the stdout\n");
else
printf("signaled on the stderr\n");
if (cnt > 5) {
printf("\n\nOK You have seen enough.\n"
"Let's force read on the stderr.\n"
"Notice that signal for stderr was never returned\n"
"from the poll although we are providing it to the\n"
"poll in a second pollfd\n\n\n");
pfd[i].fd = stderr_pipe[0];
zeropoll = 1;
}
else {
printf("\nWe are now in endless loop\n"
"poll will always return 1 with revents == 0\n"
"This is event for stdout, although the real\n"
"event is for stderr!\n");
sleep(1);
}
}
if (pfd[i].fd == stdout_pipe[0]) {
rd = read(stdout_pipe[0], b, sizeof(b));
if (rd > 0) {
write(1, b, rd);
}
else {
if (rd == 0)
printf("Closing stdout due to zero read\n");
else
printf("Closing stdout due to error %d\n", errno);
close(stdout_pipe[0]);
stdout_pipe[0] = -1;
}
}
if (pfd[i].fd == stderr_pipe[0]) {
rd = read(stderr_pipe[0], b, sizeof(b));
if (rd > 0) {
write(1, b, rd);
}
else {
if (rd == 0)
printf("Closing stderr due to zero read\n");
else
printf("Closing stderr due to error %d\n", errno);
close(stderr_pipe[0]);
stderr_pipe[0] = -1;
}
}
}
}
waitpid(pid, &status, WUNTRACED);
return status;
}