|  | #ifdef plan9 | 
|  |  | 
|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #define 	stderr	2 | 
|  |  | 
|  | #define RDNETIMEOUT	60000 | 
|  | #define WRNETIMEOUT	60000 | 
|  |  | 
|  | #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); | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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 bcnt = 0; | 
|  | int rv = 0; | 
|  |  | 
|  | 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 int tindx = 0; | 
|  | char tmpf[20]; | 
|  | int tmpfd; | 
|  |  | 
|  | sprint(tmpf, "/var/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 <0x%x> instead\n", *jobbuf); | 
|  | rv = 0; | 
|  | } else rv = 1; | 
|  | alarm(0); | 
|  | return(rv); | 
|  | } | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | char *devdir; | 
|  | int i, rv, netfd, bsize; | 
|  | int datafd; | 
|  |  | 
|  | #ifndef plan9 | 
|  |  | 
|  | void (*oldhandler)(); | 
|  |  | 
|  | #endif | 
|  |  | 
|  | devdir = nil; | 
|  | /* make connection */ | 
|  | if (argc != 2) { | 
|  | fprint(stderr, "usage: %s network!destination!service\n", argv[0]); | 
|  | exits("incorrect number of arguments"); | 
|  | } | 
|  |  | 
|  | /* 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 %s\n", devdir); | 
|  | perror("dial"); | 
|  | 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 while 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 while 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 | 
|  |  | 
|  | } |