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