How a Job Is Chosen for a Printer
The Xi-Text scheduler does not look for a printer for a job. It works the other way round: it walks its own list of printers, and for each printer that is idle it walks the job queue from the front and takes the first job that passes every test. It then moves on to the next idle printer. The scan runs whenever anything happens to a job or a printer.
An Xi-Text job has no state and no status field. Unlike an Xi-Batch job, there is nothing to inspect that says "waiting", "blocked" or "ready" - the queue holds a set of job attributes, and selection is a filter applied to them afresh on every scan. What you can observe about a job is its attributes and its position in the queue, and nothing else.
The scheduler writes nothing when it passes over a job. There is no error, no warning, no timeout and no entry in the report file. A job whose form type matches no printer, or whose class code matches no printer, sits in the queue in its proper place for ever, looking exactly like a job that is simply waiting its turn. This is the single most important thing to know about diagnosing a stuck Xi-Text job: the diagnosis is always made by comparing the job's attributes with the printers' attributes, never by reading a log.
The queue is held in working-priority order, and sqlist lists it in that order, so a job's position in sqlist output is its position in the selection order.
Why a Queued Job Does Not Print
These are the tests the scheduler applies, in the order it applies them. The first four decide whether a printer is offered any job at all; the rest are applied to each job in turn until one passes.
- Scheduling is suspended
- ssuspend stops the scheduler selecting any job on any printer until the period it was given expires or srelease is run. Every printer stays idle and the queue simply stops moving. Nothing in splist, sqlist or spq reports that scheduling is suspended, and nothing is written to the report file, so this has to be ruled out by asking rather than by looking.
- The printer is not idle
- only a printer displaying idle is offered a job. halted, offline, error, startup, printing, shutdown and a/w oper are all passed over. Visible in the state column of splist. See the article on printer states.
- The printer has already been given a job
- between the moment the scheduler selects a job and the moment the printer takes it, the printer is skipped. This clears itself within a scan and is never the explanation for a job that has waited for minutes.
- The printer belongs to another host
- a scheduler selects jobs only for its own printers. Printers on other hosts appear in splist as host:printer and are driven by that host's scheduler.
- The job's copy count is zero
- shown as 0 in the Cps column of sqlist. Such a job is accepted, queued and never printed. It is the very first test, so nothing else about the job matters. The usual causes are a job that has already printed and was retained in the queue, and a user whose permitted copy count is zero - spr quietly sets the count to zero in that case rather than refusing the job.
- The job is already assigned to a printer, or is being aborted
- an assigned job is on its way to a printer, and its Printer column names one. A job being aborted disappears shortly afterwards.
- A hold time has been set and has not yet arrived
- the hold time is shown in the Hold column of sqlist (format letter %H), and the column is empty when no hold is set. This is the only thing in Xi-Text that corresponds to a job being "on hold"; there is no separate held flag.
- The job is smaller than the printer's minimum size
- the size is the whole job in bytes, disregarding any page range set on it. A minimum of zero accepts everything.
- The job is larger than the printer's maximum size
- a maximum of zero means no upper limit. A printer with limits set shows < or > markers in spq.
- The job came from another host and the printer is marked local-only
- a local-only printer takes local jobs only. Note that when the licence does not cover networking every printer is forced local-only, so on such a system no job from another host ever prints.
- The class codes do not intersect
- a class code is a 32-bit mask written as the letters A to P for the first sixteen bits and a to p for the rest, with a run of adjacent bits abbreviated as a range such as A-D. The job's code and the printer's code are ANDed, and if no bit is common to both the printer will not take the job. A job's class code is inherited from the submitting user's, so this is usually a property of the user rather than of the job.
- The form type does not match the printer's paper type
- the printer's form type is cut at its first . or -, and the job's form type must begin with that prefix, compared without regard to case, with the next character of the job's form type being either the end of the name or a separator. The printer's own suffix takes no part in the comparison. See the article on form types and paper types for the rule in full.
- The job names a printer and this printer's name does not match it
- the job's printer field is a pattern, not a name. It is matched against the printer's name without regard to case and understands *, ? and […], so lp* restricts a job to every printer whose name begins lp. An empty field means any printer.
Priority is not on this list. Priority decides the order of the queue, not whether a job is eligible. A job at the back of the queue still prints as soon as a printer becomes free that no job ahead of it can use.
Privileges are not on this list either. The scheduler applies no permission test of any kind when it selects a job. Every privilege - the form types a user may request, the printers a user may name, the priority a user may set, the number of copies a user may take - is enforced by spr, sqchange and spq when the job is submitted or changed, and a refusal there is reported to the user immediately with a message. A permission problem therefore shows up as a job that was never accepted, never as a job that sits in the queue. The one exception is indirect: the class code and the copy count that a user's privileges produce are carried on the job and are tested, which is why those two appear above.
Diagnostic Approach
Step 1: Check the Job
sqlist takes job numbers as arguments and restricts its listing to them. It prints the same table it always prints; there is no per-job detail display on the command line.
# Every field that takes part in the selection decision
sqlist -H -F "%N %u %f %c %C %p %H %K %P" 15033
The fields are:
- %N job number, %u owner
- %f form type
- %c copies - 0 here means the job will never print
- %C class code, as letters
- %p priority
- %H hold time, empty when no hold is set
- %K size in K
- %P the printer it is printing on, or the printer pattern it asks for, or empty for any printer
A job you cannot see is still there. sqlist and spq hide every job whose class code does not intersect your own, and the -C option cannot widen the mask beyond your own class code unless you hold the override-class privilege, which is not usually given. If a user reports a job that no listing shows, compare class codes before concluding the job was never submitted.
Step 2: Check the Printers
# The printer attributes that take part in the decision
splist -H -F "%p %d %f %s %c %y %z %j %u"
# Or open spq on the printer screen; o switches between the two screens
spq -p
The fields are the printer name, its device, its form type, its state, its class code, its minimum and maximum job sizes, and the job number and user it is printing. A printer in offline or error shows a reason after a colon where its filter or its device supplied one.
splist also accepts printer names as arguments, and needs no privilege.
Step 3: Match Form Types
Only the part of the printer's form type before the first . or - is compared, and the comparison ignores case. A printer showing a4.ps accepts a job asking for a4, A4, a4.pcl or a4-p12, and rejects a4wide.
ptr1 <lp0> a4.ps idle Match - this printer will take the job
ptr2 <lp1> letter.ps idle No match
ptr3 <lp2> a4.ps halted Match, but not idle, so not offered the job
Bear in mind that the form type splist shows is the form type of the last job the printer took, not necessarily the one an operator set: the printer's stored form type is overwritten by each job it prints.
Step 4: Check the Job's Position in the Queue
There is no job state to read, so the question to answer is whether the job is being passed over or is simply behind others.
# The queue in selection order, unprinted jobs only
sqlist -H -y -F "%N %u %f %c %C %p %H %P"
If jobs below your job in this listing are printing while it is not, the job is being rejected by one of the tests above, and the fields in the same listing say which. If nothing at all is moving and every printer is idle, suspect suspended scheduling.
Step 5: Check the System Log
tail -50 /var/spool/xi/spd/spshed_reps
Nothing about job selection is ever written here. The report file receives printer startup and setup-file failures, device and filter errors, and the scheduler's own fatal errors. A job that is not being selected produces no entry, and a clean, healthy system writes nothing at all. Read this file to find out why a printer is in error, not to find out why a job is waiting. The same file is shown by X in spq.
The spool directory is the compiled-in default above; on a system where it has been moved, take the real value from SPOOLDIR in /etc/xi/textconfig.
Resolving Common Scenarios
Scenario 1: No Printer for the Job's Form Type
Symptom:
The job sits in the queue indefinitely and no printer's paper type matches it.
sqlist -N -F "%N %f" 15033
# 15033 labels.ps
splist -N -F "%p %f %s"
# ptr1 a4.ps idle
# ptr2 letter.ps idle
# no printer whose paper type is labels
Option A: change the job's form type
sqchange -f a4.ps 15033
The job prints on ptr1 as soon as the scheduler next scans. Changing another user's job needs the "other jobs" privilege, and the new form type must be one the user is allowed to request.
Option B: change the printer's form type
spstart ptr1 labels.ps
One command is enough: given a paper type that differs from the printer's, spstart halts the printer, changes the form type and starts it again. Use spchange ptr1 labels.ps for a printer that is to stay halted. Both need the add/delete-printers privilege, which ordinary users do not have by default.
Option C: add a printer for the form type
spadd -l /dev/lp2 labels-ptr labels.ps
spstart labels-ptr
The device is given with -l and is required; the printer name and the form type are positional arguments in that order. A new printer is created halted and has to be started.
Scenario 2: All Printers Halted
Symptom:
splist -N -F "%p %s"
# every printer shows halted, and jobs accumulate
This is the normal state of every printer after the spooler has been restarted: the scheduler sets every printer it reads from the saved printer list to halted, whatever state it was in before, and does not start any of them.
Solution:
# Start one printer
spstart ptr1
# Start every printer - the name is a pattern, and -f is required
# whenever the pattern matches more than one printer
spstart -f '*'
spstart with no printer name at all starts the spooler, not the printers. A start that fails is not reported to the caller: spstart returns success because the printer is started by the scheduler in a separate process, and the reason for the failure appears only in spshed_reps. Confirm with splist, or scriptably with spstat.
Scenario 3: Job Restricted to an Unavailable Printer
Symptom:
sqlist -N -F "%N %P" 15033
# 15033 ptr2
splist -N -F "%p %s" ptr2
# ptr2 halted
Option A: remove the restriction and let the scheduler choose
sqchange -P - 15033
A bare - clears the field. The job then prints on any idle printer whose paper type and class code match.
Option B: start the printer the job asks for
spstart ptr2
Option C: point the job at a different printer
sqchange -P ptr1 15033
The value is a pattern, so sqchange -P 'ptr[13]' 15033 widens the job to two printers rather than moving it to one. A user without the "other printers" privilege may only name printers matching the pattern recorded against them.
Scenario 4: The Job Has a Hold Time
Symptom:
The job does not advance although a suitable printer is idle, and the Hold column shows a time in the future.
sqlist -H -F "%N %u %H" 15033
Solution:
sqchange -n - 15033
A bare - to -n clears the hold time and the job becomes eligible on the next scan. sqchange -n 0 also releases the job, but it sets the hold to the current instant rather than removing it, so the Hold column goes on showing a time in the past. In spq the hold time is the H key on the job screen. Find out why the job was held before clearing it.
Scenario 5: Class Codes Do Not Intersect
Symptom:
A printer with a matching paper type is idle and the job still does not print.
sqlist -N -F "%N %C" 15033
# 15033 A
splist -N -F "%p %c" ptr1
# ptr1 B
A class code is a 32-bit mask written as letters - A to P for bits 1 to 16 and a to p for bits 17 to 32 - and adjacent bits are abbreviated as a range, so a printer that accepts everything shows A-Pa-p. The test is a bitwise AND. Here the job carries only A and the printer only B, so they have no bit in common and the printer will not take the job. Codes AB and BC do intersect, in B. spq shows the same value as 32 columns with a full stop for each clear bit.
Option A: change the job's class code
sqchange -C B 15033
A user without the class-override privilege can only set bits that are already in their own class code, and a value that leaves nothing is refused. Note that the letter is not a free-form label: it names a bit position, and anything after the first character that is not a letter in the range A to P or a to p, a range separator or a full stop is discarded silently.
Option B: change the printer's class code
spchange -S AB ptr1
Option C: let the printer take everything
spchange -S A-Pa-p ptr1
Both need the add/delete-printers privilege. In spq the printer's class code is the C key and the printer must be halted first; the command line does not require that.
Scenario 6: The Job Is Behind Others in the Queue
Symptom:
Other jobs print first.
The queue is ordered by working priority, which is derived from the job's priority when the job is enqueued. A new job is inserted from the back of the queue forwards, past every job of lower priority, and its working priority is reduced by a fixed decrement for each job it passes. So a high-priority job joining a long queue of lower-priority jobs does not necessarily reach the front - the further forward it walks, the less advantage it retains. The decrement is normally 1 and is set when the spooler is started.
sqlist -H -y -F "%N %u %p %f"
Solution:
sqchange -p 200 15033
The job is taken off the queue and re-inserted under the same rule, so the effect of a priority increase depends on what is ahead of it. Priorities run from 1 to 255. Changing a priority needs the change-priority privilege, which is not in the default privilege set, and without the any-priority privilege the value must lie between the user's own minimum and maximum - 100 and 200 for a default user, whose default priority is 150.
Scenario 7: The Job Is Outside the Printer's Size Limits
Symptom:
A printer with a matching paper type and class code is idle and still does not take the job.
splist -H -F "%p %y %z" ptr1
The two fields are the minimum and maximum job size the printer will accept, in bytes. The size compared is the whole job, disregarding any page range set on it. Zero in either field means no limit in that direction.
Solution:
In spq, on the printer screen, L sets the lower limit and U the upper; entering 0 removes the limit. The printer must be halted and the halt/start privilege is required. A printer with a limit in force is marked < or > on the printer screen.
Scenario 8: The Job Has Zero Copies
Symptom:
The job is in the queue, every printer attribute matches, and it never prints.
sqlist -N -F "%N %c" 15033
# 15033 0
Zero copies is the first test the scheduler applies, and nothing about the job is examined after it. A job that has already printed and was retained in the queue shows zero here and is expected to. A job that has never printed and shows zero was submitted by a user whose permitted copy count is zero, in which case spr accepted the job and set the count to zero instead of refusing it.
Solution:
sqchange -c 1 15033
Raise the user's permitted copy count as well, or every job they submit will do the same thing.
Systematic Troubleshooting Checklist
In the order the scheduler applies the tests:
1. The system
- Scheduling has not been suspended with ssuspend
- At least one printer is in the idle state and belongs to this host
2. The job
- Copies is not 0
- The job is not already assigned to a printer
- No hold time is set, or it has passed
3. The job against the printer
- The job size is within the printer's minimum and maximum
- A remote job is not being offered a local-only printer
- The class codes share at least one bit
- The paper type - the part before the first . or - - matches
- The job's printer pattern, if it has one, matches the printer's name
4. If all of the above pass and the job still waits
- Check that jobs are being selected at all: is anything printing anywhere?
- Check the report file for a printer that is failing to start, which leaves the printer out of idle and therefore out of the running
Verification After Resolution
Selection happens within a scan, so the effect of a change is visible almost at once.
# The queue, and the printers, side by side
sqlist -H -y
splist -H
The job should show a printer name in its Printer column, that printer should show the job number in splist and be in the printing state, and the job should leave the queue when it completes unless it was submitted to be retained.
spq shows both screens and refreshes itself; -R sets the interval.
Best Practices
Leave the job's printer field empty unless it has to be set:
A job that names no printer can be taken by any printer whose paper type and class code suit it, which is the arrangement least likely to strand a job.
Use consistent paper type names:
Only the paper type takes part in matching, so paper types are the names that have to agree across printers. Suffixes are free to differ.
Do not use class codes to express preference:
A class code is an absolute barrier, not a weighting. A job whose class code intersects no printer's waits for ever with no indication of why.
Start the printers after every spooler restart:
Restarting the spooler leaves every printer halted. A startup script that starts the spooler must start the printers too, which is what spstart -f '*' is for.
Watch the length of the unprinted queue:
sqlist -N -y | wc -l
-N suppresses the heading and -y restricts the listing to jobs that have not printed, so the count is the number of jobs still waiting. Remember that jobs outside your own class code are not counted because they are not shown.
Tell users which form types exist:
A job submitted with a paper type no printer carries is accepted without complaint and waits silently. Publishing the list of valid paper types prevents the failure that is hardest to diagnose.