home *** CD-ROM | disk | FTP | other *** search
- Subject: v16i025: Public lineprinter spooler package, Part12/14
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: papowell@julius.cs.umn.edu
- Posting-number: Volume 16, Issue 25
- Archive-name: plp/part12
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 12 (of 16)."
- # Contents: doc/PLP/04.t src/lpd.c src/printcap.c
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'doc/PLP/04.t' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'doc/PLP/04.t'\"
- else
- echo shar: Extracting \"'doc/PLP/04.t'\" \(16280 characters\)
- sed "s/^X//" >'doc/PLP/04.t' <<'END_OF_FILE'
- X.ig
- X$Header: 04.t,v 1.1 88/05/21 18:39:37 papowell Locked $
- X$log$
- X..
- X.NH 1
- Printcap and PLP Operations
- X.PP
- The entries in the printcap file are used to define and control
- the actions of the PLP software.
- XEach host machine must have a printcap file that is used to specify
- the spool queues that are available on the host.
- Permissions to use a particular spool queue are determined by the
- printer permissions file.
- The following chapter discusses the structure of the printcap file,
- and the purpose of entries in the printcap file.
- X.NH 2
- Printcap Format
- X.PP
- The printcap file format is a simplified form of the
- X.IR termcap (5)
- data base.
- XEach spool queue has a corresponding printcap entry.
- XEach entry for a spool queue with multiple servers also has a printcap entry.
- XFigure 4.1 is an example of a typical printcap entry.
- X.KF
- X.DT
- X.in +1i
- X.L
- X.SM
- X.nf
- X# Default line printer is first in printcap entry
- X# Clunker Printer in Lind 23
- clunker_lind23|clunker:\e
- X :lp=/dev/lp:\e
- X :st=\et\er\e015:\e
- X :mx#20:\e
- X :wt:fl@:
- X.LG
- X.in -1i
- X.R
- X.ce
- XFigure 4.1 Sample Printcap Entry
- X.KE
- X.IP 1). 3
- Blank lines and lines which start with a
- X.L #
- are treated as comments and can be used to separate entries.
- X.IP 2). 3
- XEach entry starts in column 1,
- and is continued across multiple lines by escaping the end of line with a
- backslash (\c
- X.BR \e ).
- XFields of the entry are separated by colons (\c
- X.BR : ).
- X.IP 3). 3
- The first field of an entry is the entry name followed by a
- an optional list of aliases.
- The entry name and aliases are separated by a bars (\c
- X.BR | ).
- X.IP 4). 3
- Parameter fields start with a two letter parameter name,
- followed by a flag indicating the parameter type.
- Parameters can be strings,
- integers,
- or flags.
- String parameters have the form
- X.L xx=ssss
- X(\c
- X.L ssss
- is the string assigned to the parameter),
- integer parameters have the form
- X.L xx#nnnn ,
- and flag or boolean parameters use
- X.L xx
- to set
- X.L xx@
- to clear the flag.
- X.IP 5). 3
- Non-printing characters can be represented in a string by using
- the character escape sequences of the C programming language.
- X.IP 6). 3
- See the
- X.IR printcap \|(5)
- man page for a complete list of printcap variables and their default values.
- X.PP
- XEach entry in the printcap file specifies either a remote spool queue,
- a local spool queue with a single printer,
- a local spool queue with multiple printers,
- or a printer for a spool queue with multiple printers.
- The information in the printcap entry determines the type of entry and
- how the entries in the spool queue are to be handled.
- X.PP
- The PLP software searches the printcap file for a printcap entry with
- a matching printer name or alias.
- By convention,
- if a user does not specify a printer,
- either by using a command line option or setting the
- X.B PRINTER
- environment variable,
- the first printer in the printcap file is used as the default printer.
- X.NH 2
- Spool Queue Directory
- X.PP
- The
- X.B sd
- X(spool directory) field specifies the path name of the directory
- which will hold spool queue entries or jobs.
- The
- X.B sd
- parameter must be present for all printcap entries which are
- used for spooling.
- XFigure 4.2 is a typical printcap entry for
- a remote printer spool queue,
- whose spool directory is specified as
- X.L /usr/spool/lind33_decwriter .
- X.KF
- X.nf
- X.in +1i
- X.DT
- X.L
- X.SM
- X# Decwriter III in Lind 33
- X# Modified 11 Feb. 1988 Patrick Powell - added fx=n for ditroff
- X# queue printer permissions file
- X# maximum job size 2000K bytes, no copies
- remote\||\|lind33_decwriter:\e
- X :fx=ilpn:\e
- X :sd=/usr/spool/lind33_decwriter:\e
- X :rm=umn-cs.cs.umn.edu:rp=lind33_decrwriter:\e
- X :xu=perms:\e
- X :ex=n:ne=/usr/local/lib/ddumb:\e
- X :mx#2000:sc:
- X.LG
- X.R
- X.in -1i
- X.ce
- XFigure 4.2 Remote Printer Printcap Entry
- X.KE
- X.NH 2
- Job Submission Controls
- X.PP
- The following fields are used to control job submission.
- X.NH 3
- Job Formats
- X.PP
- XEach job in a spool queue has an associated format,
- which is represented by a lower case letter\**.
- X.FS
- Actually,
- each file in the job can have a different format,
- but the current version of
- X.I lpr
- does not support this.
- X.FE
- There are several
- X.I lpr
- options such as
- X.B -n,
- X.B -d,
- etc. which can be used to specify a limited set of formats,
- or the
- X.B \-Fx
- option can be used to specify any format.
- XFor example,
- the
- X.B \-Fn
- options specifies printing using the
- X.B n
- format,
- as below.
- X.ti +.5i
- X.L "ditroff -Tdumb -ms myfile | lpr -Fn -Premote"
- X.PP
- The format supported by a spool queue can be restricted by the
- X.B fx
- printcap field,
- which specifies the formats supported by the spool queue.
- In Figure 4.2,
- the
- X.B fx=ilpn
- field restricts formats to
- X.L i,
- X.L l,
- X.L p,
- and
- X.L n .
- Some formats require that files be printable
- X(i.e.- contain characters in the ASCII characters set,
- or other restrictions).
- By default,
- only the
- X.L f
- and
- X.L p
- formats are checked by
- X.I lpr
- for printable files.
- The
- X.B xt
- field is used to specify additional formats to be checked for printable files.
- X.NH 3
- Permissions Files
- X.PP
- XEach host has a
- printer permissions file which is checked by the
- PLP software to determine if actions requested by users or other hosts
- are permitted.
- In addition,
- the
- X.B xu
- X(check user) printcap
- field is used to specify an additional printer permission
- file which is checked for jobs in the spool queue.
- XFor example,
- the
- X.B xu=perms
- field causes the
- X.L perms
- file in the spool directory to be checked by
- X.I lpr
- when the job is submitted.
- Similarly,
- the unspooling server process will also check this file
- when the job is finally printed.
- X.NH 3
- Prefilters
- X.PP
- It may be desirable to process or filter a job at the time that it is
- spooled.
- The program used to filter a job is specified by a field of the form
- X.BR X e=filtername ,
- or
- X.BR X e=filtername,y ,
- where
- X.L X
- is the original format,
- filtername is the pathname of a program,
- and
- X.L y
- is the result format.
- The default result format is
- X.L f .
- In addition,
- the
- X.I lpr
- X.B -p
- option specifies filtering with the
- X.I pr
- program;
- X.I lpr
- invokes the program specified by the
- X.BR pr= filtername
- X(default is
- X.L /bin/pr ).
- X.NH 3
- Job Size and Number of Copies
- X.PP
- The
- X.B mx
- X(maximum size)
- field is used to specify a maximum file size (in 1 Kbyte blocks)
- that can be spooled by
- X.I lpr.
- Jobs whose total size (including duplicates) exceeds this limit are
- not spooled.
- XFor example,
- the
- X.B mx#2000
- entry restricts jobs to a maximum of 2000 Kbytes.
- The
- X.B mc
- X(maximum copies) value specifies the maximum number of copies allowed
- and the
- X.B sc
- X(suppress copies) flag which suppresses multiple copies.
- X.NH 2
- Remote Printer Spool Queues
- X.PP
- Remote spool queues specify the remote host using the
- X.B rm
- field and the remote printer using the
- X.B rp
- field.
- The remote host addressing information is obtained using the
- X.IR gethostbynam (3R)
- facility.
- The remote printer must appear in the remote host's
- printcap data base.
- The following is an example of an entry for a remote spool queue
- that will be sent to host
- X.L umn-cs.cs.umn.edu ,
- and printed on the
- X.L lind33_decwriter
- printer.
- X.NH 2
- Local Printer Queues
- X.PP
- Unspooling from a local printer queue can be done in two manners:
- using the unspooling cababilies of the PLP server created for the
- queue by the lpd,
- or by having the server process invoke a special
- X.I "queue handler"
- process.
- The latter method is discussed later in this section.
- X.PP
- Associated with each local queue is an output device.
- These can be categorized as serial line
- or a specialized device with a non-serial interface.
- If a device is attached to a serial,
- the printcap can be used to specify how the serial line
- is to be conditioned in order to communicate with the printer.
- XFigure 4.3 is a sample printcap entry for a DecWriter III printer connected
- over a 1200 baud serial line.
- X.KF
- X.DT
- X.in +1i
- X.L
- X.nf
- X.SM
- X# Local Printer Entry
- X# Decwriter III in Lind 33
- X# Modified 11 Feb. 1988 Patrick Powell - added fx=n for ditroff, max 2 retries
- lind33_decwriter\||\|clunker\||\|Decwriter Model 3:\e
- X :fx=ilpn:\e
- X :sd=/usr/spool/lind33_decwriter:\e
- X :xu=/usr/adm/perms/lind33_decwriter:\e
- X :rt=2:\e
- X :ne=/usr/local/lib/ddumb:\e
- X :lp=/dev/ttyh8:rw:\e
- X :br#1200:fs#06320:ty=-nl odd even -tabs tandem new:\e
- X :lf=error:
- X.LG
- X.in -1i
- X.R
- X.ce
- XFigure 4.3 Printcap Entry for Serial Line Device
- X.LG
- X.KE
- X.NH 3
- Output Device Pathname
- X.PP
- The
- X.B lp
- X(line printer) parameter specifies a path name of the device that is
- to be opened and output sent to.
- The device is opened for writing only (default),
- or reading and writing if the
- X.B rw
- flag is present.
- Note that there is no default value for the
- X.B lp
- field.
- If there is no physical device corresponding to the printer,
- then
- X.L "lp=/dev/null"
- should be used to specify a null device.
- X.NH 3
- Retry Limits
- X.PP
- The server will attempt to unspool and print a job for a limited number
- of times.
- The number of attempts is set by the
- X.B rt
- X(retry) parameter;
- the default value is 3,
- and a \-1 value specifies unlimited retries.
- X.NH 3
- Serial Line Characteristics
- X.PP
- Printers connected via a serial communication line
- must have the proper baud rate and line characteristics set.
- X.IP "Setting Bit (Baud) Rates" 3
- The
- X.B br
- parameter sets the bit (baud) rate for the tty line.
- XFor example,
- X300 means 300 BPI,
- X9600 means 9600 BPI.
- X.IP "Setting Line Characteristics" 3
- The
- X.B fs
- X(set bits) and
- X.B fc
- X(clear bits) parameter is used to set the serial line characteristics with the
- TIOCSETP
- X.IR ioctl \|(2)
- operation.
- The
- X.B fs
- and
- X.B fc
- set and clear bits in the line control status word returned by the
- TIOGSETP ioctl operation,
- which is then restored with the ioctl TIOCSETP.
- In the above example,
- X.B fs
- sets CRMOD,
- no parity,
- and XTABS (see
- X.IR tty \|(4)
- for details).
- The
- X.B xs
- and
- X.B xc
- are be used to set and clear local control information
- using the TIOCCLGET and TIOCCLSET ioctl calls.
- X.IP "Alternate Method" 3
- In case you find the
- X.B fs ,
- X.B fc ,
- etc.,
- method of setting line characteristics embarassingly user-hostile,
- you can use the
- X.B ty
- X(stty) parameter,
- which uses (almost) the same keywords and options as the
- X.I stty (1)
- command.
- XFor example,
- X.L "ty=nl -odd -even -tabs"
- can be used instead of the above
- X.B fs
- values.
- If you need tandem line control you
- may have to open the output device for reading and writing using the
- X.B rw
- field;
- this appears to be implementation dependent.
- The following
- X.I stty
- options are recognized by the
- X.L ty
- field and have the same meaning as the
- X.I stty
- command as supplied for 4.3 UNIX,
- with some local modifications added from SUN Microsystems UNIX.
- X.DS
- X.DT
- X.nf
- X.L
- X.SM
- X.ta 16n +16n +16n +16n +16n +16n +16n +16n +16n 8i
- bs0 bs1 [-]cbreak cooked cr0
- cr1 cr2 cr3 [-]decctlq [-]echo
- X[-]even ff0 ff1 [-]lcase [-]litout
- nl0 nl1 nl2 nl3 [-]nl
- X[-]noflsh new [-]nohang old [-]odd
- X[-]raw start stop tab0 tab1
- tab2 [-]tabs [-]tandem tek ti700
- X[-]tilde tn300 tty33 tty37 vt05
- X.DE
- X.NH 2
- Job, Log, Lock, and Status Files
- X.PP
- The pathnames for all files associated with a spool queue are
- relative to the spool queue directory or are absolute paths.
- The following sections discuss the various files that are used
- for logging and status reporting.
- X.NH 3
- Job Files
- X.PP
- A job consists of a control file and a set of data files.
- Lines in the control file provide information about the user,
- the originating hosts,
- and the names of the data files,
- and the data file unspooling formats.
- A job control file name has the form:
- X.ti +5n
- X.B cf
- X.L "[Priority \- A-Z]"
- X.L "[Job Number \- 000-999]"
- X.L "[Host Name]"
- X.br
- X.ti +5n
- X.nf
- XExample: cfA002attila.cs.umn.edu
- X.fi
- X.PP
- The first two letters identify the file as a control file,
- the next letter is the job's priority level,
- the next 3 digits are the job number,
- and the trailing characters are the host name.
- X.PP
- The priority level is set by the LPR process at the time that the job is
- submitted;
- the
- X.I lpc
- program can be used to upgrade a job's priority.
- The sequence number is obtained from a sequence file,
- which is locked when in use by
- X.I lpr
- in order to avoid duplicate sequence file names.
- XEach host has a unique sequence file,
- and the combination of host and sequence number should be unique.
- This is true as long as there are less than 1000 jobs outstanding in
- a queue.
- The host name currently used in the PLP software is currently set to be the
- network host name,
- as returned by
- X.IR gethostnam (3n).
- X.PP
- The data files associated with a job have files names with a similar form,
- as follows:
- X.ti +5n
- X.B df
- X.I [Sequence]
- X.I [Job Number]
- X.I [Host]
- X.br
- X.ti +5n
- X.nf
- XExample: dfA002attila.cs.umn.edu, dfB002attila.cs.umn.edu
- X.fi
- X.PP
- The PLP software checks to ensure that control file and data file
- names have the same corresponding form.
- X.NH 3
- Control File
- X.PP
- A control file contains the information neccessary to print a job.
- XFigure 4.4 is a typical control file for a job.
- X.KF
- X.DT
- X.L
- X.SM
- X.nf
- X.in +1i
- Hattila.cs.umn.edu
- LPatrick Powell,L136
- Ppapowell
- N(stdin)
- J(stdin)
- CZ
- fdfA001attila
- UdfA001attila
- X.in -1i
- X.LG
- X.R
- X.ce
- XFigure 4.4. Job Control File Example
- X.KE
- X.PP
- The first character on each line is a flag character
- used to decode the remainder of the line.
- Upper case flags provide information or pass options for the
- printing actions.
- Lower case characters are format indicators for specified data files.
- Note that there is a maximum limit on the size of each line of a
- control file.
- Table 4.1 lists the various control flags that are used
- in the control file.
- X.KS
- X.L
- X.SM
- X.TS
- tab(:) box center;
- l |l |l.
- XFlag:Parameter:Meaning
- X_
- C:class name:banner page
- H:host name:originating host
- I:indent:amount to indent output
- J:job name:banner page
- L:user name:name to print on banner
- M:mail:mail to user when done printing
- N:name:name of file in job
- P:Person:user's login name
- S:flag:no header request
- R:account:accounting information
- U:file name:remove file after we print it
- W:width:page width for PR
- XX:header:header title for PR
- Z:extra options:options for filters from lpr
- f:file name:ordinary file
- l:file name:text file with control chars
- p:file name:text file to print with pr(1)
- t:file name:troff(1) file
- n:file name:ditroff(1) file
- d:file name:dvi file
- g:file name:plot(1G) file
- v:file name:plain raster file
- c:file name:cifplot file
- X.TE
- X.LG
- X.R
- X.ce
- Table 4.1. Control File Flags
- X.KE
- X.NH 3
- Log File
- X.PP
- The spool queue server process writes error and informational messages to
- the server log file,
- which is specified by the
- X.B lf
- field;
- the default log file is
- X.IR log .
- If the log file does not exist,
- then a dummy log file (/dev/null) is opened for logging purposes.
- Serious error message are written both to the log file and
- logged using the
- X.IR syslog (8)
- facility if it is available,
- or to
- X.L /dev/console
- if it is not.
- X.NH 3
- Lock File
- X.PP
- The spool queue lock file
- is used to control spooling and unspooling operations.
- The lock file name is specified using the
- X.B lo
- X(lock file)
- field,
- and the default lock file name is
- X.IR lock .
- X.NH 3
- Status Files
- X.PP
- The server status file
- X(default file is
- X.IR status )
- contains status information reflecting the operations of the unspooling
- server processes.
- The
- X.B st
- X(status) field can be used to specify an explicity name for the status file.
- An additional printer status file can be specified with the
- X.B ps
- field.
- This file can be used by filter programs that generated additional status
- information that should be available for display.
- X.NH 2
- Multiple Servers
- X.PP
- If a spool queue has multiple printers or servers,
- the server names are specified by the
- X.B sv
- X(servers) parameter,
- which contains a comma separated list of server names.
- XEach server name has a printcap entry,
- and each server entry has a corresponding
- X.B ss
- X(serves) field
- which contains the name of the spool queue served by the printer.
- XEach printer will use the spool queue directory specified by the
- X.B sd
- field of the spool queue that it serves.
- Note that this name corresponds to the name of the queue,
- not the spool directory.
- X.KF
- X.in +1i
- X.DT
- X.L
- X.SM
- X.nf
- X# Multiple servers
- fast:\e
- X :sd=/usr/spool/fast:\e
- X :sv=lp1,lp2:
- lp1:\e
- X :ss=fast:\e
- X :lo=lp1:lf=log1:st=st1:lp=/dev/lp1:
- lp2:\e
- X :ss=fast:\e
- X :lo=lp2:lf=log2:st=st2:lp=/dev/lp2:
- X.LG
- X.R
- X.in -1i
- X.ce
- XFigure 4.5 Multiple Server Printcap Entry
- X.KE
- X.PP
- In the example in Figure 4.5,
- the
- X.L lp1
- and
- X.L lp2
- printers are used to serve the
- X.L fast
- spool queue.
- Note that each of them explicitly specified the name of their lock and
- log files;
- as they will use the same queue directory the names of the lock,
- log,
- and status files must be different.
- END_OF_FILE
- if test 16280 -ne `wc -c <'doc/PLP/04.t'`; then
- echo shar: \"'doc/PLP/04.t'\" unpacked with wrong size!
- fi
- # end of 'doc/PLP/04.t'
- fi
- if test -f 'src/lpd.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/lpd.c'\"
- else
- echo shar: Extracting \"'src/lpd.c'\" \(16723 characters\)
- sed "s/^X//" >'src/lpd.c' <<'END_OF_FILE'
- X/***************************************************************************
- X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
- X ***************************************************************************
- X * MODULE: lpd.c
- X * lpd main program
- X ***************************************************************************
- X * Revision History: Created Fri Jan 1 16:19:30 CST 1988
- X * $Log: lpd.c,v $
- X * Revision 3.1 88/07/25 19:51:03 papowell
- X * Distribution version
- X *
- X *
- X * Revision 3.1 88/06/18 09:34:40 papowell
- X * Version 3.0- Distributed Sat Jun 18 1988
- X *
- X * Revision 2.3 88/05/19 10:34:08 papowell
- X * Fixed open() calls to have a 0 parameter, ie: open(f, perms, 0), where needed
- X *
- X * Revision 2.2 88/05/14 10:18:06 papowell
- X * Use long format for job file names;
- X * Added 'fd', no forward flag;
- X * Control file has to have hostname and origination agree.
- X *
- X * Revision 2.1 88/05/09 10:08:46 papowell
- X * PLP: Released Version
- X *
- X * Revision 1.9 88/04/27 20:26:17 papowell
- X * Modified to remove unused variables
- X *
- X * Revision 1.8 88/04/26 15:51:31 papowell
- X * Added a Reapchild() call in cleanup and each time a server is started
- X * This should gather up the orphans that are left in some UNIX implementations
- X *
- X * Revision 1.7 88/04/15 13:06:17 papowell
- X * Std_environ() call added, to ensure that fd 0 (stdin), 1 (stdout), 2(stderr)
- X * have valid file descriptors; if not open, then /dev/null is used.
- X *
- X * Revision 1.6 88/04/07 12:31:11 papowell
- X *
- X * Revision 1.5 88/04/06 12:12:53 papowell
- X * Minor updates, changes in error message formats.
- X * Elimination of the AF_UNIX connections, use AF_INET only.
- X * Better error messages.
- X *
- X * Revision 1.4 88/03/25 15:00:06 papowell
- X * Debugged Version:
- X * 1. Added the PLP control file first transfer
- X * 2. Checks for MX during file transfers
- X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
- X * apparently they open files and then assume that they will stay
- X * open.
- X * 4. Made sure that stdin, stdout, stderr was available at all times.
- X *
- X * Revision 1.3 88/03/11 19:28:58 papowell
- X * Minor Changes, Updates
- X *
- X * Revision 1.2 88/03/05 15:01:20 papowell
- X * Minor Corrections, Lint Problems
- X *
- X * Revision 1.1 88/03/01 11:08:37 papowell
- X * Initial revision
- X *
- X ***************************************************************************/
- X#ifndef lint
- static char id_str1[] =
- X "$Header: lpd.c,v 3.1 88/07/25 19:51:03 papowell Locked $ PLP Copyright 1988 Patrick Powell";
- X#endif lint
- X/***************************************************************************
- X * lpd -- line Printer daemon.
- X * 1. Get options
- X * 2. Check for existence of another daemon and exit if it exists.
- X * Note: this is done by locking a file
- X * 3. Initialize error logging
- X * 4. Start up all the queue handlers
- X * 5. Set up communication sockets
- X * 6. Loop forever doing:
- X * Listen for a connection and perform the requested operation.
- X * Operations are:
- X * \1Printer\n
- X * check the queue for jobs and print any found.
- X * \2Printer\n
- X * receive a job from another machine and queue it.
- X * \3Printer [users ...] [jobs ...]\n
- X * return the current state of the queue (short form).
- X * \4Printer [users ...] [jobs ...]\n
- X * return the current state of the queue (long form).
- X * \5Printer Person [users ...] [jobs ...]\n
- X * remove jobs from the queue.
- X * \6Printer Person operation
- X * enable/disable queueing, etc.
- X * Note: a process is forked for each operation
- X *
- X * Strategy to maintain protected spooling area:
- X * 1. Spooling area is writable only by daemon and daemon group
- X * 2. lpr runs setuid ROOT; it uses the user UID
- X * to access any file it wants (verifying things before
- X * with access(2)) and sets ownership of files in the spooling
- X * directory to be owner DAEMON, group DAEMON.
- X * 3. Critical files in spooling area are owned by DAEMON, group DAEMON
- X * with mode 660.
- X * 4. lpd, lpq and lprm run setuid ROOT, and change group to DAEMON
- X * Users can't get to anything w/o help of lpq and lprm programs.
- X * 5. LPD forks a server process to service each device
- X * in the printcap entry. These processes run setuid root
- X * and setgrp to their PID. They open neccessary files, and
- X * fork "filter" processes to process the output. Filter
- X * processes run UID DAEMON.
- X * 6. The server process group is set to the main server PID. This is used
- X * by the LPRM program, which signals them with killpg(2)
- X *
- X * Error reporting:
- X * lpd will open a standard logging file. If not already present,
- X * no messages will be logged. Critial messages will also be logged
- X * using syslog(8).
- X * The individual daemon processes will open log files also. If
- X * they cannot be opened, then no logging will be done for the process.
- X ***************************************************************************/
- X
- X#include "lp.h"
- X
- int cleanup(); /* file system scullery maid, cleans up files */
- static int lpdpid; /* lpd process id */
- X
- main(argc, argv)
- X int argc;
- X char **argv;
- X{
- X int f; /* Acme Integer, Inc. strikes again */
- X int finet; /* Internet socket */
- X int defreadfds, readfds; /* select()ed connections */
- X int req; /* request on this socket */
- X int port_num; /* used to get the inet server information */
- X struct sockaddr_in sock_in, frominet; /* networking addresses */
- X int fromlen;
- X int nfds; /* accept return value */
- X
- X /*
- X * explicitly set umask
- X */
- X (void)umask(0);
- X
- X /*
- X * Set fd 0, 1, 2 to /dev/null if not open. This protects against
- X * being started in strange environments.
- X */
- X Std_environ();
- X /*
- X * we set up the alternate version if XPERIMENT is defined
- X */
- X# ifdef XPERIMENT
- X Setup_test();
- X# endif XPERIMENT
- X
- X /*
- X * process command line options
- X */
- X Getoptions(argc, argv);
- X
- X /*
- X * set up the pathnames for information files
- X */
- X Tailor_names();
- X /*
- X * check to see that the is not a spooler present. The LO is used
- X * when we exit to clean out the lock file information.
- X * Note that we check first, as the initial process, and then we fork
- X * another process. This allows the output message to be printed
- X * on the stdout device in a synchronous manner.
- X */
- X LO = Masterlock;
- X if( (Lfd = Getlockfile(LO, &f, (char *)0, 0, (struct stat *)0)) == NULL ){
- X (void)fprintf( stdout, "active LPD %d\n", f );
- X exit(0);
- X }
- X (void)fclose(Lfd);
- X /* We can let parent die */
- X if ((f = fork()) < 0 ){
- X logerr_die( XLOG_INFO, "lpd: fork failed" );
- X } else if( f ){
- X exit(0);
- X }
- X /*
- X * We can now start logging
- X */
- X Setuplog(Lpdlogf, 0 );
- X /*
- X * Note that we do the locking at this point, as the lock may not
- X * continue across a fork.
- X */
- X if((Lfd=Getlockfile(LO,&f,(char *)0, 0, (struct stat *)0)) == NULL){
- X log(XLOG_INFO, "active LPD %d\n", f );
- X exit(0);
- X }
- X /*
- X * save the PID of the LPD process; it will be useful later
- X */
- X lpdpid = getpid();
- X /*
- X * drop the control tty. This prevents the user which started
- X * LPD from inadvertenly generation a signal and clobbering LPD
- X */
- X if( (f = open("/dev/tty", O_RDWR, 0) ) >= 0) {
- X (void)ioctl(f, TIOCNOTTY, (struct sgttyb *)0);
- X (void)close(f);
- X }
- X /* set pgrp to pid, so we can kill our kids later */
- X (void)setpgrp(0, lpdpid);
- X
- X /* put PID and time started in the lockfile */
- X Setlockfile(LO, Lfd, lpdpid,Time_str());
- X
- X /*
- X * set up signals; note that SIGPIPE may not be needed, but it
- X * never hurts to make sure.
- X */
- X (void)signal(SIGCHLD, Reapchild);
- X (void)signal(SIGPIPE, SIG_IGN);
- X (void)signal(SIGHUP, cleanup);
- X (void)signal(SIGINT, cleanup);
- X (void)signal(SIGQUIT, cleanup);
- X (void)signal(SIGTERM, cleanup);
- X /*
- X * Restart all the Printers.
- X */
- X Startup();
- X
- X /*
- X * Set up the IPC. This is very ugly, and is stolen directly from
- X * the original LPD code and the 4.3 BSD Interprocess Communication
- X * Tutorial, which seems to have borrowed it from RLOGIN, etc.
- X *
- X * Set up INET socket
- X */
- X if( ( finet = socket(AF_INET, SOCK_STREAM, 0) ) < 0 ){
- X logerr_die( XLOG_CRIT,"lpd: cannot create AF_INET socket");
- X }
- X port_num = Link_port_num();
- X /*
- X * zero the sockaddr structure before using it
- X */
- X bzero( (char *)&sock_in, sizeof(sock_in) );
- X /*
- X * the INADDR_ANY will allow different nets to be used
- X */
- X sock_in.sin_family = AF_INET;
- X sock_in.sin_addr.s_addr = INADDR_ANY;
- X sock_in.sin_port = port_num;
- X if (bind(finet, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0) {
- X logerr_die( XLOG_CRIT,"lpd: bind failed internet domain");
- X }
- X if( listen(finet, 5) < 0 ){
- X logerr_die( XLOG_CRIT, "lpd: listen failed internet domain" );
- X }
- X if(Debug>2)log( XLOG_DEBUG, "lpd: AF_INET socket %d", finet );
- X
- X /*
- X * Set up the sockets on which we will do an accept
- X */
- X defreadfds = (1 << finet);
- X
- X /*
- X * Main loop: accept, do a request, continue.
- X */
- X for (;;) {
- X /*
- X * Defensive Coding a la Brain Damaged Unix Implementations
- X * A particular UNIX implementation will not cause SIGCHLD
- X * signals in a particularly reliable way. Apparently if there
- X * is a SIGCHLD while it is masked off (error printing does this)
- X * IT IS IGNORED!!! So we put a Reapchild() here; at least
- X * we will gather them up each time we start a server
- X */
- X Reapchild();
- X if(Debug>2)log( XLOG_DEBUG,"lpd: starting select");
- X
- X readfds = defreadfds;
- X nfds = select(20, (fd_set *)&readfds, (fd_set *)0, (fd_set *)0,
- X (struct timeval *)0); /* wait patiently */
- X if (nfds <= 0) {
- X if (nfds < 0 && errno != EINTR) {
- X logerr_die( XLOG_CRIT,"lpd: select error");
- X }
- X continue;
- X }
- X /*
- X * incredible, but true: could have NO requests
- X */
- X if( readfds ){
- X if(Debug>2)log( XLOG_DEBUG,"lpd: doing select 0x%x", readfds );
- X /* get the socket causing the select */
- X fromlen = sizeof(frominet);
- X req = accept(finet, (struct sockaddr *)&frominet, &fromlen);
- X if (req < 0) {
- X logerr( XLOG_CRIT,"lpd: finet accept");
- X continue;
- X }
- X if (!From_host(&frominet)){
- X logerr(XLOG_INFO,"lpd: From_host failed");
- X continue;
- X }
- X /*
- X * Fork process to handle activity requested
- X */
- X (void) fflush(stdout);
- X (void) fflush(stderr);
- X if ((f = fork()) == 0) { /* daughter */
- X (void) fclose(Lfd);
- X Lfd = NULL;
- X if( dup2(req, 1) < 0 ){
- X logerr_die( XLOG_CRIT, "lpd: dup2 for server failed" );
- X }
- X (void) close(req);
- X (void) close(finet);
- X servicereq();
- X exit(0);
- X } else if( f < 0 ){
- X logerr( XLOG_CRIT, "lpd: fork failed" );
- X }
- X (void) close(req);
- X }
- X }
- X}
- X
- X/***************************************************************************
- X * Getoptions(argv, argc)
- X * char **argv; int argc;
- X * Purpose:
- X * extracts options and arguments from command line using Getopt(2)
- X * Side Effects:
- X * -X : calls setup_test() to modify defaults
- X * -D nn : sets Debug level
- X * -L file : sets log file
- X ***************************************************************************/
- Getoptions(argc, argv)
- X int argc;
- X char **argv;
- X{
- X int c;
- X while ((c = Getopt(argc, argv, "XD:L:")) != EOF){
- X switch(c){
- X case 'X': /* test version */
- X# ifdef DEBUG
- X Setup_test();
- X Tailor_names();
- X# else
- X (void)fprintf( stderr, "%s: -X not allowed\n", Name );
- X exit(1);
- X# endif DEBUG
- X break;
- X case 'D': /* turn on Debugging */
- X if( Optarg == NULL ){
- X exit(1);
- X }
- X Debug= atoi( Optarg );
- X break;
- X case 'L':
- X if( Optarg == NULL ){
- X exit(1);
- X }
- X (void)strcpy(Lpdlogf, Optarg);
- X break;
- X default:
- X exit(1);
- X }
- X }
- X if( Optind < argc ){
- X (void)fprintf( stderr, "%s: extra argument %s\n", Name, argv[Optind] );
- X exit(1);
- X }
- X}
- X/***************************************************************************
- X * Setuplog( char *logfile, int saveme )
- X * Purpose: to set up a standard error logging environment
- X * saveme will prevent stdin from being clobbered
- X * 1. open /dev/null on fd 0;
- X * 2. if saveme not 1, dup 0 to fd 1
- X * 3. If logfile is "-" or NULL, output file is alread opened
- X * 4. Open logfile; if unable to, then open /dev/null for output
- X ***************************************************************************/
- Setuplog( logfile, saveme )
- X int saveme;
- X char *logfile;
- X{
- X int fd;
- X
- X /*
- X * we want 0 (stdin) to be /dev/null
- X */
- X (void)fflush(stdout);
- X (void)fflush(stderr);
- X (void)close(0);
- X if( (fd = open( "/dev/null", O_RDWR, 0 )) != 0 ){
- X logerr_die( XLOG_CRIT, "Setuplog: /dev/null opened as %d", fd);
- X }
- X /*
- X * do the dup if necessary
- X */
- X if( saveme != 1 ){
- X (void)close(1);
- X if( dup2(0,1) < 0){
- X logerr_die( XLOG_CRIT, "Setuplog: dup2 failed" );
- X }
- X }
- X if(Debug>4)log(XLOG_DEBUG,"Setuplog: opening log file %s", logfile );
- X /*
- X * open logfile; if it is "-", use stderr
- X */
- X if( logfile && *logfile && strcmp(logfile, "-") ){
- X if( (fd = open_daemon(logfile, O_WRONLY|O_APPEND, 0)) < 0 ){
- X if(Debug>0)logerr(XLOG_DEBUG,"cannot open logfile %s",logfile);
- X /* write to stderr if debugging, /dev/null otherwise */
- X if(Debug>0){
- X fd = 2;
- X } else {
- X fd = 0;
- X }
- X }
- X if( fd != 2 && dup2(fd,2) < 0){
- X logerr_die( XLOG_CRIT, "Setuplog: dup2 failed" );
- X }
- X if( fd > 2 ){
- X (void)close(fd);
- X }
- X }
- X}
- X
- X/*
- X * When a child dies, it will generate a SIGCHLD,
- X * and Reapchild() will lay the body to rest with a wait3().
- X */
- Reapchild()
- X{
- X int pid;
- X union wait status;
- X
- X while ((pid =wait3(&status, WNOHANG, (struct rusage *)0)) > 0){
- X if(Debug>3)log(XLOG_DEBUG,"process %d, status %s", pid,
- X Decode_status( &status ) );
- X }
- X}
- X
- X/*
- X * Clean up; kill off children and remove sockets and files
- X */
- cleanup()
- X{
- X int pid;
- X
- X (void)sigblock(sigmask(SIGCHLD)|sigmask(SIGHUP)
- X |sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM));
- X if( Lfd ){
- X Closelockfile(LO, Lfd);
- X Lfd = 0;
- X }
- X pid = getpid();
- X if(Debug>0)log( XLOG_DEBUG, "cleanup" );
- X /*
- X * if the recfiles server, clean up files
- X */
- X rm_recfiles();
- X if( pid == getpgrp(0) ){
- X /* Kill children too */
- X (void)kill(0, SIGINT);
- X (void)kill(0, SIGCONT);
- X if( pid == lpdpid){
- X log(XLOG_CRIT, "cleanup: lpd process %d terminating", pid);
- X }
- X }
- X
- X /*
- X * Wait for children until the bodies rot
- X */
- X Reapchild();
- X exit(Errorcode);
- X}
- X
- X/***********************************************************************
- X * From_host( sockaddr_in *f )
- X * This routine will extract the remote Host Name and remote Host
- X * port number information. The remote process must connect on
- X * a port in a "reserved" range; these are reserved for use
- X * by priviledged processes (setuid/uid root).
- X * Side Effects: sets the "from" string to the Name of the remote Host.
- X * Returns: 1 if privileged and successful, 0 if not.
- X * NOTE: this code was derived from the orignal 4.2BSD LPR source,
- X * the 4.3BSD Interprocess Communications Tutorials, and other places.
- X ***********************************************************************/
- X
- XFrom_host(f)
- X struct sockaddr_in *f;
- X{
- X struct hostent *hp; /* Host entry */
- X extern char *inet_ntoa(); /* inet address translation routine */
- X int port; /* from port number */
- X
- X port = ntohs(f->sin_port);
- X if (f->sin_family != AF_INET ){
- X log(XLOG_CRIT,
- X "From_host: malformed from address, family %d, not AF_INET %d",
- X f->sin_family, AF_INET);
- X return(0);
- X }
- X if ( port > Maxportno){
- X log(XLOG_CRIT,"From_host: from port %d, max is %d",port, Maxportno);
- X return(0);
- X }
- X hp = gethostbyaddr((char *)(&f->sin_addr), sizeof(struct in_addr),
- X f->sin_family);
- X if (hp == 0){
- X logerr(XLOG_INFO,"From_host: Host Name for address '%s' unknown",
- X inet_ntoa(f->sin_addr));
- X return(0);
- X }
- X if( (From = malloc( (unsigned)strlen(hp->h_name)+1 )) == 0 ){
- X logerr_die(XLOG_CRIT,"malloc failed in From_host" );
- X }
- X (void)strcpy(From, hp->h_name);
- X return(1);
- X}
- X
- X/************************************************************************
- X * Startup()
- X * start queue servers
- X ************************************************************************/
- static
- Startup()
- X{
- X int pid;
- X char **pr;
- X union wait status;
- X
- X /*
- X * Restart the server.
- X * Start scanning the printcap for entries
- X */
- X if ((pid = fork()) < 0) {
- X logerr( XLOG_CRIT,"server for %s: cannot fork", Printer);
- X } else if(pid == 0 ) {
- X if(Debug>4)log( XLOG_DEBUG, "starting servers" );
- X for( pr = All_printers(); *pr; ++pr ){
- X Printer = *pr;
- X if ((pid = fork()) < 0) {
- X logerr( XLOG_CRIT,"server for %s: cannot fork", Printer);
- X continue;
- X } else if(pid == 0) {
- X if(Debug>2)log( XLOG_DEBUG, "started %s server, pid %d",
- X Printer,getpid());
- X (void)fclose(Lfd);
- X Lfd = 0;
- X Startprinter();
- X exit(0);
- X }
- X sleep( (unsigned)1 );
- X }
- X while ((pid=wait(&status)) > 0){
- X if(Debug>3)log(XLOG_DEBUG,"process %d, status %s", pid,
- X Decode_status( &status ) );
- X }
- X if(Debug>4)log( XLOG_DEBUG, "all servers done" );
- X exit(0);
- X }
- X}
- END_OF_FILE
- if test 16723 -ne `wc -c <'src/lpd.c'`; then
- echo shar: \"'src/lpd.c'\" unpacked with wrong size!
- fi
- # end of 'src/lpd.c'
- fi
- if test -f 'src/printcap.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/printcap.c'\"
- else
- echo shar: Extracting \"'src/printcap.c'\" \(16988 characters\)
- sed "s/^X//" >'src/printcap.c' <<'END_OF_FILE'
- X/***************************************************************************
- X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
- X ***************************************************************************
- X * MODULE: printcap.c
- X * Super slick printcap information extraction routines.
- X * Note: these routines were designed after studying the CURSES routines,
- X * which are HORRIBLE... These may not be any better, but at least
- X * they are a lot faster.
- X * The design is based on the assumption that we will know all the printcap
- X * entries that we want to know ahead of time, and get them "en mass".
- X * Further more, these can be placed in sorted order.
- X * Thus, given a set of these, we scan the printcap entry, extract the
- X * field name, determine if in the list, and then update the information
- X * only if in list. Result:
- X * 1. 1 scan of printcap file per lookup of printer name, finds K entries
- X * 2. Log N order search of list, for a K log N search of printcap
- X ***************************************************************************
- X * Revision History: Created Sun Jan 3 19:12:33 CST 1988
- X * $Log: printcap.c,v $
- X * Revision 3.1 88/06/18 09:35:22 papowell
- X * Version 3.0- Distributed Sat Jun 18 1988
- X *
- X * Revision 2.1 88/05/09 10:09:50 papowell
- X * PLP: Released Version
- X *
- X * Revision 1.6 88/04/06 12:13:22 papowell
- X * Minor updates, changes in error message formats.
- X * Elimination of the AF_UNIX connections, use AF_INET only.
- X * Better error messages.
- X *
- X * Revision 1.5 88/03/25 15:01:10 papowell
- X * Debugged Version:
- X * 1. Added the PLP control file first transfer
- X * 2. Checks for MX during file transfers
- X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
- X * apparently they open files and then assume that they will stay
- X * open.
- X * 4. Made sure that stdin, stdout, stderr was available at all times.
- X *
- X * Revision 1.4 88/03/12 10:04:29 papowell
- X * Minor Bug Fixes, Better Error Message Logging
- X *
- X * Revision 1.3 88/03/11 19:29:48 papowell
- X * Minor Changes, Updates
- X *
- X * Revision 1.2 88/03/05 15:01:37 papowell
- X * Minor Corrections, Lint Problems
- X *
- X * Revision 1.1 88/03/01 11:09:04 papowell
- X *
- X ***************************************************************************/
- X#ifndef lint
- static char id_str1[] =
- X "$Header: printcap.c,v 3.1 88/06/18 09:35:22 papowell Exp $ PLP Copyright 1988 Patrick Powell";
- X#endif lint
- X
- X#include "lp.h"
- X/*
- X * char **All_printers()
- X * returns a pointer to an array of strings which contains the
- X * names of the printers in the printcap file. The list is terminated
- X * with a 0 (NIL) entry.
- X *
- X * char *Get_first_printer()
- X * returns a pointer to the first printer in the printcap file.
- X *
- X * init_pc_entry( PC_ENTRY pc[]; int len)
- X * set the default values for variables listed in pc[]
- X *
- X * The following routines return 1 if printcap entry found, 0 otherwise.
- X * of the printcap entry.
- X *
- X * int Set_pc_entry(char *printer; PC_ENTRY pc[]; int len)
- X * set the values of the variables listed in pc from the printcap
- X * 1. scan the printcap file and extract the printcap entry for
- X * printer. Make a copy of the entry.
- X * 2. find the name of the printer (first entry) and set the First_name
- X * variable to point to it.
- X * 3. Initialize the printcap variables according to the defaults in
- X * the "pc" array.
- X * 4. Scan the printcap entry, and for each tagged field in the entry:
- X * a. search the "pc" array for the tag using a binary search.
- X * b. if the entry is found, determine the type;
- X * c. if an integer, convert from string to integer and update variable.
- X * d. if a flag, set or clear it
- X * e. if a string, copy into a buffer and set the string variable to point
- X * to it.
- X * int Get_pc_entry(char *printer; PC_ENTRY pc[]; int len)
- X * call init_pc_entry() and then Set_pc_entry.
- X */
- X
- static FILE *pfp = NULL; /* printcap data base file pointer */
- X/*
- X * The capbuf buffer holds printcap, the strbuf holds strings
- X */
- static char capbuf[BUFSIZ]; /* buffer to hold printcap */
- static char strbuf[BUFSIZ]; /* buffer to hold strings */
- static char *strloc; /* next string location */
- X
- static char *pr_names; /* buffer to hold them */
- static int max_names; /* max number of names in printcap */
- static char **pr_list; /* printer names in printcap */
- static int max_list; /* size of list */
- X/*
- X * All_printers: reads the /etc/printcap file, and forms
- X * a list of all the printer names in the file.
- X */
- X
- char **
- All_printers()
- X{
- X int i; /* ACME Integer, Inc. */
- X char *bp, *cp, *ep; /* ACME Pointer, Inc. */
- X
- loop:
- X if(Debug>5)log(XLOG_DEBUG,"All_printers: max_names %d, max_list %d",
- X max_names, max_list);
- X if( max_names == 0 ){
- X max_list = BUFSIZ;
- X max_names = MAXPCNAMES;
- X pr_names = (char *)malloc((unsigned)(max_list));
- X if( pr_names == NULL ){
- X logerr_die( XLOG_INFO, "All_printers: malloc failed" );
- X }
- X pr_list = (char **)malloc((unsigned)(max_names*sizeof(char *)));
- X if( pr_list == NULL ){
- X logerr_die( XLOG_INFO, "All_printers: malloc failed" );
- X }
- X }
- X i = 0;
- X bp = pr_names;
- X ep = pr_names + max_list;
- X closeprent();
- X while( pc_entry( capbuf, sizeof(capbuf)) ){
- X init_pc_entry( Status_pc_vars, Status_pc_len);
- X getpcvars(Status_pc_vars, Status_pc_len);
- X /* get the printcap variable values */
- X if(Debug>6)show_pc( Status_pc_vars, Status_pc_len);
- X if( SD != 0 && *SD != 0 ){
- X pr_list[i] = bp;
- X if( (cp = index( capbuf, '|')) == 0 ){
- X cp = index( capbuf, ':');
- X }
- X *cp = 0;
- X bp = estrcp( bp, capbuf, ep );
- X if( bp ){
- X ++bp;
- X }
- X ++i;
- X if( i >= max_names || bp == 0 || bp >= ep){
- X if(Debug>1)log(XLOG_DEBUG,"All_printers: need more space" );
- X max_list += BUFSIZ;
- X max_names += MAXPCNAMES;
- X pr_names = (char *)realloc(pr_names,(unsigned)(max_list));
- X if( pr_names == NULL ){
- X logerr_die( XLOG_INFO, "All_printers: malloc failed" );
- X }
- X pr_list = (char **)
- X realloc((char *)pr_list,(unsigned)max_names*sizeof(char *));
- X if( pr_list == 0 ){
- X logerr_die( XLOG_INFO, "All_printers: malloc failed" );
- X }
- X goto loop;
- X }
- X pr_list[i] = 0;
- X }
- X }
- X closeprent();
- X if(Debug>5){
- X int j;
- X (void)fprintf(stdout,"All_printers: %d,", i);
- X for( j = 0; j < i; ++j ){
- X (void)fprintf(stdout," %s", pr_list[j] );
- X }
- X (void)fprintf(stdout,"\n");
- X }
- X return( pr_list );
- X}
- X
- X/*
- X * First_printer: reads the /etc/printcap file
- X * and finds the first printer name
- X */
- char *
- XFirst_printer()
- X{
- X char *bp;
- X static char first[PRNAMELEN+1];
- X
- X closeprent();
- X while( pc_entry( capbuf, sizeof(capbuf)) ){
- X init_pc_entry( Status_pc_vars, Status_pc_len);
- X getpcvars(Status_pc_vars, Status_pc_len);
- X /* get the printcap variable values */
- X if(Debug>6)show_pc( Status_pc_vars, Status_pc_len);
- X if( SD != 0 && *SD != 0 ){
- X if( (bp = index( capbuf, '|')) == 0 ){
- X bp = index( capbuf, ':');
- X }
- X *bp = 0;
- X (void)strcpy( first, capbuf );
- X closeprent();
- X if(Debug>4)log(XLOG_DEBUG,"First_printer: %s", first );
- X return( first );
- X }
- X }
- X fatal( XLOG_INFO, "First_printer: bad printcap %s", Printcap );
- X return( (char *)0 );
- X}
- X
- X/*
- X * pc_entry(char *tb; int size)
- X * Used to scan the printcap file.
- X * finds the next entry in the printcap file;
- X * it places it in the tb buffer, and returns
- X * 1 if entry found, 0 otherwise
- X */
- static int
- pc_entry(tb, size)
- X char *tb;
- X int size;
- X{
- X char buf[BUFSIZ]; /* read buffer */
- X char *bp, *cp, *ep; /* next read position */
- X int l; /* line length */
- X
- X /*
- X * get the printcap file
- X */
- X if (pfp == NULL ){
- X if( (pfp = fopen_daemon(Printcap, "r")) == NULL){
- X logerr_die( XLOG_CRIT, "open %s failed", Printcap );
- X }
- X }
- X /*
- X * We read a single printcap entry into the bp buffer.
- X * We scan lines until we hit one that does not start with a
- X * # or is a blank line.
- X */
- X while( bp = fgets( buf, sizeof(buf), pfp) ){
- X if( buf[0] != '#' && buf[0] != '\n' ){
- X break;
- X }
- X }
- X if( bp == 0 ){
- X return(0);
- X }
- X /*
- X * read the file in, stripping off the \<newlines>
- X */
- X ep = tb + size;
- X bp = tb;
- X do{
- X cp = index( buf, '\n' );
- X if( cp == 0 ){
- X fatal( XLOG_INFO, "bad line in printcap file '%s'", buf );
- X }
- X *cp = 0;
- X if( cp != buf ){
- X l = (cp[-1] == '\\');
- X if( l ){
- X cp[-1] = 0;
- X }
- X } else {
- X l = 0;
- X }
- X bp = estrcp( bp, buf, ep );
- X if( bp == 0 ){
- X fatal( XLOG_INFO, "printcap entry too long %s", tb );
- X }
- X if( l == 0 ){
- X if(Debug>8)log(XLOG_DEBUG,"pc_entry: %s", tb );
- X return( 1 );
- X }
- X } while( fgets( buf, sizeof(buf), pfp) );
- X log( XLOG_INFO,"bad termcap entry %s", tb);
- X return( 0 );
- X}
- X
- X/*
- X * init_pc_entry( PC_ENTRY pc_vars[]; int pc_len)
- X * initialize the variables in pc_vars with their default values.
- X * sets the strloc to start of string buffer.
- X */
- init_pc_entry( pc_vars, pc_len)
- X PC_ENTRY pc_vars[]; /* the printcap vars used for the printer */
- X int pc_len; /* the number of printcap vars */
- X{
- X int i; /* ACME Integer, Inc. */
- X /*
- X * intialize with the defaults
- X */
- X First_name = 0;
- X for( i = 0; i < pc_len; ++ i ){
- X switch( pc_vars[i].kind ){
- X case PC_NUM:
- X *(int *)pc_vars[i].var = pc_vars[i].idefault;
- X break;
- X case PC_FLAG:
- X *(int *)pc_vars[i].var = pc_vars[i].idefault;
- X break;
- X case PC_STRING:
- X *(char **)pc_vars[i].var = pc_vars[i].sdefault;
- X break;
- X default:
- X fatal( XLOG_INFO,"getpcvars; bad kind %d in table entry %d",
- X pc_vars[i].kind,i);
- X }
- X }
- X strloc = strbuf;
- X if(Debug>7)log(XLOG_DEBUG,"init_pc_entry: done");
- X}
- X
- X/*
- X * char *fix_str( char *str )
- X * makes a copy of str in strbuf and returns the start
- X * Side Effect: modifies strloc
- X */
- static char *
- fix_str(str)
- X char *str;
- X{
- X char *cp; /* ACME Pointers, Inc. */
- X int l;
- X
- X l = strlen(str);
- X cp = strloc;
- X if( ! ((strloc+l) < (strbuf+sizeof(strbuf))) ){
- X fatal( XLOG_INFO,"fix_str: string table overflow" );
- X }
- X (void)strcpy(strloc,str);
- X strloc += (l+1);
- X return(cp);
- X}
- X
- X/*
- X * Set_pc_entry( char *name; PC_ENTRY pc_vars[]; int pc_len)
- X * 1. gets the "name" printcap entry; capbuf[] holds it;
- X * strbuf is used to hold string constants.
- X * 2. checks to see if the entry has a continuation (tc= field),
- X * and gets the continuations
- X * 3. calls getpcvar routine to find fields in the printcap entry and set
- X * variables.
- X * Returns: 1 if successful, and printcap entry found; 0 otherwise.
- X * Side Effect: sets the First_name to the first name in the printcap entry.
- X */
- Set_pc_entry(name, pc_vars, pc_len)
- X char *name; /* the printer name */
- X PC_ENTRY pc_vars[]; /* the printcap vars used for the printer */
- X int pc_len; /* the number of printcap vars */
- X{
- X int found; /* ACME Integers and Pointers */
- X char *cp;
- X
- X if(Debug>7)log(XLOG_DEBUG,"Set_pc_entry: %s", name);
- X if( name == 0 || *name == 0 ){
- X return( 0 );
- X }
- X found = 0;
- X while( found == 0 && pc_entry(capbuf, sizeof(capbuf)) > 0 ) {
- X if(Debug>7)log(XLOG_DEBUG,"Set_pc_entry: pc_entry %s", capbuf);
- X if (Chk_name(capbuf, name)) {
- X found = 1;
- X }
- X }
- X closeprent();
- X if( found == 0 ){
- X return(0);
- X }
- X if( First_name == 0 ){
- X if( (cp = index( capbuf, '|')) == 0 ){
- X cp = index( capbuf, ':');
- X }
- X found = *cp;
- X *cp = 0;
- X First_name = fix_str( capbuf );
- X *cp = found;
- X }
- X if(Debug>8){
- X (void)fprintf(stderr,"printer %s, %s, len %d printcap ",
- X name,First_name,pc_len);
- X (void)fprintf(stderr,capbuf);
- X (void)fprintf(stderr,"\n");
- X }
- X /*
- X * get the printcap variable values
- X */
- X getpcvars(pc_vars, pc_len); /* get the printcap variable values */
- X if(Debug>6)show_pc( pc_vars, pc_len);
- X return(1);
- X}
- X
- X/*
- X * Get_pc_entry( char *name; PC_ENTRY pc_vars[]; int pc_len)
- X * 1. calls init_pc_entry() to initialize variables
- X * 2. calls Set_pc_entry() to set them
- X */
- Get_pc_entry(name, pc_vars, pc_len)
- X char *name; /* the printer name */
- X PC_ENTRY pc_vars[]; /* the printcap vars used for the printer */
- X{
- X if(Debug>7)log(XLOG_DEBUG,"Get_pc_entry: %s", name);
- X init_pc_entry( pc_vars, pc_len);
- X return( Set_pc_entry(name, pc_vars, pc_len) );
- X}
- X
- X
- X/*
- X * Chk_name deals with name matching. The first line of the printcap
- X * entry is a sequence of names separated by |'s, so we compare
- X * against each such name. The normal : terminator after the last
- X * name (before the first field) stops us.
- X */
- static int
- Chk_name(buf, name)
- X char *buf, *name;
- X{
- X int l;
- X
- X l = strlen(name);
- X
- X while( buf && *buf ){
- X if( strncmp( name, buf, l ) == 0 && (buf[l] == ':' || buf[l] == '|')) {
- X /* match */
- X return(1);
- X }
- X if( buf = index(buf, '|' ) ){
- X buf = buf + 1;
- X }
- X }
- X /* no match */
- X return(0);
- X}
- X
- X/*
- X * Return the (numeric) option id.
- X * Numeric options look like
- X * li#80
- X * i.e. the option string is separated from the numeric value by
- X * a # character. If the option is not found we return -1.
- X * Note that we handle octal numbers beginning with 0.
- X */
- static int
- getnum(bp)
- X char *bp;
- X{
- X int i, base;
- X
- X if (*bp != '#'){
- X return(-1);
- X }
- X bp = bp + 1;
- X i = 0;
- X base = 10;
- X if (*bp == '0'){
- X base = 8;
- X }
- X while(isdigit(*bp)){
- X i = i*base + (*bp - '0');
- X ++bp;
- X }
- X return (i);
- X}
- X
- X/*
- X * Handle a flag option.
- X * Flag options are given "naked", i.e. followed by a :, @ or end
- X * of the buffer. Return 1 if we find the option, or 0 if it is
- X * not given.
- X */
- static int
- getflag(bp)
- X char *bp;
- X{
- X if (*bp == 0 || *bp == ':'){
- X return (1);
- X }
- X return(0);
- X}
- X
- X/*
- X * Get a string valued option.
- X * These are given as xx=string
- X * There is a limited amount of decoding done.
- X * \n -> '\n', \nnn -> '\nnn'
- X * ^<CHAR> -> CTRL-CHAR
- X */
- static char *
- getstr(str)
- X char *str; /* points to string entry */
- X{
- X char buf[BUFSIZ];
- X char *bp, *cp, *op; /* ACME Chain and Pointers, Inc */
- X int c, i; /* ACME Integers, Inc */
- X static char norm[] = "E^\\:nrtbf";
- X static char esc[] = "\033^\\:\n\r\t\b\f";
- X
- X bp = str;
- X op = buf; /* output area */
- X if (*bp != '=')
- X return( (char *)0 );
- X bp++;
- X
- X while ((c = *bp++) && c != ':') {
- X switch (c) {
- X case '^':
- X c = *bp++;
- X if( c == 0 ){
- X fatal(XLOG_INFO,"getstr: bad escape string in printcap" );
- X }
- X c = c & 037;
- X break;
- X case '\\':
- X c = *bp++;
- X if( c == 0 ){
- X fatal(XLOG_INFO,"getstr: bad escape string in printcap" );
- X }
- X cp = index( norm, c );
- X if( cp ){
- X c = esc[cp - norm];
- X } else if (isdigit(c)) {
- X c = c - '0';
- X for( i = 0; i < 3 && isdigit(*bp); ++i ){
- X c = 8*c + *bp - '0';
- X ++bp;
- X }
- X }
- X break;
- X }
- X *op++ = c;
- X }
- X *op++ = 0;
- X if( strlen( buf ) > 0 ){
- X return( fix_str(buf) );
- X } else {
- X return( (char *)0 );
- X }
- X}
- X
- X/*
- X * getpcvars- passed a table of entries, tries to find them all
- X */
- getpcvars(pc_vars, pc_len)
- X PC_ENTRY *pc_vars;
- X int pc_len;
- X{
- X int h, l, m; /* high, low, middle */
- X char *cp; /* string value */
- X int dir; /* direction */
- X
- X /*
- X * now scan the printcap entry, getting the variables
- X */
- X cp = capbuf;
- X while( cp && (cp = index(cp, ':' ))){
- X /*
- X * we are now positioned at the ":"
- X */
- X cp = cp+1;
- X if( cp[-2] == '\\' ){
- X continue;
- X }
- X /* note: islower is a macro! do not combine the above statement! */
- X if( !islower( cp[0] ) || !islower( cp[1] )){
- X continue;
- X }
- X if(Debug>7)log(XLOG_DEBUG,"get_pc_vars: entry %c%c", cp[0], cp[1]);
- X /*
- X * binary search the table
- X */
- X l = 0; h = pc_len-1;
- X dir = 1;
- X while( dir && l <= h ){
- X m = (l+h)/2;
- X if(Debug>8)log( XLOG_DEBUG, "get_pc_vars: l %d, m %d, h %d",l,m,h);
- X dir = strncmp( cp, pc_vars[m].pc_name, 2 );
- X if( dir < 0 ){
- X h = m-1; /* too high */
- X } else {
- X l = m + 1; /* too low */
- X }
- X }
- X if( dir == 0 ){
- X /* bingo! we found it */
- X if(Debug>7)log(XLOG_DEBUG,"get_pc_vars: found %c%c%c%c%c",
- X cp[0], cp[1], cp[2], cp[3], cp[4] );
- X fixentry( &pc_vars[m], cp+2 );
- X }
- X /*
- X * we finished, on to next
- X */
- X }
- X}
- X
- X/*
- X * fixentry: fix up the value for the entry in the printcap
- X */
- X
- fixentry( pc_vars, cp )
- X PC_ENTRY *pc_vars;
- X char *cp;
- X{
- X if(Debug>6)log(XLOG_DEBUG,"found %c%c", cp[-2], cp[-1] );
- X switch( pc_vars->kind ){
- X case PC_NUM:
- X *(int *)pc_vars->var = getnum(cp);
- X break;
- X case PC_FLAG:
- X *(int *)pc_vars->var = getflag(cp);
- X break;
- X case PC_STRING:
- X *(char **)pc_vars->var = getstr(cp);
- X break;
- X default:
- X fatal( XLOG_INFO, "fixentry: bad kind in the pc_vars" );
- X break;
- X }
- X}
- X
- X/*
- X * Debugging and display information
- X */
- X
- show_pc( pc_vars, pc_len )
- X PC_ENTRY *pc_vars;
- X int pc_len;
- X{
- X int i;
- X
- X (void)fprintf(stderr, "printcap entry %s: \n", First_name );
- X for( i = 0; i < pc_len; ++i ){
- X (void)fprintf(stderr, "%s: ", pc_vars[i].pc_name );
- X switch( pc_vars[i].kind ){
- X case PC_NUM:
- X (void)fprintf(stderr, "PC_NUM %d\n", *(int *)pc_vars[i].var );
- X break;
- X case PC_FLAG:
- X (void)fprintf(stderr, "PC_FLAG %d\n", *(int *)pc_vars[i].var );
- X break;
- X case PC_STRING:
- X (void)fprintf(stderr, "PC_STRING %s\n", *(char **)pc_vars[i].var );
- X break;
- X }
- X }
- X (void)fflush(stderr);
- X}
- X/***************************************************************************
- X * closeprent
- X * closes the file that the printcap was read from
- X ***************************************************************************/
- closeprent()
- X{
- X if (pfp != NULL)
- X (void) fclose(pfp);
- X pfp = NULL;
- X}
- END_OF_FILE
- if test 16988 -ne `wc -c <'src/printcap.c'`; then
- echo shar: \"'src/printcap.c'\" unpacked with wrong size!
- fi
- # end of 'src/printcap.c'
- fi
- echo shar: End of archive 12 \(of 16\).
- cp /dev/null ark12isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 16 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-
-