#ifdef plan9
#include <u.h>
#include <libc.h>
enum {
stderr = 2,
RDNETIMEOUT = 30*60*1000,
WRNETIMEOUT = RDNETIMEOUT,
};
#else
/* not for plan 9 */
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#define create creat
#define seek lseek
#define fprint fprintf
#define sprint sprintf
#define exits exit
#define ORDWR O_RDWR
#define OTRUNC O_TRUNC
#define ORCLOSE 0
#define RDNETIMEOUT 60
#define WRNETIMEOUT 60
#endif
#define MIN(a,b) ((a<b)?a:b)
#define ACK(a) write(a, "", 1)
#define NAK(a) write(a, "\001", 1)
#define LPDAEMONLOG "/tmp/lpdaemonl"
#define LNBFSZ 4096
char lnbuf[LNBFSZ];
int dbgstate = 0;
char *dbgstrings[] = {
"",
"rcvack1",
"send",
"rcvack2",
"response",
"done"
};
#ifdef plan9
void
error(int level, char *s1, ...)
{
va_list ap;
long thetime;
char *chartime;
char *args[8];
int argno = 0;
if (level == 0) {
time(&thetime);
chartime = ctime(thetime);
fprint(stderr, "%.15s ", &(chartime[4]));
}
va_start(ap, s1);
while(args[argno++] = va_arg(ap, char*))
;
va_end(ap);
fprint(stderr, s1, *args);
}
int
alarmhandler(void *foo, char *note) {
USED(foo);
if(strcmp(note, "alarm")==0) {
fprint(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
return(1);
} else return(0);
}
#else
void
error(int level, char *s1, ...)
{
time_t thetime;
char *chartime;
if (level == 0) {
time(&thetime);
chartime = ctime(&thetime);
fprintf(stderr, "%.15s ", &(chartime[4]));
}
fprintf(stderr, s1, &s1 + 1);
}
void
alarmhandler() {
fprintf(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
}
#endif
/* get a line from inpfd using nonbuffered input. The line is truncated if it is too
* long for the buffer. The result is left in lnbuf and the number of characters
* read in is returned.
*/
int
readline(int inpfd)
{
register char *ap;
register int i;
ap = lnbuf;
i = 0;
do {
if (read(inpfd, ap, 1) != 1) {
error(0, "read error in readline, fd=%d\n", inpfd);
break;
}
} while ((++i < LNBFSZ - 2) && *ap++ != '\n');
if (i == LNBFSZ - 2) {
*ap = '\n';
i++;
}
*ap = '\0';
return(i);
}
#define RDSIZE 512
char jobbuf[RDSIZE];
int
pass(int inpfd, int outfd, int bsize)
{
int rv, bcnt;
for(bcnt=bsize; bcnt > 0; bcnt -= rv) {
alarm(WRNETIMEOUT); /* to break hanging */
if((rv=read(inpfd, jobbuf, MIN(bcnt,RDSIZE))) < 0) {
error(0, "read error during pass, %d remaining\n", bcnt);
break;
} else if((write(outfd, jobbuf, rv)) != rv) {
error(0, "write error during pass, %d remaining\n", bcnt);
break;
}
}
alarm(0);
return(bcnt);
}
/* get whatever stdin has and put it into the temporary file.
* return the file size.
*/
int
prereadfile(int inpfd)
{
int rv, bsize;
bsize = 0;
do {
if((rv=read(0, jobbuf, RDSIZE))<0) {
error(0, "read error while making temp file\n");
exits("read error while making temp file");
} else if((write(inpfd, jobbuf, rv)) != rv) {
error(0, "write error while making temp file\n");
exits("write error while making temp file");
}
bsize += rv;
} while (rv!=0);
return(bsize);
}
int
tempfile(void)
{
static tindx = 0;
char tmpf[20];
int tmpfd;
sprint(tmpf, "/tmp/lp%d.%d", getpid(), tindx++);
if((tmpfd=create(tmpf,
#ifdef plan9
ORDWR|OTRUNC,
#endif
0666)) < 0) {
error(0, "cannot create temp file %s\n", tmpf);
exits("cannot create temp file");
}
close(tmpfd);
if((tmpfd=open(tmpf, ORDWR
#ifdef plan9
|ORCLOSE|OTRUNC
#endif
)) < 0) {
error(0, "cannot open temp file %s\n", tmpf);
exits("cannot open temp file");
}
return(tmpfd);
}
int
recvACK(int netfd)
{
int rv;
*jobbuf = '\0';
alarm(RDNETIMEOUT);
if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') {
error(0, "failed to receive ACK, ");
if (*jobbuf == '\0')
error(1, "read failed\n");
else
error(1, "received <%#x> instead\n", (uchar)*jobbuf);
rv = 0;
} else rv = 1;
alarm(0);
return(rv);
}
void
main(int argc, char *argv[])
{
int i, rv, netfd, bsize, datafd;
#ifndef plan9
void (*oldhandler)();
#endif
/* make connection */
if (argc != 2) {
fprint(stderr, "usage: %s network!destination!service\n",
argv[0]);
exits("usage");
}
/* read options line from stdin into lnbuf */
i = readline(0);
/* read stdin into tempfile to get size */
datafd = tempfile();
bsize = prereadfile(datafd);
/* network connection is opened after data is in to avoid timeout */
if ((netfd = dial(argv[1], 0, 0, 0)) < 0) {
fprint(stderr, "dialing ");
perror(argv[1]);
exits("can't dial");
}
/* write out the options we read above */
if (write(netfd, lnbuf, i) != i) {
error(0, "write error while sending options\n");
exits("write error sending options");
}
/* send the size of the file to be sent */
sprint(lnbuf, "%d\n", bsize);
i = strlen(lnbuf);
if ((rv=write(netfd, lnbuf, i)) != i) {
perror("write error while sending size");
error(0, "write returned %d\n", rv);
exits("write error sending size");
}
if (seek(datafd, 0L, 0) < 0) {
error(0, "error seeking temp file\n");
exits("seek error");
}
/* mirror performance in readfile() in lpdaemon */
#ifdef plan9
atnotify(alarmhandler, 1);
#else
oldhandler = signal(SIGALRM, alarmhandler);
#endif
dbgstate = 1;
if(!recvACK(netfd)) {
error(0, "failed to receive ACK before sending data\n");
exits("recv ack1 failed");
}
dbgstate = 2;
if ((i=pass(datafd, netfd, bsize)) != 0) {
NAK(netfd);
error(0, "failed to send %d bytes\n", i);
exits("send data failed");
}
ACK(netfd);
dbgstate = 3;
if(!recvACK(netfd)) {
error(0, "failed to receive ACK after sending data\n");
exits("recv ack2 failed");
}
/* get response, as from lp -q */
dbgstate = 4;
while((rv=read(netfd, jobbuf, RDSIZE)) > 0) {
if((write(1, jobbuf, rv)) != rv) {
error(0, "write error while sending to stdout\n");
exits("write error while sending to stdout");
}
}
dbgstate = 5;
#ifdef plan9
atnotify(alarmhandler, 0);
/* close down network connections and go away */
exits("");
#else
signal(SIGALRM, oldhandler);
exit(0);
#endif
}
|