We seem to be getting improper returns from blocking reads on unix
domain sockets. We're getting a 0 return where we expect to block (and
do on all other platforms including redhat 8.0). We've seen this issue
on AS 3 and 4 as well as other variants.
Attached is a program that shows the problem. Failure is indicated by
"read returned 0."
I haven't found a bug that seems to address this. Inserting an
additional select in front of the read corrects this behavior, but
doesn't seem correct.
Has anybody seen this behavior before? Is there already a bug for it?
Thanks,
Scott
#include <sys/types.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#define BUFSIZE 340
char buf[BUFSIZE];
struct sockaddr_un sockaddr;
void parent(int do_select) {
int fd, new_fd;
int rc;
ssize_t read_rc;
struct sockaddr_un new_addr;
struct timeval to;
int attr_size;
fd_set readfds;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
exit(1);
}
if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
perror("bind");
exit(1);
}
if (listen(fd, 30)) {
perror("listen");
exit(1);
}
while (1) {
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
rc = select(FD_SETSIZE, &readfds, 0,0,0);
if (rc == 1) {
attr_size = sizeof(new_addr);
new_fd = accept(fd, (struct sockaddr *)&new_addr, &attr_size);
if (new_fd < 0) {
perror("accept");
exit(1);
}
if (do_select) {
FD_ZERO(&readfds);
FD_SET(new_fd, &readfds);
to.tv_sec = 1;
to.tv_usec = 0;
while (select(FD_SETSIZE, &readfds, 0,0,&to) < 0) {
perror("select");
FD_ZERO(&readfds);
FD_SET(new_fd, &readfds);
to.tv_sec = 1;
to.tv_usec = 0;
}
}
read_rc = read(new_fd, &buf, sizeof(buf));
if (read_rc <= 0) {
if (read_rc < 0) {
perror("read");
}
fprintf(stderr, "read returned %d\n", read_rc);
}
close(new_fd);
}
}
}
void child() {
int fd;
int rc;
int cycle = 0;
ssize_t write_rc;
sleep(2);
while (1) {
cycle++;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
exit(1);
}
if (connect(fd, (struct sockaddr *) &sockaddr,
sizeof(sockaddr))) {
perror("connect");
fprintf(stderr, "cycle %d\n", cycle);
close(fd);
continue;
}
write_rc = write(fd, &buf, sizeof(buf));
if (write_rc <= 0) {
if (write_rc < 0) {
perror("write");
}
fprintf(stderr, "write returned %d\n", write_rc);
}
close(fd);
}
}
main (int argc, char *argv[]) {
pid_t fork_rc;
int opt;
int do_select = 0;
char dirbuf[1024];
while ((opt = getopt(argc, argv, "s")) != -1) {
if (opt == 's') {
do_select = 1;
continue;
}
else {
fprintf(stderr, "unsupported option: %c\n", opt);
exit(1);
}
}
bzero(&sockaddr, sizeof(sockaddr));
sockaddr.sun_family = AF_UNIX;
getcwd(dirbuf, sizeof(dirbuf));
sprintf(sockaddr.sun_path, "%s/sock%d", dirbuf, getpid());
unlink(sockaddr.sun_path);
fork_rc = fork();
if (fork_rc < 0) {
perror("fork");
exit(1);
}
if (fork_rc) parent(do_select);
else child();
}