xref: /plan9/sys/src/cmd/lp/lpdsend.c (revision 027288c8a8763d34db13dc89d8bcd6514dbc2163)
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
cleanup(void)30 cleanup(void)
31 {
32 	unlink(tmpfilename);
33 }
34 
35 void
debug(char * str)36 debug(char *str)
37 {
38 	if (debugflag)
39 		fprintf(stderr, "%s", str);
40 }
41 
42 void
alarmhandler(int sig)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
copyfile(int in,int out,long tosend)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
killjob(int printerfd)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
checkqueue(int printerfd)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
getack(int printerfd,int as)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
sendctrl(int printerfd)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
senddata(int inputfd,int printerfd,long size)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
sendjob(int inputfd,int printerfd)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 *
netmkaddr(char * linear,char * defnet,char * defsrv)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 void
main(int argc,char * argv[])282 main(int argc, char *argv[])
283 {
284 	int c, usgflg = 0, inputfd, printerfd, sendport;
285 	char *desthostname, *hnend;
286 	char portstr[4];
287 
288 	if (signal(SIGALRM, alarmhandler) == SIG_ERR) {
289 		fprintf(stderr, "failed to set alarm handler\n");
290 		exit(1);
291 	}
292 	while ((c = getopt(argc, argv, "Dd:k:qs:t:H:P:")) != -1)
293 		switch (c) {
294 		case 'D':
295 			debugflag = 1;
296 			debug("debugging on\n");
297 			break;
298 		case 'd':
299 			printername = optarg;
300 			break;
301 		case 'k':
302 			if (statflag) {
303 				fprintf(stderr, "cannot have both -k and -q flags\n");
304 				exit(1);
305 			}
306 			killflag = 1;
307 			killarg = optarg;
308 			break;
309 		case 'q':
310 			if (killflag) {
311 				fprintf(stderr, "cannot have both -q and -k flags\n");
312 				exit(1);
313 			}
314 			statflag = 1;
315 			break;
316 		case 's':
317 			seqno = strtol(optarg, NULL, 10);
318 			if (seqno < 0 || seqno > 999)
319 				seqno = 0;
320 			break;
321 		case 't':
322 			switch (filetype) {
323 			case 'c':
324 			case 'd':
325 			case 'f':
326 			case 'g':
327 			case 'l':
328 			case 'n':
329 			case 'o':
330 			case 'p':
331 			case 'r':
332 			case 't':
333 			case 'v':
334 			case 'z':
335 				filetype = optarg[0];
336 				break;
337 			default:
338 				usgflg++;
339 				break;
340 			}
341 			break;
342 		case 'H':
343 			strncpy(hostname, optarg, MAXHOSTNAMELEN);
344 			break;
345 		case 'P':
346 			username = optarg;
347 			break;
348 		default:
349 		case '?':
350 			fprintf(stderr, "unknown option %c\n", c);
351 			usgflg++;
352 		}
353 	if (argc < 2) usgflg++;
354 	if (optind < argc) {
355 		desthostname = argv[optind++];
356 	} else
357 		usgflg++;
358 	if (usgflg) {
359 		fprintf(stderr, "usage: to send a job - %s -d printer -H hostname -P username [-s seqno] [-t[cdfgklnoprtvz]] desthost [filename]\n", argv[0]);
360 		fprintf(stderr, "     to check status - %s -d printer -q desthost\n", argv[0]);
361 		fprintf(stderr, "       to kill a job - %s -d printer -P username -k jobname desthost\n", argv[0]);
362 		exit(1);
363 	}
364 
365 /* make sure the file to send is here and ready
366  * otherwise the TCP connection times out.
367  */
368 	if (!statflag && !killflag) {
369 		if (optind < argc) {
370 			inputname = argv[optind++];
371 			debug("open("); debug(inputname); debug(")\n");
372 			inputfd = open(inputname, O_RDONLY);
373 			if (inputfd < 0) {
374 				fprintf(stderr, "open(%s) failed\n", inputname);
375 				exit(1);
376 			}
377 		} else {
378 			inputname = "stdin";
379 			tmpnam(tmpfilename);
380 			debug("using stdin\n");
381 			if ((inputfd = open(tmpfilename, O_RDWR|O_CREAT, 0600)) < 0) {
382 				fprintf(stderr, "open(%s) failed\n", tmpfilename);
383 				exit(1);
384 			}
385 			atexit(cleanup);
386 			debug("copy input to temp file ");
387 			debug(tmpfilename);
388 			debug("\n");
389 			if (!copyfile(0, inputfd, 0L)) {
390 				fprintf(stderr, "failed to copy file to temporary file\n");
391 				exit(1);
392 			}
393 			if (lseek(inputfd, 0L, 0) < 0) {
394 				fprintf(stderr, "failed to seek back to the beginning of the temporary file\n");
395 				exit(1);
396 			}
397 		}
398 	}
399 
400 	sprintf(strbuf, "%s", netmkaddr(desthostname, "tcp", "printer"));
401 	fprintf(stderr, "connecting to %s\n", strbuf);
402 	for (sendport=721; sendport<=731; sendport++) {
403 		sprintf(portstr, "%3.3d", sendport);
404 		fprintf(stderr, " trying from port %s...", portstr);
405 		debug(" dial("); debug(strbuf); debug(", "); debug(portstr); debug(", 0, 0) ...");
406 		printerfd = dial(strbuf, portstr, 0, 0);
407 		if (printerfd >= 0) {
408 			fprintf(stderr, "connected\n");
409 			break;
410 		}
411 		fprintf(stderr, "failed\n");
412 		sleep(REDIALTIMEOUT);
413 	}
414 	if (printerfd < 0) {
415 		fprintf(stderr, "Cannot open a valid port!\n");
416 		fprintf(stderr, "-  All source ports [721-731] may be busy.\n");
417 		fprintf(stderr, "-  Is recipient ready and online?\n");
418 		fprintf(stderr, "-  If all else fails, cycle the power!\n");
419 		exit(1);
420 	}
421 /*	hostname[8] = '\0'; */
422 #ifndef PLAN9
423 	if (gethostname(hostname, sizeof(hostname)) < 0) {
424 		perror("gethostname");
425 		exit(1);
426 	}
427 #endif
428 /*	if ((hnend = strchr(hostname, '.')) != NULL)
429 		*hnend = '\0';
430  */
431 	if (statflag) {
432 		checkqueue(printerfd);
433 	} else if (killflag) {
434 		killjob(printerfd);
435 	} else {
436 		sendjob(inputfd, printerfd);
437 	}
438 	exit(0);
439 }
440