Printing in the Solaris Environment: Using the LP Subsystem
Printing in the Solaris Environment: Using the LP Subsystem    

June 2000

This collection of tips and ideas is written for developers of printing solutions for the SolarisTM operating environment and is made up of two parts:


Contents

Functions Provided by the LP Subsystem

Printing Integration Under the LP Service

LP Server Scalability


Functions Provided by the LP Subsystem

Application developers working with printing functions generally want to perform two simple tasks:

  • Query a list of available printers
  • Submit a print job to a selected printer

Call Level Interfaces

Solaris contains no Application Programmer Interfaces (APIs) for printing. It does, however, contain three printing-related Call Level Interfaces (CLIs).

  • Lpstat(1) queries the status of a printer
  • lp(1) submits print jobs
  • cancel(1) cancels print jobs

The complete definitions for these CLIs can be found in the corresponding Solaris man pages.

How to Query Available Printers

To generate a list of available printers, use the lpstat(1) interface.

There are many options available in lpstat(1) and, because they operate and report across all configured printers, the list may take a long time to complete. The fastest way to gather a simple list of printers is to use the /usr/bin/lpstat –v command, which performs a naming lookup and returns the results without querying the servers.

Example

The following example generates a list of printers by running the lpstat command with the popen(3C) function and then parsing the input stream.

/*
* Copyright (C) 1998, by Sun Microsystems, Inc.
* All Rights Reserved
*/

#pragma ident   "@(#)popen_printer_list.c       1.2     98/05/11 SMI"

#include &ltstdio.h>
#include &ltstrings.h>

main(int ac, char *av[])
{
FILE *fp;

if ((fp = popen("/usr/bin/lpstat -v", "r+")) != NULL) {
char buf[BUFSIZ];

while (fgets(buf, sizeof (buf), fp) != NULL) {
strtok(buf, ": ");      /* eat system/device */
strtok(NULL, ": ");     /* eat for */
printf("printer: %s\n", strtok(NULL, ": "));
}
fclose(fp);
}
}


How to Submit a Print Job

To submit a print job, use the lp(1) interface.

Like the lpstat(1) interface, lp(1) also has many options, but only a few of them are important when submitting a job from an application. When the lp command is invoked, the instance of the command parses the command line arguments and builds a print request. This request is actually derived from the command line options, configuration data, and the data to be printed. Many of the lp(1) command line options set request characteristics, such as data type, filter options, reporting actions, etc.

Job Data

By default, the job data is printed by reference when an lp(1) request is made. This means that when an application submits data to print, the job data is kept in a temporary file, it must tell lp(1) to copy the data before printing. If the data is piped to lp(1), it will automatically be placed in a temporary file by lp(1).

PostScript versus Text

The LP server also checks to determine if the print data is in PostScriptTM format or not.

  • If the data begins with %! or ^d%!, the print spooler considers the data to be PostScript. Otherwise, the data is considered to be simple text.

  • To print PostScript data that does not begin with %! or ^d%!, add the -T postscript option to the command line. For example, % lp -T postscript file.

  • To print PostScript data as simple text, add the -T simple option to the command line. For example,
    % lp -T simple file.

Example

The following example shows some ways to submit print jobs programatically to the LP Subsystem in Solaris. The example is complete enough to run, but should be tailored to your needs. Please note that:

  • snprintf(3c) is used to avoid buffer overflow problems. This interface is available starting with the Solaris 2.6 release.

  • popen(3C) can fail when file descriptors 0-255 are in use because of file pointer limitation. If your application uses a large number of file descriptors, you may want to run lp using pipe(2), fork(2), and exec(2), or simply using system(), depending on your needs. Actually, popen(3C) and system(3S) should only be used with great care since they use a shell to process the command that has been supplied.

/*
* Copyright (C) 1998, by Sun Microsystems, Inc.
* All Rights Reserved
*/

#pragma ident   "@(#)popen_submit.c     1.2     98/05/11 SMI"

#include 
#include 

/*
* Assuming that the command accepts any user defined data, it is a good
* idea to replace any shell meta characters that might breakup the command
* and/or allow the user to run something of their own.  This is particularly
* important if the program is SUID.
*/
void remove_shell_meta_characters(char *command)
{
char *ptr = command;
while ((ptr = strpbrk(ptr, " &;|>^$\\")) != NULL)
*ptr =
}

/*
* This function returns a file pointer that can be used to send output
* to.  Once the file pointer is closed, the job is submitted.
*/
FILE * fp_submit(char *printer)
{
char command[BUFSIZ];
FILE *fp;

snprintf(command, sizeof (command), "/usr/bin/lp -s -d %s", printer);
remove_shell_meta_characters(command);

return (popen(command, "w+"));
}

/*
* This function submits a file to print
*/
int filename_submit(char *printer, char *file)
{
char command[BUFSIZ]

snprintf(command, sizeof (command), "/usr/bin/lp -s -c -d %s %s",
printer, file);
remove_shell_meta_characters(command);

return (system(command));
}

/*
* sample job submission from an application
*/
main(int ac, char *av[])
{
char *printer = av[1];
char *file = av[2];
FILE *fp;
filename_submit(printer, file);

if ((fp = fp_submit(printer)) != NULL) {
fprintf(fp, "this is printing via a file pointer\n");
fclose(fp);
}
}

Integrating Print Under the LP Service

This section provides a more detailed description of LP server facilities, operation, and integration than that found in the Solaris Administrator's Guide.

Background

The LP service is the core print functionality in the Solaris operating environment. The LP service changed considerably between the releases of Solaris 2.5.1 and Solaris 2.6. The changes between these releases include a significant architectural shift, as well as a philosophical shift, including the reimplementation, replacement, or elimination of faulty services with newer, more flexible and more efficient components. These changes are primarily centered around client-side functionality and networking support.

As of this writing, changes to the LP server (lpsched) have basically been completed. It is this server that is being highlighted here.

LP Server Flow: Releases Prior to Solaris 2.6

Following is a flow diagram of lp running under pre-Solaris 2.6 releases.

http://developers.sun.com/solaris/articles/print/svpb1pb2.jpg

Note: SAF = Service Access Facility

LP Server Flow: Releases Starting with Solaris 2.6

Following is a flow diagram of lp running under Solaris 2.6.

http://developers.sun.com/solaris/articles/print/svpb1pb3.jpg

Notes:

  • The client begins at lp. The server begins at inetd (via RFC-1179+).
  • The spooler begins at bsd_lpsched, which is a shared object.
  • In.lpd is started from inetd.
  • lp.tell is used to communicate with the lp server.
  • Solaris 2.6 does not contain a BSD-style print system. It contains a SYSV-style lp server with software to take requests using lpd protocol. The server is very much like the SYSV.

LP Server Functions

The primary purpose of the Solaris LP server is to provide print spooling to users for dedicated print devices. This is done through the cooperation of a number of facilities under a single process, including:

  • A specialized queue manager
  • A filtering (data transformation/matching) interface
  • A back-end (printer/communication) interface
  • A faulting interface
  • An alerting/notification interface
  • A media management interface.

To understand how the various LP server components work together, look at the following example, which starts with job submission.

When you make a print request, the requester contacts the LP server (lpsched), with a copy of the print request. The first thing the scheduler determines is if the request can be handled by validating that the requester has access to the destination and that the data contained in the request can be transformed into a format that the destination can handle. Once the request has been validated, the server accepts the request and informs the requester that it has done so. From there, the real work begins. If the request is already in a format that is acceptable to the destination device, or if the transformation to an acceptable format only requires fast filtering, then the job is placed in the destination's queue for printing. However, if the transformation requires a slow filter to perform the transformation, the print job is placed in a filtering queue so it can be transformed before being queued to the destination.

Once the job reaches the top of the destinations print queue, the scheduler (lpsched) starts a child process to manage the print job. This child process in turn opens and locks the print device and starts a child process to pass this open device and run the interface script. It is the interface script that actually runs any fast filters and completes processing of the job. At any time during this processing, the interface script can detect and report error conditions and recovery from error conditions. This information is reported back to the scheduler using lp.tell. Upon receipt of an error condition from the interface script, the scheduler will use the configured alerting mechanism for the destination to alert the administrator or user. Upon receipt of an "all clear" from the interface script, the scheduler will terminate its alerting and continue processing normally.

Some points to note about lp:

  • There is an lp parsing problem when using the -o option. The problem is that the -o option is not parsed correctly when one of the arguments for the -o option contains a space. The Sun BUGID is 4077583. The workaround is to not allow spaces in arguments passed through the -o option. The fix is in Solaris 7.

  • When adding a printer queue to lpsched, the printer type must be defined in the terminfo database. The lpadmin -p foo -T lex7000 command assumes that lex7000 is already defined in the terminfo database. You should use tic(1M) to define the type in the terminfo database.

  • Printer Type is an implied Content Type, but still needs a valid Content Type. Note that admintool has been known to set Content Type to some default value for content types that it does not recognize; this admintool bug has been resolved in Solaris 8.

Unsupported Techniques

A developer who would like to have greater control over lp might be tempted to rename /usr/bin/lp and have an lp_wrapper script call it under certain conditions. This renders the printing software unsupported.

For example, you might rename /usr/bin/lp to /usr/bin/lp/system_lp and then create another shell script named /usr/bin/lp that calls /usr/bin/lp/system_lp in order to set job attributes dynamically at time of job submission. This method is strongly discouraged since the binary contained in the file /usr/bin/lp looks at argv[0] to determine how to parse arguments and function. Also, there are other files in the file system that are symbolic links to this file and expect the original binary to be in that file.

The preferred method is to use supported interfaces, such as filters, or to write a wrapper and have the user call the wrapper instead of lp(1).

LP Server Interfaces

Filtering Interface

The Solaris LP server supports two different types of print filters.

  • Slow filters are to be used when the data transformation is likely to take longer than it will take to print the data. This filtering is run "out-of-band" before the print request is actually scheduled to print, so the printer does not have to wait for the data to be transformed.

  • Fast filters are to be used when direct interaction with the printer is required, or when the transformation is not likely to be a bottleneck. Fast filters run in-line, on the way to the printer.

Filters are configured under LP using the lpfilter(1M) command. This command takes in a filter description file and configures a filter under lpsched. The filter description file and its format are described in the man pages.

When adding filters to a Solaris LP server, there are a couple of important things to understand.

  • First and foremost, a filter will only run if it is required to run for the job to print.

  • Second, filters may be required to run either to convert data to a format acceptable to a printer or to fold in special required options called modes.

Note: The online documentation states that a print filter program cannot be used as a filter if it references external files since the LP print service does not recognize references to 'include' files from a filter program. This statement applies to word processing programs such as troff and nroff, since they require other files or macros to complete their processing. This does not apply to shared libraries.

For further information on writing and integrating filters, see the lpfilter(1M) man page or the System Administrators Guide.

Back-End Interface

One very important interface in the Solaris LP server is its back-end interface. This interface ultimately runs and communicates the print job to the printer. The interface itself is simply a call-out to an external program. This program can be written in any language, whether compiled or interpreted. The most common method of implementing this program is using a shell script, and because of this, the interface is generally referred to as an interface script.

Interface Script Inputs

An interface script has a variety of inputs. The first set of inputs is the command line arguments. These arguments supply the printer name, request ID, user, title, number of copies, an options list, and the list of files to print. With the exception of the options list, each of these pieces of information is supplied in a separate command line argument. The options list is supplied as a single command line argument, but it can contain multiple options.

Another input to the interface script is the LP server's calling environment. The LP server takes great care when constructing a calling environment for an interface script to run in. This environment contains information about the character set the printer is to use, any fast filter that should be run in-line to the printer, localization information, printer type, time zone information, and a token for communicating status back to the scheduler. Each of these pieces of information is stored in an environment variable that can be accessed using getenv(3C) in C or $VARIABLE in most shell languages.

A critical input to the interface script is actually the set of open files descriptors. When lpsched starts the interface script, it opens up the actual print device for the script and passes the opened device to the script as standard output. Standard error is actually an output. It is a back channel to lpsched that can be used to communicate error information back to the scheduler.

The last input to the interface script is an asynchronous input. That is, the input can be supplied to the interface script at any time while it is running. This input is supplied using signals. Actually, the only signal that will be supplied from the scheduler is the SIGTERM (15) signal. This signal is supplied when the scheduler wants the interface script to stop what it is doing and terminate. It is usually supplied when the scheduler is being shut down or someone is attempting to perform maintenance on the printer.

Interface Script Outputs

In addition to the above inputs, the interface script can communicate information back to the scheduler using two outputs.

  • The first output is the ability to send text messages back to the scheduler using standard error. These messages are used, verbatim, as the printer status message for lpstat.

  • The second output is the exit code of the interface script. The exit status of the interface script communicates whether the script completed successfully. If it failed, the job should either be reprinted or tossed out.

For further information on interface scripts, how they work, and how to write them, see docs.sun.com, and the System Administrators Guide. Sample interface scripts are located in /usr/lib/lp/model/.

Faulting Interface

As mentioned above, when an error is detected while passing a print job to an output device, the interface script reports this error back to the scheduler. This is done through the faulting interface. The faulting interface is largely made up of a single program, lp.tell, which is external to the scheduler.

The lp.tell program sends messages back from the interface script to lpsched using the scheduler's communication FIFO. The messages contain a type (fault, clear fault), destination, key, and some amount of text. These messages are what trigger the alerting/notification process in the scheduler. As stated, lp.tell is the program that passes these messages to the scheduler. lp.tell is a relatively simple program. It is placed in the process pipeline by an interface script while communicating with the output device. It reads back the status messages from the communication program (described below), makes a determination as to whether the message is an error or not, and sends a message to the scheduler.

Alert/Notification Interface

Upon detecting an error condition or when user interaction is required, the LP server will attempt to perform a notification. This notification is a simple call-out to an external program and is configured under LP using the lpadmin(1M) command. Since alerting is done using a call-out, this program can perform any task. Depending on the configuration, the notification can occur a single time or at x minute intervals while the error condition or requirement for user interaction persists.

Note: When lp.cat sends a message to lp.tell that the printer is okay and when lp.tell sends a clear fault message to lpsched, this acknowledges that the lp alert message from the filter has been handled. This sequence shuts down the alert.

For further information on writing and integrating notification programs, see the lpadmin(1M) man page, or the System Administrators Guide.

Forms (Preprinted Media) Interface

The LP server comes with built-in support for arbitrary preprinted media. This media support, referred to as form support, allows end users to select the media they require to be loaded in the printer at the time a print job is printed. By default, all print jobs have no media selection when submitted.

A user requesting a type of media must supply either a media or form name by using the -f option to lp when submitting the job. Also, the server for the printer must have a definition for the requested media, the destination must be allowed to mount the media, and the user must be allowed to use the media. If the media is not present on the destination at the time the job is ready to print, the LP server will hold the job and request an operator to mount the media on the destination. Once mounted, the operator must use lpadmin(1M) to notify the LP Server that the media has been placed in the printer.

Some specific points to note on the use of forms follows:

  • The forms system (lpforms) can be used to address paper-type issues. The syntax of lpforms is:

lpforms -f form-name -A alert-type [ -P paper-name [ -d ] ]
[ -Q requests ] [ -W minutes ]
  • In the form description, the character set can be used to deal with changing ink cartridges for different job types. For notification of changing empty cartridges, the interface script should pass back a fault and the lp alert mechanism (specified by the -A option) should notify the user of the fault.

  • When a media change is requested at the printer, lpsched usually requires confirmation. This confirmation is passed via lpadmin(1M).

For further information on integrating media/forms support, see the lpadmin(1M) man page, lpforms(1M) man page, or the System Administrators Guide.

Communication Interfaces

Although communication with the printer is handled via the back-end interface script, the interface scripts supplied with Solaris run one of the three different programs (postio, lp.cat, and netpr) to do the actual communication. These three programs send the data to be printed to the output device and gather status where possible. The program that is selected to communicate with the printer depends on the interface script being run and the type of printer being communicated with.

postio

The first program, postio, is used to communicate with PostScript (PS, PSR) printers attached to a serial port or parallel port (BPP, SPIF, ECPP). The program is configured under the standard interface script and reports errors back to lpsched using this same interface script.

Which errors it reports depends largely on the connectivity to the printer. If the printer is connected to a serial port, postio will send a ^t PostScript status request to the printer and retrieve the printer's response before each block of data is sent to the printer. This has the effect of providing for any error that the printer can generate. If the printer is connected to a parallel port, postio assumes that the connection to the printer is unidirectional. Since the connection is believed to be unidirectional, the results of a ^t status request cannot be retrieved. Instead, postio checks the pin status from the parallel port itself. This allows postio to discern and return the following error conditions: power off, off-line, paper out, and busy.

lp.cat

The lp.cat program is used to communicate with all other locally attached printers. It is also configured to run under the standard interface script and report back errors using this script.

Again, the errors detected and reported back depend on the type of connection to the printer. If the printer is connected to a parallel port, it can return the same types of errors that postio does when it communicates with a printer on a parallel port. If the printer is hooked up to a serial port, the reporting is essentially limited to "can't communicate with printer". This is because many serially connected printers don't support any form of status request. With the exception of PostScript printers, those that do support a status request do so in a vendor-specific manner.

There is one small anomaly to watch out for. A serial port is not always a serial port. In some cases developers build elaborate mechanisms to make their pie, pty, socket, driver, etc. appear to the software as a tty. Some simply push the ldterm streams modules on the stack. If the device responds correctly to isatty(3C), it is considered a serial port by postio and lp.cat. Both postio and lp.cat check the endpoint type using the following decision flow:

Is it a BPP parallel port? Use the BPP port pin status. Is it an ECPP parallel port? Use the ECPP port status. Is it a SPIF parallel port? Use the SPIF port status. Is it a tty? Use ^t status under postio (act like a dumb connection under lp.cat). Else...   It must be dumb. Just send data in blocks. If it hangs, report an error.

netpr

The final program used to communicate with printers is netpr, which is available in releases of Solaris 2.6 and later, allows the LP system to communicate with network attached printers. The netpr program is capable of sending job data to printers that either implement the BSD Print protocol (RFC-1179) or taking a straight TCP stream of print data. It is configured to run under the netstandard interface script, which is also available in releases of Solaris 2.6 and later.

As with lp.cat's ability to communicate over a serial port, this program's ability to detect and report more than rudimentary error conditions is limited. Again, status results returned from network-attached printers are generally returned using vendor-specific mechanisms or formats, which limits netpr’s error reporting to transport-related errors only.

Using Solaris Private Printing Interfaces

As mentioned above, the only public printing-related interfaces under Solaris are lp(1), lpstat(1), and cancel(1), which are described in the SVID or XPG4. These interfaces include client-side functionality only. That being said, any of the other interfaces described here can be used to integrate support under the LP system with the understanding that the interfaces will be changed or removed eventually (as was first stated with the release of Solaris 2.3).

The following examples illustrate the use of the various LP components discussed above. Company pPrint is working on a host-based routing information protocol (RIP) solution for its inkjet printer products. A host-based RIP is a utility where the input is a job (for example, a PostScript file) that gets interpreted and converted into some binary form specific to the target printer. The output from the host-based RIP program is then submitted to the printer.

Double Queue Solution

You should strictly use supported public interfaces and set up a front_filter that will route the job on the fly. With the output from the RIP, you might call lp again to resubmit the job rather than putting the routed result back into the lp spool directory since the hierarchy of the lp spool directory, /var/spool/lp/, is considered private, and known only to the LP print service. The design might look like the following figure.

http://developers.sun.com/solaris/articles/print/svpb1pb4.jpg

The problem with this design is that the print queue for the first lp call is different from the print queue for the second lp call. When the job leaves the first print queue, from an end-user perspective, the job appears to have been printed as it no longer appears in the first queue in which the job was submitted to. But, the job may not necessarily have been printed as it could have gotten stuck in the second queue and the end-user will not know this, nor should they.

Single Queue Solution

One solution to avoid double queuing is to have a fast_filter that calls the RIP service via a pipe and have the routed data stored back into lp’s spool temp directory, /var/spool/lp/tmp. This solution eliminates the need to call lp again to requeue the job. The design might look like the following figure.

http://developers.sun.com/solaris/articles/print/svpb1pb5.jpg

The hierarchy of /var/spool/lp is private, Sun's Solaris software group does not approve of its use. However, when using the spool, precede the jobname with f and have the RIP service daemon write the file and pass the name back to the fast_filter. A benefit to using /var/spool/lp is that lp will automatically cleanup when it is done printing. This solution only works for local host-based RIP service spooling.

LP Server Scalability

The number of printers the Solaris LP server can support is scalable and is up to the amount of available system resources (that is, virtual memory and process table space).

Prior to the release of Solaris 2.6, the number of active printers that can be reliably supported was effectively around 125, depending on a number of factors. The printing system made heavy use of file pointers via fopen(3S) and fdopen(3S) and required file descriptors in the range of 0-255. In releases of Solaris 2.6 and later, this dependency on file pointers has been removed and a number of other improvements have been implemented.

For example in testing, an SS20 with 256Mb of VM was found to be able to support around 1000 printers before reaching the limits of its system resources. In order to support more than 2000 printers, it is necessary to use the -p flag when starting lpsched(1M).

The number of remote printers supported should be unlimited, since they only use system resources when an active request (print/xfer job, status, cancel) is being processed.

For further information, see topics on configuring network-attached printers in the online documentation.

Using GhostScript

The Solaris Software Companion CD distributed freely with Solaris 8 contains a pre-compiled and pre-packaged version of GhostScript as a convenience to Solaris users. This package also contains an interface script that allows easy configuration of GhostScript under the LP system.

June 2000

Rate and Review Tell us what you think of the content of this page. Excellent   Good   Fair   Poor   Comments:
If you would like a reply to your comment, please submit your email address:
Note: We may not respond to all submitted comments.
Close    To Top
  • Prev Article-OS:
  • Next Article-OS:
  • Now: Tutorial for Web and Software Design > OS > Solaris > OS Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction