SUMMARY: CAP + Solaris printing

Craig D Rice (cdr@stolaf.edu)
Wed, 02 Apr 1997 08:35:54 -0600

> We are currently printing to several dozen Apple Laserwriters via CAP
> (Columbia Appletalk Package [6.0/pl198]) running on a SunOS (4.1.3)
> box. In anticipation of moving our print services over to Solaris, I
> have compiled CAP and can use CAP (papif) to successfully print to our
> Laserwriters on a Solaris box.

First, my thanks to the following people for their helpful responses:

"Karl E. Vogel" <vogelke@c17mis.region2.wpafb.af.mil>
Christopher Welsh <cris@deakin.edu.au>
David Fetrow <fetrow@biostat.washington.edu>
David Montgomery <david@cs.newcastle.edu.au>
Ian MacPhedran <Ian_MacPhedran@mackenzie.usask.ca>
James Hsieh <jhsieh@soe.ucsd.edu>
Jason Keltz <cs911089@red.ariel.cs.yorku.ca>
John Justin Hough <john@oncology.uthscsa.edu>
Kevin Davidson <tkld@cogsci.ed.ac.uk>
Pierre Padovani <padovani@aaec.com>
Stephen Campbell <steve@avalon.dartmouth.edu>
brian@chimera.psych.unimelb.edu.au (Brian Desmond)
stuart@cs.adelaide.edu.au (Stuart Beck)

Several people recommended installing the BSD-style lpd system. We
did consider this option but decided to bite the bullet and figure out
how to do it the "Solaris"-way.

For anyone interested in this approach, however:

> From: Jason Keltz <cs911089@red.ariel.cs.yorku.ca>

> I would suggest abandoning the Solaris SysV-style print system, and get a
> BSD printing system for Solaris. We use LPRng and are very happy with it.
> You can get LPRng from ftp://dickory.sdsu.edu/pub/LPRng/. It's supported
> on a wide variety of platforms.

Other respondents directed me to cap/applications/papif/add_at_printer.
Having worked with a much older version of CAP, I hadn't even thought
to look for something in the CAP distro that would do most of the work
for me! add_at_printer is this beast.

The most complete answer, which included the add_at_printer script and
a script to do most of the work :-), came from James Hsieh (see
below). In addition to the files furnished by James, it is also
necessary to update the cap.printers by hand (to map the UNIX printer
name to an AppleTalk printer name) -- this could be integrated into
add_at_printer.

Also, it is worth mentioning that, although the printing with
CAP+Solaris does work, it does not work identically to printing with
CAP+SunOS. It may be my still basic understanding of Solaris
printing, but it seems a lot harder to debug printing problems under
Solaris. For example, under SunOS, you can get a pretty good idea of
what's going on with a printer by looking at /var/spool/lpd/foo/errsa*
and /var/spool/lpd/foo/errlog. Under Solaris, the usual helpful error
messages ("AppleTalk printer foo not found" and the like) did not
appear (at least not in /etc/lp/logs). Further, checking the status
of a print job under SunOS (lpq -Pfoo) would show the process of
papif: idle, busy, waiting, Out of Paper, etc. -- none of this seems
to be available with CAP+Solaris printing.

Thanks again to all of you who responded; James's note is enclosed
below for your reference.

Craig

--
Craig D. Rice		UNIX Systems Specialist
cdr@stolaf.edu		Academic Computing Center, St. Olaf College
+1 507 646-3631		1510 St. Olaf Avenue
+1 507 646-3096 FAX	Northfield, MN  55057-1097   USA
URL:  http://www.stolaf.edu/people/cdr/

----- CUT HERE -----

Date: Thu, 20 Mar 1997 12:20:24 -0800 From: James Hsieh <jhsieh@soe.ucsd.edu> --------

> The trick is how to integrate CAP's "papif" with Solaris printing > system. It was no problem to integrate "papif" with BSD-style > printing filters, but it's not at all obvious how we might do this > under Solaris.

[...]

I did this some time ago as a project, and it did work fine, but someone made a decision soon after getting this all up and running to use a BSD machine to do printing (or something like that).

I'm including 3 files:

1) The add_at_printer file I've modified to add a printer with papif hooks...

2) The interface.template file you need to install as the printer interface (you'll need to define "MYHOME" -- see the early lines in the script). The thingie is set to run gid lp -- you can feel free to fix it in any other manner you choose to be secure. This was a proof of concept and not put into full production -- only limited production.

3) The instructions I wrote for the department on how to get this to work. I think it's pretty clear even though there are a lot of specific paths in there. Let me know what's not and I'll try to edit it more carefully.

You can take a look at this, decide if you want to do this, and then do with this message as you see fit... :-) I have no ego to bruise.

BTW, this was Solaris 2.4 about 8 months ago.

Good luck.

--James

--
[ James Hsieh                 O          Programmer, School of Engineering ]
[ UC San Diego              --+--          Office of Engineering Computing ]
[ 9500 Gilman Dr 0405         |                 email: jhsieh@soe.ucsd.edu ]
[ La Jolla, CA  92093-0405   / \   http://www-soe.ucsd.edu/personal/jhsieh ]

----

################# ---- Item 1 ----- #################

#!/bin/sh # # Copyright (C) September 1993 appro@fy.chalmers.se # # Script to add an AppleTalk printer on a Solaris 2.N host #

MYHOME=`dirname $0`; [ "$MYHOME" = "." ] && MYHOME=`pwd` case $MYHOME in .*) echo "Use absolute pathname for $0." exit 1 ;; esac

CAPHOME=${CAPHOME:-$MYHOME}; PAPIFHOME=${CAPHOME} if [ ! -x $PAPIFHOME/papif ]; then PAPIFHOME=${PAPIFHOME}/bin if [ ! -x $PAPIFHOME/papif ]; then echo "FATAL: can't find $PAPIFHOME/papif," echo " make sure that $0 resides in the same directory as papif" echo " or set CAPHOME variable properly." exit fi fi export PAPIFHOME

PATH=$PATH:$CAPHOME:$CAPHOME/bin:$PAPIFHOME

SUFFIX=`basename $0`.$$; export SUFFIX trap 'rm /tmp/*.$SUFFIX > /dev/null 2>&1' 0 trap 'exit' 1 2 3

echo "" echo "Enter AppleTalk zone [*] > \c"; read ZONE [ "$ZONE" ] || ZONE="*"

echo "" echo "Looking for =:LaserWriter@$ZONE ..." $CAPHOME/bin/atlook -n "=:LaserWriter@$ZONE" | egrep -e '\[Net:[0-9]*'

echo "" echo "Enter AppleTalk printer name > \c"; read ATPRINTER [ "$ATPRINTER" ] || exit [ "`echo "$ATPRINTER" | awk -F@ '{ print $2 }'`" ] || ATPRINTER="$ATPRINTER@$ZONE" [ "`echo "$ATPRINTER" | awk -F: '{ print $2 }'`" ] || ATPRINTER="`echo "$ATPRINTER" | awk -F@ '{ printf "%s:LaserWriter@%s",$1,$2; }'`"

TEMP=`echo "$ATPRINTER" | awk -F: '{ print $1 }' | awk '{ for(i=1;i<=NF;i++) printf "%s",$i; }'` echo "Enter Unix printer name [$TEMP] > \c"; read UXPRINTER [ "$UXPRINTER" ] || UXPRINTER=$TEMP

if lpstat -p $UXPRINTER >/dev/null 2>&1; then echo "\07" echo "WARNING: $UXPRINTER already exists," echo " destination is \"`cat /etc/lp/printers/$UXPRINTER/comment`\"." echo "Continue [Y/n]?\c"; read OK case $OK in [Nn]*) exit; ;; *) ;; esac disable -c -r "Redefining to $ATPRINTER" $UXPRINTER reject $UXPRINTER else echo "\07" echo "Defining \"$ATPRINTER\" AppleTalk printer as $UXPRINTER ..." echo "Continue [Y/n]?\c"; read OK case $OK in [Nn]*) exit; ;; *) ;; esac fi echo ""

if [ ! -r /etc/lp/filter.table ]; then echo "Initializing of default printer filters..." for f in `ls /etc/lp/fd | sed 's/.fd$//'`; do lpfilter -f $f -F/etc/lp/fd/$f.fd done fi

if lpfilter -f atalkio -l > /dev/null 2>&1; then echo "AppleTalk printer filter is already defined." else echo "Defining AppleTalk printer filter ..." cat > /etc/lp/fd/atalkio.fd << EOF #ident "@(#)atalkio.fd 1.0 (C) July 1993 appro@fy.chalmers.se"

Input types: postscript Output types: PS Printer types: AppleTalk Printers: any Filter type: fast Command: \$PAPIFHOME/papif 2>>\$ERRFILE EOF lpfilter -f atalkio -F /etc/lp/fd/atalkio.fd fi

if [ ! -f /usr/share/lib/terminfo/A/AppleTalk ]; then echo "Defining terminfo for AppleTalk printers ..." cat > /tmp/terminfo.$SUFFIX << EOF AppleTalk, cols#80, lines#66, cpi=null, csnm=^D, lpi=null, scs=^D, slines=^D, u9=^D, EOF tic /tmp/terminfo.$SUFFIX fi

LOCKSDIR=/var/spool/lp/tmp/AppleTalk if [ ! -d $LOCKSDIR ]; then mkdir -m 0771 $LOCKSDIR chown lp $LOCKSDIR chgrp lp $LOCKSDIR fi PSEUDODEVICE=$LOCKSDIR/$UXPRINTER touch $PSEUDODEVICE chown lp $PSEUDODEVICE chgrp lp $PSEUDODEVICE chmod 0600 $PSEUDODEVICE

cat $MYHOME/interface.template > /etc/lp/interfaces/$UXPRINTER chown lp /etc/lp/interfaces/$UXPRINTER chgrp lp /etc/lp/interfaces/$UXPRINTER chmod 2775 /etc/lp/interfaces/$UXPRINTER

echo "(Re)defining $UXPRINTER ..." ( lpadmin -p $UXPRINTER -D "$ATPRINTER" \ -T AppleTalk \ -v $PSEUDODEVICE \ -I PS \ -i /etc/lp/interfaces/$UXPRINTER \ -A none && \ accept $UXPRINTER && \ enable $UXPRINTER \ ) || exit

echo "\07" echo "Do you want to make it available to network [Y/n]?\c"; read OK case $OK in [Nn]*) exit; ;; *) ;; esac

echo "" if sacadm -l -p tcp > /dev/null; then echo "tcp listener is already defined." else echo "Defining tcp listener ..." sacadm -a -p tcp -t listen \ -c "/usr/lib/saf/listen tcp" \ -v `nlsadmin -V` \ -n 9999 fi

ADDR_LPD="\\x`lpsystem -A`"; export ADDR_LPD ADDR_0=`echo $ADDR_LPD | sed -e 's/\\x00020203/\\x00020ACE/g'`; export ADDR_0

if pmadm -l -p tcp -s 0 > /dev/null; then echo "<0> service is already defined." else echo "Defining <0>/tcp service ..." pmadm -a -p tcp -s 0 -i root \ -m `nlsadmin -c /usr/lib/saf/nlps_server -A $ADDR_0` \ -v `nlsadmin -V` fi

if pmadm -l -p tcp -s lp > /dev/null; then echo "<lp> service is already defined." else echo "Defining <lp>/tcp service ..." pmadm -a -p tcp -s lp -i root \ -m `nlsadmin -o /var/spool/lp/fifos/listenS5` \ -v `nlsadmin -V` fi

if pmadm -l -p tcp -s lpd > /dev/null; then echo "<lpd> service is already defined." else echo "Defining <lpd>/tcp service ..." pmadm -a -p tcp -s lpd -i root \ -m `nlsadmin -o /var/spool/lp/fifos/listenBSD -A $ADDR_LPD` \ -v `nlsadmin -V` fi

if nistest printers.org_dir; then if nistest "[printer_name=$UXPRINTER]printers.org_dir"; then nistbladm -m printer_host=`uname -n` \ description="$ATPRINTER" \ "[printer_name=$UXPRINTER]printers.org_dir" else nistbladm -a printer_name=$UXPRINTER \ printer_host=`uname -n` \ description="$ATPRINTER" \ printers.org_dir fi fi

################ ---- Item 2 ---- ################

#ident "@(#)papif.interface 1.1 93/09/06 appro@fy.chalmers.se" /* CAP6.0 */ #modified by James Hsieh (SOE -- jhsieh@soe.ucsd.edu) 10/4/95

########### ## ## AppleTalk printer interface program. ## ## For use with the Solaris 2.N print system, refer to the "Setting up ## Printers" manual. Used in conjunction with the add_at_printer script ## ###########

##### # # Until we get to the point below where the printer port # and physical printer are initialized, we can't do much # except exit if the Spooler/Scheduler cancels us. ##### trap 'exit' 15

##### # # We can be clever about getting a hangup or interrupt, though, at least # until the filter runs. Do this early, even though $LPTELL # isn't defined, so that we're covered. ##### catch_hangup () { if [ -n "${LPTELL}" ] then echo \ "The connection to the printer dropped; perhaps the printer went off-line?" \ | ${LPTELL} ${printer} fi return 0 } catch_interrupt () { if [ -n "${LPTELL}" ] then echo \ "Received an interrupt from the printer. The reason is unknown, although a common cause is that the baud rate is too high." \ | ${LPTELL} ${printer} fi return 0 } trap 'catch_hangup; exit_code=129 exit 129' 1 trap 'catch_interrupt; exit_code=129 exit 129' 2 3

##### # # Most of the time we don't want the standard error to be captured # by the Spooler, mainly to avoid "Terminated" messages that the # shell puts out when we get a SIGTERM. We'll save the standard # error channel under another number, so we can use it when it # should be captured. # # Open another channel to the printer port, for use when the # regular standard output won't be directed there, such as in # command substitution (`cmd`). ##### exec 5>&2 2>/dev/null 3>&1

##### # # Set some globally used variables and functions. #####

: ${TMPDIR:=/tmp} : ${SPOOLDIR:=/usr/spool/lp} : ${TERMINFO:=/usr/lib/terminfo} : ${CHARSETDIR:=/usr/lib/charsets}

: ${LOCALPATH:=${SPOOLDIR}/bin} PATH="/bin:/usr/bin:${LOCALPATH}"

MAX_COLS_SMALL_BANNER=40

# Define where PAPIF is... PAPIFHOME=/usr/local/cap60; export PAPIFHOME

##### # # On the 3.2 release of the 386unix product, the parallel port does # not support any ioctl calls. As a result, we cannot set the opost # and onlcr attributes to have <NL>'s expanded to <CR><NL>. This # "filter" gets the job done for us. ##### : ${FIX386BD:=${LOCALPATH}/386parallel} if [ -n "${FIX386BD}" -a -x "${FIX386BD}" ] then FIX386BD="| ${FIX386BD}" else FIX386BD="" fi

##### # Use ${TMPPREFIX} as the prefix for all temporary files, so # that cleanup is easy. The prefix may be up to 13 characters # long, so you only have space for one more character to make # a file name. If necessary, make a directory using this prefix # for better management of unique temporary file names. ##### TMPPREFIX=${TMPDIR}/`uname -n`$$

##### # Before exiting, set ${exit_code} to the value with which to exit. # Otherwise, the exit from this script will be 0. ##### trap 'rm -fr ${TMPPREFIX}*; exit ${exit_code}' 0

##### # ${LPTELL} is the name of a program that will send its # standard input to the Spooler. It is used to forward # the description of a printer fault to the Spooler, # which uses it in an alert to the administrator. ##### #if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ] #then # fake_lptell () { # header="no" # while read line # do # if [ "no" = "${header}" ] # then # errmsg ERROR ${E_IP_UNKNOWN} \ # "unknown printer/interface failure" \ # "consult your system administrator; # reasons for failure (if any) follow:" # header=yes # fi # echo "${line}" >&2 # done # return 1 # } # LPTELL=fake_lptell #fi # #jhsieh was here # LPTELL="/usr/local/cap60/papif"

##### # ${DRAIN} is the name of a program that will wait # long enough for data sent to the printer to print. ##### if [ -x "${LOCALPATH}/drain.output" ] then DRAIN="${LOCALPATH}/drain.output 5" # wait only five seconds else DRAIN= fi

##### # ${LPCAT} is the name of a program to use as a default # filter. Minimally it should copy its standard input to # the standard output, but it should also trap printer # faults. The current LPCAT traps hangups (DCD dropping, SIGHUP), # interrupts (SIGINT, SIGQUIT), broken pipe (SIGPIPE), and # excess delays in sending data to the printer, interpreting all # as printer faults. ##### if [ ! -x "${LPCAT:=${LOCALPATH}/lp.cat}" ] then LPCAT="cat" echo $LPCAT >> /tmp/out fi

##### # ${LPSET} is the name of a program that will set the # character pitch, line pitch, page width, page length, # and character set. It helps to have this in a single # binary program so that (1) it's faster than calls # to "tput"; and (2) it can access the new Terminfo # capabilities for printers (on pre SVR3.2 machines, tput can't). ##### if [ ! -x "${LPSET:=${LOCALPATH}/lp.set}" ] then fake_lpset () { echo H V W L S >&2 false } LPSET=fake_lpset fi

internal_lpset () { ##### # # The funny business with the "2>&1 1>&3" is to let us capture # the standard ERROR, not the standard OUTPUT as is the usual case # with foo=`cmd`. The standard output will go to the printer. ##### [ -n "${stty1}" ] && stty ${stty1} 0<&1 chk=`${LPSET} "$1" "$2" "$3" "$4" "$5" 2>&1 1>&3` [ -n "${stty2}" ] && stty ${stty2} 0<&1

##### # # The standard error of the delivered ${LPSET} program # is a string of letters, H, V, W, L, S, which correspond # to cpi, lpi, width, length, and character set. A letter # is present only if the corresponding attribute could not # be set. ##### for err in ${chk} do case ${err} in H ) errmsg WARNING ${E_IP_BADCPI} \ "can't select the character pitch \"${cpi}\"" \ "check the valid pitches for the printer, or consult your system administrator; printing continues" ;; V ) errmsg WARNING ${E_IP_BADLPI} \ "can't select the line pitch \"${lpi}\"" \ "check the valid pitches for the printer, or consult your system administrator; printing continues" ;; W ) width=${cols} errmsg WARNING ${E_IP_BADWIDTH} \ "can't select the page width \"${width}\"" \ "check the valid widths for the printer, or consult your system administrator; printing continues" ;; L ) length=${lines} errmsg WARNING ${E_IP_BADLENGTH} \ "can't select the page length \"${length}\"" \ "check the valid lengths for the printer, or consult your system administrator; printing continues" ;; S ) errmsg WARNING ${E_IP_BADCHARSET} \ "can't select the character set \"${CHARSET}\"" \ "check the name given in the -S option, or consult your system administrator; printing continues" ;; esac done }

##### # ${TPUT} is "tput" IF it works. We'll disable it if we get an # ugly error message the first time we use it. See the TERM variable # later in the script. # # NOTE: The check we use to see if "tput" works is to use an OLD # Terminfo capability, like "lines". If it works with that it may # still fail with some of the newer capabilities like "init" (SVR3.0) # or "swidm" (SVR3.2), because the version of "tput" we have on your # machine is older. Thus, on some of the code where ${TPUT} is used # you'll see "2>/dev/null" being used to avoid ugly error messages. ##### TPUT=tput

##### # Error message formatter: # # Invoke as # # errmsg severity message-number problem help # # where severity is "ERROR" or "WARNING", message-number is # a unique identifier, problem is a short description of the # problem, and help is a short suggestion for fixing the problem. #####

LP_ERR_LABEL="UX:lp"

E_IP_ARGS=1 E_IP_OPTS=2 #E_IP_FILTER=3 E_IP_STTY=4 E_IP_UNKNOWN=5 E_IP_BADFILE=6 E_IP_BADCHARSET=7 E_IP_BADCPI=8 E_IP_BADLPI=9 E_IP_BADWIDTH=10 E_IP_BADLENGTH=11 E_IP_ERRORS=12 # (in slow.filter)

errmsg () { case $1 in ERROR ) sev=" ERROR"; ;; WARNING ) sev="WARNING"; ;; esac # tag=`expr "${LP_ERR_LABEL}" : "\(.*\):"``expr "${LP_ERR_LABEL}" : ".*:\(.*\)"` echo "${LP_ERR_LABEL}: ${sev}: $3 TO FIX: $4" >&5 }

########### ## ## Check arguments ###########

parse () { echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`" }

##### # # This program is invoked as # # ${SPOOLDIR}/.../printer request-id user title copies options files... # # The first three arguments are simply reprinted on the banner page, # the fourth (copies) is used to control the number of copies to print, # the fifth (options) is a blank separated list (in a single argument) # of user or Spooler supplied options (without the -o prefix), # and the last arguments are the files to print. #####

if [ $# -lt 5 ] then errmsg ERROR ${E_IP_ARGS} \ "wrong number of arguments to interface program" \ "consult your system administrator" exit 1 fi

printer=`basename $0` request_id=$1 user_name=$2 title=$3 copies=$4 option_list=$5

shift 5 files="$*"

nobanner="yes" # enable banners here (set it to "no") nofilebreak="no" stty=

inlist= for i in ${option_list} do case "${inlist}${i}" in

nobanner ) nobanner="yes" ;;

nofilebreak ) nofilebreak="yes" ;;

##### # # If you want to add simple options (e.g. -o simple) # identify them here. ##### # simple ) # simple="yes" # ;;

cpi=pica ) cpi=10 ;; cpi=elite ) cpi=12 ;; cpi=* ) cpi=`parse ${i}` ;;

lpi=* ) lpi=`parse ${i}` ;;

length=* ) length=`parse ${i}` ;;

width=* ) width=`parse ${i}` ;;

##### # # If you want to add simple-value options (e.g. -o value=a) # identify them here. ##### # value=* ) # value=`parse ${i}` # ;;

##### # # If you want to add options that, like "stty", # take a list (e.g. -o lopt='a b c'), identify # them here and below (look for LOPT). ##### stty=* | flist=* | lpd=* ) #LOPT stty=* | flist=* | lpd=* | lopt=* )

inlist=`expr "${inlist}${i}" : "^\([^=]*=\)"` case "${i}" in ${inlist}\'*\' ) item=`expr "${i}" : "^[^=]*='*\(.*\)'\$"` ;; ${inlist}\' ) continue ;; ${inlist}\'* ) item=`expr "${i}" : "^[^=]*='*\(.*\)\$"` ;; ${inlist}* ) item=`expr "${i}" : "^[^=]*=\(.*\)\$"` ;; *\' ) item=`expr "${i}" : "^\(.*\)'\$"` ;; * ) item="${i}" ;; esac

##### # # We don't dare use "eval" because a clever user could # put something in an option value that we'd end up # exec'ing. ##### case "${inlist}" in stty= ) stty="${stty} ${item}" ;; flist= ) flist="${flist} ${item}" ;; lpd= ) lpd="${lpd} ${item}" ;; #LOPT lopt= ) #LOPT lopt="${lopt} ${item}" #LOPT ;; esac

case "${i}" in ${inlist}\'*\' ) inlist= ;; ${inlist}\'* ) ;; *\' | ${inlist}* ) inlist= ;; esac ;;

* ) errmsg WARNING ${E_IP_OPTS} \ "unrecognized \"-o ${i}\" option" \ "check the option, resubmit if necessary printing continues" ;; esac done

##### # # Additional ``parameters'' are passed via Shell environment # variables: # # TERM The printer type (used for Terminfo access) # CHARSET The character set to choose # FILTER The filter to run #####

##### # Set defaults for unset variables. #####

: ${TERM:=unknown} tput lines 1>/dev/null 2>&1 || TPUT=:

: ${CHARSET:=cs0}

if [ -z "${FILTER}" ] then ##### # # If no filter is being used, we have a little routine that # will push the data to the printer. It traps hangups (loss # of carrier) and checks for excessive delays in sending the # data to the printer. The lesser of the print rate of the printer # (obtained from Terminfo) or the baud rate is used to compute # the expected delay. If neither of these is correct, you # may be experiencing false alarms. If so, give the correct # rate, in characters per second, as a single argument. # An argument of 0 means don't check for delays. # Give an -r option to get a printout of actual delays. # (QUOTES ARE IMPORTANT!) ##### # FILTER="${LPCAT} 120" # e.g. 120 CPS FILTER="${LPCAT} 0" # allow infinite delays # FILTER="${LPCAT} -r 0 2>/tmp/delays" # check actual delays # FILTER=${LPCAT} fi

########### ## ## Initialize the printer port ###########

##### # # SERIAL PORTS: # Initialize everything. # # PARALLEL PORTS: # Don't initialize baud rate. # # It's not obvious how to tell if a port is parallel or serial. # However, by splitting the initialization into two steps and letting # the serial-only part fail nicely, it'll work. # # Another point: The output must be a ``tty'' device. If not, don't # bother with any of this. ##### stty1= stty2= tty 0<&1 1>/dev/null 2>&1 && {

##### # # First set the default parameters, # then the requested parameters. #####

stty \ 9600 \ 0<&1 2>/dev/null 1>&2 stty \ cs8 -cstopb -parenb -parodd \ ixon -ixany \ opost -olcuc onlcr -ocrnl -onocr -onlret -ofill \ nl0 cr0 tab0 bs0 vt0 ff0 \ 0<&1 2>/dev/null 1>&2

if [ -n "${stty}" ] then if stty ${stty} 0<&1 1>/dev/null 2>&5 then : else errmsg ERROR ${E_IP_STTY} \ "stty option list failed" \ "check the \"-o stty\" option you used, or consult your system administrator" exit 1 fi fi

##### # # Here you may want to add other port initialization code. # Some examples: # # estty # for printer needing hardware flow control (3B2/EPORTS) # fctty # for printer needing hardware flow control (3B15,3B20) ##### #estty 0<&1 #fctty 0<&1

########## # # Find out if we have to turn off opost before initializing the # printer and on after. Likewise, check clocal. # # Turning OFF opost (output postprocessing) keeps the UNIX system # from changing what we try to send to the printer. Turning ON # clocal keeps the UNIX system from dropping what we are trying to # send if the printer drops DTR. An example of the former is the # AT&T 479, which wants to send a linefeed (ASCII 10) when a page # width of 10 is set; with opost on, this COULD BE turned into a # carriage-return/linefeed pair. An example of the latter is the # AT&T 455, which momentarily drops DTR when it gets the # initialization string, is2; with clocal off, the UNIX system # stops sending the rest of the initialization sequence at that # point. # # THIS CODE MUST FOLLOW THE REST OF THE PORT INITIALIZATION CODE. ########## cur_stty=`stty -a 0<&3` expr "${cur_stty}" : '.*-opost' 1>/dev/null 2>&1 \ || stty1="${stty1} -opost" stty2="${stty2} opost" expr "${cur_stty}" : '.*-clocal' 1>/dev/null 2>&1 \ && stty1="${stty1} clocal" stty2="${stty2} -clocal" expr "${cur_stty}" : '.* opost.*' 1>/dev/null 2>&1 \ || banner_filter=${FIX386BD}

}

########### ## ## Initialize the physical printer (Part I). ## Here we bring the printer to a sane state and set the page size. ###########

########## # # WARNING! The "echo" command will catch backslashes (\) and # try to interpret the characters following it. Thus, using # "echo" to print string values obtained from "tput" is dangerous. ##########

##### # We're confident that most printers don't have backslashes # in the control sequences for carriage return and form-feed. # We're also confident that these don't contain newlines. # We're also confident that most printers have a linefeed # in the control sequence for doing a newline (move to beginning # of next line), but we can't capture it like we do the # carriage return or form-feed. Thus we set it unconditionally. # We don't set form-feed if it isn't defined, however, because # maybe the printer doesn't have a formfeed. If not set, we're # out of luck. #####

CR=`${TPUT} cr` [ -z "${CR}" ] && CR="\r"

FF=`${TPUT} ff`

NL="${CR}\n"

lines=`${TPUT} lines` [ -z "${lines}" -o 0 -ge "${lines}" ] && lines=66

cols=`${TPUT} cols` [ -z "${cols}" -o 0 -ge "${cols}" ] && cols=132

##### # # Basic initialization. The ``else'' clause is equivalent, # but covers cases where old Terminal Information Utilities are present. ##### [ -n "${stty1}" ] && stty ${stty1} 0<&1 if ${TPUT} init 2>/dev/null then : else pgm=`${TPUT} iprog` if [ -x "${pgm}" ] then eval ${pgm} fi

${TPUT} is1 ${TPUT} is2

tabset= if [ "8" != "`${TPUT} it`" ] then stty tab3 0<&1 1>/dev/null 2>&1

elif `${TPUT} ht >/dev/null` then tabset="/usr/lib/tabset/${TERM}" if [ -r ${tabset} ] then cat -s ${tabset} fi stty tab3 0<&1 1>/dev/null 2>&1 fi

file=`${TPUT} if` if [ "${tabset}" != "${file}" -a -r "${file}" ] then cat -s "${file}" fi

${TPUT} is3 echo "${CR}\c" fi [ -n "${stty2}" ] && stty ${stty2} 0<&1

##### # # Set the page size and print spacing, but not the character set. # We will be doing the character set later (after the header). ##### internal_lpset "${cpi}" "${lpi}" "${width}" "${length}" ""

##### # # The banner page (and cancellation page) will # use double width characters if they're available. ##### WIDE_CS=`${TPUT} swidm 2>/dev/null` && NORM_CS=`${TPUT} rwidm 2>/dev/null` PAD="#####${NL}"

##### # # Some printers need to have the banner page filtered. ##### case "${TERM}" in

PS | PSR ) banner_filter="/usr/lib/lp/postscript/postprint | /usr/lib/lp/postscript/postio" ;;

AppleTalk ) banner_filter="$PAPIFHOME/papof" ;;

esac if [ -n "${banner_filter}" ] then banner_filter="| ${banner_filter}" fi

##### # # Now that the printer is ready for printing, we're able # to record on paper a cancellation. #####

cancel_banner () { echo "${PAD}${PAD}\c" echo "#####${WIDE_CS} Job ${request_id}${NORM_CS}${NL}\c" echo "#####${WIDE_CS} suspended or canceled${NORM_CS}${NL}\c" echo "${PAD}${PAD}\c" }

canceled () { ${TPUT} scs 0 2>/dev/null echo "${CR}\c" if [ "${width:-${cols}}" -lt "${MAX_COLS_SMALL_BANNER}" ] then WIDE_CS= NORM_CS= fi cancel_banner if [ -n "${FF}" ] then echo "${CR}${FF}\c" fi }

##### doesn't supported yet #trap 'eval canceled ${banner_filter}; exit_code=0 exit' 15 trap 'exit_code=0 exit' 15

########### ## ## Print the banner page ###########

BANNER="/tmp/banner.$$"; export BANNER BANNERFIRST="yes"; export BANNERFIRST #BANNERLAST="yes"; export BANNERLAST

##### # # You may want to change the following code to get a custom banner. #####

regular_banner () { echo "${CR}\c" echo "${PAD}${PAD}${PAD}${PAD}${PAD}\c" echo "#####${WIDE_CS} User: ${user_name}${NORM_CS}${NL}\c" if [ -n "$ALIAS_USERNAME" ] then echo "${PAD}\c" echo "#####${WIDE_CS} Alias: ${ALIAS_USERNAME}${NORM_CS}${NL}\c" fi if [ -n "${title}" ] then echo "${PAD}\c" echo "#####${WIDE_CS} Title: ${title}${NORM_CS}${NL}\c" fi echo "${PAD}\c" echo "#####${WIDE_CS} Printed: `date '+%a %H:%M %h %d, 19%y'`${NORM_CS}${NL}\c" echo "${PAD}\c" echo "#####${WIDE_CS} Job number: ${request_id}${NORM_CS}${NL}\c" echo "${PAD}${PAD}${PAD}${PAD}${PAD}\c" if [ -n "${FF}" ] then echo "${CR}${FF}\c" fi }

small_banner () { echo "${CR}\c" echo "${PAD}\c" echo "##### User: ${user_name}${NL}\c" if [ -n "${title}" ] then echo "##### Title: ${title}${NL}\c" fi echo "##### Date: `date '+%a %H:%M %h %d, 19%y'`${NL}\c" echo "##### Job: ${request_id}${NL}\c" echo "${PAD}\c" if [ -n "${FF}" ] then echo "${CR}${FF}\c" fi }

if [ "${width:-${cols}}" -lt "${MAX_COLS_SMALL_BANNER}" ] then banner=small_banner else banner=regular_banner fi

if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" ] then eval "${banner} ${banner_filter}" fi

########### ## ## Initialize the physical printer (Part II) ## Here we select the character set. ## One could argue that this should be done before the banner is printed, ## but we don't, to keep the banner page looking consistent for the ## operator. You can move this code before the banner code if you ## disagree. If you do, combine it with the other call to "internal_lpset" ## to do everything in one shot. ########### internal_lpset "" "" "" "" "${CHARSET}"

########### ## ## Print some copies of the file(s) ###########

##### # # The protocol between the interface program and the Spooler # is fairly simple: # # All standard error output is assumed to indicate a # fault WITH THE REQUEST. The output is mailed to the # user who submitted the print request and the print # request is finished. # # If the interface program sets a zero exit code, # it is assumed that the file printed correctly. # If the interface program sets a non-zero exit code # less than 128, it is assumed that the file did not # print correctly, and the user will be notified. # In either case the print request is finished. # # If the interface program sets an exit code greater # than 128, it is assumed that the file did not print # because of a printer fault. If an alert isn't already # active (see below) one will be activated. (Exit code # 128 should not be used at all. The shell, which executes # this program, turns SIGTERM, used to kill this program # for a cancellation or disabling, into exit 128. The # Spooler thus interpretes 128 as SIGTERM.) # # A message sent to the standard input of the ${LPTELL} # program is assumed to describe a fault WITH THE PRINTER. # The output is used in an alert (if alerts are defined). # If the fault recovery is "wait" or "begin", the printer # is disabled (killing the interface program if need be), # and the print request is left on the queue. # If the fault recovery is "continue", the interface program # is allowed to wait for the printer fault to be cleared so # it can resume printing. # # This interface program relies on filters to detect printer faults. # In absence of a filter provided by the customer, it uses a simple # filter (${LPCAT}) to detect the class of faults that cause DCD # (``carrier'') drop. The protocol between the interface program and # the filter: # # The filter should exit with zero if printing was # successful and non-zero if printing failed because # of a printer fault. This interface program turns a # non-zero exit of the filter into an "exit 129" from # itself, thus telling the Spooler that a printer fault # (still) exists. # # The filter should report printer faults via a message # to its standard error. This interface program takes all # standard error output from the filter and feeds it as # standard input to the ${LPTELL} program. # # The filter should wait for a printer fault to clear, # and should resume printing when the fault clears. # Preferably it should resume at the top of the page # that was being printed when the fault occurred. # If it waits and finishes printing, it should exit # with a 0 exit code. If it can't wait, it should exit # with a non-zero exit code. # # The interface program expects that ANY message on the # standard error from the filter indicates a printer fault. # Therefore, a filter should not put user (input) error # messages on the standard error, but on the standard output # (where the user can read them when he or she examines # the print-out). # #####

##### build papif command options #jhsieh was here #building papif command options ONLY #FILTER="${FILTER} -P $printer -U $user_name" FILTER="-P ${printer}" #####

badfileyet= i=1 while [ $i -le $copies ] do for file in ${files} do if [ -r "${file}" ] then ##### # # Here's where we set up the $LPTELL program to # capture fault messages, and... # # Here's where we print the file. # # We set up a pipeline to $LPTELL, but play a trick # to get the filter's standard ERROR piped instead of # its standard OUTPUT: Divert the standard error (#2) to # the standard output (#1) IN THE PIPELINE. The shell # will have changed #1 to be the pipe, not the # printer, so diverting #2 connects it to the pipe. # We then change the filter's #1 to a copy of the real # standard output (the printer port) made earlier, # so that is connected back to the printer again. # # We do all this inside a parenthesized expression # so that we can get the exit code; this is necessary # because the exit code of a pipeline is the exit # code of the right-most command, which isn't the # filter. # # These two tricks could be avoided by using a named # pipe to connect the standard error to $LPTELL. In # fact an early prototype of this script did just # that; however, the named pipe introduced a timing # problem. The processes that open a named pipe hang # until both ends of the pipe are opened. Cancelling # a request or disabling the printer often killed one # of the processes, causing the other process to hang # forever waiting for the other end of the pipe to # be opened. ##### EXIT_CODE=${TMPPREFIX}e trap '' 1 # Let the filter handle a hangup trap '' 2 3 # and interrupts ( ##### # Put the 0<${file} before the "eval" to keep # clever users from giving a file name that # evaluates as something to execute. ##### #jhsieh was here # 0<${file} eval ${FILTER} 2>&1 1>&3 # echo $? >${EXIT_CODE} #changes were to actually get things to LPTELL (papif) in the way _it_ expects #it to get there, not what the default print mechanism specified previously 0<${file} eval ${LPCAT} ) | ${LPTELL} -P ${printer} trap 'catch_hangup; exit_code=129 exit 129' 1 trap 'catch_interrupt; exit_code=129 exit 129' 2 3 exit_code=`cat ${EXIT_CODE}`

if [ -n "${exit_code}" -a 0 -ne "${exit_code}" ] then trap '' 15 # Avoid dying from disable sleep 4 # Give $LPTELL a chance to tell exit_code=129 exit 129 fi

if [ -n "${FF}" -a "no" = "${nofilebreak}" ] then echo "${CR}${FF}\c" fi

else

##### # # Don't complain about not being able to read # a file on second and subsequent copies, unless # we've not complained yet. This removes repeated # messages about the same file yet reduces the # chance that the user can remove a file and not # know that we had trouble finding it. ##### if [ "${i}" -le 1 -o -z "${badfileyet}" ] then errmsg WARNING ${E_IP_BADFILE} \ "cannot read file \"${file}\"" \ "see if the file still exists and is readable, or consult your system administrator; printing continues" badfileyet=yes fi

fi

done i=`expr $i + 1`

done

######## banner is already handled #if [ "no" = "${nobanner}" -a "${TERM}" = "PSR" ] #then # eval "${banner} ${banner_filter}" #fi ########

if [ -n "${exit_code}" -a 0 -ne "${exit_code}" ] then exit ${exit_code} fi

##### # # Always ensure the complete job ends with a ``formfeed'', to # let the next job start on a new page. (If someone wants to # concatenate files, they can give them in one job.) # So, if we haven't been putting out a ``formfeed'' between files, # it means we haven't followed the last file with a formfeed, # so we do it here. ##### if [ -n "${FF}" -a "yes" = "${nofilebreak}" ] then echo "${CR}${FF}\c" fi

${DRAIN}

exit_code=0 exit 0

################ ---- Item 3 ---- ################

To get printing to work, I took an interface file that was supplied with the cap60 software, but then had to tweak it a bit from what was there to get the postscript properly loaded into the papif file. The file is not printer name specific, but it does need to exist for every printer interface. In /usr/local/cap60, there is a script called "add_at_printers.ece" which will do the proper things to the various lp configuration files, and install the interface file from the interface.template file in /usr/local/cap60.

cap.printers is in /etc, along with etalk.local. afpvols is in /usr/local/cap60.

The ece-ps6 interface file is owned by lp group lp and set to run gid lp. The /dev/le device (or, to be exact, /devices/pseudo/clone@0:le) is also permitted mode 660 group lp. All interfaces must run gid lp or else the print job just can't be sent over the ethernet. add_at_printers.ece takes care of this.

I would say that this should make the printer work just like any other printer that has to go through the Solaris SVR4-style printer interface, and have all the same limitations. This configuration is clearly not fully supported by the people who do CAP, so any future prolems may or may not be solvable based upon the limitations of this software.

The other change to your system is the addition of S99appletalk to your /etc/rc3.d directory which will start up CAP and AUFS upon entering run-level 3. It can also be used to shutdown CAP and AUFS services.

Let me know if you have questions or if something doesn't seem to be working correctly. Otherwise, I'll consider that the CAP and CAP related issues are resolved.