Unix Programming - child doesn't get eof from socketpair

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > April 2006 > child doesn't get eof from socketpair





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author child doesn't get eof from socketpair
Mr. Uh Clem

2006-04-27, 7:56 am

I have a program which does TCP communications with remote
devices. When I needed to do encryption to some new devices,
the simple answer seemed to be to run that device's filter
program as a child and have my program talk to it instead
of the device:

prog <---> device

prog <---> crypt <---> device

The simplest way to accomplish this seemed to be to
launch the crypto program as a child and connect up
a socketpair from the parent to the child's stdin &
stdout. No code in the parent changes other than
creating the child on the socketpair instead of making
a TCP connection. This works great on Solaris, but
hangs on HP 11.11 (gcc 3.4.3) because the child never
sees EOF on stdin.

This program illustrates the problem:

/* test socketpair */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <signal.h>

main(int argc, char *argv)
{
int status;
int sv[2];
char buf[50] = "hello world\n";

sigignore(SIGCHLD);

status = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
if (status)
{
perror("socketpair");
exit(1);
}
status = fork();
switch(status)
{
case -1:

perror("fork");
exit(1);

case 0: /* child */

dup2(sv[1], 0); /* stdin */
dup2(sv[1], 1); /* stdout */
close(sv[1]);
close(sv[0]);

execl("/bin/dd", "dd", "of=/tmp/hi", (char *)0 );
perror("exec");
exit(1);

default: /* parent */

close(sv[1]);
sleep(1);
write(sv[0], buf, strlen(buf));
shutdown(sv[0], 1);
for (status = 1; status > 0; )
{
status = read(sv[0], buf, sizeof(buf));
printf("read %d\n", status);
}
exit(0);
}
}

Truss from Solaris:

7637: execve("a.out", 0x08047C80, 0x08047C88) argc = 1
....
7637: sigaction(SIGCLD, 0x08047B7C, 0x00000000) = 0
7637: sigfillset(0xDFB41708) = 0
7637: sigprocmask(SIG_UNBLOCK, 0x08047BD8, 0x00000000) = 0
7637: so_socket(1, 2, 0, "", 1) = 3
7637: so_socket(1, 2, 0, "", 1) = 4
7637: so_socketpair(0x08047C48) = 0
7637: close(3) = 0
7637: fork() = 7638
7638: fork() (returning as child ...) = 7637
7637: close(4) = 0
7638: fcntl(4, F_DUP2FD, 0x00000000) = 0
7637: alarm(0) = 0
7638: fcntl(4, F_DUP2FD, 0x00000001) = 1
7638: close(4) = 0
7637: sigaction(SIGALRM, 0x08047B24, 0x08047BB0) = 0
7638: close(5) = 0
7637: sigprocmask(SIG_BLOCK, 0x08047BA0, 0x08047B90) = 0
7637: alarm(1) = 0
7638: execve("/bin/dd", 0x08047BE8, 0x08047C88) argc = 2
.... = 0
7638: sysi86(SI86FPHW, 0xDFBCB634, 0x08047C2C, 0xDFBFDCA4) = 0x00000000
7638: brk(0x0804CD78) = 0
7638: brk(0x0804ED78) = 0
7638: dup(0) = 3
7638: creat64("/tmp/hi", 0666) = 4
7638: sysconfig(_CONFIG_PAGESIZE) = 4096
7638: brk(0x0804ED78) = 0
7638: brk(0x08050D78) = 0
7638: sigaction(SIGINT, 0x08047A78, 0x08047AD4) = 0
7638: sigaction(SIGINT, 0x08047A78, 0x08047AD4) = 0
7637: Received signal #14, SIGALRM, in sigsuspend() [caught]
7637: sigsuspend(0x08047B80) Err#4 EINTR
7637: setcontext(0x0804796C)
7637: alarm(0) = 0
7637: sigprocmask(SIG_UNBLOCK, 0x08047BA0, 0x00000000) = 0
7637: sigaction(SIGALRM, 0x08047B24, 0x00000000) = 0
7637: write(5, " h e l l o w o r l d\n", 12) = 12
7638: read(3, " h e l l o w o r l d\n", 512) = 12
7637: shutdown(5, 1, 1) = 0
7638: read(3, 0x0804E000, 512) = 0
7638: write(4, " h e l l o w o r l d\n", 12) = 12
7638: close(4) = 0
7638: close(1) = 0
7638: write(2, " 0 + 1", 3) = 3
7638: write(2, " r e c o r d s i n\n", 12) = 12
7638: write(2, " 0 + 1", 3) = 3
7638: write(2, " r e c o r d s o u t".., 13) = 13
7638: llseek(0, 0, SEEK_CUR) Err#29 ESPIPE
7638: _exit(0)
7637: read(5, 0x08047C16, 50) = 0
7637: ioctl(1, TCGETA, 0x08046E48) = 0
7637: write(1, " r e a d 0\n", 7) = 7
7637: llseek(0, 0, SEEK_CUR) = 15370
7637: _exit(0)


Truss from HPUX :

20120: execve("./a.out", 0x7b0404bc, 0x7b0404c4) = 0 [32-bit]
....
20120: sigaction(SIGCLD, 0x7b0406a8, NULL) = 0
20120: socketpair(1, 1, 0, 0x7b0405d0) = 0
20120: fork() , 20121
20121: fork() (returning as child ...) , 20120
20121: dup2(5, 0) = 0
20120: close(5) = 0
20121: dup2(5, 1) = 1
20120: sigprocmask(SIG_BLOCK, 0x7b0406c8, 0x7b0406e8) = 0
20121: close(5) = 0
20120: getitimer(ITIMER_REAL, 0x7b040708) = 0
20121: close(4) = 0
20120: time(NULL) = 1145540547
20121: execve("/bin/dd", 0x7b0406b8, 0x7b0404a4) = 0 [32-bit]
....
20121: open("/usr/lib/nls////dd.cat", O_RDONLY, 0177777) ERR#2 ENOENT
20121: dup(0) = 4
20121: fstat64(4, 0x7b040640) = 0
20121: open("/tmp/hi", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 5
20121: fstat64(5, 0x7b0406b8) = 0
20121: brk(0x40007000) = 0
20121: brk(0x40007240) = 0
20121: sigsetreturn(0x7b023f36, 0x6211988, 1392) = 0
20121: sigvec(SIGINT, 0x7b0407c0, 0x7b0407d0) = 0
20121: sigvec(SIGINT, 0x7b0407c0, 0x7b0407d0) = 0
20120: sigtimedwait(0x7b040718, NULL, 0x7b040738) ERR#11 EAGAIN
20120: time(NULL) = 1145540548
20120: sigprocmask(SIG_SETMASK, 0x7b0406e8, NULL) = 0
20121: read(4, "h e l l o w o r l d \n", 512) = 12
20120: write(4, "h e l l o w o r l d \n", 12) = 12
20120: shutdown(4, SHUT_WR) = 0
20120: read(4, 0x7b0405d8, 50) [sleeping]

Is there a portable way to have a parent talk to a child through
a socketpair, or am I going to have to recode the parent to use
a pair of pipes and recode to have potentially different input and
output descriptors in the parent?

--
Clem
"If you push something hard enough, it will fall over."
- Fudd's first law of opposition
Nils O. Selåsdal

2006-04-27, 7:56 am

Mr. Uh Clem wrote:
> I have a program which does TCP communications with remote
> devices. When I needed to do encryption to some new devices,
> the simple answer seemed to be to run that device's filter
> program as a child and have my program talk to it instead
> of the device:
>
> prog <---> device
>
> prog <---> crypt <---> device
>
> The simplest way to accomplish this seemed to be to
> launch the crypto program as a child and connect up
> a socketpair from the parent to the child's stdin &
> stdout. No code in the parent changes other than
> creating the child on the socketpair instead of making
> a TCP connection. This works great on Solaris, but
> hangs on HP 11.11 (gcc 3.4.3) because the child never
> sees EOF on stdin.
>
> This program illustrates the problem:
>
> /* test socketpair */
>
> #include <stdlib.h>
> #include <stdio.h>
> #include <unistd.h>
> #include <sys/socket.h>
> #include <signal.h>
>
> main(int argc, char *argv)
> {
> int status;
> int sv[2];
> char buf[50] = "hello world\n";
>
> sigignore(SIGCHLD);
>
> status = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
> if (status)
> {
> perror("socketpair");
> exit(1);
> }
> status = fork();
> switch(status)
> {
> case -1:
>
> perror("fork");
> exit(1);
>
> case 0: /* child */
>
> dup2(sv[1], 0); /* stdin */
> dup2(sv[1], 1); /* stdout */
> close(sv[1]);
> close(sv[0]);

If you dup these descriptors you want to close the originals too.
Close /all/ that you don't use.
Nils O. Selåsdal

2006-04-27, 7:56 am

Nils O. Selåsdal wrote:
> Mr. Uh Clem wrote:

[snip]
> If you dup these descriptors you want to close the originals too.
> Close /all/ that you don't use.

So much for not beeing able to read.
Nevermind me.
Mr. Uh Clem

2006-04-27, 7:56 am

Nils O. Selåsdal wrote:
> Mr. Uh Clem wrote:
....[vbcol=seagreen]

default: /* parent */

close(sv[1]);
sleep(1);
write(sv[0], buf, strlen(buf));

[vbcol=seagreen]
> If you dup these descriptors you want to close the originals too.
> Close /all/ that you don't use.



I believe this program winds up with the parent having
sv[0] as its end of the socketpair and the child having
the other end on stdin and stdout. All other unnecessary
descriptors are closed. (parent sv[1], child sv[0] & sv[1])

I'm suspicious that having the socket duped to two different
descriptors in the child is the problem.

--
Clem
"If you push something hard enough, it will fall over."
- Fudd's first law of opposition
purple_stars

2006-04-27, 7:56 am


Mr. Uh Clem wrote:
[snip]
> I believe this program winds up with the parent having
> sv[0] as its end of the socketpair and the child having
> the other end on stdin and stdout. All other unnecessary
> descriptors are closed. (parent sv[1], child sv[0] & sv[1])
>
> I'm suspicious that having the socket duped to two different
> descriptors in the child is the problem.
>
> --
> Clem
> "If you push something hard enough, it will fall over."
> - Fudd's first law of opposition


how about watching for the child's pid when it exits and using either
EOF or your child's exit as your conditional, yeah, i know, it's a
hack, but it would work. as for the actual answer to your question i'm
going to think about it because i've been there/done that, but it's
been a while and i'm not recalling it at the moment.

Mr. Uh Clem

2006-04-27, 7:56 am

purple_stars wrote:

> how about watching for the child's pid when it exits


It doesn't get EOF when I shutdown() the socket,
hence will not exit.

--
Clem
"If you push something hard enough, it will fall over."
- Fudd's first law of opposition
purple_stars

2006-04-27, 7:56 am

Mr. Uh Clem wrote:
> purple_stars wrote:
>
>
> It doesn't get EOF when I shutdown() the socket,
> hence will not exit.
>
> --
> Clem
> "If you push something hard enough, it will fall over."
> - Fudd's first law of opposition


how about just sending it an EOF ... that is, forget the shutdown and
just write(2) an EOF character across the socket descriptor.

Mr. Uh Clem

2006-04-27, 7:56 am

purple_stars wrote:
> Mr. Uh Clem wrote:
>
> how about just sending it an EOF ... that is, forget the shutdown and
> just write(2) an EOF character across the socket descriptor.
>

There's no such thing as an EOF character. The semantics are that
the EOF condition is sensed by a read returning 0 bytes. This happens
when the remote end of a pipe or socket is closed, or shutdown is
performed on a socket.

--
Clem
"If you push something hard enough, it will fall over."
- Fudd's first law of opposition
Gordon Burditt

2006-04-27, 7:56 am

>>>> how about watching for the child's pid when it exits
>There's no such thing as an EOF character. The semantics are that
>the EOF condition is sensed by a read returning 0 bytes.


>This happens
>when the remote end of a pipe or socket is closed,


No, this happens when *all* of the write ends of a pipe or socket
are closed. Usually not getting EOF is a problem of not closing
off extra pipe ends after a fork() in the process that's NOT
using the write end.

>or shutdown is
>performed on a socket.


Gordon L. Burditt
Mr. Uh Clem

2006-04-27, 7:56 am

Gordon Burditt wrote:
>
>
> No, this happens when *all* of the write ends of a pipe or socket
> are closed. Usually not getting EOF is a problem of not closing
> off extra pipe ends after a fork() in the process that's NOT
> using the write end.
>
>
> Gordon L. Burditt


Yes, I forgot "all". Normally I do this sort of
things with pipes, not a socket. But I'm trying to
avoid recoding for (potentially) separate output and
input fds in my program (parent).


-----< child stdout
/
parent <>---- socket -------+
\-----> child stdin

It does work on Solaris, but not HP. Actually,
I'm rather surprised Solaris works. If one considers
that there are two handles at the child end, but on
the other hand, shouldn't shutdown() by the parent
result in an eof condition at any child ends of the
socket??


--
Clem
"If you push something hard enough, it will fall over."
- Fudd's first law of opposition
Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com