xref: /plan9/sys/src/cmd/lp/lpsend.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #ifdef plan9
2 
3 #include <u.h>
4 #include <libc.h>
5 #define 	stderr	2
6 
7 #define RDNETIMEOUT	60000
8 #define WRNETIMEOUT	60000
9 
10 #else
11 
12 /* not for plan 9 */
13 #include <stdio.h>
14 #include <errno.h>
15 #include <time.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 
19 #define	create	creat
20 #define	seek	lseek
21 #define	fprint	fprintf
22 #define	sprint	sprintf
23 #define	exits	exit
24 
25 #define	ORDWR	O_RDWR
26 #define	OTRUNC	O_TRUNC
27 #define	ORCLOSE	0
28 
29 #define RDNETIMEOUT	60
30 #define WRNETIMEOUT	60
31 
32 #endif
33 
34 #define MIN(a,b)	((a<b)?a:b)
35 
36 #define	ACK(a)	write(a, "", 1)
37 #define NAK(a)	write(a, "\001", 1)
38 
39 #define LPDAEMONLOG	"/tmp/lpdaemonl"
40 
41 #define LNBFSZ	4096
42 char lnbuf[LNBFSZ];
43 int dbgstate = 0;
44 char *dbgstrings[] = {
45 	"",
46 	"rcvack1",
47 	"send",
48 	"rcvack2",
49 	"response",
50 	"done"
51 };
52 
53 #ifdef plan9
54 
55 void
56 error(int level, char *s1, ...)
57 {
58 	va_list ap;
59 	long thetime;
60 	char *chartime;
61 	char *args[8];
62 	int argno = 0;
63 
64 	if (level == 0) {
65 		time(&thetime);
66 		chartime = ctime(thetime);
67 		fprint(stderr, "%.15s ", &(chartime[4]));
68 	}
69 	va_start(ap, s1);
70 	while(args[argno++] = va_arg(ap, char*));
71 	va_end(ap);
72 	fprint(stderr, s1, *args);
73 	return;
74 }
75 
76 int
77 alarmhandler(void *foo, char *note) {
78 	USED(foo);
79 	if(strcmp(note, "alarm")==0) {
80 		fprint(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
81 		return(1);
82 	} else return(0);
83 }
84 
85 #else
86 
87 void
88 error(int level, char *s1, ...)
89 {
90 	time_t thetime;
91 	char *chartime;
92 
93 	if (level == 0) {
94 		time(&thetime);
95 		chartime = ctime(&thetime);
96 		fprintf(stderr, "%.15s ", &(chartime[4]));
97 	}
98 	fprintf(stderr, s1, (&s1+1));
99 	return;
100 }
101 
102 void
103 alarmhandler() {
104 	fprintf(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
105 }
106 
107 #endif
108 
109 /* get a line from inpfd using nonbuffered input.  The line is truncated if it is too
110  * long for the buffer.  The result is left in lnbuf and the number of characters
111  * read in is returned.
112  */
113 int
114 readline(int inpfd)
115 {
116 	register char *ap;
117 	register int i;
118 
119 	ap = lnbuf;
120 	i = 0;
121 	do {
122 		if (read(inpfd, ap, 1) != 1) {
123 			error(0, "read error in readline, fd=%d\n", inpfd);
124 			break;
125 		}
126 	} while ((++i < LNBFSZ - 2) && *ap++ != '\n');
127 	if (i == LNBFSZ - 2) {
128 		*ap = '\n';
129 		i++;
130 	}
131 	*ap = '\0';
132 	return(i);
133 }
134 
135 #define	RDSIZE 512
136 char jobbuf[RDSIZE];
137 
138 int
139 pass(int inpfd, int outfd, int bsize)
140 {
141 	int bcnt = 0;
142 	int rv = 0;
143 
144 	for(bcnt=bsize; bcnt > 0; bcnt -= rv) {
145 		alarm(WRNETIMEOUT);	/* to break hanging */
146 		if((rv=read(inpfd, jobbuf, MIN(bcnt,RDSIZE))) < 0) {
147 			error(0, "read error during pass, %d remaining\n", bcnt);
148 			break;
149 		} else if((write(outfd, jobbuf, rv)) != rv) {
150 			error(0, "write error during pass, %d remaining\n", bcnt);
151 			break;
152 		}
153 	}
154 	alarm(0);
155 	return(bcnt);
156 }
157 
158 /* get whatever stdin has and put it into the temporary file.
159  * return the file size.
160  */
161 int
162 prereadfile(int inpfd)
163 {
164 	int rv, bsize;
165 
166 	bsize = 0;
167 	do {
168 		if((rv=read(0, jobbuf, RDSIZE))<0) {
169 			error(0, "read error while making temp file\n");
170 			exits("read error while making temp file");
171 		} else if((write(inpfd, jobbuf, rv)) != rv) {
172 			error(0, "write error while making temp file\n");
173 			exits("write error while making temp file");
174 		}
175 		bsize += rv;
176 	} while (rv!=0);
177 	return(bsize);
178 }
179 
180 int
181 tempfile(void)
182 {
183 	static tindx = 0;
184 	char tmpf[20];
185 	int tmpfd;
186 
187 	sprint(tmpf, "/tmp/lp%d.%d", getpid(), tindx++);
188 	if((tmpfd=create(tmpf,
189 
190 #ifdef plan9
191 
192 						ORDWR|OTRUNC,
193 
194 #endif
195 
196 												0666)) < 0) {
197 		error(0, "cannot create temp file %s\n", tmpf);
198 		exits("cannot create temp file");
199 	}
200 	close(tmpfd);
201 	if((tmpfd=open(tmpf, ORDWR
202 
203 #ifdef plan9
204 
205 						|ORCLOSE|OTRUNC
206 
207 #endif
208 
209 									)) < 0) {
210 		error(0, "cannot open temp file %s\n", tmpf);
211 		exits("cannot open temp file");
212 	}
213 	return(tmpfd);
214 }
215 
216 int
217 recvACK(int netfd)
218 {
219 	int rv;
220 
221 	*jobbuf = '\0';
222 	alarm(RDNETIMEOUT);
223 	if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') {
224 		error(0, "failed to receive ACK, ");
225 		if (*jobbuf == '\0')
226 			error(1, "read failed\n");
227 		else
228 			error(1, "received <0x%x> instead\n", *jobbuf);
229 		rv = 0;
230 	} else rv = 1;
231 	alarm(0);
232 	return(rv);
233 }
234 
235 void
236 main(int argc, char *argv[])
237 {
238 	char *devdir;
239 	int i, rv, netfd, bsize;
240 	int datafd;
241 
242 #ifndef plan9
243 
244 	void (*oldhandler)();
245 
246 #endif
247 
248 	/* make connection */
249 	if (argc != 2) {
250 		fprint(stderr, "usage: %s network!destination!service\n", argv[0]);
251 		exits("incorrect number of arguments");
252 	}
253 
254 	/* read options line from stdin into lnbuf */
255 	i = readline(0);
256 
257 	/* read stdin into tempfile to get size */
258 	datafd = tempfile();
259 	bsize = prereadfile(datafd);
260 
261 	/* network connection is opened after data is in to avoid timeout */
262 	if ((netfd=dial(argv[1], 0, 0, 0)) < 0) {
263 		fprint(stderr, "dialing %s\n", devdir);
264 		perror("dial");
265 		exits("can't dial");
266 	}
267 
268 	/* write out the options we read above */
269 	if (write(netfd, lnbuf, i) != i) {
270 		error(0, "write error while sending options\n");
271 		exits("write error while sending options");
272 	}
273 
274 	/* send the size of the file to be sent */
275 	sprint(lnbuf, "%d\n", bsize);
276 	i = strlen(lnbuf);
277 	if ((rv=write(netfd, lnbuf, i)) != i) {
278 		perror("write error while sending size");
279 		error(0, "write returned %d\n", rv);
280 		exits("write error while sending size");
281 	}
282 
283 	if (seek(datafd, 0L, 0) < 0) {
284 		error(0, "error seeking temp file\n");
285 		exits("seek error");
286 	}
287 	/* mirror performance in readfile() in lpdaemon */
288 
289 #ifdef plan9
290 
291 	atnotify(alarmhandler, 1);
292 
293 #else
294 
295 	oldhandler = signal(SIGALRM, alarmhandler);
296 
297 #endif
298 
299 	dbgstate = 1;
300 	if(!recvACK(netfd)) {
301 		error(0, "failed to receive ACK before sending data\n");
302 		exits("recv ack1 failed");
303 	}
304 	dbgstate = 2;
305 	if ((i=pass(datafd, netfd, bsize)) != 0) {
306 		NAK(netfd);
307 		error(0, "failed to send %d bytes\n", i);
308 		exits("send data failed");
309 	}
310 	ACK(netfd);
311 	dbgstate = 3;
312 	if(!recvACK(netfd)) {
313 		error(0, "failed to receive ACK after sending data\n");
314 		exits("recv ack2 failed");
315 	}
316 
317 	/* get response, as from lp -q */
318 	dbgstate = 4;
319 	while((rv=read(netfd, jobbuf, RDSIZE)) > 0) {
320 		if((write(1, jobbuf, rv)) != rv) {
321 			error(0, "write error while sending to stdout\n");
322 			exits("write error while sending to stdout");
323 		}
324 	}
325 	dbgstate = 5;
326 
327 #ifdef plan9
328 
329 	atnotify(alarmhandler, 0);
330 	/* close down network connections and go away */
331 	exits("");
332 
333 #else
334 
335 	signal(SIGALRM, oldhandler);
336 	exit(0);
337 
338 #endif
339 
340 }
341