Back to Knowledege base

Determining When an Xi-Text Printer Was Last Used

Xi-Text records no last-used time, so the only evidence is the optional per-printer log - what it holds and omits

The Short Answer

Xi-Text records no last-used time for a printer. The printer record held in shared memory and written to spshed_pfile contains a name, a device, a form type, a state, flags, a class code and size limits, and no time field of any kind. The files in a printer's definition directory are written when the printer is defined and are opened read-only thereafter, so even their modification times do not move. The report file records failures, not activity. The charging tool was withdrawn in 2010 and now does nothing but print its own usage.

So a printer that last printed in 1998 is indistinguishable, from every persistent file the product maintains, from one that printed five minutes ago - unless a per-printer log was configured in advance.

Everything below is therefore either that log, or an inference. The methods are given in order of how much they actually prove, and each says what it cannot tell you. This matters because the usual reason for asking the question is to decide whether a printer can be removed, and a wrong answer removes a working printer.

Method 1: the Printer Log File

This is the only method that produces evidence rather than inference, and it works only where the logfile keyword was put in the printer's setup file before the period you are asking about.

Finding the log file

Each printer has its own directory under the printers directory, by default /var/spool/xi/printers. When a printer starts, its daemon reads, in this order:

  1. .device, if present - the interface settings
  2. a file named after the printer's current form type, with any suffix after a dot or a hyphen removed
  3. default, if no form-specific file exists

Where .device is present it is read first and then the form or default file is read after it, so the logfile keyword can be in either. Check all of them:

cd /var/spool/xi/printers/printername
grep logfile= .device default * 2>/dev/null

The value is everything after the equals sign. A relative path is resolved against that printer's own directory - the printer's setup is parsed with the printers directory and the printer name prefixed automatically - so logfile=.usage means /var/spool/xi/printers/printername/.usage.

The form type used in the lookup is the printer's current one, which is the form type of the last job it loaded rather than a property of the printer. A printer that has been used with several form types may have several setup files and several logs, and the one that is being written to now depends on what it last printed.

Nothing checks that the log file can be opened. If the path is wrong, or the directory is not writable by the spooler account, the printer starts normally and simply logs nothing.

Reading the log file

Log file entries are single lines of pipe-separated fields:

09/06|11:52:18|11:56:53|0:05:13|jmc|CS|a|947|tty1|laser|150|3|14408

The fields are, in order:

PositionContent
1Date the job started printing, as dd/mm - or mm/dd on a machine four or more hours west of UTC. No year.
2Time the job started printing
3Time the job finished printing
4Elapsed time from submission to finish, h:mm:ss
5User who submitted the job
6Job title, or the unnamed-job placeholder
7Form type
8Characters sent to the device
9Device
10Printer name
11Priority
12Pages printed
13Job number, or host:jobnumber for a job owned by a remote machine

Field 4 is measured from the moment the job was submitted, so on a busy printer it is mostly queueing time rather than printing time. If the job crosses midnight the finish time in field 3 belongs to the following day while the date in field 1 does not.

The reference manual undertakes that the pipe separator and the meaning of the thirteen fields above will be kept across future releases, and that any new fields will be added at the end of the line. A script that splits on the separator and indexes the fields it needs, ignoring anything beyond field 13, will therefore keep working.

To see the most recent activity:

tail -5 /var/spool/xi/printers/printername/.usage

The absence of a year is the limitation that matters for this question. A last entry reading 09/06 could be last week or fifteen years ago. Take the age of the last activity from the file's own modification time instead, and use the log line for the detail:

ls -l /var/spool/xi/printers/printername/.usage
tail -1 /var/spool/xi/printers/printername/.usage

That is sound as long as nobody has copied or rotated the file, which is why the rotation procedure below archives by copy and truncates in place.

Method 2: Retained Jobs on the Queue

A job submitted with the retain flag stays on the queue after it prints, and when it finishes printing the scheduler resets its submission time to that moment. So for a retained job that has printed, the %W field is the time it last printed - with a four-digit year, unlike the printer log.

sqlist -H -p -F "%N %u %h %P %W"

-p restricts the listing to jobs that have printed. The %P column is the printer pattern the job asked for, which is often blank, so this identifies a printer only where the submitter named one.

This is a narrow method - it sees only retained jobs that are still on the queue, and a retained job is itself deleted a fixed number of hours after it last printed, 24 by default. But where it applies it gives a dated answer without any prior configuration, which nothing else does.

Method 3: the Current Printer State

The splist command shows the live state of each printer. This tells you what is happening now, and almost nothing about the past:

splist -H -F "%p %s %j %u"

That is the printer name, the state with any message from the device, the job number being printed, and the user who owns it. Use %t instead of %s for the bare state with no message appended. Printer names may be given as arguments to restrict the listing, and they are matched exactly.

The eight states, in the lower case in which they are printed:

printing
The printer is in use right now. The job number and user are shown.
idle
The printer's daemon is running and waiting for work. This does carry one piece of historical information: every printer is set to halted when the spooler starts, so a printer that is idle was started by someone since the last spooler restart.
startup, shutdown
Transient states while the daemon starts or stops.
a/w oper
The printer is waiting for an operator to answer, typically about an alignment page.
halted
The printer is stopped. This is the state after an administrator halts it and the state of every printer immediately after a spooler restart, so on its own it means nothing about usage.
error, offline
The device or its daemon reported a problem. Both are recoverable conditions that are cleared by restarting the printer; neither is a verdict on whether the printer is still wanted.

Each of halted, error and offline describes the printer's condition at this instant and nothing more. A site that restarts its spooler and starts only the printers it needs that morning will show every other printer as halted, however heavily it was used the day before.

Method 4: Jobs Currently Aimed at the Printer

Jobs waiting for a printer indicate that somebody expects it to be available:

sqlist -H -Z -q printername -F "%N %u %h %W"

Two things to know about -q. The printer field on a job is a pattern, and -q asks whether a job's pattern is contained within the one you give; and jobs that named no printer at all are included by default, which is why -Z is given above to exclude them. So this counts jobs that deliberately restricted themselves to this printer, which is a much smaller set than the jobs that would print on it.

A count of zero therefore does not mean the printer is unused. Most jobs name no printer and are matched to one by form type and class code at the moment they print.

Method 5: the Report File

The scheduler and the printer daemons append to spshed_reps in the spool directory, by default /var/spool/xi/spd/spshed_reps. Each entry is a line of the form time, date and the reporting program's name, followed by a separator line and the message text.

tail -40 /var/spool/xi/spd/spshed_reps

Three limitations. Only failures are written - a healthy spooler writes nothing at all, so silence is the normal state and proves nothing. The header line names the program rather than the printer, so the printer name appears only where the message text includes it. And the date carries a day and a month but no year, exactly as the printer log does.

This is useful for the narrow question "did this printer fail recently", and the X key in spq displays the same file from inside the queue manager.

A Practical Script for Checking Last Activity

The following combines the log-file method with the state check, and says explicitly when it has no evidence rather than implying an answer. It is written in POSIX shell for portability across the platforms the product ships on.

#!/bin/sh
# last-printer-activity.sh - report what is known about a Xi-Text printer
# Usage: last-printer-activity.sh printername

PRINTER=$1
SPOOLPT=${SPOOLPT:-/var/spool/xi/printers}

if [ -z "$PRINTER" ]; then
    echo "Usage: $0 printername"
    exit 1
fi

STATE=`splist -N -F "%t" "$PRINTER" 2>/dev/null`
if [ -z "$STATE" ]; then
    echo "Printer '$PRINTER' not found"
    exit 1
fi

echo "Printer:       $PRINTER"
echo "Current state: $STATE"

PTRDIR="$SPOOLPT/$PRINTER"
if [ ! -d "$PTRDIR" ]; then
    echo "No definition directory at $PTRDIR"
    exit 1
fi

# The form-specific setup file is named after the form type with any
# suffix after a dot or hyphen removed; .device is read before it.
FORMTYPE=`splist -N -F "%f" "$PRINTER" | sed 's/[.-].*//'`

LOGPATH=
for SETUP in "$PTRDIR/.device" "$PTRDIR/$FORMTYPE" "$PTRDIR/default"; do
    [ -f "$SETUP" ] || continue
    L=`sed -n 's/^[ 	]*logfile=//p' "$SETUP" | tail -1`
    if [ -n "$L" ]; then
        LOGPATH=$L
    fi
done

if [ -z "$LOGPATH" ]; then
    echo "No logfile keyword configured - no usage history exists"
    exit 2
fi

case "$LOGPATH" in
    /*) ;;
    *)  LOGPATH="$PTRDIR/$LOGPATH" ;;
esac

if [ ! -f "$LOGPATH" ]; then
    echo "Logging configured as $LOGPATH but the file does not exist"
    echo "(nothing has been printed since it was configured)"
    exit 2
fi

echo "Log file:      $LOGPATH"
echo "Entries:       `wc -l < \"$LOGPATH\"`"
echo "Last modified:"
ls -l "$LOGPATH"
echo "Last entry:"
tail -1 "$LOGPATH"
echo "  (the date field in that line carries no year - take the age"
echo "   of the last activity from the modification time above)"
exit 0

The exit statuses distinguish the three answers the question can have: 0 means evidence was found, 2 means no evidence exists, and 1 means the printer or its directory could not be found. A script that treats "no evidence" as "unused" is the error this article exists to prevent.

Enabling Logging for Future Tracking

Where a printer has no logging, it can be added. The setup file is only read when the printer starts, so the printer has to be stopped and started for the change to take effect.

  1. Halt the printer at the end of its current job:
sphalt printername
  1. Edit the setup file - either the form-specific one or default:
vi /var/spool/xi/printers/printername/default
  1. Add the keyword. A relative path is created in the printer's own directory:
logfile=.usage
  1. Start the printer again:
spstart printername

Where the printer is used with several form types and each has its own setup file, add the keyword to each of them, or put it in .device, which is read for every form type.

Managing Log File Growth

Log files grow indefinitely and nothing rotates or purges them.

The file is opened once, when the printer starts, and held open for as long as the printer runs. That decides the rotation method: renaming or moving the file does not work, because the daemon keeps writing to the file it already has open until the printer is next restarted. Copy and truncate in place instead. The file is opened in append mode, so a truncation is safe.

#!/bin/sh
# rotate-printer-log.sh - archive and truncate a printer log file
# Usage: rotate-printer-log.sh printername logfile

PRINTER=$1
LOGFILE=$2
ARCHIVE="$LOGFILE.`date +%Y%m%d`"

RESTART=no
PSTATE=`splist -N -F "%t" "$PRINTER"`
case "$PSTATE" in
    halted|offline|error) ;;
    *)
        sphalt "$PRINTER"
        while true; do
            PSTATE=`splist -N -F "%t" "$PRINTER"`
            case "$PSTATE" in
                halted|offline|error) break ;;
            esac
            sleep 5
        done
        RESTART=yes
        ;;
esac

cp "$LOGFILE" "$ARCHIVE"
: > "$LOGFILE"

if [ "$RESTART" = yes ]; then
    spstart "$PRINTER"
fi

echo "Archived to $ARCHIVE"

Halting first is still worth doing, because it guarantees no line is being written while the copy is taken. Note that the archive file's modification time becomes the rotation date, so keep the archives if the age of the last activity might be asked about later.

Best Practices

Enable logfile on every printer as a matter of course. The overhead is one line written per completed job, and it is the only mechanism in the product that can answer this article's question. A printer without it has no history and never will have.

Prefer .device to the per-form setup files when adding the keyword, so that the log covers every form type the printer is used with.

Keep the archives when rotating, and rely on file modification times rather than the dates inside the log, since the log lines carry no year.

To survey the whole system, loop over the printer names with the heading suppressed:

splist -N -F "%p" | while read PTR; do
    sh last-printer-activity.sh "$PTR"
    echo ""
done

Treat the result as three groups rather than two: printers with dated evidence of recent use, printers with dated evidence of disuse, and printers about which the product knows nothing. The third group is usually the largest, and it is a question for the people who own the printers rather than for the spooler.

Identifying and Removing Redundant Xi-Batch Jobs
What the scheduler records about a job's last run, and why a repeating job in Err or Abrt is a fault to fix