1#ident "@(#)netatalk 0.7 99/06/22 job@uchicago.edu" /* Netatalk 1.4*/ 2 3##### 4# User configuration: 5# 6# Set timeout for pap($time) and papstatus($time2). both are in seconds. 7# extraneous if you do not have NETATALKHOME/bin/timeout 8# set how many times to loop before we just abort entirely ($attempts) 9# what flags pap is run w/. -c makes pap claim to have been waiting forever 10##### 11 12time=1800 13time2=60 14attempts=3 15pap_flags="-c" 16 17##### 18# this should get "fixed" to something like 19# NETATALKHOME=/opt ; export NETATALKHOME 20# by the add_netatalk_printer script 21# 22# DO NOT use the user's env for this or the PATH above. 23##### 24 25NETATALKHOME=DEFAULT_NETATALK_HOME ; export NETATALKHOME 26#NETATALKHOME=/opt ; export NETATALKHOME 27 28if [ "${NETATALKHOME}" = "DEFAULT_NETATALK_HOME" ]; then 29 echo "bleah, NETATALKHOME not set, exiting..." ; exit 5 30fi 31 32##### 33# BUGS/TODO: 34# move all TMP stuff to use a directory for security reasons 35# run nbplkup to check if the reason we cannot print, is that it's not on 36# the net 37# move to "${foo}" from $foo and ${foo} 38# add debuging info that gets sent to "logger lpd.debug" 39# if we timeout while printing the banner page, we do not keep trying to print. 40# perhaps filter_timeout should only complain once per job; like badfile. 41# check if the timeout messages are duplicated by the lp system 42# psa will not drop in for pap to use accting. perhaps lp does not need it? 43# make pap print all the files at once; kill for file in ($files) 44# move badfile error to printfile function 45##### 46 47# This File is released under the Perl Artistic Licence. 48# See http://www.perl.org for details 49# 50# Or you can use it under the licence that accompanies Netatalk 1.3 =) 51 52########### 53## Netatalk printer interface. Heavily modified from 54## /usr/lib/lp/model/standard on Sparc Solaris 2.5.1 (May 97) 55## 56## Meant to be used w/ add_netatalk_printer 57########### 58 59##### 60# This program is invoked as 61# 62# ${SPOOLDIR}/.../printer request-id user title copies options files... 63# 64# The first three arguments are simply reprinted on the banner page, 65# the fourth (copies) is used to control the number of copies to print, 66# the fifth (options) is a blank separated list (in a single argument) 67# of user or Spooler supplied options (without the -o prefix), 68# and the last argument(s) is/are the file(s) to print. 69##### 70 71##### 72# 73# The protocol between the interface program and the Spooler 74# is fairly simple: 75# 76# All standard error output is assumed to indicate a 77# fault WITH THE REQUEST. The output is mailed to the 78# user who submitted the print request and the print 79# request is finished. 80# 81# If the interface program sets a zero exit code, 82# it is assumed that the file printed correctly. 83# If the interface program sets a non-zero exit code 84# less than 128, it is assumed that the file did not 85# print correctly, and the user will be notified. 86# In either case the print request is finished. 87# 88# If the interface program sets an exit code greater 89# than 128, it is assumed that the file did not print 90# because of a printer fault. If an alert isn't already 91# active (see below) one will be activated. (Exit code 92# 128 should not be used at all. The shell, which executes 93# this program, turns SIGTERM, used to kill this program 94# for a cancellation or disabling, into exit 128. The 95# Spooler thus interpretes 128 as SIGTERM.) 96# 97# A message sent to the standard input of the ${LPTELL} 98# program is assumed to describe a fault WITH THE PRINTER. 99# The output is used in an alert (if alerts are defined). 100# If the fault recovery is "wait" or "begin", the printer 101# is disabled (killing the interface program if need be), 102# and the print request is left on the queue. 103# If the fault recovery is "continue", the interface program 104# is allowed to wait for the printer fault to be cleared so 105# it can resume printing. 106# 107##### 108 109########################################################################### 110# 111# Set up the basic traps. and other important things 112# 113########################################################################### 114 115##### 116# For the time being, just exit if we are poked. 117##### 118 119# SIGTERM handler 120 121trap 'exit' 15 122 123##### 124# We can be clever about getting a hangup or interrupt, though, at least 125# until the filter runs. Do this early, even though $LPTELL 126# isn't defined, so that we're covered. 127##### 128 129trap 'catch_hangup; exit_code=129 exit 129' 1 130trap 'catch_interrupt; exit_code=129 exit 129' 2 3 131 132##### 133# VARIBLE DECLARED - put here so we don't ever run the trap below w/o 134# TMPPREFIX defined. We hard code /tmp for the moment, but fix that later 135# 136# Use ${TMPPREFIX} as the prefix for all temporary files, so 137# that cleanup is easy. The prefix may be up to 13 characters 138# long, so you only have space for one more character to make 139# a file name. If necessary, make a directory using this prefix 140# for better management of unique temporary file names. 141##### 142 143TMPPREFIX=/tmp/`uname -n`$$ 144 145##### 146# Before exiting, set ${exit_code} to the value with which to exit. 147# Otherwise, the exit from this script will be 0. 148##### 149 150trap 'rm -fr ${TMPPREFIX}*; exit ${exit_code}' 0 151 152catch_hangup () { 153 if [ -n "${LPTELL}" ] 154 then 155 echo \ 156 "Humm, we got a SIGHUP. Not sure what it means, but... we'll keep going anyway" \ 157 | ${LPTELL} "${printer}" 158 fi 159 return 0 160} 161 162catch_interrupt () { 163 if [ -n "${LPTELL}" ] 164 then 165 echo \ 166 "Received an interrupt from the printer. The reason is unknown." \ 167 | ${LPTELL} "${printer}" 168 fi 169 return 0 170} 171 172##### 173# Most of the time we don't want the standard error to be captured 174# by the Spooler, mainly to avoid "Terminated" messages that the 175# shell puts out when we get a SIGTERM. We'll save the standard 176# error channel under another number, so we can use it when it 177# should be captured. 178# 179# Open another channel to the printer port, for use when the 180# regular standard output won't be directed there, such as in 181# command substitution (`cmd`). 182##### 183 184exec 5>&2 2>/dev/null 3>&1 185 186########################################################################### 187# 188# Define local varibles and such 189# 190########################################################################### 191 192##### 193# There is one more varible set by the shell that execs us. 194# FILTER The filter to run ; we ignore this directive 195##### 196 197##### 198# Use the user set env, or else default to standard values. 199##### 200 201: ${SPOOLDIR:=/usr/spool/lp} 202: ${TMPDIR:=/tmp} ; export TMPDIR 203: ${LOCALPATH:=${SPOOLDIR}/bin} ; export LOCALPATH 204 205PATH="/bin:/usr/bin:${LOCALPATH}:${NETATALKHOME}/bin:${NETATALKHOME}/etc" 206export PATH 207 208TMPPREFIX=${TMPDIR}/`uname -n`$$ 209 210##### 211# Error levels for the errmsg() func. 212##### 213 214LP_ERR_LABEL="UX:lp" ; export LP_ERR_LABEL 215 216E_IP_ARGS=1 217E_IP_OPTS=2 218E_IP_UNKNOWN=5 219E_IP_BADFILE=6 220 221##### 222# Error message formatter: 223# 224# Invoke as 225# 226# errmsg severity message-number problem help 227# 228# where severity is "ERROR" or "WARNING", message-number is 229# a unique identifier, problem is a short description of the 230# problem, and help is a short suggestion for fixing the problem. 231##### 232 233errmsg () { 234 case $1 in 235 ERROR ) 236 sev=" ERROR"; 237 ;; 238 WARNING ) 239 sev="WARNING"; 240 ;; 241 esac 242 echo "${LP_ERR_LABEL}: ${sev}: $3 243 TO FIX: $4" >&5 244} 245 246parse () { 247 echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`" 248} 249 250##### 251# die quickly if we do not have the right number of arguments. 252##### 253 254if [ $# -lt 5 ] 255then 256 errmsg ERROR ${E_IP_ARGS} \ 257 "wrong number of arguments to interface program" \ 258 "consult your system administrator" 259 exit 1 260fi 261 262printer=`basename $0` 263request_id=$1 264 265# this will formated be machine!username, so we want to split that up... 266 267user_name=$2 268machine=`echo $user_name | cut -d! -f1` 269user_name=`echo $user_name | cut -d! -f2` 270 271title=$3 272copies=$4 273option_list=$5 274 275shift 5 276files="$*" 277 278nobanner="yes" 279 280inlist= 281 282for i in ${option_list} 283do 284case "${inlist}${i}" in 285 nobanner ) 286 nobanner="yes" ;; 287 banner ) 288 nobanner="no" ;; 289##### 290# 291# If you want to add simple options (e.g. -o simple) 292# identify them here. 293##### 294# simple ) 295# simple="yes" ;; 296 297##### 298# These get ignored, but would matter little anyway since all we see 299# here is PS anyway. 300##### 301 cpi=* ) 302# cpi=`parse ${i}` ;; 303 true ;; 304 lpi=* ) 305# lpi=`parse ${i}` ;; 306 true ;; 307 length=* ) 308# length=`parse ${i}` ;; 309 true ;; 310 width=* ) 311# width=`parse ${i}` ;; 312 true ;; 313 314 ##### 315 # If you want to add simple-value options (e.g. -o value=a) 316 # identify them here. 317 ##### 318 #value=* ) 319 # value=`parse ${i}` ;; 320 321 flist=* ) 322 flist=`parse ${i}` ;; 323 input* ) 324 true ;; 325 * ) 326 errmsg WARNING ${E_IP_OPTS} \ 327 "unrecognized \"-o ${i}\" option" \ 328 "check the option, resubmit if necessary 329 printing continues" ;; 330 331 esac 332done 333 334##### 335# A bit ugly, but grabs the appletalk printer name from the lp system printer 336# description, so it's right up there in admintool. the appletalk name must 337# be delimited by [ and ]. 338# 339# eg - 'this is the printer [hp-mrsec-l114:lasershared@Research Insitutes] that i use.' 340##### 341 342PAPDEST="`lpstat -D -p "${printer}" | grep -i descrip | sed 's/.*Description:.*\[//g' | sed 's/\].*//g'`" 343 344export PAPDEST 345 346########################################################################### 347# 348# Define our local functions (parse is declared above option parsing) 349# 350########################################################################### 351 352banner () { 353 echo "##### User: ${user_name}" 354 echo "" 355 echo "##### Machine: ${user_name}" 356 echo "" 357 358 if [ -n "${title}" ] 359 then 360 echo "##### Title: ${title}" 361 echo "" 362 fi 363 364 echo "##### Files: ${flist}" 365 echo "" 366 367##### 368# this should deal w/ the year 2000 ok. But will die in 2038. =) 369##### 370 371 YEAR=`date '+%y'` 372 YEAR=`expr 1900 + ${YEAR}` 373 374 echo "##### Date: `date '+%a %H:%M %h %d,'` ${YEAR}" 375 echo "" 376 echo "##### Job: ${request_id}" 377 echo "" 378 379} 380 381print_banner() { 382 if [ -x ${NETATALKHOME}/bin/timeout ] 383 then 384 banner | ${NETATALKHOME}/etc/psf \ 385 | ${NETATALKHOME}/bin/timeout "${time}" ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}" 386 else 387 banner | ${NETATALKHOME}/etc/psf \ 388 | ${NETATALKHOME}/bin/pap -c -p "{PAPDEST}" 389 fi 390 391 if [ ${?} -ne 0 ] 392 then 393 filter_timeout 394 fi 395} 396 397##### 398# ${LPTELL} is the name of a program that will send its 399# standard input to the Spooler. It is used to forward 400# the description of a printer fault to the Spooler, 401# which uses it in an alert to the administrator. 402##### 403if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ] 404then 405 fake_lptell(){ 406 header="no" 407 while read line 408 do 409 if [ "no" = "${header}" ] 410 then 411 errmsg ERROR ${E_IP_UNKNOWN} \ 412 "unknown printer/interface failure" \ 413 "consult your system administrator; \ 414 reasons for failure (if any) follow:" 415 header=yes 416 fi 417 echo "${line}" >&2 418 done 419 return 1 420 } 421 LPTELL=fake_lptell 422fi 423 424 425##### 426# timeout catcher for the printing filter 427##### 428 429filter_timeout() { 430 431 cat > ${TMPPREFIX}D <<EOF 432 433The printer ${printer} either timed out at ${time} seconds or pap exited 434abnormally. As well, we may have exceeded ${print_tries} print attempts. 435The job ${request_id} from ${user_name} on ${machine} was 436printing when this happened. 437 438It may be that the only problem is the size of the job and the speed 439of the printer. 440 441Here is what $NETATALKHOME/bin/papstatus reports as the current 442state of the printer: 443 444EOF 445##### 446# We don't need to test for timeout, since we cannot get here w/o it. 447##### 448 449 ${NETATALKHOME}/bin/timeout ${time2} ${NETATALKHOME}/bin/papstatus -p "${PAPDEST}" 2>&1 >> ${TMPPREFIX}D 450 paperr=${?} 451 452 errmsg WARNING ${E_IP_UNKNOWN} "`cat ${TMPPREFIX}D`" "printing continues" 453##### 454# This ought to deal w/ the problem of nonexistent appletalk names, but... 455# for the moment, it calls filter_death. But it sends the papstatus 456# info to LPTELL anyhow, so you should be able to see the error. 457##### 458 459 if [ ${paperr} -ne 0 -o ${too_many} = "1" ]; then 460 paperr= 461 filter_death 462 fi 463 paperr= 464 echo "serverdict begin 0 exitserver systemdict /quit get exec" | \ 465 ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}" 466 return 0 467} 468 469##### 470# Death catcher for filter_timeout 471##### 472 473filter_death() { 474 475 cat > ${TMPPREFIX}Z <<EOF 476Excessive delays w/ the printer ${printer}! 477 478While processing on printer ${printer} the job ${request_id} 479from ${user_name} on ${machine} timed out at ${time} seconds. 480 481Then while cleaning that timeout, the cleanup operation failed as 482well. 483 484EOF 485 errmsg ERROR ${E_IP_UNKNOWN} "`cat ${TMPPREFIX}Z`" 486 exit 129 487##### 488# Exit and fault the printer. 489##### 490} 491 492##### 493# Print the job 494##### 495printfile() { 496 trap '' 1 # Let the filter handle a hangup 497 trap '' 2 3 # and interrupts 498##### 499# We use timeout so as to not hang the print queue indefinately. (pap does not 500# timeout on it's own.) 501# 502# Put the 0<${files} before the "eval" to keep clever users from giving 503# a file name that evaluates as something to execute. 504##### 505 if [ "${TERM}" == "Netatalk-R" ]; then 506 if [ -x ${NETATALKHOME}/bin/timeout ]; then 507 0<${file} /usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/bin/timeout ${time} ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}" 508 else 509 0<${file} /usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}" 510 fi 511 else 512 if [ -x ${NETATALKHOME}/bin/timeout ]; then 513 0<${file} ${NETATALKHOME}/bin/timeout ${time} ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}" 514 else 515 0<${file} ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}" 516 fi 517 fi 518 paperr=${?} 519 print_tries=`expr "${print_tries}" + 1` 520 if [ "${paperr}" != "0" -a "${print_tries}" -gt "${attempts}" ]; then 521 too_many=1 522 fi 523 trap 'catch_hangup; exit_code=129 exit 129' 1 524 trap 'catch_interrupt; exit_code=129 exit 129' 2 3 525 return ${paperr} 526} 527 528##### 529# Some basic sanity checking: 530##### 531 532if [ ! -x $NETATALKHOME/bin/pap ] 533then 534 echo "Opps, cannot find $NETATALKHOME/bin/pap, so I don't know how to" 535 echo "print things" 536 # exit w/ less than 128 to mark an error w/ the job, and call it done 537 exit 1 538fi 539 540########################################################################### 541# 542# Start the main section of the program. 543# 544########################################################################### 545 546##### 547# Here i should have a "job canceled" page ready in a trap in case of getting killed 548# but, alas, that would most likely muck up the PS. So we just drop the job on the 549# floor. 550##### 551 552##### 553# If you want a custom banner, change the code up in the functions section. 554##### 555 556if [ "no" = "${nobanner}" -a "${TERM}" != "Netatalk-R" ] 557then 558 print_banner 559fi 560 561##### 562# Print some copies of the file(s) 563##### 564 565badfileyet= 566i=1 567while [ $i -le $copies ] 568do 569 for file in ${files} 570 do 571 if [ -r "${file}" ] 572 then 573 print_tries=0 574 until printfile 575 do 576 filter_timeout; 577 done 578 else 579##### 580# Don't complain about not being able to read a file on second and 581# subsequent copies, unless we've not complained yet. This removes 582# repeated messages about the same file yet reduces the chance that the 583# user can remove a file and not know that we had trouble finding it. 584##### 585 if [ "${i}" -le 1 -o -z "${badfileyet}" ] 586 then 587 errmsg WARNING ${E_IP_BADFILE} \ 588 "cannot read file \"${file}\" " \ 589 "see if the file still exists and is readable by the user\ 590 lp (or world), or consult your system administrator; \ 591 We will keep trying to print the other files or copies" 592 badfileyet=yes 593 fi 594 fi 595 done 596 i=`expr $i + 1` 597done 598 599##### 600# print the banner page if we are a reversed queue 601##### 602if [ "no" = "${nobanner}" -a "${TERM}" == "Netatalk-R" ] 603then 604 print_banner 605fi 606 607echo "serverdict begin 0 exitserver systemdict /quit get exec" | \ 608 ${NETATALKHOME}/bin/pap -c -p "${PAPDEST}" 609 610Exit_code=0 exit 0 611