Click on our Sponsors to help Support SunWorld

Set up a print server with Solaris

Find out how to print multiple content types with lpadmin -- a proven methodology

By Errol Fouquet and Robert Krumm

SunWorld
April  1999
[Next story]
[Table of Contents]
[Search]
Subscribe to SunWorld, it's free!

Abstract
Setting up a print server that handles multiple content types may not seem like a difficult task -- and it isn't, as long as third-party software figures into the equation. The difficulties arise when you decide to go strictly with lpadmin. Unix sysadmins Errol Fouquet and Robert Krumm guide you through this process with a proven methodology for setting up a print server that can successfully handle plain text, PostScript, and printable binary (pcl, rtl) content types. (2,900 words)


Mail this
article to
a friend
As Unix system administrators contracted out by our employer, one of the many services we provide to our customers is printing and plotting support. One particular customer has a fairly complex printing environment, which includes 8 36-inch HP plotters, 1 24-inch HP plotter, approximately 20 HP DeskJet 1600c plotters, and approximately 20 HP 3si/4si/5si printers. All the devices run LPD, have PostScript drivers, and are accessed as network printers using TCP/IP. Printer network cards are evenly divided between HP JetDirect and XCD XJet cards.

We run Zeh Graphic Systems's ZPS plotting software on a Sun Ultra Enterprise 450 supporting several applications that output many graphic formats. We also support LPD from a few other applications, and user printing from Netscape, as well as user printing at the command line. The print clients in our environment are mostly Sun servers and workstations numbering close to 200. We also have one Windows NT server running Citrix Winframe, which is also a print client.

Apart from applications, which plot through Zeh, and, therefore, use our Ultra 450 as a spooler, all other print requests are transmitted from the client directly to the network printer. Functionally, this works fine, printing all the content types we need, including text, PostScript, and printable binary (pcl and rtl). The problem is that the setup is an administration nightmare. Often, a huge, unacceptable binary file, like a GIF or JPEG, would be printing out reams of garbage, and we would have no easily available method of determining the source of the request. We would know only that a workstation or server somewhere in the building was sending a bad job to the printer. Unfortunately, banner pages weren't an option, as the user community didn't want to waste paper (ironic, huh?).

We did manage to write a shell script that would rsh over to all the machines in the building and report the activity to a given printer. With this information we could execute the Cancel command. But this was horribly slow and inefficient.

The customer's own technical-computing strategy indicated that the solution was to implement a print server for the Unix environment, but it failed to offer details for this configuration. In addition, a recent NOMAN (network operations management) assessment, conducted by Sprint Paranet, also identified implementing a print server to meet the customer's printing needs.

We knew that a print server was the right solution, and if we could just set it up, all print requests could be managed from a single machine, greatly simplifying the entire process. As it turned out this wound up being much more difficult than we anticipated.

Attempt 1: Trial and error
Initially, we wanted to set up standard Solaris print spooling. We designated a test machine, called nolsn099 (an Ultra-1 running Solaris 2.6), as the test print server, and we began several tests. The printer we began testing with was an HP 1600c, called no1316p in our NIS/DNS environment.

After each server setup, the client machine (an Ultra-1 running Solaris 2.5.1) was set up to access the printer/plotter with the command lpadmin -p no1316p -s nolsn099!no1316p

Server setup 1
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -T PS  \
                     -I PostScript -v /dev/null -i /usr/lib/lp/model/netstandard
Result: Client machine was able to print PostScript and binary (pcl, rtl) files fine, but text files would staircase.
Server setup 2
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -T unknown \
                     -I any -v /dev/null -i /usr/lib/lp/model/netstandard
Result: Client machine was able to print text files fine, but PostScript, rtl, and pcl files printed garbage.
Server setup 3
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -T PS -I text \
                     -v /dev/null -i /usr/lib/lp/model/netstandard
Result: Client machine was able to print text and PostScript files fine, but rtl and pcl files printed garbage.
Server setup 4
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -T unknown \
                     -I PostScript,text,simple -v /dev/null \
                     -i /usr/lib/lp/model/netstandard
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -T unknown \
                     -I any,PostScript,simple -v /dev/null \
                     -i /usr/lib/lp/model/netstandard
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -T PS \
                     -I any -v /dev/null -i /usr/lib/lp/model/netstandard
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -T unknown \
                     -I text,simple -v /dev/null
                     -i /usr/lib/lp/model/netstandard
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -T hplaser \
                     -I text,simple -v /dev/null
                     -i /usr/lib/lp/model/netstandard
Result: All of these resulted in client being able to print PostScript, pcl, and rtl, but the text files would staircase.
Server setup 5
lpadmin -p no1316p -o protocol=bsd,dest=no1316p -T PS \
                     -I any -v /dev/null -i /usr/lib/lp/model/netstandard
Result: This allowed for PostScript and text files to be printed fine, but this printed garbage for pcl and rtl files.

As you can see, we tried several combinations, but were unsuccessful in printing all three desired formats. The man page for lpadmin suggests that you also can have a list for the -T option. We decided that what we really needed was a -T unknown,PS option, but unfortunately, lpadmin doesn't allow "unknown" to be coupled with any other terminfo type.

We concluded that with the standard options available to Solaris lpadmin, we would only be able to set up a printer to handle:

Our next step was to log a call with Sun's software support. We were fortunate to get a very helpful support person on the phone, who gave us what seemed to be a solution. Although he admitted that it was something of a hack, he was sure that it would work. The solution involved setting up two queues for each printer on the print server and then employing double spooling. The first device would function as a filter and then pipe its output to the network printer. (We decided to adopt a convention of creating a printername and a printername-r for each device we configured, so we have a no1316p and a no1316p-r.)

Here are the steps:

Step 1. On the server, set up the filter device

$ lpadmin -p no1316p -v /dev/null -i /usr/lib/lp/model/standard
$ enable no1316p
$ accept no1316p

Step 2. Edit the newly created interface script (/etc/lp/interfaces/no1316p)

  • Look for the case statement that starts case $TERM in

  • For the * ) condition, comment out the line that refers to FILTER=${LPCAT}

  • Insert the line FILTER=/bin/unix2dos | lp -s -d no1316p-r

    Step 3. Set up the network printer (no1316p-r)

    $ lpadmin -p no1316p-r -o protocol=bsd,dest=no1316p -v /dev/null \
    -i /usr/lib/lp/model/netstandard
    $ enable no1316p-r
    $ accept no1316p-r
    

    Note: Without specifying the -T or -I options, the defaults "unknown" and "any" are used.

    When we first set this up, it seemed like a winner. The dos2unix command fixed the staircasing of text files, and we were able to print PostScript, pcl, and rtl files. We were almost ready to celebrate when we noticed that the quality of the pcl and rtl files was extremely poor. The dos2unix command was corrupting the binary format!

    We got back on the phone with Sun. This time we set up a conference call with several Sun engineers to report our needs and findings. They suggested using an lpadmin command (one we'd already tried) to set up the printer. When we told them how this caused the text files to staircase, two of the engineers groaned knowingly. They promised to investigate. In the meantime, we resumed our own quest to find a solution.


    Advertisements

    Attempt 2: Red tape
    Next, we tried using Hewlett-Packard's JetAdmin software, Sun's recommended solution for network printing to HPs. We set up a test machine to use as a print server, then set up the JetAdmin software. It worked great. We were able to print all three file types without error. We were able to set up clients to spool through this server fine, and the software gave us the information we needed to manage unwanted jobs. We had found our solution! Wrong. We were told that replacing the XCD cards was not an option.

    We still hadn't heard back from Sun, so we chose to revisit the "double spooling" solution.

    Attempt 3: Double spooling revisited
    We decided that double spooling had brought us too close to abandon it completely. Only the limitations of unix2dos stood in the way of success. If we could examine the input file and execute only the unix2dos on nonbinary files, we would have our solution. We wrote a simple Perl script (unix2dos.pl) to do just that:

    #!/opt/bin/perl
    # open standard input
    open(FOO, "-");
    # test if binary
    if ( -B FOO) {
       while (<FOO>) {
          print $_;
       }
    }
    # if not binary.. perform unix2dos filtering
    else {
       while (<FOO>) {
          s/\n/\r\n/; print;
       }
    }
    close(FOO);
    

    We then replaced the line in the interface file as follows:

    FILTER=/opt/utils/unix2dos.pl | lp -s -d no1316p-r
    

    That clinched it. We were able to print all three formats without any problems. Or at least that's what we thought. Unfortunately, because of the double spooling, the print jobs in the no1316p-r queue no longer had meaningful names or owners. For example, when a client printed a file, like /etc/passwd, and we executed lpq -Pno1316p on the print server, we would see something like this:

    $ lpq -Pno1316p
    Rank    Owner           Job     File(s)        Total Size
    active  dlister         313     /etc/passwd    522 bytes
    

    But after no1316p filtered the job and piped it to lp -s -dno1316p-r, and we executed lpq -Pno1316p-r on the server, we'd see something like this:

    $ lpq -Pno1316p-r
    Rank    Owner          Job      File(s)       Total Size
    active  lp             1629     1629-1        536 bytes
    

    Without properly designated filenames and owners, users couldn't track their files in the print queue, and we couldn't delete jobs from the queue upon request.

    So we contacted Sun support again. We sent a detailed e-mail to our account rep, copying the two engineers from the conference call, and explaining the progress we had made and how this new obstacle was posing a real problem to print management. The response we got in return was a bit discouraging, to say the least: "This mail describes one of the many reasons that we don't recommend or support double spooling and/or requeueing jobs."

    We were on our own.

    Attempt 4: Finding a compromise
    Because we were successful in spooling all three file types, the last thing we needed was meaningful lpq info for our network printer queue. We knew that when the print job was queued through the filtering queue (no1316p, for instance), the original filename and user name was available. We had to figure out a way of preserving this information and associating it with the job queued on the actual network printer queue (no1316p-r, for instance).

    We knew that when a job was sent through the filtering queue, the lp subsystem would return the information we were interested in. So it stood to reason that we should be able to harness this info from environment variables. As it turns out, the variable we were interested in can be harnessed in the interface script of the filtering queue: ${request_id}, ${user_name},${files}, and ${flist}. But we still faced the problem of associating this information with the lpq of the actual network printer queue. What we needed was a means of logging the requests coming into the filtering queue, and a method of mapping entries in the network printer queue back to the original information.

    The strategy we eventually adopted filtered the input to a temporary file, which had the original ${request_id} in the name. We then piped to an lp of this temporary file. The lpq info of the network printer queue now included the original ${request_id} in the form of the filename. That left only the log file. We further modified the interface script of the filtering queue to include the following logic immediately before the if [ -z "${FILTER}" ]:

    # write current request_id user_name and flist to logfile
    # if the flist variable is null, we are printing a single file,
    # therefore echo ${files}
    # else echo ${flist}
    if [ "$flist" = "" ];
    then
       echo "${request_id} ${user_name} ${files}" \
         $gt; $gt; /var/spool/lp/save_dir/logs/request.log
    else
       echo "${request_id} ${user_name} ${flist}" \
          $gt; $gt; /var/spool/lp/save_dir/logs/request.log
    fi
    

    We incorporated the creation of the temporary file into the unix2dos.pl script shown earlier. The new script is called lp.pl:

    #!/opt/bin/perl
    # open standard input
    open(FOO, "-");
    # create temp file for writing
    open(OUT, ">/var/spool/lp/save_dir/tmp/$ARGV[0].$$");
    # if input stream is binary do not filter
    if ( -B FOO) {
       while () {
           print OUT $_;
       }
    }
    # else filter the line unix2dos style
    else {
       while () {
          s/\n/\r\n/; print OUT;
       }
    }
    close(FOO);
    close(OUT);
    # send print request
    system("/bin/lp -s -d $ARGV[1] /var/spool/lp/save_dir/tmp/$ARGV[0].$$");
    

    Next, we passed lp.pl two arguments: ${request_id}, and network printer. The script then created a filtered temporary file named ${request_id}."pid of process" and executed an lp of this temporary file.

    Then, we replaced the line in the interface script of filtering queue that read

    FILTER=/opt/utils/unix2dos.pl | lp -s -d no1316p-r
    

    with

    FILTER="/var/spool/lp/save_dir/sbin/lp.pl ${request_id} no1316p-r"
    

    Let's take a break and summarize what's happening up to this point:

    At this point, if we perform an lpq on the network printer, the output will look something like this:

    $ lpq -Pno1316p-r
    Rank    Owner   Job    File(s)        Total Size
    active  lp      1629   /var/spool/lp/save_dir/tmp/no1316p-1625.2974 536 bytes
    

    Now, all we need is a script that will index into our log file, find no1316p-1625, and return the original owner and filename. To serve this need we wrote lpq.ksh:

    #!/bin/ksh
    typeset -i N
    typeset -i N2
    # set N2 equal to the default number of fields per log file entry
    N2=3
    if [ $# -ne 1 ]
    then
      echo "usage: $0 network_printer_queue"
      exit 1
    fi
    echo "Rank      Owner                   Job     File(s)         Total Size"
    /usr/ucb/lpq -P $1  | grep bytes | while read line
    do
      RANK=`echo $line | awk '{print $1}'`
      JOB=`echo $line | awk '{print $3}'`
      SIZE=`echo $line | awk '{print $5}'`
      TEMP=`echo $line | awk '{print $4}'`
      GREPSTUFF=`basename $TEMP | awk -F. '{print $1}'`
      OWNER=`grep -w $GREPSTUFF /var/spool/lp/save_dir/logs/request.log \
         | awk '{print $2}'`
      # count the number of fields in the log file: 3 means a single file
      # printed more than
      # 3 means that several files were sent at once.
      N=`grep -w $GREPSTUFF /var/spool/lp/save_dir/logs/request.log \
         | awk '{print NF}'`
      if [ $N -gt 3 ]; then
         FILE=`grep -w $GREPSTUFF /var/spool/lp/save_dir/logs/request.log \
             | awk "{print \\$$N2}"`
         echo "$RANK     $OWNER  $JOB    $FILE   $SIZE bytes"
         N2=N2+1;
      else
         N2=3
         FILE=`grep -w $GREPSTUFF /var/spool/lp/save_dir/logs/request.log \
             | awk '{print $3}'`
         echo "$RANK     $OWNER  $JOB    $FILE   $SIZE bytes"
      fi
    done
    

    This script takes a single argument: the name of the network printer queue. In our scenario, we simply enter $ lpq.ksh no1316p-r to display the following output:

    Rank    Owner      Job     File(s)             Total Size
    active  dlister    1629    /var/adm/messages   536 bytes
    

    Using this script, we were able to give our end users the information they needed to track their print jobs, and the printing support staff could better manage jobs on the print server.

    Cleanup
    Finally, we needed some means of deleting the temporary files we were creating in /var/spool/lp/save_dir/tmp. I noticed that once a job had successfully completed through the lp subsystem, an entry, in the form of T filename, appeared in the file /var/spool/lp/logs/requests.

    To clean up these temp files up we wrote a script called cleanup.ksh:

     
    #!/bin/ksh
     
    cd /var/spool/lp/save_dir/tmp
    unalias ls
    # our temp files are all begin with "no"
    if [ -f no* ];
    then
       for i in `ls no*`
       do
          grep "^T $i" /var/spool/lp/logs/requests > /dev/null 2>&1
          if [ $? -eq 0 ];
          then
             rm -f $i
          fi
    done
    fi
    

    We set up a cron job on the print server to run this script once every hour.

    Conclusion
    While we admit that this solution could use further refinement, we accomplished what we set out to do:

    In addition, we believe that the modifications described in this article could be incorporated into the printing subsystem of Solaris 2.6, eliminating the need for third-party software and hardware solutions. Sun would simply need to add a native filter for the lp subsystem, which provides the logic of our handy unix2dos Perl script, to eliminate the double spooling (officially unsupported by Sun) we are doing now.


    Click on our Sponsors to help Support SunWorld


    Resources

    Additional SunWorld articles on printing in Unix Additional SunWorld resources

    About the author
    Errol Fouquet Errol Fouquet is a certified Solaris Administrator and all around Unix bigot employed with Sprint Paranet. When not "putting out fires" at work, he can be found roaming around New Orleans with his lovely wife Kristin.

    Robert Krumm Robert Krumm is currently employed as a Technical Analyst for Sprint Paranet. Being a Certified Network Administrator, Microsoft Certified Professional, and currently Site Leader at his customer site, Robert has supported Solaris for over 2 1/2 years and supports the Zeh Plotting software.

    What did you think of this article?
    -Very worth reading
    -Worth reading
    -Not worth reading
    -Too long
    -Just right
    -Too short
    -Too technical
    -Just right
    -Not technical enough
     
     
     
        

    SunWorld
    [Table of Contents]
    Subscribe to SunWorld, it's free!
    [Search]
    Feedback
    [Next story]
    Sun's Site

    [(c) Copyright  Web Publishing Inc., and IDG Communication company]

    If you have technical problems with this magazine, contact webmaster@sunworld.com

    URL: http://www.sunworld.com/swol-04-1999/swol-04-printserver.html
    Last modified: