xref: /plan9/sys/src/cmd/lp/lpsend.c (revision 3468a4915d661daa200976acc4f80f51aae144b2)
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
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
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
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
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 #ifdef plan9
190 		ORDWR|OTRUNC,
191 #endif
192 	    0666)) < 0) {
193 		error(0, "cannot create temp file %s\n", tmpf);
194 		exits("cannot create temp file");
195 	}
196 	close(tmpfd);
197 	if((tmpfd=open(tmpf, ORDWR
198 #ifdef plan9
199 		|ORCLOSE|OTRUNC
200 #endif
201 	    )) < 0) {
202 		error(0, "cannot open temp file %s\n", tmpf);
203 		exits("cannot open temp file");
204 	}
205 	return(tmpfd);
206 }
207 
208 int
209 recvACK(int netfd)
210 {
211 	int rv;
212 
213 	*jobbuf = '\0';
214 	alarm(RDNETIMEOUT);
215 	if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') {
216 		error(0, "failed to receive ACK, ");
217 		if (*jobbuf == '\0')
218 			error(1, "read failed\n");
219 		else
220 			error(1, "received <0x%x> instead\n", *jobbuf);
221 		rv = 0;
222 	} else rv = 1;
223 	alarm(0);
224 	return(rv);
225 }
226 
227 void
228 main(int argc, char *argv[])
229 {
230 	char *devdir;
231 	int i, rv, netfd, bsize, datafd;
232 #ifndef plan9
233 	void (*oldhandler)();
234 #endif
235 
236 	/* make connection */
237 	if (argc != 2) {
238 		fprint(stderr, "usage: %s network!destination!service\n",
239 			argv[0]);
240 		exits("usage");
241 	}
242 
243 	/* read options line from stdin into lnbuf */
244 	i = readline(0);
245 
246 	/* read stdin into tempfile to get size */
247 	datafd = tempfile();
248 	bsize = prereadfile(datafd);
249 
250 	/* network connection is opened after data is in to avoid timeout */
251 	if ((netfd = dial(argv[1], 0, 0, 0)) < 0) {
252 		fprint(stderr, "dialing ");
253 		perror(argv[1]);
254 		exits("can't dial");
255 	}
256 
257 	/* write out the options we read above */
258 	if (write(netfd, lnbuf, i) != i) {
259 		error(0, "write error while sending options\n");
260 		exits("write error sending options");
261 	}
262 
263 	/* send the size of the file to be sent */
264 	sprint(lnbuf, "%d\n", bsize);
265 	i = strlen(lnbuf);
266 	if ((rv=write(netfd, lnbuf, i)) != i) {
267 		perror("write error while sending size");
268 		error(0, "write returned %d\n", rv);
269 		exits("write error sending size");
270 	}
271 
272 	if (seek(datafd, 0L, 0) < 0) {
273 		error(0, "error seeking temp file\n");
274 		exits("seek error");
275 	}
276 	/* mirror performance in readfile() in lpdaemon */
277 
278 #ifdef plan9
279 	atnotify(alarmhandler, 1);
280 #else
281 	oldhandler = signal(SIGALRM, alarmhandler);
282 #endif
283 
284 	dbgstate = 1;
285 	if(!recvACK(netfd)) {
286 		error(0, "failed to receive ACK before sending data\n");
287 		exits("recv ack1 failed");
288 	}
289 	dbgstate = 2;
290 	if ((i=pass(datafd, netfd, bsize)) != 0) {
291 		NAK(netfd);
292 		error(0, "failed to send %d bytes\n", i);
293 		exits("send data failed");
294 	}
295 	ACK(netfd);
296 	dbgstate = 3;
297 	if(!recvACK(netfd)) {
298 		error(0, "failed to receive ACK after sending data\n");
299 		exits("recv ack2 failed");
300 	}
301 
302 	/* get response, as from lp -q */
303 	dbgstate = 4;
304 	while((rv=read(netfd, jobbuf, RDSIZE)) > 0) {
305 		if((write(1, jobbuf, rv)) != rv) {
306 			error(0, "write error while sending to stdout\n");
307 			exits("write error while sending to stdout");
308 		}
309 	}
310 	dbgstate = 5;
311 
312 #ifdef plan9
313 	atnotify(alarmhandler, 0);
314 	/* close down network connections and go away */
315 	exits("");
316 #else
317 	signal(SIGALRM, oldhandler);
318 	exit(0);
319 #endif
320 }
321