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:
- .device, if present - the interface settings
- a file named after the printer's current form type, with any suffix after a dot or a hyphen removed
- 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:
| Position | Content |
|---|---|
| 1 | Date the job started printing, as dd/mm - or mm/dd on a machine four or more hours west of UTC. No year. |
| 2 | Time the job started printing |
| 3 | Time the job finished printing |
| 4 | Elapsed time from submission to finish, h:mm:ss |
| 5 | User who submitted the job |
| 6 | Job title, or the unnamed-job placeholder |
| 7 | Form type |
| 8 | Characters sent to the device |
| 9 | Device |
| 10 | Printer name |
| 11 | Priority |
| 12 | Pages printed |
| 13 | Job 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.
- Halt the printer at the end of its current job:
sphalt printername
- Edit the setup file - either the form-specific one or default:
vi /var/spool/xi/printers/printername/default
- Add the keyword. A relative path is created in the printer's own directory:
logfile=.usage
- 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.