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
You want to know when a printer last printed, usually to decide whether it can be removed. Xi-Text keeps no last-used time, so the answer comes from a per-printer log where one was configured in advance, and from inference where none was.
What the product records
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. 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 only. 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.
The five methods below are given in order of how much they prove, and each says what it cannot tell you.
Method 1: the printer log file
This is the only method that produces evidence, and it works 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. 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. 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 date field carries no year. 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 holds as long as nobody has copied or rotated the file; 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. Where it applies it gives a dated answer with no 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 leaves the question open. 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, 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 answers the narrow question "did this printer fail recently", and the X key in spq displays the same file from inside the queue manager.
A script that reports what is known
The following combines the log-file method with the state check, and says explicitly when it has no evidence. 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. Treat "no evidence" as its own answer, distinct from "unused".
Adding logging to a printer
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.
Rotating a log file
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 guarantees no line is being written while the copy is taken. The archive file's modification time becomes the rotation date, so keep the archives where the age of the last activity might be asked about later.
Surveying every printer
Enable logfile on every printer as a matter of course; the overhead is one line written per completed job, and a printer without it has no history and never will have.
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
The result falls into three groups: 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.
Related articles
Identifying and Removing Redundant Xi-Text Jobs and Printers
What the queue deletes by itself, why a printer's state is no evidence of disuse, and the two-stage removal
Configuring Xi-Text Form Types, Paper Types and Suffixes
What a form type is, the exact rule matching a job to a printer, and how a suffix changes without stopping the printer
Diagnosing Why an Xi-Text Job Will Not Print
Every condition tested before a job reaches a printer, and why an unmatched job waits for ever, reporting nothing