You should probably consider using Expect to write this; it's designed for
writing wrappers around interactive programs like ftp.
The reason your reads may be hanging is because output to pipes is usually
buffered, so you won't be able to read until the child process fills its
output buffer.
"Advanced Programming in the Unix Environment", by W. Richard Stevens, has
good examples of using pipes, but I don't recall if it discusses issues
like this. "Unix Network Programming" also has good examples of
communicating between processes.
--
Barry Margolin, bar...@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
Support the anti-spam movement; see <http://www.cauce.org/>
Please don't send technical questions directly to me, post them to newsgroups.
> good examples of using pipes, but I don't recall if it discusses issues
> like this. "Unix Network Programming" also has good examples of
> communicating between processes.
>
Yes it does, and it refers to Expect as a good example of using
pseudo-terminals (ptys) to handle normally interactive processes.
Wake.
I'll give some more examples, and in describing them, describe some
of the common problems that can make them hang. This may be of some use.
In terms of unix system calls and such (no error checking, no exec, etc),
#include <string.h>
main() {
int in, out; /* file descriptors for comm with child stdin/stdout */
int pin[2], pout[2]; /* anoymous pipes */
char *msg = "Now is the time for all good men...\n";
char buf[256];
int len;
pipe(pin);
pipe(pout);
if( fork() ){
/* parent process... */
in = pin[1]; out = pout[0];
close( pin[0] ); close( pout[1] );
write(in, msg, strlen(msg)
close(in);
while( len = read(out,buf,256) )
write( 1, buf, len );
} else {
/* child process */
close(0); close(1); close(2);
dup(pin[0]); dup(pout[1]); dup(1);
close(pin[0]); close(pin[1]);
close(pout[0]); close(pout[1]);
while( len = read(0,buf,256) )
write( 1, buf, len );
}
return 0;
}
Note that if the close(in) after the parent writes isn't there,
this little program will pend forever, the child waiting for
more commands on in (because the pipe is still open, so somebody
might still write on it), and the parent waiting for more
output on out (because the child has it open until it terminates).
The close unravels the nest of waiting, and lets the two processes
terminate, child first.
Note that you must also be careful to flush writes onto pipes before
reading (and hence pending) for input. For example if you used
fprint or fwrite to output to the parent-to-child command pipeline,
it might not be on the actual pipe when you do a read of the child.
Further caution: if you don't drain your client pipes often enough,
you may also hang, so beware of extensive output from the child
(there are several ways of dealing with this, which I'll leave as
an excersize for the student).
Another way to do this (which can be done in shell scripts)
is to create named pipe nodes, and then simply create server processes
that have their IO redirected as needed.
The following shell script demonstrates the idea crudly
(with "sleep" instead of waiting for returning text; an expect script
would work better, of course).
It also demonstrates a workaround for
another problem: the problem that ftp clients expect to be connected
to tty devices. If done with pipes directly to ftp, most ftp clients
will hang. When done on an rlogin-ed pseudo-tty, it works properly.
It would also work if ftp were spawned directly from expect, since
expect uses ptys for IO to children.
All in all, I'd recommend using expect (or expectk) for this purpose,
or perhaps a module from perl's CPAN supporting bidirectional pipes.
mknod in p
mknod out p
rlogin sheol <in >out 2>&1 &
( sleep 4
echo "ftp sheol"
sleep 4
echo throopw
sleep 4
echo watfar
sleep 4
echo dir
while read x
do echo "$x" 1>&2
done
) >in <out
( Note: the above shell example is incomplete in that it
hangs at end end waiting for more ftp output )
--
Wayne Throop thr...@sheol.org http://sheol.org/throopw
My experience has been that ftp actually uses /dev/tty, not
just stdin/stdout/stderr. (It uses /dev/tty for getting the
password.)
We worked around it by using a .netrc file.
If your ftp does use /dev/tty, the you will need use pseudo-ttys -
one of the most OS dependant things I've run into on Unix.
--
Roger M. Shimada
Internet: r...@primenet.com