#!/bin/sh ########### ## $Id: bsd.sh,v 1.1 1997/04/11 08:41:17 mw Exp $ ## ## Simple printer output filter for BSD Unix ########### ##### # # 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' 2 ################################################# # # # define some variables # # # # HOST, PORTS and FTP have to be modified # # to fit the printer specific hostname and port # # and the actual path to ftp # # HOST=_HOST_ # Printserver host name PORTS="lp1,lp2,lp3,lp4,lp5,lp6,lp7,lp8" # Printserver logical printer FTP=/usr/ucb/ftp # actual path of ftp command PASS="" # fill in password if any # PRINT=`basename $0` # Printserver printer name LOGF=/tmp/$PRINT.last # logfile PREFIX=/tmp/IC$$ # DATF=${PREFIX}.d # contains the data CMDF=${PREFIX}.c # contains ftp commands OUTF=${PREFIX}.o # contains output from ftp job WAIT=1 # retry FTP after WAIT seconds MAXR=10 # maximum no. of retries or -1 (forever) # CAT() { cat; } # FILTER=cat # ECHO=/usr/5bin/echo # # H_PRESCR='!R!FRPO P3,2;EXIT;' # set CR=CR, LF=CR+LF H_HPLJET='\033&k2G' # set CR=CR, LF=CR+LF T_PRESCR='!R!FRPO P3,1;EXIT;' # set CR=CR, LF=LF T_HPLJET='\033&k0G' # set CR=CR, LF=LF # HEADER= # TRAILER= # # exec 1> ${DATF} # stdout to file ${DATF} # echo "`date` ${PRINT} starting..." >${LOGF} # open logfile, give chmod 777 ${LOGF} 2> /dev/null || : # all permissions to world # [ -n "${HEADER}" ] && ${ECHO} ${HEADER} # # ################################################# ##### # # 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 ##### # Before exiting, set ${exit_code} to the value with which to exit. # Otherwise, the exit from this script will be 0. ##### trap 'rm -fr ${PREFIX}*; exit ${exit_code}' 0 ##### # # This program is invoked as # # cat files... | ${PRINT} [-wwidth] [-llength] # ##### uid=`id | sed "s/^.*uid=//" | sed "s/(.*//"` user_name=`grep "\:$uid\:" /etc/passwd | line | cut "-d:" -f1` parameter=$* ################################################# # echo " user: $user_name" >>${LOGF} # update logfile echo " parameter: $parameter" >>${LOGF} # # binary="yes" # # ################################################# ##### # # Now that the printer is ready for printing, we're able # to record on $LOGF a cancellation. ##### # canceled () { # echo "`date` request suspended or cancelled" >> ${LOGF} # mail ${user_name} < ${LOGF} # } trap 'echo "`date` request suspended or cancelled" >> ${LOGF} \ mail ${user_name} < ${LOGF} \ exit_code=0 exit' 2 ########### ## ## 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 an exit code # of 2, 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 # of 1, it is assumed that the file did not print # because of a printer fault. # # This interface program relies on filters to detect printer faults. # In absence of a filter provided by the customer, it uses a simple # filter (${FILTER}). # 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 1" 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 ${LOGF} 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). # ##### ##### # # Here's where we print the file. # # We set up a pipeline to $LOGF, 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 $LOGF. 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=${PREFIX}.e trap '' 1 # Let the filter handle a hangup trap '' 2 3 # and interrupts ( ${FILTER} 2>&1 1>&3 echo $? >${EXIT_CODE} ) >> ${LOGF} exit_code=`cat ${EXIT_CODE}` if [ -n "${exit_code}" -a 0 -ne "${exit_code}" ] then trap '' 2 # Avoid dying from disable echo "Exit 1 (${exit_code})" >> ${LOGF} mail $user_name < ${LOGF} exit_code=1 exit 1 fi ################################################# # [ -n "${TRAILER}" ] && ${ECHO} ${TRAILER} # # ( # build FTP command file echo "user ${user_name} ${PASS}" # [ "yes" = "${binary}" ] && echo "bin" # echo "put ${DATF} ${PORTS}" # echo "quit" # ) > ${CMDF} # # exit_code=1 # # while [ ${exit_code} -ne 0 ] do # echo "`date` waiting for FTP... " >> ${LOGF} # wait for FTP to complete # ${FTP} -v -n -i ${HOST} >${OUTF} <${CMDF} # start FTP # cat ${OUTF} >> ${LOGF} # update logfile echo "`date` ...ready" >> ${LOGF} # # if grep "226 Transfer complete." ${OUTF} >/dev/null 2>/dev/null then echo "Exit 0" >> ${LOGF} exit_code=0 exit 0 else if [ ${MAXR} -eq 0 ] then echo "Exit 1" >> ${LOGF} exit_code=1 exit 1 fi echo "Printer busy. Retrying after ${WAIT} seconds" >> ${LOGF} sleep ${WAIT} if [ ${MAXR} -gt 0 ] then WAIT=`expr ${WAIT} + ${WAIT}` MAXR=`expr ${MAXR} - 1` fi fi done # #################################################