--- p910nd-0.7/p910nd.c.orig 2007-09-29 17:03:03.000000000 -0700 +++ p910nd-0.7.new/p910nd.c 2007-09-29 17:06:53.000000000 -0700 @@ -144,73 +144,185 @@ (void)close(lockfd); } -ssize_t safe_write(int fd, char *buf, size_t count) +typedef struct { + int detectEof; + int infd; + int outfd; + int startidx; + int endidx; + int bytes; + int totalin; + int totalout; + int eof; + int err; + char buffer[8192]; +} Buffer_t; + +void initBuffer( Buffer_t *b, int infd, int outfd, int detectEof ) +{ + b->detectEof = detectEof; + b->infd = infd; + b->outfd = outfd; + b->startidx = 0; + b->endidx = 0; + b->bytes = 0; + b->totalin = 0; + b->totalout = 0; + b->eof = 0; + b->err = 0; +} + +void prepBuffer( Buffer_t *b, fd_set *readfds, fd_set *writefds ) { - size_t offset = 0; - - while (offset < count) { - ssize_t n = write(fd, buf + offset, count - offset); - - if (n < 0 && errno != EINTR) - return n; - - if (n > 0) - offset += n; + if (!b->err && b->bytes != 0) { + FD_SET(b->outfd, writefds); } + if (!b->eof && b->bytes < sizeof(b->buffer)) { + FD_SET(b->infd, readfds); + } +} - return offset; +ssize_t readBuffer( Buffer_t *b ) +{ + int avail; + ssize_t result = 1; + if (b->bytes == 0 || b->err) { + b->startidx = b->endidx = 0; + avail = sizeof(b->buffer); + } else if (b->bytes == sizeof(b->buffer)) { + avail = 0; + } else if (b->endidx > b->startidx) { + avail = sizeof(b->buffer) - b->endidx; + } else { + avail = b->startidx - b->endidx; + } + if (avail) { + result = read(b->infd, b->buffer+b->endidx, avail); + if (result > 0) { + b->endidx += result; + b->totalin += result; + b->bytes += result; + if (b->endidx == sizeof(b->buffer)) + b->endidx = 0; + } else if (result < 0 || b->detectEof) { + b->eof = 1; + } + } + return result; +} + +ssize_t writeBuffer( Buffer_t *b ) +{ + int avail; + ssize_t result = 1; + if (b->bytes == 0 || b->err) { + avail = 0; + } else if (b->endidx > b->startidx) { + avail = b->endidx - b->startidx; + } else { + avail = sizeof(b->buffer) - b->startidx; + } + if (avail) { + result = write(b->outfd, b->buffer + b->startidx, avail); + if (result < 0) { + syslog(LOG_ERR, "write: %m\n"); + b->err = 1; + } else { + b->startidx += result; + b->totalout += result; + b->bytes -= result; + if (b->startidx == sizeof(b->buffer)) + b->startidx = 0; + } + } + return result; } /* Copy network socket to FILE f until EOS */ int copy_stream(int fd, int lp) { - int nread, rcvd = 0, sent = 0; - char buffer[8192]; + int result; + Buffer_t inbuffer; + initBuffer( &inbuffer, fd, lp, 1 ); if (bidir) { - for (;;) { - fd_set readfds; - int result; + struct timeval now; + struct timeval then; + struct timeval timeout; + int timer = 0; + Buffer_t outbuffer; + initBuffer ( &outbuffer, lp, fd, 0 ); + fd_set readfds; + fd_set writefds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_SET(lp, &readfds); + FD_SET(fd, &readfds); + while ( (FD_ISSET(fd, &readfds)) || + (FD_ISSET(lp, &writefds))) { int maxfd = lp > fd ? lp : fd; - FD_ZERO(&readfds); - FD_SET(lp, &readfds); - FD_SET(fd, &readfds); - result = select(maxfd + 1, &readfds, 0, 0, 0); + if (timer) { + gettimeofday(&now,0); + // If timer expired, clear timer + // else don't try reading from printer + if ((now.tv_sec > then.tv_sec) || + (now.tv_sec == then.tv_sec && + now.tv_usec > then.tv_usec)) { + timer = 0; + } else { + timeout.tv_sec = then.tv_sec; + timeout.tv_usec = then.tv_usec; + FD_CLR(lp, &readfds); + } + } + if (timer) { + result = select(maxfd + 1, &readfds, &writefds, 0, &timeout); + } else { + result = select(maxfd + 1, &readfds, &writefds, 0, 0); + } if (result < 0) return (result); - if (result == 0) - continue; if (FD_ISSET(fd, &readfds)) { - nread = read(fd, buffer, sizeof(buffer)); - if (nread <= 0) - break; - if (safe_write(lp, buffer, nread) < 0) { - syslog(LOG_ERR, "write: %m\n"); - break; - } - rcvd += nread; + result = readBuffer(&inbuffer); } if (FD_ISSET(lp, &readfds)) { - nread = read(lp, buffer, sizeof(buffer)); - if (nread > 0) { - safe_write(fd, buffer, nread); - sent += nread; + result = readBuffer(&outbuffer); + // Pace the printer data more slowly + if (result >= 0) { + gettimeofday(&then,0); + // wait 100 msec + then.tv_usec += 100000; + if (then.tv_usec > 1000000) { + then.tv_usec -= 1000000; + then.tv_sec ++; + } + timer = 1; } } + if (FD_ISSET(lp, &writefds)) { + result = writeBuffer(&inbuffer); + } + if (FD_ISSET(fd, &writefds)) { + result = writeBuffer(&outbuffer); + /* If socket write error, stop reading from it */ + if (result < 0) + inbuffer.eof = 1; + } + FD_ZERO(&readfds); + FD_ZERO(&writefds); + prepBuffer( &inbuffer, &readfds, &writefds ); + prepBuffer( &outbuffer, &readfds, &writefds ); } syslog(LOG_NOTICE, "Finished job: %d bytes received, %d bytes sent\n", - rcvd, sent); + inbuffer.totalout, + outbuffer.totalout); return (0); } else { - while ((nread = read(fd, buffer, sizeof(buffer))) > 0) { - if (safe_write(lp, buffer, nread) < 0) { - syslog(LOG_ERR, "write: %m\n"); - break; - } - rcvd += nread; + while ((result = readBuffer( &inbuffer)) > 0) { + (void)writeBuffer( &inbuffer); } - syslog(LOG_NOTICE, "Finished job: %d bytes received\n", rcvd); - return (nread); + syslog(LOG_NOTICE, "Finished job: %d bytes received\n", inbuffer.totalout); + return (result); } }