xref: /plan9/sys/src/cmd/lp/lpsend.c (revision 14f51593fd82e19ba95969a8c07ff71131015979)
1 #ifdef plan9
2 
3 #include <u.h>
4 #include <libc.h>
5 
6 enum {
7 	stderr = 2,
8 	RDNETIMEOUT = 30*60*1000,
9 	WRNETIMEOUT = RDNETIMEOUT,
10 };
11 #else
12 
13 /* not for plan 9 */
14 #include <stdio.h>
15 #include <errno.h>
16 #include <time.h>
17 #include <fcntl.h>
18 #include <signal.h>
19 
20 #define	create	creat
21 #define	seek	lseek
22 #define	fprint	fprintf
23 #define	sprint	sprintf
24 #define	exits	exit
25 
26 #define	ORDWR	O_RDWR
27 #define	OTRUNC	O_TRUNC
28 #define	ORCLOSE	0
29 
30 #define RDNETIMEOUT	60
31 #define WRNETIMEOUT	60
32 
33 #endif
34 
35 #define MIN(a,b)	((a<b)?a:b)
36 
37 #define	ACK(a)	write(a, "", 1)
38 #define NAK(a)	write(a, "\001", 1)
39 
40 #define LPDAEMONLOG	"/tmp/lpdaemonl"
41 
42 #define LNBFSZ	4096
43 char lnbuf[LNBFSZ];
44 int dbgstate = 0;
45 char *dbgstrings[] = {
46 	"",
47 	"rcvack1",
48 	"send",
49 	"rcvack2",
50 	"response",
51 	"done"
52 };
53 
54 #ifdef plan9
55 
56 void
error(int level,char * s1,...)57 error(int level, char *s1, ...)
58 {
59 	va_list ap;
60 	long thetime;
61 	char *chartime;
62 	char *args[8];
63 	int argno = 0;
64 
65 	if (level == 0) {
66 		time(&thetime);
67 		chartime = ctime(thetime);
68 		fprint(stderr, "%.15s ", &(chartime[4]));
69 	}
70 	va_start(ap, s1);
71 	while(args[argno++] = va_arg(ap, char*))
72 		;
73 	va_end(ap);
74 	fprint(stderr, s1, *args);
75 }
76 
77 int
alarmhandler(void * foo,char * note)78 alarmhandler(void *foo, char *note) {
79 	USED(foo);
80 	if(strcmp(note, "alarm")==0) {
81 		fprint(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
82 		return(1);
83 	} else return(0);
84 }
85 
86 #else
87 
88 void
error(int level,char * s1,...)89 error(int level, char *s1, ...)
90 {
91 	time_t thetime;
92 	char *chartime;
93 
94 	if (level == 0) {
95 		time(&thetime);
96 		chartime = ctime(&thetime);
97 		fprintf(stderr, "%.15s ", &(chartime[4]));
98 	}
99 	fprintf(stderr, s1, &s1 + 1);
100 }
101 
102 void
alarmhandler()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
readline(int inpfd)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
pass(int inpfd,int outfd,int bsize)139 pass(int inpfd, int outfd, int bsize)
140 {
141 	int rv, bcnt;
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
prereadfile(int inpfd)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
tempfile(void)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
recvACK(int netfd)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 <%#x> instead\n", (uchar)*jobbuf);
220 		rv = 0;
221 	} else rv = 1;
222 	alarm(0);
223 	return(rv);
224 }
225 
226 void
main(int argc,char * argv[])227 main(int argc, char *argv[])
228 {
229 	int i, rv, netfd, bsize, datafd;
230 #ifndef plan9
231 	void (*oldhandler)();
232 #endif
233 
234 	/* make connection */
235 	if (argc != 2) {
236 		fprint(stderr, "usage: %s network!destination!service\n",
237 			argv[0]);
238 		exits("usage");
239 	}
240 
241 	/* read options line from stdin into lnbuf */
242 	i = readline(0);
243 
244 	/* read stdin into tempfile to get size */
245 	datafd = tempfile();
246 	bsize = prereadfile(datafd);
247 
248 	/* network connection is opened after data is in to avoid timeout */
249 	if ((netfd = dial(argv[1], 0, 0, 0)) < 0) {
250 		fprint(stderr, "dialing ");
251 		perror(argv[1]);
252 		exits("can't dial");
253 	}
254 
255 	/* write out the options we read above */
256 	if (write(netfd, lnbuf, i) != i) {
257 		error(0, "write error while sending options\n");
258 		exits("write error sending options");
259 	}
260 
261 	/* send the size of the file to be sent */
262 	sprint(lnbuf, "%d\n", bsize);
263 	i = strlen(lnbuf);
264 	if ((rv=write(netfd, lnbuf, i)) != i) {
265 		perror("write error while sending size");
266 		error(0, "write returned %d\n", rv);
267 		exits("write error sending size");
268 	}
269 
270 	if (seek(datafd, 0L, 0) < 0) {
271 		error(0, "error seeking temp file\n");
272 		exits("seek error");
273 	}
274 	/* mirror performance in readfile() in lpdaemon */
275 
276 #ifdef plan9
277 	atnotify(alarmhandler, 1);
278 #else
279 	oldhandler = signal(SIGALRM, alarmhandler);
280 #endif
281 
282 	dbgstate = 1;
283 	if(!recvACK(netfd)) {
284 		error(0, "failed to receive ACK before sending data\n");
285 		exits("recv ack1 failed");
286 	}
287 	dbgstate = 2;
288 	if ((i=pass(datafd, netfd, bsize)) != 0) {
289 		NAK(netfd);
290 		error(0, "failed to send %d bytes\n", i);
291 		exits("send data failed");
292 	}
293 	ACK(netfd);
294 	dbgstate = 3;
295 	if(!recvACK(netfd)) {
296 		error(0, "failed to receive ACK after sending data\n");
297 		exits("recv ack2 failed");
298 	}
299 
300 	/* get response, as from lp -q */
301 	dbgstate = 4;
302 	while((rv=read(netfd, jobbuf, RDSIZE)) > 0) {
303 		if((write(1, jobbuf, rv)) != rv) {
304 			error(0, "write error while sending to stdout\n");
305 			exits("write error while sending to stdout");
306 		}
307 	}
308 	dbgstate = 5;
309 
310 #ifdef plan9
311 	atnotify(alarmhandler, 0);
312 	/* close down network connections and go away */
313 	exits("");
314 #else
315 	signal(SIGALRM, oldhandler);
316 	exit(0);
317 #endif
318 }
319