xref: /plan9/sys/src/cmd/postscript/tcpostio/tcpostio.c (revision 14f51593fd82e19ba95969a8c07ff71131015979)
1 #define _BSD_EXTENSION
2 #define _NET_EXTENSION
3 #define _POSIX_SOURCE
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <signal.h>
12 #include <errno.h>
13 #include <select.h>
14 #include <sys/types.h>
15 #include <sys/time.h>
16 #include <sys/socket.h>
17 #include <sys/wait.h>
18 
19 extern	int dial_debug;
20 extern	int dial(char*, char*, char*, int*);
21 
22 
23 /* debug = 0 for no debugging */
24 /* debug = 1 for readprinter debugging */
25 /* debug = 2 for sendprinter debugging */
26 /* debug = 3 for full debugging, its hard to read the messages */
27 
28 int debug = 0;
29 #define READTIMEOUT	300
30 #define	RCVSELTIMEOUT	30
31 #define	SNDSELTIMEOUT	300
32 
33 void
rdtmout(void)34 rdtmout(void) {
35 	fprintf(stderr, "read timeout occurred, check printer\n");
36 }
37 
38 int
getline(int fd,char * buf,int len)39 getline(int fd, char *buf, int len) {
40 	char *bp, c;
41 	int i = 0, n;
42 
43 	bp = buf;
44 	while (alarm(READTIMEOUT),(n=read(fd, bp, 1)) == 1) {
45 		alarm(0);
46 		if (*bp == '\r') continue;
47 		i += n;
48 
49 		c = *bp++;
50 		if (c == '\n' || c == '\004' || i >= len-1)
51 			break;
52 	}
53 	alarm(0);
54 	if (n < 0)
55 		return(n);
56 	*bp = '\0';
57 	return(i);
58 }
59 
60 typedef struct {
61 	char	*state;			/* printer's current status */
62 	int	val;			/* value returned by getstatus() */
63 } Status;
64 
65 /* printer states */
66 #define	INITIALIZING	0
67 #define	IDLE		1
68 #define	BUSY		2
69 #define	WAITING		3
70 #define	PRINTING	4
71 #define	PRINTERERROR	5
72 #define	ERROR		6
73 #define	FLUSHING	7
74 #define UNKNOWN		8
75 
76 /* protocol requests and program states */
77 #define START	'S'
78 unsigned char Start[] = { START };
79 #define ID_LE		'L'
80 unsigned char Id_le[] = { ID_LE };
81 #define	REQ_STAT	'T'
82 unsigned char Req_stat[] = { REQ_STAT };
83 #define	SEND_DATA	'D'
84 unsigned char Send_data[] = { SEND_DATA };
85 #define	SENT_DATA	'A'
86 unsigned char Sent_data[] = { SENT_DATA };
87 #define	WAIT_FOR_EOJ	'W'
88 unsigned char Wait_for_eoj[] = { WAIT_FOR_EOJ };
89 #define END_OF_JOB	'E'
90 unsigned char End_of_job[] = { END_OF_JOB };
91 #define FATAL_ERROR	'F'
92 unsigned char Fatal_error[] = { FATAL_ERROR };
93 #define	WAIT_FOR_IDLE	'I'
94 unsigned char Wait_for_idle[] = { WAIT_FOR_IDLE };
95 #define	OVER_AND_OUT	'O'
96 unsigned char Over_and_out[] = { OVER_AND_OUT };
97 
98 Status	statuslist[] = {
99 	"initializing", INITIALIZING,
100 	"idle", IDLE,
101 	"busy", BUSY,
102 	"waiting", WAITING,
103 	"printing", PRINTING,
104 	"printererror", PRINTERERROR,
105 	"Error", ERROR,
106 	"flushing", FLUSHING,
107 	NULL, UNKNOWN
108 };
109 
110 
111 /* find returns a pointer to the location of string str2 in string str1,
112  * if it exists.  Otherwise, it points to the end of str1.
113  */
114 char *
find(char * str1,char * str2)115 find(char *str1, char *str2) {
116 	char *s1, *s2;
117 
118 	for (; *str1!='\0'; str1++) {
119 		for (s1=str1,s2=str2; *s2!='\0'&&*s1==*s2; s1++,s2++) ;
120 		if ( *s2 == '\0' )
121 	    		break;
122 	}
123 
124 	return(str1);
125 }
126 
127 #define	MESGSIZE	16384
128 int blocksize = 1920;		/* 19200/10, with 1 sec delay between transfers
129 				 * this keeps the queues from building up.
130 				 */
131 char	mesg[MESGSIZE];			/* exactly what came back on ttyi */
132 
133 int
parsmesg(char * buf)134 parsmesg(char *buf) {
135 	static char sbuf[MESGSIZE];
136 	char	*s;		/* start of printer messsage in mesg[] */
137 	char	*e;		/* end of printer message in mesg[] */
138 	char	*key, *val;	/* keyword/value strings in sbuf[] */
139 	char	*p;		/* for converting to lower case etc. */
140 	int	i;		/* where *key was found in statuslist[] */
141 
142 	if (*(s=find(buf, "%[ "))!='\0' && *(e=find(s, " ]%"))!='\0') {
143 		strcpy(sbuf, s+3);	/* don't change mesg[] */
144 		sbuf[e-(s+3)] = '\0';	/* ignore the trailing " ]%" */
145 
146 		for (key=strtok(sbuf, " :"); key != NULL; key=strtok(NULL, " :"))  {
147 			if (strcmp(key, "Error") == 0)
148 				return(ERROR);
149 			if ((val=strtok(NULL, ";")) != NULL && strcmp(key, "status") == 0)
150 				key = val;
151 
152 	    		for (; *key == ' '; key++) ;	/* skip any leading spaces */
153 			for (p = key; *p; p++)		/* convert to lower case */
154 				if (*p == ':')  {
155 					*p = '\0';
156 					break;
157 				} else if (isupper(*p)) *p = tolower(*p);
158 
159 			for (i=0; statuslist[i].state != NULL; i++) {
160 				if (strcmp(statuslist[i].state, key) == 0)
161 					return(statuslist[i].val);
162 			}
163 		}
164 	}
165 	return(UNKNOWN);
166 }
167 
168 char buf[MESGSIZE];
169 fd_set readfds, writefds, exceptfds;
170 struct timeval rcvtimeout = { RCVSELTIMEOUT, 0 };
171 struct timeval sndtimeout = { SNDSELTIMEOUT, 0 };
172 
173 int
readprinter(int printerfd,int pipefd)174 readprinter(int printerfd, int pipefd)
175 {
176 	unsigned char proto;
177 	int progstate = START;
178 	int print_wait_msg = 0;
179 	int tocount = 0;
180 	int c, printstat, lastprintstat, n, nfds;
181 
182 
183 	nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
184 	printstat = 0;
185 	signal(SIGALRM, rdtmout);
186 	do {
187 
188 reselect:
189 		/* ask sending process to request printer status */
190 		if (write(pipefd, Req_stat, 1) != 1) {
191 			fprintf(stderr, "request status failed\n");
192 			progstate = FATAL_ERROR;
193 			continue;
194 		}
195 		FD_ZERO(&readfds);	/* lets be anal */
196 		FD_SET(printerfd, &readfds);
197 		FD_SET(pipefd, &readfds);
198 		FD_ZERO(&exceptfds);
199 		FD_SET(printerfd, &exceptfds);
200 		FD_SET(pipefd, &exceptfds);
201 		n = select(nfds, &readfds, (fd_set *)0, &exceptfds, &rcvtimeout);
202 		if (debug&0x1) fprintf(stderr, "readprinter select returned %d\n", n);
203 		if (n == 0) {
204 			/* a timeout occurred */
205 			if (++tocount > 4) {
206 				fprintf(stderr, "printer appears to be offline.\nHP4m printers may be out of paper.\n");
207 				tocount = 0;
208 			}
209 			goto reselect;
210 		}
211 		if (n > 0 && FD_ISSET(printerfd, &exceptfds)) {
212 			/* printer problem */
213 			fprintf(stderr, "printer exception\n");
214 			if (write(pipefd, Fatal_error, 1) != 1) {
215 				fprintf(stderr, "'fatal error' write to pipe failed\n");
216 			}
217 			progstate = FATAL_ERROR;
218 			continue;
219 		}
220 		if (n > 0 && FD_ISSET(pipefd, &exceptfds)) {
221 			/* pipe problem */
222 			fprintf(stderr, "pipe exception\n");
223 			progstate = FATAL_ERROR;
224 			continue;
225 		}
226 		if (n > 0 && FD_ISSET(pipefd, &readfds)) {
227 			/* protocol pipe wants to be read */
228 			if (debug&0x1) fprintf(stderr, "pipe wants to be read\n");
229 			if (read(pipefd, &proto, 1) != 1) {
230 				fprintf(stderr, "read protocol pipe failed\n");
231 				progstate = FATAL_ERROR;
232 				continue;
233 			}
234 			if (debug&0x1) fprintf(stderr, "readprinter: proto=%c\n", proto);
235 			/* change state? */
236 			switch (proto) {
237 			case SENT_DATA:
238 				break;
239 			case WAIT_FOR_EOJ:
240 				if (!print_wait_msg) {
241 					print_wait_msg = 1;
242 					fprintf(stderr, "waiting for end of job\n");
243 				}
244 				progstate = proto;
245 				break;
246 			default:
247 				fprintf(stderr, "received unknown protocol request <%c> from sendfile\n", proto);
248 				break;
249 			}
250 			n--;
251 		}
252 		if (n > 0 && FD_ISSET(printerfd, &readfds)) {
253 			/* printer wants to be read */
254 			if (debug&0x1) fprintf(stderr, "printer wants to be read\n");
255 			if ((c=getline(printerfd, buf, MESGSIZE)) < 0) {
256 				fprintf(stderr, "read printer failed\n");
257 				progstate = FATAL_ERROR;
258 				continue;
259 			}
260 			if (debug&0x1) fprintf(stderr, "%s\n", buf);
261 			if (c==1 && *buf == '\004') {
262 				if (progstate == WAIT_FOR_EOJ) {
263 					if (debug&0x1) fprintf(stderr, "progstate=%c, ", progstate);
264 					fprintf(stderr, "%%[ status: endofjob ]%%\n");
265 /*					progstate = WAIT_FOR_IDLE; */
266 					progstate = OVER_AND_OUT;
267 					if (write(pipefd, Over_and_out, 1) != 1) {
268 						fprintf(stderr, "'fatal error' write to pipe failed\n");
269 					}
270 					continue;
271 				} else {
272 					if (printstat == ERROR) {
273 						progstate = FATAL_ERROR;
274 						continue;
275 					}
276 					if (progstate != START && progstate != WAIT_FOR_IDLE)
277 						fprintf(stderr, "warning: EOF received; program status is '%c'\n", progstate);
278 
279 				}
280 				continue;
281 			}
282 
283 			/* figure out if it was a status line */
284 			lastprintstat = printstat;
285 			printstat = parsmesg(buf);
286 			if (printstat == UNKNOWN || printstat == ERROR
287 			    || lastprintstat != printstat) {
288 				/* print whatever it is that was read */
289 				fprintf(stderr, buf);
290 				fflush(stderr);
291 				if (printstat == UNKNOWN) {
292 					printstat = lastprintstat;
293 					continue;
294 				}
295 			}
296 			switch (printstat) {
297 			case UNKNOWN:
298 				continue;	/* shouldn't get here */
299 			case FLUSHING:
300 			case ERROR:
301 				progstate = FATAL_ERROR;
302 				/* ask sending process to die */
303 				if (write(pipefd, Fatal_error, 1) != 1) {
304 					fprintf(stderr, "Fatal_error mesg write to pipe failed\n");
305 				}
306 				continue;
307 			case INITIALIZING:
308 			case PRINTERERROR:
309 				sleep(1);
310 				break;
311 			case IDLE:
312 				if (progstate == WAIT_FOR_IDLE) {
313 					progstate = OVER_AND_OUT;
314 					if (write(pipefd, Over_and_out, 1) != 1) {
315 						fprintf(stderr, "'fatal error' write to pipe failed\n");
316 					}
317 					continue;
318 				}
319 				progstate = SEND_DATA;
320 
321 				goto dowait;
322 			case BUSY:
323 			case WAITING:
324 			default:
325 				sleep(1);
326 dowait:
327 				switch (progstate) {
328 				case WAIT_FOR_IDLE:
329 				case WAIT_FOR_EOJ:
330 				case START:
331 					sleep(5);
332 					break;
333 
334 				case SEND_DATA:
335 					if (write(pipefd, Send_data, 1) != 1) {
336 						fprintf(stderr, "send data write to pipe failed\n");
337 						progstate = FATAL_ERROR;
338 						continue;
339 					}
340 					break;
341 				default:
342 					fprintf(stderr, "unexpected program state %c\n", progstate);
343 					exit(1);
344 				}
345 				break;
346 			}
347 			n--;
348 		}
349 		if (n > 0) {
350 			fprintf(stderr, "more fds selected than requested!\n");
351 			exit(1);
352 		};
353 	} while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
354 
355 	if (progstate == FATAL_ERROR)
356 		return(1);
357 	else
358 		return(0);
359 }
360 
361 int
sendfile(int infd,int printerfd,int pipefd)362 sendfile(int infd, int printerfd, int pipefd)
363 {
364 	unsigned char proto;
365 	int progstate = START;
366 	int i, n, nfds;
367 	int bytesread,  bytesent = 0;
368 
369 	nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
370 
371 	if (write(printerfd, "\004", 1)!=1) {
372 		perror("sendfile:write:");
373 		progstate = FATAL_ERROR;
374 	}
375 	do {
376 		FD_ZERO(&readfds);	/* lets be anal */
377 		FD_SET(pipefd, &readfds);
378 		n = select(nfds, &readfds, (fd_set *)0, (fd_set *)0, &sndtimeout);
379 		if (debug&02) fprintf(stderr, "sendfile select returned %d\n", n);
380 		if (n > 0 && FD_ISSET(pipefd, &readfds)) {
381 			/* protocol pipe wants to be read */
382 			if (read(pipefd, &proto, 1) != 1) {
383 				fprintf(stderr, "read protocol pipe failed\n");
384 				return(1);
385 			}
386 			/* change state? */
387 			if (debug&02) fprintf(stderr, "sendfile command - <%c>\n", proto);
388 			switch (proto) {
389 			case OVER_AND_OUT:
390 			case END_OF_JOB:
391 				progstate = proto;
392 				break;
393 			case SEND_DATA:
394 				bytesread = 0;
395 				do {
396 					i = read(infd, &buf[bytesread], blocksize-bytesread);
397 					if (debug&02) fprintf(stderr, "read %d bytes\n", i);
398 					if (i > 0)
399 						bytesread += i;
400 				} while((i > 0) && (bytesread < blocksize));
401 				if (i < 0) {
402 					fprintf(stderr, "input file read error\n");
403 					progstate = FATAL_ERROR;
404 					break;	/* from switch */
405 				}
406 				if (bytesread > 0) {
407 					if (debug&02) fprintf(stderr, "writing %d bytes\n", bytesread);
408 					if (write(printerfd, buf, bytesread)!=bytesread) {
409 						perror("sendfile:write:");
410 						progstate = FATAL_ERROR;
411 					} else if (write(pipefd, Sent_data, 1)!=1) {
412 						perror("sendfile:write:");
413 						progstate = FATAL_ERROR;
414 					} else {
415 						bytesent += bytesread;
416 					}
417 					fprintf(stderr, "%d sent\n", bytesent);
418 					fflush(stderr);
419 
420 				/* we have reached the end of the input file */
421 				}
422 				if (i == 0) {
423 					if (progstate != WAIT_FOR_EOJ) {
424 						if (write(printerfd, "\004", 1)!=1) {
425 							perror("sendfile:write:");
426 							progstate = FATAL_ERROR;
427 						} else if (write(pipefd, Wait_for_eoj, 1)!=1) {
428 							perror("sendfile:write:");
429 							progstate = FATAL_ERROR;
430 						} else {
431 							progstate = WAIT_FOR_EOJ;
432 						}
433 					}
434 				}
435 				break;
436 			case REQ_STAT:
437 				if (write(printerfd, "\024", 1)!=1) {
438 					fprintf(stderr, "write to printer failed\n");
439 					progstate = FATAL_ERROR;
440 				}
441 				if (debug&02) fprintf(stderr, "^T");
442 				break;
443 			case FATAL_ERROR:
444 				progstate = FATAL_ERROR;
445 			}
446 		} else if (n < 0) {
447 			perror("sendfile:select:");
448 			progstate = FATAL_ERROR;
449 		} else if (n == 0) {
450 			sleep(1);
451 			fprintf(stderr, "sendfile timeout\n");
452 			progstate = FATAL_ERROR;
453 		}
454 	} while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
455 	if (write(printerfd, "\004", 1)!=1) {
456 		perror("sendfile:write:");
457 		progstate = FATAL_ERROR;
458 	}
459 
460 	if (debug&02) fprintf(stderr, "%d bytes sent\n", bytesent);
461 	if (progstate == FATAL_ERROR)
462 		return(1);
463 	else
464 		return(0);
465 }
466 
main(int argc,char * argv[])467 void main(int argc, char *argv[]) {
468 	int c, usgflg=0, infd, printerfd;
469 	int cpid, sprv;
470 	int pipefd[2];
471 	char *dialstr;
472 	unsigned long rprv;
473 
474 	dialstr = 0;
475 
476 	while ((c = getopt(argc, argv, "b:d:")) != -1)
477 		switch (c) {
478 		case 'b':
479 			blocksize = atoi(optarg)/10;
480 			if (blocksize > MESGSIZE || blocksize < 1)
481 				blocksize = MESGSIZE;
482 			break;
483 		case 'd':
484 			debug = atoi(optarg);
485 			dial_debug = debug;
486 			break;
487 		case '?':
488 			fprintf(stderr, "unknown option %c\n", c);
489 			usgflg++;
490 			break;
491 		}
492 	if (optind < argc)
493 		dialstr = argv[optind++];
494 	else
495 		usgflg++;
496 	if (usgflg) {
497 		fprintf(stderr, "usage: %s [-b baudrate] net!host!service [infile]\n",
498 			argv[0]);
499 		exit (2);
500 	}
501 	if (optind < argc) {
502 		infd = open(argv[optind], 0);
503 		if (infd < 0) {
504 			fprintf(stderr, "cannot open %s\n", argv[optind]);
505 			exit(1);
506 		}
507 		optind++;
508 	} else
509 		infd = 0;
510 
511 	if (debug & 02)
512 		fprintf(stderr, "blocksize=%d\n", blocksize);
513 	if (debug)
514 		fprintf(stderr, "dialing address=%s\n", dialstr);
515 	printerfd = dial(dialstr, 0, 0, 0);
516 	if (printerfd < 0)
517 		exit(1);
518 
519 	fprintf(stderr, "printer startup\n");
520 
521 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipefd) < 0) {
522 		perror("socketpair");
523 		exit(1);
524 	}
525 	switch(cpid = fork()){
526 	case -1:
527 		perror("fork error");
528 		exit(1);
529 	case 0:				/* child - to printer */
530 		close(pipefd[1]);
531 		sprv = sendfile(infd, printerfd, pipefd[0]);
532 		if (debug)
533 			fprintf(stderr, "to remote - exiting\n");
534 		exit(sprv);
535 	default:			/* parent - from printer */
536 		close(pipefd[0]);
537 		rprv = readprinter(printerfd, pipefd[1]);
538 		if (debug)
539 			fprintf(stderr, "from remote - exiting\n");
540 		while(wait(&sprv) != cpid)
541 			;
542 		exit(rprv|sprv);
543 	}
544 }
545