xref: /plan9/sys/src/cmd/lp/lpsend.c (revision 14f51593fd82e19ba95969a8c07ff71131015979)
17dd7cddfSDavid du Colombier #ifdef plan9
27dd7cddfSDavid du Colombier 
3bd389b36SDavid du Colombier #include <u.h>
4bd389b36SDavid du Colombier #include <libc.h>
57dd7cddfSDavid du Colombier 
67ec5746aSDavid du Colombier enum {
77ec5746aSDavid du Colombier 	stderr = 2,
87ec5746aSDavid du Colombier 	RDNETIMEOUT = 30*60*1000,
97ec5746aSDavid du Colombier 	WRNETIMEOUT = RDNETIMEOUT,
107ec5746aSDavid du Colombier };
117dd7cddfSDavid du Colombier #else
12bd389b36SDavid du Colombier 
13bd389b36SDavid du Colombier /* not for plan 9 */
147dd7cddfSDavid du Colombier #include <stdio.h>
157dd7cddfSDavid du Colombier #include <errno.h>
167dd7cddfSDavid du Colombier #include <time.h>
177dd7cddfSDavid du Colombier #include <fcntl.h>
187dd7cddfSDavid du Colombier #include <signal.h>
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier #define	create	creat
217dd7cddfSDavid du Colombier #define	seek	lseek
227dd7cddfSDavid du Colombier #define	fprint	fprintf
237dd7cddfSDavid du Colombier #define	sprint	sprintf
247dd7cddfSDavid du Colombier #define	exits	exit
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier #define	ORDWR	O_RDWR
277dd7cddfSDavid du Colombier #define	OTRUNC	O_TRUNC
287dd7cddfSDavid du Colombier #define	ORCLOSE	0
297dd7cddfSDavid du Colombier 
307dd7cddfSDavid du Colombier #define RDNETIMEOUT	60
317dd7cddfSDavid du Colombier #define WRNETIMEOUT	60
327dd7cddfSDavid du Colombier 
337dd7cddfSDavid du Colombier #endif
34bd389b36SDavid du Colombier 
35bd389b36SDavid du Colombier #define MIN(a,b)	((a<b)?a:b)
36bd389b36SDavid du Colombier 
37bd389b36SDavid du Colombier #define	ACK(a)	write(a, "", 1)
38bd389b36SDavid du Colombier #define NAK(a)	write(a, "\001", 1)
39bd389b36SDavid du Colombier 
40bd389b36SDavid du Colombier #define LPDAEMONLOG	"/tmp/lpdaemonl"
41bd389b36SDavid du Colombier 
42bd389b36SDavid du Colombier #define LNBFSZ	4096
43bd389b36SDavid du Colombier char lnbuf[LNBFSZ];
44bd389b36SDavid du Colombier int dbgstate = 0;
45bd389b36SDavid du Colombier char *dbgstrings[] = {
46bd389b36SDavid du Colombier 	"",
47bd389b36SDavid du Colombier 	"rcvack1",
48bd389b36SDavid du Colombier 	"send",
49bd389b36SDavid du Colombier 	"rcvack2",
50bd389b36SDavid du Colombier 	"response",
51bd389b36SDavid du Colombier 	"done"
52bd389b36SDavid du Colombier };
537dd7cddfSDavid du Colombier 
547dd7cddfSDavid du Colombier #ifdef plan9
557dd7cddfSDavid du Colombier 
56bd389b36SDavid du Colombier void
error(int level,char * s1,...)57bd389b36SDavid du Colombier error(int level, char *s1, ...)
58bd389b36SDavid du Colombier {
597dd7cddfSDavid du Colombier 	va_list ap;
60bd389b36SDavid du Colombier 	long thetime;
61bd389b36SDavid du Colombier 	char *chartime;
62bd389b36SDavid du Colombier 	char *args[8];
63bd389b36SDavid du Colombier 	int argno = 0;
64bd389b36SDavid du Colombier 
65bd389b36SDavid du Colombier 	if (level == 0) {
66bd389b36SDavid du Colombier 		time(&thetime);
67bd389b36SDavid du Colombier 		chartime = ctime(thetime);
687dd7cddfSDavid du Colombier 		fprint(stderr, "%.15s ", &(chartime[4]));
69bd389b36SDavid du Colombier 	}
70bd389b36SDavid du Colombier 	va_start(ap, s1);
7143af371bSDavid du Colombier 	while(args[argno++] = va_arg(ap, char*))
7243af371bSDavid du Colombier 		;
73bd389b36SDavid du Colombier 	va_end(ap);
747dd7cddfSDavid du Colombier 	fprint(stderr, s1, *args);
75bd389b36SDavid du Colombier }
76bd389b36SDavid du Colombier 
77bd389b36SDavid du Colombier int
alarmhandler(void * foo,char * note)78bd389b36SDavid du Colombier alarmhandler(void *foo, char *note) {
79bd389b36SDavid du Colombier 	USED(foo);
809a747e4fSDavid du Colombier 	if(strcmp(note, "alarm")==0) {
817dd7cddfSDavid du Colombier 		fprint(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
82bd389b36SDavid du Colombier 		return(1);
83bd389b36SDavid du Colombier 	} else return(0);
84bd389b36SDavid du Colombier }
85bd389b36SDavid du Colombier 
867dd7cddfSDavid du Colombier #else
877dd7cddfSDavid du Colombier 
887dd7cddfSDavid du Colombier void
error(int level,char * s1,...)897dd7cddfSDavid du Colombier error(int level, char *s1, ...)
907dd7cddfSDavid du Colombier {
917dd7cddfSDavid du Colombier 	time_t thetime;
927dd7cddfSDavid du Colombier 	char *chartime;
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier 	if (level == 0) {
957dd7cddfSDavid du Colombier 		time(&thetime);
967dd7cddfSDavid du Colombier 		chartime = ctime(&thetime);
977dd7cddfSDavid du Colombier 		fprintf(stderr, "%.15s ", &(chartime[4]));
987dd7cddfSDavid du Colombier 	}
9943af371bSDavid du Colombier 	fprintf(stderr, s1, &s1 + 1);
1007dd7cddfSDavid du Colombier }
1017dd7cddfSDavid du Colombier 
1027dd7cddfSDavid du Colombier void
alarmhandler()1037dd7cddfSDavid du Colombier alarmhandler() {
1047dd7cddfSDavid du Colombier 	fprintf(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
1057dd7cddfSDavid du Colombier }
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier #endif
1087dd7cddfSDavid du Colombier 
109bd389b36SDavid du Colombier /* get a line from inpfd using nonbuffered input.  The line is truncated if it is too
110bd389b36SDavid du Colombier  * long for the buffer.  The result is left in lnbuf and the number of characters
111bd389b36SDavid du Colombier  * read in is returned.
112bd389b36SDavid du Colombier  */
113bd389b36SDavid du Colombier int
readline(int inpfd)114bd389b36SDavid du Colombier readline(int inpfd)
115bd389b36SDavid du Colombier {
116bd389b36SDavid du Colombier 	register char *ap;
117bd389b36SDavid du Colombier 	register int i;
118bd389b36SDavid du Colombier 
119bd389b36SDavid du Colombier 	ap = lnbuf;
120bd389b36SDavid du Colombier 	i = 0;
121bd389b36SDavid du Colombier 	do {
122bd389b36SDavid du Colombier 		if (read(inpfd, ap, 1) != 1) {
123bd389b36SDavid du Colombier 			error(0, "read error in readline, fd=%d\n", inpfd);
124bd389b36SDavid du Colombier 			break;
125bd389b36SDavid du Colombier 		}
126bd389b36SDavid du Colombier 	} while ((++i < LNBFSZ - 2) && *ap++ != '\n');
127bd389b36SDavid du Colombier 	if (i == LNBFSZ - 2) {
128bd389b36SDavid du Colombier 		*ap = '\n';
129bd389b36SDavid du Colombier 		i++;
130bd389b36SDavid du Colombier 	}
131bd389b36SDavid du Colombier 	*ap = '\0';
132bd389b36SDavid du Colombier 	return(i);
133bd389b36SDavid du Colombier }
134bd389b36SDavid du Colombier 
135bd389b36SDavid du Colombier #define	RDSIZE 512
136bd389b36SDavid du Colombier char jobbuf[RDSIZE];
137bd389b36SDavid du Colombier 
138bd389b36SDavid du Colombier int
pass(int inpfd,int outfd,int bsize)139bd389b36SDavid du Colombier pass(int inpfd, int outfd, int bsize)
140bd389b36SDavid du Colombier {
141*14f51593SDavid du Colombier 	int rv, bcnt;
142bd389b36SDavid du Colombier 
143bd389b36SDavid du Colombier 	for(bcnt=bsize; bcnt > 0; bcnt -= rv) {
144bd389b36SDavid du Colombier 		alarm(WRNETIMEOUT);	/* to break hanging */
145bd389b36SDavid du Colombier 		if((rv=read(inpfd, jobbuf, MIN(bcnt,RDSIZE))) < 0) {
146bd389b36SDavid du Colombier 			error(0, "read error during pass, %d remaining\n", bcnt);
147bd389b36SDavid du Colombier 			break;
148bd389b36SDavid du Colombier 		} else if((write(outfd, jobbuf, rv)) != rv) {
149bd389b36SDavid du Colombier 			error(0, "write error during pass, %d remaining\n", bcnt);
150bd389b36SDavid du Colombier 			break;
151bd389b36SDavid du Colombier 		}
152bd389b36SDavid du Colombier 	}
153bd389b36SDavid du Colombier 	alarm(0);
154bd389b36SDavid du Colombier 	return(bcnt);
155bd389b36SDavid du Colombier }
156bd389b36SDavid du Colombier 
157bd389b36SDavid du Colombier /* get whatever stdin has and put it into the temporary file.
158bd389b36SDavid du Colombier  * return the file size.
159bd389b36SDavid du Colombier  */
160bd389b36SDavid du Colombier int
prereadfile(int inpfd)161bd389b36SDavid du Colombier prereadfile(int inpfd)
162bd389b36SDavid du Colombier {
163bd389b36SDavid du Colombier 	int rv, bsize;
164bd389b36SDavid du Colombier 
165bd389b36SDavid du Colombier 	bsize = 0;
166bd389b36SDavid du Colombier 	do {
167bd389b36SDavid du Colombier 		if((rv=read(0, jobbuf, RDSIZE))<0) {
168bd389b36SDavid du Colombier 			error(0, "read error while making temp file\n");
169bd389b36SDavid du Colombier 			exits("read error while making temp file");
170bd389b36SDavid du Colombier 		} else if((write(inpfd, jobbuf, rv)) != rv) {
171bd389b36SDavid du Colombier 			error(0, "write error while making temp file\n");
172bd389b36SDavid du Colombier 			exits("write error while making temp file");
173bd389b36SDavid du Colombier 		}
174bd389b36SDavid du Colombier 		bsize += rv;
175bd389b36SDavid du Colombier 	} while (rv!=0);
176bd389b36SDavid du Colombier 	return(bsize);
177bd389b36SDavid du Colombier }
178bd389b36SDavid du Colombier 
179bd389b36SDavid du Colombier int
tempfile(void)1807dd7cddfSDavid du Colombier tempfile(void)
181bd389b36SDavid du Colombier {
182bd389b36SDavid du Colombier 	static tindx = 0;
183bd389b36SDavid du Colombier 	char tmpf[20];
184bd389b36SDavid du Colombier 	int tmpfd;
185bd389b36SDavid du Colombier 
186bd389b36SDavid du Colombier 	sprint(tmpf, "/tmp/lp%d.%d", getpid(), tindx++);
1877dd7cddfSDavid du Colombier 	if((tmpfd=create(tmpf,
1887dd7cddfSDavid du Colombier #ifdef plan9
1897dd7cddfSDavid du Colombier 		ORDWR|OTRUNC,
1907dd7cddfSDavid du Colombier #endif
1917dd7cddfSDavid du Colombier 	    0666)) < 0) {
192bd389b36SDavid du Colombier 		error(0, "cannot create temp file %s\n", tmpf);
193bd389b36SDavid du Colombier 		exits("cannot create temp file");
194bd389b36SDavid du Colombier 	}
1957dd7cddfSDavid du Colombier 	close(tmpfd);
1967dd7cddfSDavid du Colombier 	if((tmpfd=open(tmpf, ORDWR
1977dd7cddfSDavid du Colombier #ifdef plan9
1987dd7cddfSDavid du Colombier 		|ORCLOSE|OTRUNC
1997dd7cddfSDavid du Colombier #endif
2007dd7cddfSDavid du Colombier 	    )) < 0) {
2017dd7cddfSDavid du Colombier 		error(0, "cannot open temp file %s\n", tmpf);
2027dd7cddfSDavid du Colombier 		exits("cannot open temp file");
2037dd7cddfSDavid du Colombier 	}
204bd389b36SDavid du Colombier 	return(tmpfd);
205bd389b36SDavid du Colombier }
206bd389b36SDavid du Colombier 
207bd389b36SDavid du Colombier int
recvACK(int netfd)208bd389b36SDavid du Colombier recvACK(int netfd)
209bd389b36SDavid du Colombier {
210bd389b36SDavid du Colombier 	int rv;
211bd389b36SDavid du Colombier 
212bd389b36SDavid du Colombier 	*jobbuf = '\0';
213bd389b36SDavid du Colombier 	alarm(RDNETIMEOUT);
214bd389b36SDavid du Colombier 	if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') {
215bd389b36SDavid du Colombier 		error(0, "failed to receive ACK, ");
216bd389b36SDavid du Colombier 		if (*jobbuf == '\0')
217bd389b36SDavid du Colombier 			error(1, "read failed\n");
218bd389b36SDavid du Colombier 		else
2195da35e22SDavid du Colombier 			error(1, "received <%#x> instead\n", (uchar)*jobbuf);
220bd389b36SDavid du Colombier 		rv = 0;
221bd389b36SDavid du Colombier 	} else rv = 1;
222bd389b36SDavid du Colombier 	alarm(0);
223bd389b36SDavid du Colombier 	return(rv);
224bd389b36SDavid du Colombier }
225bd389b36SDavid du Colombier 
226bd389b36SDavid du Colombier void
main(int argc,char * argv[])227bd389b36SDavid du Colombier main(int argc, char *argv[])
228bd389b36SDavid du Colombier {
2293b86f2f8SDavid du Colombier 	int i, rv, netfd, bsize, datafd;
2307dd7cddfSDavid du Colombier #ifndef plan9
2317dd7cddfSDavid du Colombier 	void (*oldhandler)();
2327dd7cddfSDavid du Colombier #endif
233bd389b36SDavid du Colombier 
234bd389b36SDavid du Colombier 	/* make connection */
2357dd7cddfSDavid du Colombier 	if (argc != 2) {
2363b86f2f8SDavid du Colombier 		fprint(stderr, "usage: %s network!destination!service\n",
2373b86f2f8SDavid du Colombier 			argv[0]);
2383b86f2f8SDavid du Colombier 		exits("usage");
239bd389b36SDavid du Colombier 	}
2407dd7cddfSDavid du Colombier 
2417dd7cddfSDavid du Colombier 	/* read options line from stdin into lnbuf */
2427dd7cddfSDavid du Colombier 	i = readline(0);
2437dd7cddfSDavid du Colombier 
2447dd7cddfSDavid du Colombier 	/* read stdin into tempfile to get size */
2457dd7cddfSDavid du Colombier 	datafd = tempfile();
2467dd7cddfSDavid du Colombier 	bsize = prereadfile(datafd);
2477dd7cddfSDavid du Colombier 
2487dd7cddfSDavid du Colombier 	/* network connection is opened after data is in to avoid timeout */
2497dd7cddfSDavid du Colombier 	if ((netfd = dial(argv[1], 0, 0, 0)) < 0) {
2503b86f2f8SDavid du Colombier 		fprint(stderr, "dialing ");
2513b86f2f8SDavid du Colombier 		perror(argv[1]);
252bd389b36SDavid du Colombier 		exits("can't dial");
253bd389b36SDavid du Colombier 	}
254bd389b36SDavid du Colombier 
2557dd7cddfSDavid du Colombier 	/* write out the options we read above */
256bd389b36SDavid du Colombier 	if (write(netfd, lnbuf, i) != i) {
257bd389b36SDavid du Colombier 		error(0, "write error while sending options\n");
2583b86f2f8SDavid du Colombier 		exits("write error sending options");
259bd389b36SDavid du Colombier 	}
260bd389b36SDavid du Colombier 
261bd389b36SDavid du Colombier 	/* send the size of the file to be sent */
2627dd7cddfSDavid du Colombier 	sprint(lnbuf, "%d\n", bsize);
2637dd7cddfSDavid du Colombier 	i = strlen(lnbuf);
264219b2ee8SDavid du Colombier 	if ((rv=write(netfd, lnbuf, i)) != i) {
265219b2ee8SDavid du Colombier 		perror("write error while sending size");
266219b2ee8SDavid du Colombier 		error(0, "write returned %d\n", rv);
2673b86f2f8SDavid du Colombier 		exits("write error sending size");
268bd389b36SDavid du Colombier 	}
269bd389b36SDavid du Colombier 
270bd389b36SDavid du Colombier 	if (seek(datafd, 0L, 0) < 0) {
271bd389b36SDavid du Colombier 		error(0, "error seeking temp file\n");
272bd389b36SDavid du Colombier 		exits("seek error");
273bd389b36SDavid du Colombier 	}
274bd389b36SDavid du Colombier 	/* mirror performance in readfile() in lpdaemon */
2757dd7cddfSDavid du Colombier 
2767dd7cddfSDavid du Colombier #ifdef plan9
277bd389b36SDavid du Colombier 	atnotify(alarmhandler, 1);
2787dd7cddfSDavid du Colombier #else
2797dd7cddfSDavid du Colombier 	oldhandler = signal(SIGALRM, alarmhandler);
2807dd7cddfSDavid du Colombier #endif
2817dd7cddfSDavid du Colombier 
282bd389b36SDavid du Colombier 	dbgstate = 1;
283bd389b36SDavid du Colombier 	if(!recvACK(netfd)) {
284bd389b36SDavid du Colombier 		error(0, "failed to receive ACK before sending data\n");
285bd389b36SDavid du Colombier 		exits("recv ack1 failed");
286bd389b36SDavid du Colombier 	}
287bd389b36SDavid du Colombier 	dbgstate = 2;
288bd389b36SDavid du Colombier 	if ((i=pass(datafd, netfd, bsize)) != 0) {
289bd389b36SDavid du Colombier 		NAK(netfd);
290bd389b36SDavid du Colombier 		error(0, "failed to send %d bytes\n", i);
291bd389b36SDavid du Colombier 		exits("send data failed");
292bd389b36SDavid du Colombier 	}
293bd389b36SDavid du Colombier 	ACK(netfd);
294bd389b36SDavid du Colombier 	dbgstate = 3;
295bd389b36SDavid du Colombier 	if(!recvACK(netfd)) {
296bd389b36SDavid du Colombier 		error(0, "failed to receive ACK after sending data\n");
297bd389b36SDavid du Colombier 		exits("recv ack2 failed");
298bd389b36SDavid du Colombier 	}
299bd389b36SDavid du Colombier 
300bd389b36SDavid du Colombier 	/* get response, as from lp -q */
301bd389b36SDavid du Colombier 	dbgstate = 4;
302bd389b36SDavid du Colombier 	while((rv=read(netfd, jobbuf, RDSIZE)) > 0) {
303bd389b36SDavid du Colombier 		if((write(1, jobbuf, rv)) != rv) {
304bd389b36SDavid du Colombier 			error(0, "write error while sending to stdout\n");
305bd389b36SDavid du Colombier 			exits("write error while sending to stdout");
306bd389b36SDavid du Colombier 		}
307bd389b36SDavid du Colombier 	}
308bd389b36SDavid du Colombier 	dbgstate = 5;
3097dd7cddfSDavid du Colombier 
3107dd7cddfSDavid du Colombier #ifdef plan9
311bd389b36SDavid du Colombier 	atnotify(alarmhandler, 0);
312bd389b36SDavid du Colombier 	/* close down network connections and go away */
313bd389b36SDavid du Colombier 	exits("");
3147dd7cddfSDavid du Colombier #else
3157dd7cddfSDavid du Colombier 	signal(SIGALRM, oldhandler);
3167dd7cddfSDavid du Colombier 	exit(0);
3177dd7cddfSDavid du Colombier #endif
318bd389b36SDavid du Colombier }
319