> I have a problem with a program that is written for SCO 3.2V4.2, but I try to run
> it on SCO OpenServer 5.
> The problem is this: If I start the program, the output from the program is
> garbeled. If I check the packets on the LAN, it shows that the packets contents are
> equal to the packets I get on screen. Ok, not the terminal emulator that is the
> problem. Then, I try on the console, same problem. But, if I run the program like
> this:
> program | cat
> then it works OK !!! No garbles or anything. It's also OK if I pipe it to tee, and
> logs to /dev/null, like this:
> program | tee /dev/null
> If I run it on 3.2V4.2, it's quite OK.
This is caused by a change in the O_NONBLOCK handling in OSR5. POSIX
says that I/O to a file descriptor marked O_NONBLOCK cannot block,
period. Under 3.2v4.2, O_NONBLOCK *usually* only applied to input on
tty fds. Under OSR5, it also applies to output. If you write a large
amount of data to a tty fd, you will eventually fill up a kernel buffer
and the driver will want to put the process to sleep (i.e. block on
output). Because you have set O_NONBLOCK on your tty fd, this block is
not allowed. write(S) instead returns with a short write.
In practice, very few programs actually want non-blocking *output*. You
probably set it to get non-blocking *input*. There are a number of
possible workarounds:
o Use two fds. Open the same device twice, once with O_NONBLOCK and
once without. (Note: dup(), dup2(), fcntl(F_DUPFD), all return a
second fd which is identical to the first -- setting O_NONBLOCK on
either fd sets it on both). Use the O_NONBLOCK fd for input, the
other for output. Note -- if it's a tty device that your program
started out with, you might need to do something like:
if (isatty(0)) {
close(1);
ofd = open(ttyname(0), O_WRONLY);
if (ofd < 0) {
/* perror("reopen stdin"); exit(1); */
/* instead, just let stdin/stdout alone, weird results may
ensue -- but may not, so let the user decide */
}
else if (ofd != 1) dup2(ofd, 1); /* slam it onto stdout */
imode = fcntl(0, F_GETFL);
if (imode >= 0) fcntl(0, F_SETFL, imode | O_NONBLOCK);
}
o Use one fd. Maintain it normally in the blocking state, but
surround any input operations with fcntl() calls to turn O_NONBLOCK
on and back off. This may be easy to do if your program's input
functions are well isolated.
o Use one fd. Maintain it normally in the non-blocking state, but
surround any output operations with fcntl() calls to turn O_NONBLOCK
off and back on. This may be easy to do if your program's output
functions are well isolated.
o Use one fd. Maintain it in the non-blocking state. Perform all
output through a function which checks the return from write(S) and
re-writes any output which didn't get written. Warning: do not do
this blindly, or you will create a CPU spin loop. The code should
look something like:
while ((obytes = write(outfd, buffer, bufsize)) != bufsize) {
if (obytes == -1) {
/* deal with error condition & exit loop */
}
else {
/* account for bytes already written */
buffer += obytes;
bufsize -= obytes;
/* use select(S) to wait for outfd to be writable */
FD_ZERO(&tmpfds);
FD_SET(outfd, &tmpfds);
if (select(outfd + 1, NULL, tmpfds, NULL, NULL) < 0) {
/* deal with error condition & exit loop */
}
}
}
This is the most "proper" solution, in that it is *always* possible
you will get a short write, so all writes should be done this way.
In practice, very few Unix programs anticipate short writes; watch
the fireworks when a filesystem gets full.
Running your program as `program | cat` works because cat implements
such a loop.
Quote:>Bela<