Reputation: 5678
The following little C program (let's call it pointless
):
/* pointless.c */
#include <stdio.h>
#include <unistd.h>
void main(){
write(STDOUT_FILENO, "", 0); /* pointless write() of 0 bytes */
sleep(1);
write(STDOUT_FILENO, "still there!\n", 13);
}
will print "still there!" after a small delay, as expected. However,
rlwrap ./pointless
prints nothing under AIX and exits immediatly.
Apparently, rlwrap
reads 0 bytes after the first write()
and
(incorrectly) decides that pointless
has called it quits.
When running pointless
without rlwrap
, and with rlwrap
on all
other systems I could lay my hand on (Linux, OSX, FreeBSD), the "still
there!" gets printed, as expected.
The relevant rlwrap
(pseudo-)code is this:
/* master is the file descriptor of the master end of a pty, while the slave is 'pointless's stdout */
/* master was opened with O_NDELAY */
while(pselect(nfds, &readfds, .....)) {
if (FD_ISSET(master, &readfds)) { /* master is "ready" for reading */
nread = read(master, buf, BUFFSIZE - 1); /* so try to read a buffer's worth */
if (nread == 0) /* 0 bytes read... */
cleanup_and_exit(); /* ... usually means EOF, doens't it? */
Apparently, on all systems, except AIX, write
ing 0 bytes on the
slave end of a pty is a no-op, while on AIX it wakes up the
select()
on the master end. Writing 0 bytes seems pointless, but one
of my test programs writes random-length chunks of text, which may
actually happen to have length 0.
On linux, man 2 read
states "on success, the number of bytes read is
returned (zero indicates end of file)" (italics are mine) This
question has come up
before
without mention of this scenario.
This begs the question: how can I portably determine whether the
slave end has been closed? (In this case I can probably just wait for
a SIGCHLD
and then close shop, but that might open another can of
worms I'd rather avoid)
Edit: POSIX states:
Writing a zero-length buffer (nbyte is 0) to a STREAMS device sends 0 bytes with 0 returned. However, writing a zero-length buffer to a STREAMS-based pipe or FIFO sends no message and 0 is returned. The process may issue I_SWROPT ioctl() to enable zero-length messages to be sent across the pipe or FIFO.
On AIX, pty
is indeed a STREAMS device, moreover, not a pipe or FIFO. ioctl(STDOUT_FILENO, I_SWROPT, 0)
seems to make it possible to make the pty conform to the rest of the Unix world. The sad thing is that this has to be called from the slave side, and so is outside rlwrap
s sphere of infuence (even though we could call the ioctl()
between fork()
and exec()
- that would not guarantee that the executed command won't change it back)
Upvotes: 4
Views: 713
Reputation: 22094
From the linux man page
If count is zero and fd refers to a regular file, then write() may return a failure status if one of the errors below is detected. If no errors are detected, or error detection is not performed, 0 will be returned without causing any other effect. If count is zero and fd refers to a file other than a regular file, the results are not specified.
So, since it is unspecified, it can do whatever it likes in your case.
Upvotes: 2
Reputation: 1
When attempting to read from an empty pipe or FIFO:
- If no process has the pipe open for writing, read() shall return 0 to indicate end-of-file."
So the "read of zero bytes means EOF" is POSIX-compliant.
On the write()
side (bolding mine):
Before any action described below is taken, and if
nbyte
is zero and the file is a regular file, thewrite()
function may detect and return errors as described below. In the absence of errors, or if error detection is not performed, thewrite()
function shall return zero and have no other results. Ifnbyte
is zero and the file is not a regular file, the results are unspecified.
Unfortunately, that means you can't portably depend on a write()
of zero bytes to have no effect because AIX is compliant with the POSIX standard for write()
here.
You probably have to rely on SIGCHLD
.
Upvotes: 2