xref: /plan9-contrib/sys/src/cmd/lp/lpdsend.c (revision b39189fd423aed869c5cf5189bc504918cff969b)
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <signal.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <sys/param.h>
10 
11 #define	REDIALTIMEOUT	15
12 #ifdef PLAN9
13 #include <Plan9libnet.h>
14 #endif
15 
16 enum {
17 	TIMEOUT = 30*60,
18 	SBSIZE = 8192,
19 };
20 
21 char tmpfilename[L_tmpnam+1];
22 unsigned char sendbuf[SBSIZE];
23 
24 int alarmstate = 0;
25 int debugflag = 0;
26 int killflag = 0;
27 int statflag = 0;
28 
29 void
30 cleanup(void)
31 {
32 	unlink(tmpfilename);
33 }
34 
35 void
36 debug(char *str)
37 {
38 	if (debugflag)
39 		fprintf(stderr, "%s", str);
40 }
41 
42 void
43 alarmhandler(int sig)
44 {
45 	fprintf(stderr, "timeout occurred, check printer.\n");
46 	exit(2);
47 }
48 
49 /* send a message after each WARNPC percent of data sent */
50 #define WARNPC	5
51 
52 int
53 copyfile(int in, int out, long tosend)
54 {
55 	int n;
56 	int sent = 0;
57 	int percent = 0;
58 
59 	if (debugflag)
60 		fprintf(stderr, "lpdsend: copyfile(%d,%d,%ld)\n",
61 			in, out, tosend);
62 	while ((n=read(in, sendbuf, SBSIZE)) > 0) {
63 		if (debugflag)
64 			fprintf(stderr, "lpdsend: copyfile read %d bytes from %d\n",
65 				n, in);
66 		alarm(TIMEOUT);
67 		alarmstate = 1;
68 		if (write(out, sendbuf, n) != n) {
69 			alarm(0);
70 			fprintf(stderr, "write to fd %d failed\n", out);
71 			return(0);
72 		}
73 		alarm(0);
74 		if (debugflag)
75 			fprintf(stderr, "lpdsend: copyfile wrote %d bytes to %d\n",
76 				n, out);
77 		sent += n;
78 		if (tosend && sent*100/tosend >= percent+WARNPC) {
79 			percent += WARNPC;
80 			fprintf(stderr, ": %5.2f%% sent\n", sent*100.0/tosend);
81 		}
82 	}
83 	if (debugflag)
84 		fprintf(stderr, "lpdsend: copyfile read %d bytes from %d\n",
85 			n, in);
86 	return(!n);
87 }
88 
89 char  strbuf[120];
90 char hostname[MAXHOSTNAMELEN], *username, *printername, *killarg;
91 char *inputname;
92 char filetype = 'o';	/* 'o' is for PostScript */
93 int seqno = 0;
94 char *seqfilename;
95 
96 void
97 killjob(int printerfd)
98 {
99 	int strlength;
100 
101 	if (printername==0) {
102 		fprintf(stderr, "no printer name\n");
103 		exit(1);
104 	}
105 	if (username==0) {
106 		fprintf(stderr, "no user name given\n");
107 		exit(1);
108 	}
109 	if (killarg==0) {
110 		fprintf(stderr, "no job to kill\n");
111 		exit(1);
112 	}
113 	sprintf(strbuf, "%c%s %s %s\n", '\5', printername, username, killarg);
114 	strlength = strlen(strbuf);
115 	if (write(printerfd, strbuf, strlength) != strlength) {
116 		fprintf(stderr, "write(printer) error\n");
117 		exit(1);
118 	}
119 	copyfile(printerfd, 2, 0L);
120 }
121 
122 void
123 checkqueue(int printerfd)
124 {
125 	int n, strlength;
126 	unsigned char sendbuf[1];
127 
128 	sprintf(strbuf, "%c%s\n", '\4', printername);
129 	strlength = strlen(strbuf);
130 	if (write(printerfd, strbuf, strlength) != strlength) {
131 		fprintf(stderr, "write(printer) error\n");
132 		exit(1);
133 	}
134 	copyfile(printerfd, 2, 0L);
135 /*
136 	while ((n=read(printerfd, sendbuf, 1)) > 0) {
137 		write(2, sendbuf, n);
138 	}
139 */
140 }
141 
142 void
143 getack(int printerfd, int as)
144 {
145 	char resp;
146 	int rv;
147 
148 	alarm(TIMEOUT);
149 	alarmstate = as;
150 	if ((rv = read(printerfd, &resp, 1)) != 1 || resp != '\0') {
151 		fprintf(stderr, "getack failed: read returned %d, "
152 			"read value (if any) %d, alarmstate=%d\n",
153 			rv, resp, alarmstate);
154 		exit(1);
155 	}
156 	alarm(0);
157 }
158 
159 /* send control file */
160 void
161 sendctrl(int printerfd)
162 {
163 	char cntrlstrbuf[256];
164 	int strlength, cntrlen;
165 
166 	sprintf(cntrlstrbuf, "H%s\nP%s\n%cdfA%3.3d%s\n", hostname, username, filetype, seqno, hostname);
167 	cntrlen = strlen(cntrlstrbuf);
168 	sprintf(strbuf, "%c%d cfA%3.3d%s\n", '\2', cntrlen, seqno, hostname);
169 	strlength = strlen(strbuf);
170 	if (write(printerfd, strbuf, strlength) != strlength) {
171 		fprintf(stderr, "write(printer) error\n");
172 		exit(1);
173 	}
174 	getack(printerfd, 3);
175 	if (write(printerfd, cntrlstrbuf, cntrlen) != cntrlen) {
176 		fprintf(stderr, "write(printer) error\n");
177 		exit(1);
178 	}
179 	if (write(printerfd, "\0", 1) != 1) {
180 		fprintf(stderr, "write(printer) error\n");
181 		exit(1);
182 	}
183 	getack(printerfd, 4);
184 }
185 
186 /* send data file */
187 void
188 senddata(int inputfd, int printerfd, long size)
189 {
190 	int strlength;
191 
192 	sprintf(strbuf, "%c%d dfA%3.3d%s\n", '\3', size, seqno, hostname);
193 	strlength = strlen(strbuf);
194 	if (write(printerfd, strbuf, strlength) != strlength) {
195 		fprintf(stderr, "write(printer) error\n");
196 		exit(1);
197 	}
198 	getack(printerfd, 5);
199 	if (!copyfile(inputfd, printerfd, size)) {
200 		fprintf(stderr, "failed to send file to printer\n");
201 		exit(1);
202 	}
203 	if (write(printerfd, "\0", 1) != 1) {
204 		fprintf(stderr, "write(printer) error\n");
205 		exit(1);
206 	}
207 	fprintf(stderr, "%d bytes sent, status: waiting for end of job\n", size);
208 	getack(printerfd, 6);
209 }
210 
211 void
212 sendjob(int inputfd, int printerfd)
213 {
214 	struct stat statbuf;
215 	int strlength;
216 
217 	if (fstat(inputfd, &statbuf) < 0) {
218 		fprintf(stderr, "fstat(%s) failed\n", inputname);
219 		exit(1);
220 	}
221 	sprintf(strbuf, "%c%s\n", '\2', printername);
222 	strlength = strlen(strbuf);
223 	if (write(printerfd, strbuf, strlength) != strlength) {
224 		fprintf(stderr, "write(printer) error\n");
225 		exit(1);
226 	}
227 	getack(printerfd, 2);
228 	debug("send data\n");
229 	senddata(inputfd, printerfd, statbuf.st_size);
230 	debug("send control info\n");
231 	sendctrl(printerfd);
232 	fprintf(stderr, "%ld bytes sent, status: end of job\n", statbuf.st_size);
233 }
234 
235 /*
236  *  make an address, add the defaults
237  */
238 char *
239 netmkaddr(char *linear, char *defnet, char *defsrv)
240 {
241 	static char addr[512];
242 	char *cp;
243 
244 	/*
245 	 *  dump network name
246 	 */
247 	cp = strchr(linear, '!');
248 	if(cp == 0){
249 		if(defnet==0){
250 			if(defsrv)
251 				snprintf(addr, sizeof addr, "net!%s!%s", linear, defsrv);
252 			else
253 				snprintf(addr, sizeof addr, "net!%s", linear);
254 		}
255 		else {
256 			if(defsrv)
257 				snprintf(addr, sizeof addr, "%s!%s!%s", defnet, linear, defsrv);
258 			else
259 				snprintf(addr, sizeof addr, "%s!%s", defnet, linear);
260 		}
261 		return addr;
262 	}
263 
264 	/*
265 	 *  if there is already a service, use it
266 	 */
267 	cp = strchr(cp+1, '!');
268 	if(cp)
269 		return linear;
270 
271 	/*
272 	 *  add default service
273 	 */
274 	if(defsrv == 0)
275 		return linear;
276 	sprintf(addr, "%s!%s", linear, defsrv);
277 
278 	return addr;
279 }
280 
281 main(int argc, char *argv[])
282 {
283 	int c, usgflg = 0, inputfd, printerfd, sendport;
284 	char *desthostname, *hnend;
285 	char portstr[4];
286 
287 	if (signal(SIGALRM, alarmhandler) == SIG_ERR) {
288 		fprintf(stderr, "failed to set alarm handler\n");
289 		exit(1);
290 	}
291 	while ((c = getopt(argc, argv, "Dd:k:qs:t:H:P:")) != -1)
292 		switch (c) {
293 		case 'D':
294 			debugflag = 1;
295 			debug("debugging on\n");
296 			break;
297 		case 'd':
298 			printername = optarg;
299 			break;
300 		case 'k':
301 			if (statflag) {
302 				fprintf(stderr, "cannot have both -k and -q flags\n");
303 				exit(1);
304 			}
305 			killflag = 1;
306 			killarg = optarg;
307 			break;
308 		case 'q':
309 			if (killflag) {
310 				fprintf(stderr, "cannot have both -q and -k flags\n");
311 				exit(1);
312 			}
313 			statflag = 1;
314 			break;
315 		case 's':
316 			seqno = strtol(optarg, NULL, 10);
317 			if (seqno < 0 || seqno > 999)
318 				seqno = 0;
319 			break;
320 		case 't':
321 			switch (filetype) {
322 			case 'c':
323 			case 'd':
324 			case 'f':
325 			case 'g':
326 			case 'l':
327 			case 'n':
328 			case 'o':
329 			case 'p':
330 			case 'r':
331 			case 't':
332 			case 'v':
333 			case 'z':
334 				filetype = optarg[0];
335 				break;
336 			default:
337 				usgflg++;
338 				break;
339 			}
340 			break;
341 		case 'H':
342 			strncpy(hostname, optarg, MAXHOSTNAMELEN);
343 			break;
344 		case 'P':
345 			username = optarg;
346 			break;
347 		default:
348 		case '?':
349 			fprintf(stderr, "unknown option %c\n", c);
350 			usgflg++;
351 		}
352 	if (argc < 2) usgflg++;
353 	if (optind < argc) {
354 		desthostname = argv[optind++];
355 	} else
356 		usgflg++;
357 	if (usgflg) {
358 		fprintf(stderr, "usage: to send a job - %s -d printer -H hostname -P username [-s seqno] [-t[cdfgklnoprtvz]] desthost [filename]\n", argv[0]);
359 		fprintf(stderr, "     to check status - %s -d printer -q desthost\n", argv[0]);
360 		fprintf(stderr, "       to kill a job - %s -d printer -P username -k jobname desthost\n", argv[0]);
361 		exit(1);
362 	}
363 
364 /* make sure the file to send is here and ready
365  * otherwise the TCP connection times out.
366  */
367 	if (!statflag && !killflag) {
368 		if (optind < argc) {
369 			inputname = argv[optind++];
370 			debug("open("); debug(inputname); debug(")\n");
371 			inputfd = open(inputname, O_RDONLY);
372 			if (inputfd < 0) {
373 				fprintf(stderr, "open(%s) failed\n", inputname);
374 				exit(1);
375 			}
376 		} else {
377 			inputname = "stdin";
378 			tmpnam(tmpfilename);
379 			debug("using stdin\n");
380 			if ((inputfd = open(tmpfilename, O_RDWR|O_CREAT, 0600)) < 0) {
381 				fprintf(stderr, "open(%s) failed\n", tmpfilename);
382 				exit(1);
383 			}
384 			atexit(cleanup);
385 			debug("copy input to temp file ");
386 			debug(tmpfilename);
387 			debug("\n");
388 			if (!copyfile(0, inputfd, 0L)) {
389 				fprintf(stderr, "failed to copy file to temporary file\n");
390 				exit(1);
391 			}
392 			if (lseek(inputfd, 0L, 0) < 0) {
393 				fprintf(stderr, "failed to seek back to the beginning of the temporary file\n");
394 				exit(1);
395 			}
396 		}
397 	}
398 
399 	sprintf(strbuf, "%s", netmkaddr(desthostname, "tcp", "printer"));
400 	fprintf(stderr, "connecting to %s\n", strbuf);
401 	for (sendport=721; sendport<=731; sendport++) {
402 		sprintf(portstr, "%3.3d", sendport);
403 		fprintf(stderr, " trying from port %s...", portstr);
404 		debug(" dial("); debug(strbuf); debug(", "); debug(portstr); debug(", 0, 0) ...");
405 		printerfd = dial(strbuf, portstr, 0, 0);
406 		if (printerfd >= 0) {
407 			fprintf(stderr, "connected\n");
408 			break;
409 		}
410 		fprintf(stderr, "failed\n");
411 		sleep(REDIALTIMEOUT);
412 	}
413 	if (printerfd < 0) {
414 		fprintf(stderr, "Cannot open a valid port!\n");
415 		fprintf(stderr, "-  All source ports [721-731] may be busy.\n");
416 		fprintf(stderr, "-  Is recipient ready and online?\n");
417 		fprintf(stderr, "-  If all else fails, cycle the power!\n");
418 		exit(1);
419 	}
420 /*	hostname[8] = '\0'; */
421 #ifndef PLAN9
422 	if (gethostname(hostname, sizeof(hostname)) < 0) {
423 		perror("gethostname");
424 		exit(1);
425 	}
426 #endif
427 /*	if ((hnend = strchr(hostname, '.')) != NULL)
428 		*hnend = '\0';
429  */
430 	if (statflag) {
431 		checkqueue(printerfd);
432 	} else if (killflag) {
433 		killjob(printerfd);
434 	} else {
435 		sendjob(inputfd, printerfd);
436 	}
437 	exit(0);
438 }
439