/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" #include "stdio.h" #include "termio.h" #include "sys/types.h" #include "errno.h" #include "signal.h" #include "sys/times.h" #include "string.h" #include "limits.h" #include #include "lp.h" #include /** ** Begin Sun Additions for Parallel ports **/ extern char *_sys_errlist[]; #include #include #include #include #include #include #include #include #include #include /* * the parameter structure for the parallel port */ struct ppc_params_t { int flags; /* same as above */ int state; /* status of the printer interface */ int strobe_w; /* strobe width, in uS */ int data_setup; /* data setup time, in uS */ int ack_timeout; /* ACK timeout, in secs */ int error_timeout; /* PAPER OUT, etc... timeout, in secs */ int busy_timeout; /* BUSY timeout, in seconds */ }; static void printer_info(char *fmt, ...); /* These are the routines avaliable to others for use */ int is_a_parallel_bpp(int); int bpp_state(int); int parallel_comm(int, int()); int get_ecpp_status(int fd); int is_a_prnio(int); int prnio_state(int); #define PRINTER_ERROR_PAPER_OUT 1 #define PRINTER_ERROR_OFFLINE 2 #define PRINTER_ERROR_BUSY 3 #define PRINTER_ERROR_ERROR 4 #define PRINTER_ERROR_CABLE_POWER 5 #define PRINTER_ERROR_UNKNOWN 6 #define PRINTER_ERROR_TIMEOUT 7 #define PRINTER_IO_ERROR 129 /****************************************************************************/ /** * for BPP PARALLEL interfaces **/ int is_a_parallel_bpp(int fd) { if (ioctl(fd, BPPIOC_TESTIO) == 0 || errno == EIO) return(1); return(0); } #if defined(DEBUG) && defined(NOTDEF) char *BppState(int state) { static char buf[BUFSIZ]; memset(buf, 0, sizeof(buf)); sprintf(buf, "State (0x%.4x) - (%s%s%s%s)\n", state, ((state & BPP_SLCT_ERR) ? "offline " : ""), ((state & BPP_BUSY_ERR) ? "busy " : ""), ((state & BPP_PE_ERR) ? "paper " : ""), ((state & BPP_ERR_ERR) ? "error " : "")); return(buf); } #endif int bpp_state(int fd) { if (ioctl(fd, BPPIOC_TESTIO)) { struct bpp_error_status bpp_stat; int state; if (ioctl(fd, BPPIOC_GETERR, &bpp_stat) < 0) exit(PRINTER_IO_ERROR); state = bpp_stat.pin_status; #if defined(DEBUG) && defined(NOTDEF) logit("%s", BppState(state)); #endif if (state == (BPP_PE_ERR | BPP_ERR_ERR | BPP_SLCT_ERR)) { /* paper is out */ return(PRINTER_ERROR_PAPER_OUT); } else if (state & BPP_BUSY_ERR) { /* printer is busy */ return(PRINTER_ERROR_BUSY); } else if (state & BPP_SLCT_ERR) { /* printer is offline */ return(PRINTER_ERROR_OFFLINE); } else if (state & BPP_ERR_ERR) { /* printer is errored */ return(PRINTER_ERROR_ERROR); } else if (state == BPP_PE_ERR) { /* printer is off/unplugged */ return(PRINTER_ERROR_CABLE_POWER); } else if (state) { return(PRINTER_ERROR_UNKNOWN); } else return(0); } return(0); } /* * For ecpp parallel port */ int get_ecpp_status(int fd) { int state; struct ecpp_transfer_parms transfer_parms; if (ioctl(fd, ECPPIOC_GETPARMS, &transfer_parms) == -1) { return(-1); } state = transfer_parms.mode; /* * We don't know what all printers will return in * nibble mode, therefore if we support nibble mode we will * force the printer to be in CENTRONICS mode. */ if (state != ECPP_CENTRONICS) { transfer_parms.mode = ECPP_CENTRONICS; if (ioctl(fd, ECPPIOC_SETPARMS, &transfer_parms) == -1) { return(-1); } else { state = ECPP_CENTRONICS; } } return(state); } /** * For prnio(7I) - generic printer interface **/ int is_a_prnio(int fd) { uint_t cap; /* check if device supports prnio */ if (ioctl(fd, PRNIOC_GET_IFCAP, &cap) == -1) { return (0); } /* we will use 1284 status if available */ if ((cap & PRN_1284_STATUS) == 0) { /* some devices may only support 1284 status in unidir. mode */ if (cap & PRN_BIDI) { cap &= ~PRN_BIDI; (void) ioctl(fd, PRNIOC_SET_IFCAP, &cap); } } return (1); } int prnio_state(int fd) { uint_t status; uchar_t pins; if ((ioctl(fd, PRNIOC_GET_STATUS, &status) == 0) && (status & PRN_READY)) { return(0); } if (ioctl(fd, PRNIOC_GET_1284_STATUS, &pins) != 0) { return(PRINTER_ERROR_UNKNOWN); } if ((pins & ~PRN_1284_BUSY) == PRN_1284_PE) { /* paper is out */ return(PRINTER_ERROR_PAPER_OUT); } else if (pins == (PRN_1284_PE | PRN_1284_SELECT | PRN_1284_NOFAULT | PRN_1284_BUSY)) { /* printer is off/unplugged */ return(PRINTER_ERROR_CABLE_POWER); } else if ((pins & PRN_1284_SELECT) == 0) { /* printer is offline */ return(PRINTER_ERROR_OFFLINE); } else if ((pins & PRN_1284_NOFAULT) == 0) { /* printer is errored */ return(PRINTER_ERROR_ERROR); } else if (pins & PRN_1284_PE) { /* paper is out */ return(PRINTER_ERROR_PAPER_OUT); } else if (pins ^ (PRN_1284_SELECT | PRN_1284_NOFAULT)) { return(PRINTER_ERROR_UNKNOWN); } return(0); } /** * Common routines **/ /*ARGSUSED0*/ static void ByeByeParallel(int sig) { /* try to shove out the EOT */ (void) write(1, "\004", 1); exit(0); } /*ARGSUSED0*/ static void printer_info(char *fmt, ...) { char mesg[BUFSIZ]; va_list ap; va_start(ap, fmt); vsprintf(mesg, fmt, ap); va_end(ap); /* fprintf(stderr, "%%%%[ PrinterError: %s; source: parallel ]%%%%\n", mesg); */ fprintf(stderr, "%s\n", mesg); fflush(stderr); fsync(2); } static void printer_error(int error) { switch (error) { case -1: printer_info("ioctl(): %s", _sys_errlist[errno]); break; case PRINTER_ERROR_PAPER_OUT: printer_info("out of paper"); break; case PRINTER_ERROR_OFFLINE: printer_info("offline"); break; case PRINTER_ERROR_BUSY: printer_info("busy"); break; case PRINTER_ERROR_ERROR: printer_info("printer error"); break; case PRINTER_ERROR_CABLE_POWER: printer_info("printer powered off or disconnected"); break; case PRINTER_ERROR_UNKNOWN: printer_info("unknown error"); break; case PRINTER_ERROR_TIMEOUT: printer_info("communications timeout"); break; default: printer_info("get_status() failed"); } } static void wait_state(int fd, int get_state()) { int state; int was_faulted = 0; while (state = get_state(fd)) { was_faulted=1; printer_error(state); sleep(15); } if (was_faulted) { fprintf(stderr, "printer ok\n"); fflush(stderr); fsync(2); } } /** ** end of Sun Additions for parallel port **/ #define IDENTICAL(A,B) (A.st_dev==B.st_dev && A.st_ino==B.st_ino) #define ISBLK(A) ((A.st_mode & S_IFMT) == S_IFBLK) #define ISCHR(A) ((A.st_mode & S_IFMT) == S_IFCHR) #define E_SUCCESS 0 #define E_BAD_INPUT 1 #define E_BAD_OUTPUT 2 #define E_BAD_TERM 3 #define E_IDENTICAL 4 #define E_WRITE_FAILED 5 #define E_TIMEOUT 6 #define E_HANGUP 7 #define E_INTERRUPT 8 #define SAFETY_FACTOR 2.0 #define R(F) (int)((F) + .5) #define DELAY(N,D) R(SAFETY_FACTOR * ((N) / (double)(D))) extern int sys_nerr; extern char *sys_errlist[], *getenv(); extern int atoi(); char buffer[BUFSIZ]; void sighup(), sigint(), sigquit(), sigpipe(), sigalrm(), sigterm(); #if defined(baudrate) # undef baudrate #endif int baudrate(); int nop(int fd) { return (0); } int bpp_state(int); /** ** main() **/ int main(int argc, char *argv[]) { int nin, nout, effective_rate, max_delay = 0, n; int report_rate; short print_rate; struct stat in, out; struct tms tms; long epoch_start, epoch_end; char *TERM; int (*func)(int fd); /* * The Spooler can hit us with SIGTERM for three reasons: * * - the user's job has been canceled * - the printer has been disabled while we were printing * - the Spooler heard that the printer has a fault, * and the fault recovery is wait or beginning * * We should exit cleanly for the first two cases, * but we have to be careful with the last. If it was THIS * PROGRAM that told the Spooler about the fault, we must * exit consistently. * * The method of avoiding any problem is to turn off the * trapping of SIGTERM before telling the Spooler about * the fault. * * Faults that we can detect: * - hangup (drop of carrier) * - interrupt (printer sent a break or quit character) * - SIGPIPE (output port is a FIFO, and was closed early) * - failed or incomplete write() * - excess delay in write() (handled with SIGALRM later) * * Pseudo-faults (errors in use): * - No input/output, or strange input/output * - Input/output identical * - No TERM defined or trouble reading Terminfo database */ signal (SIGTERM, sigterm); signal (SIGHUP, sighup); signal (SIGINT, sigint); signal (SIGQUIT, sigint); signal (SIGPIPE, sigpipe); if (argc > 1 && STREQU(argv[1], "-r")) { report_rate = 1; argc--; argv++; } else report_rate = 0; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); /* * Stat the standard output to be sure it is defined. */ if (fstat(1, &out) < 0) { signal (SIGTERM, SIG_IGN); fprintf ( stderr, gettext("Can't stat output (%s);\nincorrect use of lp.cat!\n"), PERROR ); exit (E_BAD_OUTPUT); } /* * Stat the standard input to be sure it is defined. */ if (fstat(0, &in) < 0) { signal (SIGTERM, SIG_IGN); fprintf ( stderr, gettext("Can't stat input (%s);\nincorrect use of lp.cat!\n"), PERROR ); exit (E_BAD_INPUT); } /* * If the standard output is not a character special file or a * block special file, make sure it is not identical to the * standard input. * * If we are an ecpp parallel port in centronics mode treat * ourselves as a bpp compatible device. */ if (is_a_prnio(1)) { func = prnio_state; } else if (is_a_parallel_bpp(1) || (get_ecpp_status(1) == ECPP_CENTRONICS)) { func = bpp_state; } else if (isatty(1)) { /* serial connection (probably) - continue as usual */ func = nop; } else { func = nop; } if (!ISCHR(out) && !ISBLK(out) && IDENTICAL(out, in)) { signal (SIGTERM, SIG_IGN); fprintf ( stderr, gettext("Input and output are identical; incorrect use of lp.cat!\n") ); exit (E_IDENTICAL); } /* * The effective data transfer rate is the lesser * of the transmission rate and print rate. If an * argument was passed to us, it should be a data * rate and it may be lower still. * Based on the effective data transfer rate, * we can predict the maximum delay we should experience. * But there are other factors that could introduce * delay, so let's be generous; after all, we'd rather * err in favor of waiting too long to detect a fault * than err too often on false alarms. */ if ( !(TERM = getenv("TERM")) || !*TERM ) { signal (SIGTERM, SIG_IGN); fprintf ( stderr, gettext("No TERM variable defined! Trouble with the Spooler!\n") ); exit (E_BAD_TERM); } if ( !STREQU(TERM, NAME_UNKNOWN) && tidbit(TERM, "cps", &print_rate) == -1 ) { signal (SIGTERM, SIG_IGN); fprintf ( stderr, gettext("Trouble identifying printer type \"%s\"; check the Terminfo database.\n"), TERM ); exit (E_BAD_TERM); } if (STREQU(TERM, NAME_UNKNOWN)) print_rate = -1; effective_rate = baudrate() / 10; /* okay for most bauds */ if (print_rate != -1 && print_rate < effective_rate) effective_rate = print_rate; if (argc > 1 && (n = atoi(argv[1])) >= 0 && n < effective_rate) effective_rate = n; /* 0 means infinite delay */ if (effective_rate) max_delay = DELAY(BUFSIZ, effective_rate); /* * We'll use the "alarm()" system call to keep us from * waiting too long to write to a printer in trouble. */ if (max_delay) signal (SIGALRM, sigalrm); /* * While not end of standard input, copy blocks to * standard output. */ while ((nin = read(0, buffer, BUFSIZ)) > 0) { char *ptr = buffer; /* * We should be safe from incomplete writes to a full * pipe, as long as the size of the buffer we write is * a even divisor of the pipe buffer limit. As long as * we read from files or pipes (not communication devices) * this should be true for all but the last buffer. The * last will be smaller, and won't straddle the pipe max * limit (think about it). */ #if PIPE_BUF < BUFSIZ || (PIPE_MAX % BUFSIZ) this_wont_compile; #endif if (report_rate) epoch_start = times(&tms); do { wait_state(1, func); if (max_delay) alarm (max_delay); nout = write(1, ptr, nin); alarm(0); if (nout < 0) { fprintf ( stderr, gettext("Write failed (%s);\nperhaps the printer has gone off-line.\n"), PERROR ); fflush(stderr); if (errno != EINTR) /* I/O error on device, get lpcshed to retry */ exit(PRINTER_IO_ERROR); else /* wait for printer to come back online */ sleep(15); } else { nin -= nout; ptr += nout; } } while (nin > 0); if (max_delay) alarm (0); else if (report_rate) { epoch_end = times(&tms); if (epoch_end - epoch_start > 0) fprintf ( stderr, "%d CPS\n", R((100 * BUFSIZ) / (double)(epoch_end - epoch_start)) ); } } return (E_SUCCESS); } /** ** sighup() - CATCH A HANGUP (LOSS OF CARRIER) **/ void sighup () { signal (SIGTERM, SIG_IGN); signal (SIGHUP, SIG_IGN); fprintf (stderr, gettext(HANGUP_FAULT_LPCAT)); exit (E_HANGUP); } /** ** sigint() - CATCH AN INTERRUPT **/ void sigint () { signal (SIGTERM, SIG_IGN); signal (SIGINT, SIG_IGN); fprintf (stderr, gettext(INTERRUPT_FAULT)); exit (E_INTERRUPT); } /** ** sigpipe() - CATCH EARLY CLOSE OF PIPE **/ void sigpipe () { signal (SIGTERM, SIG_IGN); signal (SIGPIPE, SIG_IGN); fprintf (stderr, gettext(PIPE_FAULT)); exit (E_INTERRUPT); } /** ** sigalrm() - CATCH AN ALARM **/ void sigalrm () { signal (SIGTERM, SIG_IGN); fprintf ( stderr, gettext("Excessive write delay; perhaps the printer has gone off-line.\n") ); exit (E_TIMEOUT); } /** ** sigterm() - CATCH A TERMINATION SIGNAL **/ void sigterm () { signal (SIGTERM, SIG_IGN); /* * try to flush the output queue in the case of ecpp port. * ignore the return code as this may not be the ecpp. */ ioctl(1, I_FLUSH, FLUSHW); exit (E_SUCCESS); } /** ** baudrate() - RETURN BAUD RATE OF OUTPUT LINE **/ static int baud_convert[] = { 0, 50, 75, 110, 135, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 153600, 230400, 307200, 460800 }; int baudrate () { struct termio tm; struct termios tms; int speed; if (ioctl(1, TCGETS, &tms) < 0) { if (ioctl(1, TCGETA, &tm) < 0) return (1200); else speed = tm.c_cflag&CBAUD; } else speed = cfgetospeed(&tms); return (speed ? baud_convert[speed] : 1200); }