xref: /titanic_44/usr/src/cmd/lp/model/lp.cat.c (revision 54925bf60766fbb4f1f2d7c843721406a7b7a3fb)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <termio.h>
35 #include <sys/types.h>
36 #include <errno.h>
37 #include <signal.h>
38 #include <sys/times.h>
39 #include <string.h>
40 #include <limits.h>
41 #include <sys/prnio.h>
42 
43 #include "lp.h"
44 
45 #include <locale.h>
46 
47 /**
48  **	Begin Sun Additions for Parallel ports
49  **/
50 
51 #include <string.h>
52 #include <stdarg.h>
53 #include <signal.h>
54 #include <unistd.h>
55 #include <sys/types.h>
56 #include <sys/ioccom.h>
57 #include <sys/ioctl.h>
58 
59 #include <sys/bpp_io.h>
60 #include <sys/ecppsys.h>
61 #include <stropts.h>
62 
63 /*
64  * the parameter structure for the parallel port
65  */
66 struct ppc_params_t {
67 	int		flags;		/* same as above */
68 	int		state;		/* status of the printer interface */
69 	int		strobe_w;	/* strobe width, in uS */
70 	int		data_setup;	/* data setup time, in uS */
71 	int		ack_timeout;	/* ACK timeout, in secs */
72 	int		error_timeout;	/* PAPER OUT, etc... timeout, in secs */
73 	int		busy_timeout;	/* BUSY timeout, in seconds */
74 };
75 
76 
77 
78 static void printer_info(char *fmt, ...);
79 
80 /*	These are the routines avaliable to others for use 	*/
81 int is_a_parallel_bpp(int);
82 int bpp_state(int);
83 int parallel_comm(int, int());
84 int get_ecpp_status(int fd);
85 int is_a_prnio(int);
86 int prnio_state(int);
87 
88 #define PRINTER_ERROR_PAPER_OUT		1
89 #define PRINTER_ERROR_OFFLINE		2
90 #define PRINTER_ERROR_BUSY		3
91 #define PRINTER_ERROR_ERROR		4
92 #define PRINTER_ERROR_CABLE_POWER	5
93 #define PRINTER_ERROR_UNKNOWN		6
94 #define PRINTER_ERROR_TIMEOUT		7
95 #define	PRINTER_IO_ERROR		129
96 
97 
98 /****************************************************************************/
99 
100 /**
101  *	for BPP PARALLEL interfaces
102  **/
103 
104 int is_a_parallel_bpp(int fd)
105 {
106 	if (ioctl(fd, BPPIOC_TESTIO) == 0 || errno == EIO)
107 		return(1);
108 	return(0);
109 }
110 
111 
112 #if defined(DEBUG) && defined(NOTDEF)
113 char *BppState(int state)
114 {
115 	static char buf[BUFSIZ];
116 
117 	memset(buf, 0, sizeof(buf));
118 	sprintf(buf, "State (0x%.4x) - (%s%s%s%s)\n", state,
119 		((state & BPP_SLCT_ERR) ?  "offline " : ""),
120 		((state & BPP_BUSY_ERR) ?  "busy " : ""),
121 		((state & BPP_PE_ERR) ?  "paper " : ""),
122 		((state & BPP_ERR_ERR) ?  "error " : ""));
123 
124 	return(buf);
125 }
126 #endif
127 
128 int bpp_state(int fd)
129 {
130 	if (ioctl(fd, BPPIOC_TESTIO)) {
131 		struct bpp_error_status  bpp_stat;
132 		int state;
133 
134 		if (ioctl(fd, BPPIOC_GETERR, &bpp_stat) < 0)
135 			exit(PRINTER_IO_ERROR);
136 		state = bpp_stat.pin_status;
137 
138 #if defined(DEBUG) && defined(NOTDEF)
139 		logit("%s", BppState(state));
140 #endif
141 
142 		if (state == (BPP_PE_ERR | BPP_ERR_ERR | BPP_SLCT_ERR)) {
143 			/* paper is out */
144 			return(PRINTER_ERROR_PAPER_OUT);
145 		} else if (state & BPP_BUSY_ERR) {
146 			/* printer is busy */
147 			return(PRINTER_ERROR_BUSY);
148 		} else if (state & BPP_SLCT_ERR) {
149 			/* printer is offline */
150 			return(PRINTER_ERROR_OFFLINE);
151 		} else if (state & BPP_ERR_ERR) {
152 			/* printer is errored */
153 			return(PRINTER_ERROR_ERROR);
154 		} else if (state == BPP_PE_ERR) {
155 			/* printer is off/unplugged */
156 			return(PRINTER_ERROR_CABLE_POWER);
157 		} else if (state) {
158 			return(PRINTER_ERROR_UNKNOWN);
159 		} else
160 			return(0);
161 	}
162 	return(0);
163 }
164 
165 /*
166  * For ecpp parallel port
167  */
168 
169 int
170 get_ecpp_status(int fd)
171 {
172 	int state;
173 	struct ecpp_transfer_parms transfer_parms;
174 
175 
176 	if (ioctl(fd, ECPPIOC_GETPARMS, &transfer_parms) == -1) {
177 		return(-1);
178 	}
179 
180 	state = transfer_parms.mode;
181 	/*
182 	 * We don't know what all printers will return in
183 	 * nibble mode, therefore if we support nibble mode we will
184 	 * force the printer to be in CENTRONICS mode.
185 	 */
186 	if (state != ECPP_CENTRONICS) {
187 		transfer_parms.mode = ECPP_CENTRONICS;
188 		if (ioctl(fd, ECPPIOC_SETPARMS, &transfer_parms) == -1) {
189 			return(-1);
190 		} else {
191 			state = ECPP_CENTRONICS;
192 		}
193 	}
194 
195 
196 	return(state);
197 }
198 
199 /**
200  * For prnio(7I) - generic printer interface
201  **/
202 int is_a_prnio(int fd)
203 {
204 	uint_t	cap;
205 
206 	/* check if device supports prnio */
207 	if (ioctl(fd, PRNIOC_GET_IFCAP, &cap) == -1) {
208 		return (0);
209 	}
210 	/* we will use 1284 status if available */
211 	if ((cap & PRN_1284_STATUS) == 0) {
212 		/* some devices may only support 1284 status in unidir. mode */
213 		if (cap & PRN_BIDI) {
214 			cap &= ~PRN_BIDI;
215 			(void) ioctl(fd, PRNIOC_SET_IFCAP, &cap);
216 		}
217 	}
218 	return (1);
219 }
220 
221 int prnio_state(int fd)
222 {
223 	uint_t	status;
224 	uchar_t	pins;
225 
226 	if ((ioctl(fd, PRNIOC_GET_STATUS, &status) == 0) &&
227 	    (status & PRN_READY)) {
228 		return(0);
229 	}
230 
231 	if (ioctl(fd, PRNIOC_GET_1284_STATUS, &pins) != 0) {
232 		return(PRINTER_ERROR_UNKNOWN);
233 	}
234 
235 	if ((pins & ~PRN_1284_BUSY) == PRN_1284_PE) {
236 		/* paper is out */
237 		return(PRINTER_ERROR_PAPER_OUT);
238 	} else if (pins == (PRN_1284_PE | PRN_1284_SELECT |
239 				PRN_1284_NOFAULT | PRN_1284_BUSY)) {
240 		/* printer is off/unplugged */
241 		return(PRINTER_ERROR_CABLE_POWER);
242 	} else if ((pins & PRN_1284_SELECT) == 0) {
243 		/* printer is offline */
244 		return(PRINTER_ERROR_OFFLINE);
245 	} else if ((pins & PRN_1284_NOFAULT) == 0) {
246 		/* printer is errored */
247 		return(PRINTER_ERROR_ERROR);
248 	} else if (pins & PRN_1284_PE) {
249 		/* paper is out */
250 		return(PRINTER_ERROR_PAPER_OUT);
251 	} else if (pins ^ (PRN_1284_SELECT | PRN_1284_NOFAULT)) {
252 		return(PRINTER_ERROR_UNKNOWN);
253 	}
254 
255 	return(0);
256 }
257 
258 /**
259  *	Common routines
260  **/
261 
262 /*ARGSUSED0*/
263 static void
264 ByeByeParallel(int sig)
265 {
266 	/* try to shove out the EOT */
267 	(void) write(1, "\004", 1);
268 	exit(0);
269 }
270 
271 
272 /*ARGSUSED0*/
273 static void
274 printer_info(char *fmt, ...)
275 {
276 	char mesg[BUFSIZ];
277 	va_list ap;
278 
279 	va_start(ap, fmt);
280 	vsprintf(mesg, fmt, ap);
281 	va_end(ap);
282 /*
283 	fprintf(stderr,
284 		"%%%%[ PrinterError: %s; source: parallel ]%%%%\n",
285 		mesg);
286 */
287 	fprintf(stderr, "%s\n", mesg);
288 	fflush(stderr);
289 	fsync(2);
290 
291 }
292 
293 static void
294 printer_error(int error)
295 {
296 	switch (error) {
297 		case -1:
298 			printer_info("ioctl(): %s", strerror(errno));
299 			break;
300 		case PRINTER_ERROR_PAPER_OUT:
301 			printer_info("out of paper");
302 			break;
303 		case PRINTER_ERROR_OFFLINE:
304 			printer_info("offline");
305 			break;
306 		case PRINTER_ERROR_BUSY:
307 			printer_info("busy");
308 			break;
309 		case PRINTER_ERROR_ERROR:
310 			printer_info("printer error");
311 			break;
312 		case PRINTER_ERROR_CABLE_POWER:
313 			printer_info("printer powered off or disconnected");
314 			break;
315 		case PRINTER_ERROR_UNKNOWN:
316 			printer_info("unknown error");
317 			break;
318 		case PRINTER_ERROR_TIMEOUT:
319 			printer_info("communications timeout");
320 			break;
321 		default:
322 			printer_info("get_status() failed");
323 	}
324 }
325 
326 
327 static void
328 wait_state(int fd, int get_state())
329 {
330 	int state;
331 	int was_faulted = 0;
332 
333 	while (state = get_state(fd)) {
334 		was_faulted=1;
335 		printer_error(state);
336 		sleep(15);
337 	}
338 
339 	if (was_faulted) {
340 		fprintf(stderr, "printer ok\n");
341 		fflush(stderr);
342 		fsync(2);
343 	}
344 }
345 
346 /**
347  **  end of Sun Additions for parallel port
348  **/
349 #define	IDENTICAL(A,B)	(A.st_dev==B.st_dev && A.st_ino==B.st_ino)
350 #define ISBLK(A)	((A.st_mode & S_IFMT) == S_IFBLK)
351 #define ISCHR(A)	((A.st_mode & S_IFMT) == S_IFCHR)
352 
353 #define E_SUCCESS	0
354 #define E_BAD_INPUT	1
355 #define E_BAD_OUTPUT	2
356 #define E_BAD_TERM	3
357 #define E_IDENTICAL	4
358 #define	E_WRITE_FAILED	5
359 #define	E_TIMEOUT	6
360 #define E_HANGUP	7
361 #define E_INTERRUPT	8
362 
363 #define SAFETY_FACTOR	2.0
364 #define R(F)		(int)((F) + .5)
365 #define DELAY(N,D)	R(SAFETY_FACTOR * ((N) / (double)(D)))
366 
367 char			buffer[BUFSIZ];
368 
369 void			sighup(),
370 			sigint(),
371 			sigquit(),
372 			sigpipe(),
373 			sigalrm(),
374 			sigterm();
375 
376 #if	defined(baudrate)
377 # undef	baudrate
378 #endif
379 
380 int			baudrate();
381 
382 
383 int nop(int fd) { return (0); }
384 int bpp_state(int);
385 
386 
387 /**
388  ** main()
389  **/
390 
391 int
392 main(int argc, char *argv[])
393 {
394 	int			nin,
395 				nout,
396 				effective_rate,
397 				max_delay	= 0,
398 				n;
399 
400 	int			report_rate;
401 
402 	short			print_rate;
403 
404 	struct stat		in,
405 				out;
406 
407 	struct tms		tms;
408 
409 	long			epoch_start,
410 				epoch_end;
411 
412 	char			*TERM;
413 
414 	int			(*func)(int fd);
415 
416 	/*
417 	 * The Spooler can hit us with SIGTERM for three reasons:
418 	 *
419 	 *	- the user's job has been canceled
420 	 *	- the printer has been disabled while we were printing
421 	 *	- the Spooler heard that the printer has a fault,
422 	 *	  and the fault recovery is wait or beginning
423 	 *
424 	 * We should exit cleanly for the first two cases,
425 	 * but we have to be careful with the last. If it was THIS
426 	 * PROGRAM that told the Spooler about the fault, we must
427 	 * exit consistently.
428 	 *
429 	 * The method of avoiding any problem is to turn off the
430 	 * trapping of SIGTERM before telling the Spooler about
431 	 * the fault.
432 	 *
433 	 * Faults that we can detect:
434 	 *	- hangup (drop of carrier)
435 	 *	- interrupt (printer sent a break or quit character)
436 	 *	- SIGPIPE (output port is a FIFO, and was closed early)
437 	 *	- failed or incomplete write()
438 	 *	- excess delay in write() (handled with SIGALRM later)
439 	 *
440 	 * Pseudo-faults (errors in use):
441 	 *	- No input/output, or strange input/output
442 	 *	- Input/output identical
443 	 *	- No TERM defined or trouble reading Terminfo database
444 	 */
445 	signal (SIGTERM, sigterm);
446 	signal (SIGHUP, sighup);
447 	signal (SIGINT, sigint);
448 	signal (SIGQUIT, sigint);
449 	signal (SIGPIPE, sigpipe);
450 
451 
452 	if (argc > 1 && STREQU(argv[1], "-r")) {
453 		report_rate = 1;
454 		argc--;
455 		argv++;
456 	} else
457 		report_rate = 0;
458 
459 	(void) setlocale(LC_ALL, "");
460 #if !defined(TEXT_DOMAIN)
461 #define TEXT_DOMAIN "SYS_TEST"
462 #endif
463 	(void) textdomain(TEXT_DOMAIN);
464 
465 	/*
466 	 * Stat the standard output to be sure it is defined.
467 	 */
468 	if (fstat(1, &out) < 0) {
469 		signal (SIGTERM, SIG_IGN);
470 		fprintf (
471 			stderr,
472 		gettext("Can't stat output (%s);\nincorrect use of lp.cat!\n"),
473 			PERROR
474 		);
475 		exit (E_BAD_OUTPUT);
476 	}
477 
478 	/*
479 	 * Stat the standard input to be sure it is defined.
480 	 */
481 	if (fstat(0, &in) < 0) {
482 		signal (SIGTERM, SIG_IGN);
483 		fprintf (
484 			stderr,
485 		gettext("Can't stat input (%s);\nincorrect use of lp.cat!\n"),
486 			PERROR
487 		);
488 		exit (E_BAD_INPUT);
489 	}
490 
491 	/*
492 	 * If the standard output is not a character special file or a
493 	 * block special file, make sure it is not identical to the
494 	 * standard input.
495 	 *
496 	 * If we are an ecpp parallel port in centronics mode treat
497 	 * ourselves as a bpp compatible device.
498 	 */
499 
500 	if (is_a_prnio(1)) {
501 		func = prnio_state;
502 	} else if (is_a_parallel_bpp(1) ||
503 		    (get_ecpp_status(1) == ECPP_CENTRONICS)) {
504 		func = bpp_state;
505         } else if (isatty(1)) {
506 		/* serial connection (probably) - continue as usual */
507 		func = nop;
508         } else {
509 		func = nop;
510         }
511 
512 	if (!ISCHR(out) && !ISBLK(out) && IDENTICAL(out, in)) {
513 		signal (SIGTERM, SIG_IGN);
514 		fprintf (
515 			stderr,
516 	gettext("Input and output are identical; incorrect use of lp.cat!\n")
517 		);
518 		exit (E_IDENTICAL);
519 	}
520 
521 	/*
522 	 * The effective data transfer rate is the lesser
523 	 * of the transmission rate and print rate. If an
524 	 * argument was passed to us, it should be a data
525 	 * rate and it may be lower still.
526 	 * Based on the effective data transfer rate,
527 	 * we can predict the maximum delay we should experience.
528 	 * But there are other factors that could introduce
529 	 * delay, so let's be generous; after all, we'd rather
530 	 * err in favor of waiting too long to detect a fault
531 	 * than err too often on false alarms.
532 	 */
533 
534 	if (
535 		!(TERM = getenv("TERM"))
536 	     || !*TERM
537 	) {
538 		signal (SIGTERM, SIG_IGN);
539 		fprintf (
540 			stderr,
541 	gettext("No TERM variable defined! Trouble with the Spooler!\n")
542 		);
543 		exit (E_BAD_TERM);
544 	}
545 	if (
546 		!STREQU(TERM, NAME_UNKNOWN)
547 	     && tidbit(TERM, "cps", &print_rate) == -1
548 	) {
549 		signal (SIGTERM, SIG_IGN);
550 		fprintf (
551 			stderr,
552 gettext("Trouble identifying printer type \"%s\"; check the Terminfo database.\n"),
553 			TERM
554 		);
555 		exit (E_BAD_TERM);
556 	}
557 	if (STREQU(TERM, NAME_UNKNOWN))
558 		print_rate = -1;
559 
560 	effective_rate = baudrate() / 10; /* okay for most bauds */
561 	if (print_rate != -1 && print_rate < effective_rate)
562 		effective_rate = print_rate;
563 	if (argc > 1 && (n = atoi(argv[1])) >= 0 && n < effective_rate)
564 		effective_rate = n;	  /* 0 means infinite delay */
565 	if (effective_rate)
566 		max_delay = DELAY(BUFSIZ, effective_rate);
567 
568 	/*
569 	 * We'll use the "alarm()" system call to keep us from
570 	 * waiting too long to write to a printer in trouble.
571 	 */
572 	if (max_delay)
573 		signal (SIGALRM, sigalrm);
574 
575 	/*
576 	 * While not end of standard input, copy blocks to
577 	 * standard output.
578 	 */
579 	while ((nin = read(0, buffer, BUFSIZ)) > 0) {
580 		char *ptr = buffer;
581 
582 		/*
583 		 * We should be safe from incomplete writes to a full
584 		 * pipe, as long as the size of the buffer we write is
585 		 * a even divisor of the pipe buffer limit. As long as
586 		 * we read from files or pipes (not communication devices)
587 		 * this should be true for all but the last buffer. The
588 		 * last will be smaller, and won't straddle the pipe max
589 		 * limit (think about it).
590 		 */
591 #if	PIPE_BUF < BUFSIZ || (PIPE_MAX % BUFSIZ)
592 		this_wont_compile;
593 #endif
594 		if (report_rate)
595 			epoch_start = times(&tms);
596 		do {
597 			wait_state(1, func);
598 
599 			if (max_delay)
600 				alarm (max_delay);
601 			nout = write(1, ptr, nin);
602 			alarm(0);
603 			if (nout < 0) {
604 				fprintf (
605 					stderr,
606 	gettext("Write failed (%s);\nperhaps the printer has gone off-line.\n"),
607 					PERROR
608 				);
609 				fflush(stderr);
610 				if (errno != EINTR)
611 				/* I/O error on device, get lpcshed to retry */
612 					exit(PRINTER_IO_ERROR);
613 				else /* wait for printer to come back online */
614 					sleep(15);
615 			} else {
616 				nin -= nout;
617 				ptr += nout;
618 			}
619 		} while (nin > 0);
620 
621 		if (max_delay)
622 			alarm (0);
623 		else if (report_rate) {
624 			epoch_end = times(&tms);
625 			if (epoch_end - epoch_start > 0)
626 				fprintf (
627 					stderr,
628 					"%d CPS\n",
629 		R((100 * BUFSIZ) / (double)(epoch_end - epoch_start))
630 				);
631 		}
632 
633 	}
634 
635 	return (E_SUCCESS);
636 }
637 
638 /**
639  ** sighup() - CATCH A HANGUP (LOSS OF CARRIER)
640  **/
641 
642 void			sighup ()
643 {
644 	signal (SIGTERM, SIG_IGN);
645 	signal (SIGHUP, SIG_IGN);
646 	fprintf (stderr, gettext(HANGUP_FAULT_LPCAT));
647 	exit (E_HANGUP);
648 }
649 
650 /**
651  ** sigint() - CATCH AN INTERRUPT
652  **/
653 
654 void			sigint ()
655 {
656 	signal (SIGTERM, SIG_IGN);
657 	signal (SIGINT, SIG_IGN);
658 	fprintf (stderr, gettext(INTERRUPT_FAULT));
659 	exit (E_INTERRUPT);
660 }
661 
662 /**
663  ** sigpipe() - CATCH EARLY CLOSE OF PIPE
664  **/
665 
666 void			sigpipe ()
667 {
668 	signal (SIGTERM, SIG_IGN);
669 	signal (SIGPIPE, SIG_IGN);
670 	fprintf (stderr, gettext(PIPE_FAULT));
671 	exit (E_INTERRUPT);
672 }
673 
674 /**
675  ** sigalrm() - CATCH AN ALARM
676  **/
677 
678 void			sigalrm ()
679 {
680 	signal (SIGTERM, SIG_IGN);
681 	fprintf (
682 		stderr,
683 	gettext("Excessive write delay; perhaps the printer has gone off-line.\n")
684 	);
685 	exit (E_TIMEOUT);
686 }
687 
688 /**
689  ** sigterm() - CATCH A TERMINATION SIGNAL
690  **/
691 
692 void			sigterm ()
693 {
694 	signal (SIGTERM, SIG_IGN);
695 	/*
696 	 * try to flush the output queue in the case of ecpp port.
697 	 * ignore the return code as this may not be the ecpp.
698 	 */
699 	ioctl(1, I_FLUSH, FLUSHW);
700 	exit (E_SUCCESS);
701 }
702 
703 /**
704  ** baudrate() - RETURN BAUD RATE OF OUTPUT LINE
705  **/
706 
707 static int		baud_convert[] =
708 {
709 	0, 50, 75, 110, 135, 150, 200, 300, 600, 1200,
710 	1800, 2400, 4800, 9600, 19200, 38400, 57600,
711 	76800, 115200, 153600, 230400, 307200, 460800
712 };
713 
714 int			baudrate ()
715 {
716 	struct termio		tm;
717 	struct termios		tms;
718 	int			speed;
719 
720 	if (ioctl(1, TCGETS, &tms) < 0) {
721 		if (ioctl(1, TCGETA, &tm) < 0)
722 			return (1200);
723 		else
724 			speed = tm.c_cflag&CBAUD;
725 	} else
726 		speed = cfgetospeed(&tms);
727 
728 	return (speed ? baud_convert[speed] : 1200);
729 }
730