xref: /plan9-contrib/sys/src/cmd/lp/lpsend.c (revision 34c2901791623ea03308d4cc8cd056b841394d48)
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 		;
72 	va_end(ap);
73 	fprint(stderr, s1, *args);
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 }
100 
101 void
102 alarmhandler() {
103 	fprintf(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
104 }
105 
106 #endif
107 
108 /* get a line from inpfd using nonbuffered input.  The line is truncated if it is too
109  * long for the buffer.  The result is left in lnbuf and the number of characters
110  * read in is returned.
111  */
112 int
113 readline(int inpfd)
114 {
115 	register char *ap;
116 	register int i;
117 
118 	ap = lnbuf;
119 	i = 0;
120 	do {
121 		if (read(inpfd, ap, 1) != 1) {
122 			error(0, "read error in readline, fd=%d\n", inpfd);
123 			break;
124 		}
125 	} while ((++i < LNBFSZ - 2) && *ap++ != '\n');
126 	if (i == LNBFSZ - 2) {
127 		*ap = '\n';
128 		i++;
129 	}
130 	*ap = '\0';
131 	return(i);
132 }
133 
134 #define	RDSIZE 512
135 char jobbuf[RDSIZE];
136 
137 int
138 pass(int inpfd, int outfd, int bsize)
139 {
140 	int bcnt = 0;
141 	int rv = 0;
142 
143 	for(bcnt=bsize; bcnt > 0; bcnt -= rv) {
144 		alarm(WRNETIMEOUT);	/* to break hanging */
145 		if((rv=read(inpfd, jobbuf, MIN(bcnt,RDSIZE))) < 0) {
146 			error(0, "read error during pass, %d remaining\n", bcnt);
147 			break;
148 		} else if((write(outfd, jobbuf, rv)) != rv) {
149 			error(0, "write error during pass, %d remaining\n", bcnt);
150 			break;
151 		}
152 	}
153 	alarm(0);
154 	return(bcnt);
155 }
156 
157 /* get whatever stdin has and put it into the temporary file.
158  * return the file size.
159  */
160 int
161 prereadfile(int inpfd)
162 {
163 	int rv, bsize;
164 
165 	bsize = 0;
166 	do {
167 		if((rv=read(0, jobbuf, RDSIZE))<0) {
168 			error(0, "read error while making temp file\n");
169 			exits("read error while making temp file");
170 		} else if((write(inpfd, jobbuf, rv)) != rv) {
171 			error(0, "write error while making temp file\n");
172 			exits("write error while making temp file");
173 		}
174 		bsize += rv;
175 	} while (rv!=0);
176 	return(bsize);
177 }
178 
179 int
180 tempfile(void)
181 {
182 	static tindx = 0;
183 	char tmpf[20];
184 	int tmpfd;
185 
186 	sprint(tmpf, "/tmp/lp%d.%d", getpid(), tindx++);
187 	if((tmpfd=create(tmpf,
188 #ifdef plan9
189 		ORDWR|OTRUNC,
190 #endif
191 	    0666)) < 0) {
192 		error(0, "cannot create temp file %s\n", tmpf);
193 		exits("cannot create temp file");
194 	}
195 	close(tmpfd);
196 	if((tmpfd=open(tmpf, ORDWR
197 #ifdef plan9
198 		|ORCLOSE|OTRUNC
199 #endif
200 	    )) < 0) {
201 		error(0, "cannot open temp file %s\n", tmpf);
202 		exits("cannot open temp file");
203 	}
204 	return(tmpfd);
205 }
206 
207 int
208 recvACK(int netfd)
209 {
210 	int rv;
211 
212 	*jobbuf = '\0';
213 	alarm(RDNETIMEOUT);
214 	if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') {
215 		error(0, "failed to receive ACK, ");
216 		if (*jobbuf == '\0')
217 			error(1, "read failed\n");
218 		else
219 			error(1, "received <0x%x> instead\n", *jobbuf);
220 		rv = 0;
221 	} else rv = 1;
222 	alarm(0);
223 	return(rv);
224 }
225 
226 void
227 main(int argc, char *argv[])
228 {
229 	char *devdir;
230 	int i, rv, netfd, bsize;
231 	int datafd;
232 
233 #ifndef plan9
234 
235 	void (*oldhandler)();
236 
237 #endif
238 
239 	/* make connection */
240 	if (argc != 2) {
241 		fprint(stderr, "usage: %s network!destination!service\n", argv[0]);
242 		exits("incorrect number of arguments");
243 	}
244 
245 	/* read options line from stdin into lnbuf */
246 	i = readline(0);
247 
248 	/* read stdin into tempfile to get size */
249 	datafd = tempfile();
250 	bsize = prereadfile(datafd);
251 
252 	/* network connection is opened after data is in to avoid timeout */
253 	if ((netfd=dial(argv[1], 0, 0, 0)) < 0) {
254 		fprint(stderr, "dialing %s\n", devdir);
255 		perror("dial");
256 		exits("can't dial");
257 	}
258 
259 	/* write out the options we read above */
260 	if (write(netfd, lnbuf, i) != i) {
261 		error(0, "write error while sending options\n");
262 		exits("write error while sending options");
263 	}
264 
265 	/* send the size of the file to be sent */
266 	sprint(lnbuf, "%d\n", bsize);
267 	i = strlen(lnbuf);
268 	if ((rv=write(netfd, lnbuf, i)) != i) {
269 		perror("write error while sending size");
270 		error(0, "write returned %d\n", rv);
271 		exits("write error while sending size");
272 	}
273 
274 	if (seek(datafd, 0L, 0) < 0) {
275 		error(0, "error seeking temp file\n");
276 		exits("seek error");
277 	}
278 	/* mirror performance in readfile() in lpdaemon */
279 
280 #ifdef plan9
281 
282 	atnotify(alarmhandler, 1);
283 
284 #else
285 
286 	oldhandler = signal(SIGALRM, alarmhandler);
287 
288 #endif
289 
290 	dbgstate = 1;
291 	if(!recvACK(netfd)) {
292 		error(0, "failed to receive ACK before sending data\n");
293 		exits("recv ack1 failed");
294 	}
295 	dbgstate = 2;
296 	if ((i=pass(datafd, netfd, bsize)) != 0) {
297 		NAK(netfd);
298 		error(0, "failed to send %d bytes\n", i);
299 		exits("send data failed");
300 	}
301 	ACK(netfd);
302 	dbgstate = 3;
303 	if(!recvACK(netfd)) {
304 		error(0, "failed to receive ACK after sending data\n");
305 		exits("recv ack2 failed");
306 	}
307 
308 	/* get response, as from lp -q */
309 	dbgstate = 4;
310 	while((rv=read(netfd, jobbuf, RDSIZE)) > 0) {
311 		if((write(1, jobbuf, rv)) != rv) {
312 			error(0, "write error while sending to stdout\n");
313 			exits("write error while sending to stdout");
314 		}
315 	}
316 	dbgstate = 5;
317 
318 #ifdef plan9
319 
320 	atnotify(alarmhandler, 0);
321 	/* close down network connections and go away */
322 	exits("");
323 
324 #else
325 
326 	signal(SIGALRM, oldhandler);
327 	exit(0);
328 
329 #endif
330 
331 }
332