xref: /plan9/sys/src/cmd/lp/lpsend.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1bd389b36SDavid du Colombier /* for plan 9 */
2bd389b36SDavid du Colombier #include <u.h>
3bd389b36SDavid du Colombier #include <libc.h>
4bd389b36SDavid du Colombier #include <stdarg.h>
5bd389b36SDavid du Colombier 
6bd389b36SDavid du Colombier /* not for plan 9 */
7bd389b36SDavid du Colombier /* #include <errno.h> */
8bd389b36SDavid du Colombier /* #include <time.h> */
9bd389b36SDavid du Colombier /* #include <ipc.h> */
10bd389b36SDavid du Colombier 
11bd389b36SDavid du Colombier #define MIN(a,b)	((a<b)?a:b)
12bd389b36SDavid du Colombier 
13bd389b36SDavid du Colombier #define	ACK(a)	write(a, "", 1)
14bd389b36SDavid du Colombier #define NAK(a)	write(a, "\001", 1)
15bd389b36SDavid du Colombier 
16bd389b36SDavid du Colombier #define RDNETIMEOUT	60000
17bd389b36SDavid du Colombier #define WRNETIMEOUT	60000
18bd389b36SDavid du Colombier #define LPDAEMONLOG	"/tmp/lpdaemonl"
19bd389b36SDavid du Colombier 
20bd389b36SDavid du Colombier #define LNBFSZ	4096
21bd389b36SDavid du Colombier char lnbuf[LNBFSZ];
22bd389b36SDavid du Colombier int dbgstate = 0;
23bd389b36SDavid du Colombier char *dbgstrings[] = {
24bd389b36SDavid du Colombier 	"",
25bd389b36SDavid du Colombier 	"rcvack1",
26bd389b36SDavid du Colombier 	"send",
27bd389b36SDavid du Colombier 	"rcvack2",
28bd389b36SDavid du Colombier 	"response",
29bd389b36SDavid du Colombier 	"done"
30bd389b36SDavid du Colombier };
31bd389b36SDavid du Colombier void
32bd389b36SDavid du Colombier error(int level, char *s1, ...)
33bd389b36SDavid du Colombier {
34bd389b36SDavid du Colombier 	long thetime;
35bd389b36SDavid du Colombier 	char *chartime;
36bd389b36SDavid du Colombier 	va_list ap;
37bd389b36SDavid du Colombier 	char *args[8];
38bd389b36SDavid du Colombier 	int argno = 0;
39bd389b36SDavid du Colombier 
40bd389b36SDavid du Colombier 	if (level == 0) {
41bd389b36SDavid du Colombier 		time(&thetime);
42bd389b36SDavid du Colombier 		chartime = ctime(thetime);
43bd389b36SDavid du Colombier 		fprint(2, "%.15s ", &(chartime[4]));
44bd389b36SDavid du Colombier 	}
45bd389b36SDavid du Colombier 	va_start(ap, s1);
46bd389b36SDavid du Colombier 	while(args[argno++] = va_arg(ap, char*));
47bd389b36SDavid du Colombier 	va_end(ap);
48bd389b36SDavid du Colombier 	fprint(2, s1, *args);
49bd389b36SDavid du Colombier 	return;
50bd389b36SDavid du Colombier }
51bd389b36SDavid du Colombier 
52bd389b36SDavid du Colombier int
53bd389b36SDavid du Colombier alarmhandler(void *foo, char *note) {
54bd389b36SDavid du Colombier 	USED(foo);
55bd389b36SDavid du Colombier 	if(strncmp(note, "alarm", ERRLEN)==0) {
56bd389b36SDavid du Colombier 		fprint(2, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
57bd389b36SDavid du Colombier 		return(1);
58bd389b36SDavid du Colombier 	} else return(0);
59bd389b36SDavid du Colombier }
60bd389b36SDavid du Colombier 
61bd389b36SDavid du Colombier /* get a line from inpfd using nonbuffered input.  The line is truncated if it is too
62bd389b36SDavid du Colombier  * long for the buffer.  The result is left in lnbuf and the number of characters
63bd389b36SDavid du Colombier  * read in is returned.
64bd389b36SDavid du Colombier  */
65bd389b36SDavid du Colombier int
66bd389b36SDavid du Colombier readline(int inpfd)
67bd389b36SDavid du Colombier {
68bd389b36SDavid du Colombier 	register char *ap;
69bd389b36SDavid du Colombier 	register int i;
70bd389b36SDavid du Colombier 
71bd389b36SDavid du Colombier 	ap = lnbuf;
72bd389b36SDavid du Colombier 	i = 0;
73bd389b36SDavid du Colombier 	do {
74bd389b36SDavid du Colombier 		if (read(inpfd, ap, 1) != 1) {
75bd389b36SDavid du Colombier 			error(0, "read error in readline, fd=%d\n", inpfd);
76bd389b36SDavid du Colombier 			break;
77bd389b36SDavid du Colombier 		}
78bd389b36SDavid du Colombier 	} while ((++i < LNBFSZ - 2) && *ap++ != '\n');
79bd389b36SDavid du Colombier 	if (i == LNBFSZ - 2) {
80bd389b36SDavid du Colombier 		*ap = '\n';
81bd389b36SDavid du Colombier 		i++;
82bd389b36SDavid du Colombier 	}
83bd389b36SDavid du Colombier 	*ap = '\0';
84bd389b36SDavid du Colombier 	return(i);
85bd389b36SDavid du Colombier }
86bd389b36SDavid du Colombier 
87bd389b36SDavid du Colombier #define	RDSIZE 512
88bd389b36SDavid du Colombier char jobbuf[RDSIZE];
89bd389b36SDavid du Colombier 
90bd389b36SDavid du Colombier int
91bd389b36SDavid du Colombier pass(int inpfd, int outfd, int bsize)
92bd389b36SDavid du Colombier {
93bd389b36SDavid du Colombier 	int bcnt, rv;
94bd389b36SDavid du Colombier 
95bd389b36SDavid du Colombier 	for(bcnt=bsize; bcnt > 0; bcnt -= rv) {
96bd389b36SDavid du Colombier 		alarm(WRNETIMEOUT);	/* to break hanging */
97bd389b36SDavid du Colombier 		if((rv=read(inpfd, jobbuf, MIN(bcnt,RDSIZE))) < 0) {
98bd389b36SDavid du Colombier 			error(0, "read error during pass, %d remaining\n", bcnt);
99bd389b36SDavid du Colombier 			break;
100bd389b36SDavid du Colombier 		} else if((write(outfd, jobbuf, rv)) != rv) {
101bd389b36SDavid du Colombier 			error(0, "write error during pass, %d remaining\n", bcnt);
102bd389b36SDavid du Colombier 			break;
103bd389b36SDavid du Colombier 		}
104bd389b36SDavid du Colombier 	}
105bd389b36SDavid du Colombier 	alarm(0);
106bd389b36SDavid du Colombier 	return(bcnt);
107bd389b36SDavid du Colombier }
108bd389b36SDavid du Colombier 
109bd389b36SDavid du Colombier /* get whatever stdin has and put it into the temporary file.
110bd389b36SDavid du Colombier  * return the file size.
111bd389b36SDavid du Colombier  */
112bd389b36SDavid du Colombier int
113bd389b36SDavid du Colombier prereadfile(int inpfd)
114bd389b36SDavid du Colombier {
115bd389b36SDavid du Colombier 	int rv, bsize;
116bd389b36SDavid du Colombier 
117bd389b36SDavid du Colombier 	bsize = 0;
118bd389b36SDavid du Colombier 	do {
119bd389b36SDavid du Colombier 		if((rv=read(0, jobbuf, RDSIZE))<0) {
120bd389b36SDavid du Colombier 			error(0, "read error while making temp file\n");
121bd389b36SDavid du Colombier 			exits("read error while making temp file");
122bd389b36SDavid du Colombier 		} else if((write(inpfd, jobbuf, rv)) != rv) {
123bd389b36SDavid du Colombier 			error(0, "write error while making temp file\n");
124bd389b36SDavid du Colombier 			exits("write error while making temp file");
125bd389b36SDavid du Colombier 		}
126bd389b36SDavid du Colombier 		bsize += rv;
127bd389b36SDavid du Colombier 	} while (rv!=0);
128bd389b36SDavid du Colombier 	return(bsize);
129bd389b36SDavid du Colombier }
130bd389b36SDavid du Colombier 
131bd389b36SDavid du Colombier int
132bd389b36SDavid du Colombier tmpfile(void)
133bd389b36SDavid du Colombier {
134bd389b36SDavid du Colombier 	static tindx = 0;
135bd389b36SDavid du Colombier 	char tmpf[20];
136bd389b36SDavid du Colombier 	int tmpfd;
137bd389b36SDavid du Colombier 
138bd389b36SDavid du Colombier 	sprint(tmpf, "/tmp/lp%d.%d", getpid(), tindx++);
139bd389b36SDavid du Colombier 	if((tmpfd=create(tmpf, ORDWR|ORCLOSE|OTRUNC, 0666)) < 0) {
140bd389b36SDavid du Colombier 		error(0, "cannot create temp file %s\n", tmpf);
141bd389b36SDavid du Colombier 		exits("cannot create temp file");
142bd389b36SDavid du Colombier 	}
143bd389b36SDavid du Colombier 	return(tmpfd);
144bd389b36SDavid du Colombier }
145bd389b36SDavid du Colombier 
146bd389b36SDavid du Colombier int
147bd389b36SDavid du Colombier recvACK(int netfd)
148bd389b36SDavid du Colombier {
149bd389b36SDavid du Colombier 	int rv;
150bd389b36SDavid du Colombier 
151bd389b36SDavid du Colombier 	*jobbuf = '\0';
152bd389b36SDavid du Colombier 	alarm(RDNETIMEOUT);
153bd389b36SDavid du Colombier 	if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') {
154bd389b36SDavid du Colombier 		error(0, "failed to receive ACK, ");
155bd389b36SDavid du Colombier 		if (*jobbuf == '\0')
156bd389b36SDavid du Colombier 			error(1, "read failed\n");
157bd389b36SDavid du Colombier 		else
158bd389b36SDavid du Colombier 			error(1, "received <0x%x> instead\n", *jobbuf);
159bd389b36SDavid du Colombier 		rv = 0;
160bd389b36SDavid du Colombier 	} else rv = 1;
161bd389b36SDavid du Colombier 	alarm(0);
162bd389b36SDavid du Colombier 	return(rv);
163bd389b36SDavid du Colombier }
164bd389b36SDavid du Colombier 
165bd389b36SDavid du Colombier void
166bd389b36SDavid du Colombier main(int argc, char *argv[])
167bd389b36SDavid du Colombier {
168bd389b36SDavid du Colombier 	char *devdir;
169bd389b36SDavid du Colombier 	int i, rv, netfd, bsize, datafd;
170bd389b36SDavid du Colombier 
171bd389b36SDavid du Colombier 	/* make connection */
172bd389b36SDavid du Colombier 	if (argc != 4) {
173bd389b36SDavid du Colombier 		fprint(2, "usage: %s destination network service\n", argv[0]);
174bd389b36SDavid du Colombier 		exits("incorrect number of arguments");
175bd389b36SDavid du Colombier 	}
176bd389b36SDavid du Colombier 	if ((netfd=dial((devdir=netmkaddr(argv[1], argv[2], argv[3])), 0, 0, 0)) < 0) {
177bd389b36SDavid du Colombier 		fprint(2, "dialing %s\n", devdir);
178bd389b36SDavid du Colombier 		perror("dial");
179bd389b36SDavid du Colombier 		exits("can't dial");
180bd389b36SDavid du Colombier 	}
181bd389b36SDavid du Colombier 
182bd389b36SDavid du Colombier 	/* read options line from stdin and send it */
183bd389b36SDavid du Colombier 	i = readline(0);
184bd389b36SDavid du Colombier 	if (write(netfd, lnbuf, i) != i) {
185bd389b36SDavid du Colombier 		error(0, "write error while sending options\n");
186bd389b36SDavid du Colombier 		exits("write error while sending options");
187bd389b36SDavid du Colombier 	}
188bd389b36SDavid du Colombier 
189bd389b36SDavid du Colombier 	/* read stdin into tmpfile to get size */
190bd389b36SDavid du Colombier 	datafd = tmpfile();
191bd389b36SDavid du Colombier 	bsize = prereadfile(datafd);
192bd389b36SDavid du Colombier 
193bd389b36SDavid du Colombier 	/* send the size of the file to be sent */
194bd389b36SDavid du Colombier 	i = sprint(lnbuf, "%d\n", bsize);
195*219b2ee8SDavid du Colombier 	if ((rv=write(netfd, lnbuf, i)) != i) {
196*219b2ee8SDavid du Colombier 		perror("write error while sending size");
197*219b2ee8SDavid du Colombier 		error(0, "write returned %d\n", rv);
198bd389b36SDavid du Colombier 		exits("write error while sending size");
199bd389b36SDavid du Colombier 	}
200bd389b36SDavid du Colombier 
201bd389b36SDavid du Colombier 	if (seek(datafd, 0L, 0) < 0) {
202bd389b36SDavid du Colombier 		error(0, "error seeking temp file\n");
203bd389b36SDavid du Colombier 		exits("seek error");
204bd389b36SDavid du Colombier 	}
205bd389b36SDavid du Colombier 	/* mirror performance in readfile() in lpdaemon */
206bd389b36SDavid du Colombier 	atnotify(alarmhandler, 1);
207bd389b36SDavid du Colombier 	dbgstate = 1;
208bd389b36SDavid du Colombier 	if(!recvACK(netfd)) {
209bd389b36SDavid du Colombier 		error(0, "failed to receive ACK before sending data\n");
210bd389b36SDavid du Colombier 		exits("recv ack1 failed");
211bd389b36SDavid du Colombier 	}
212bd389b36SDavid du Colombier 	dbgstate = 2;
213bd389b36SDavid du Colombier 	if ((i=pass(datafd, netfd, bsize)) != 0) {
214bd389b36SDavid du Colombier 		NAK(netfd);
215bd389b36SDavid du Colombier 		error(0, "failed to send %d bytes\n", i);
216bd389b36SDavid du Colombier 		exits("send data failed");
217bd389b36SDavid du Colombier 	}
218bd389b36SDavid du Colombier 	ACK(netfd);
219bd389b36SDavid du Colombier 	dbgstate = 3;
220bd389b36SDavid du Colombier 	if(!recvACK(netfd)) {
221bd389b36SDavid du Colombier 		error(0, "failed to receive ACK after sending data\n");
222bd389b36SDavid du Colombier 		exits("recv ack2 failed");
223bd389b36SDavid du Colombier 	}
224bd389b36SDavid du Colombier 
225bd389b36SDavid du Colombier 	/* get response, as from lp -q */
226bd389b36SDavid du Colombier 	dbgstate = 4;
227bd389b36SDavid du Colombier 	while((rv=read(netfd, jobbuf, RDSIZE)) > 0) {
228bd389b36SDavid du Colombier 		if((write(1, jobbuf, rv)) != rv) {
229bd389b36SDavid du Colombier 			error(0, "write error while sending to stdout\n");
230bd389b36SDavid du Colombier 			exits("write error while sending to stdout");
231bd389b36SDavid du Colombier 		}
232bd389b36SDavid du Colombier 	}
233bd389b36SDavid du Colombier 	dbgstate = 5;
234bd389b36SDavid du Colombier 	atnotify(alarmhandler, 0);
235bd389b36SDavid du Colombier 	/* close down network connections and go away */
236bd389b36SDavid du Colombier 	exits("");
237bd389b36SDavid du Colombier }
238