Skip to content
Xi Software

Xi-Text File Permissions and Ownership

Which files Xi-Text needs, who must own them, why almost every command is set-user-id, and the errors when it is wrong

Xi-TextXi-Textinstallationpermissionstroubleshooting

Xi-Text refuses to start when its spool directory has the wrong owner or mode, and its commands fail when a set-user-id bit has been lost. This article gives the ownership and permissions each file needs, how to check them, and how to put them back.

Which account owns what

Xi-Text runs as a system service. The scheduler (spshed) is installed set-user-id to root, reads the licence and the password file, then permanently drops to the spooler account before it touches anything in the spool directory. Everything it creates from that point on belongs to spooler.

The live job and printer queues are held in shared memory. The files in the spool directory are the saved copies, rewritten when the queue changes and at shutdown, plus the lock files that serialise access to the shared memory segments. Client commands reach the scheduler through a Unix domain socket in a separate sockets directory.

Before it opens anything, spshed changes directory to the spool directory and validates it. The directory must be owned by the spooler account and must grant the owner read, write and execute; if either test fails the scheduler prints a message and aborts. The same test is applied to each numbered subdirectory when SPOOLSUBDS is in use.

The spooler account and its group

Spooler User
System account that owns the Xi-Text processes and files

Name: spooler

Created by the package during installation

Spooler Group
The group differs between distribution formats, and Xi-Text does not care which it is

The RPM packages put the account into an existing system group - daemon, or bin or root if daemon is absent - and label the installed files group daemon

The Debian packages create a group of the account's own name

Because of this, always write chown spooler: with an empty group rather than chown spooler:spooler, which fails on an RPM install

Verify the account exists:

id spooler

The group in the output depends on how Xi-Text was installed, for example:

uid=xxx(spooler) gid=2(daemon) groups=2(daemon)

The directories and files

Finding your own directories

The paths below are the compiled-in defaults. A site may have moved any of them, and a custom build blend moves all of them, so start by reading the master configuration file:

grep -E 'SPOOLDIR|SPUDSOCKS|SPOOLPT|SPROGDIR|SPHELPDIR|USERPATH' /etc/xi/textconfig

A setting that is still at its default is written out commented, so a line beginning with # means the default below applies. An uncommented line is the path in use on that system. Xi-Text reads its directories from this file only - an environment variable named SPOOLDIR has no effect.

The spool directory

Default location: /var/spool/xi/spd/

drwxr-xr-x  spooler  daemon  /var/spool/xi/spd/

Purpose: Saved job and printer queues, spooled print data, and system state

Requirement: Owned by spooler and mode rwx for the owner, or the scheduler aborts at startup. The packages create it mode 755; the Administration manual asks for 700, which the scheduler also accepts and which keeps the job list private.

Contains:

  • Spooled job data (SPnnnnnnnn), page files (PFnnnnnnnn) and error files (ERnnnnnnnn), plus SPDEnnnnnn temporary files while a job is printing
  • Saved queues (spshed_jfile, spshed_pfile)
  • System log (spshed_reps)
  • User permissions file (spufile23 - the number is the format version of your release)
  • Lock files (spjob.lock, spptr.lock, spxfer.lock), present only while the spooler is running
  • Numbered subdirectories 000, 001 … when SPOOLSUBDS is set, each holding a share of the job files

The sockets directory

Default location: /var/spool/xi/spuds/

drwxr-xr-x  spooler  daemon  /var/spool/xi/spuds/

Purpose: The Unix domain sockets spshed_req and spshed_req_udp, through which every client command reaches the scheduler. The scheduler creates them at startup, sets them mode 600 and owner spooler, and removes them when it stops.

Several operating systems, including AIX, Solaris and HP-UX, do not check permissions on a socket before letting a client connect, but all of them check the containing directory - so the permissions of this directory are what protect the request channel on those platforms.

The printers directory

Default location: /var/spool/xi/printers/

drwxr-xr-x  spooler  daemon  /var/spool/xi/printers/

Purpose: One subdirectory per printer, holding that printer's setup files

drwxr-xr-x  spooler  daemon  printers/ljet/
drwxr-xr-x  spooler  daemon  printers/colour/

The program and help directories

Xi-Text's programs are in three locations:

  • /usr/local/bin - the user commands (spq, spr, splist, sqlist, sqchange, sqdel, spuser, spstart, sstop and the rest), and the xt- prefixed administration tools
  • /usr/libexec/xi - the internal programs the user never runs directly (spshed, spd, spdinit, spexec, spmdisp, sppwchk, spwrite, spjobdump, xtnetserv, the LPD interface and the terminal-server interfaces), together with the licence file .xitext.lic
  • /usr/share/xi - the help and message files (int-config, rest.help, spq.help, spuser.help, splpd.help)

The master configuration file itself lives at /etc/xi/textconfig, alongside /etc/xi/text-hosts and, where used, /etc/xi/usermap and /etc/xi/pwfile.

The permissions each file needs

Files in the spool directory

Job files:

-r--------  spooler  daemon  SP00000123
-rw-------  spooler  daemon  PF00000123
-rw-------  spooler  daemon  ER00000123
  • SP files are created 400 - the spooled data is written once and then only read
  • PF and ER files are 600
  • All of them hold user data and none of them is readable by anyone but spooler

State files:

-rw-------  spooler  daemon  spshed_jfile
-rw-------  spooler  daemon  spshed_pfile
-rw-------  spooler  daemon  spshed_reps
-rw-------  spooler  daemon  spufile23
  • All four are 600. There is no file in the spool directory that Xi-Text expects to be world-readable, the system log included
  • The user permissions file carries the format version in its name, so it is spufile23 on a release 23 system
  • Ownership matters more than the mode: the scheduler forces the owner of the log and the user file back to spooler each time it creates them, but it will not tighten a mode that has been widened by hand

Lock files:

-rw-------  spooler  daemon  spjob.lock
-rw-------  spooler  daemon  spptr.lock
-rw-------  spooler  daemon  spxfer.lock
  • Permissions: 600, created fresh at every startup and removed at shutdown
  • They are empty by design; a zero length is normal

Files named spmm_jobi, spmm_jobd, spmm_ptrs and spmm_xfer belong to a memory-mapped variant other than the one that ships. On a supported installation they are absent, and their presence means the spool directory is carrying leftovers from an older or a special build.

Files in the programs directory

The internal programs in /usr/libexec/xi carry a different mode, owner or group almost one by one:

Mode Owner Group Programs
4755 root root spshed, spexec, spmdisp, sppwchk, xtnetserv, xilp, xtlpc, xtlpd, xtlpq, xtlprm
6755 spooler lp spd
4755 spooler daemon spjobdump
2755 spooler tty spwrite
755 spooler daemon spdinit, sendudp, longlist, shortlist, remove, pclbanner, psbanner, xtelnet, xtftp, xtlhp
644 spooler daemon Sysvardescr, xtlpc-ctrl, xtlpd-ctrl, xtlhp-ctrl, xtsnmpdef

The ten root-owned programs need root for reasons of their own: the scheduler reads the password file before it drops privilege, spexec runs a user's own macro command back under that user's identity and needs root to shed the set-user-id one cleanly, sppwchk validates passwords, and the LPD programs bind the privileged printer service port. Changing any of them to be owned by spooler stops it working.

spd is set-group-id to lp so that it can open printer devices that are group lp, and spwrite is set-group-id to tty so that it can write messages to users' terminals. Replacing either group with the spooler account's own group breaks that access.

User commands:

-rwsr-xr-x  spooler  daemon  /usr/local/bin/spq
-rwsr-xr-x  spooler  daemon  /usr/local/bin/spr
-rwsr-xr-x  spooler  daemon  /usr/local/bin/sstop
  • Every ordinary user command is 4755 owned by spooler - spq, spr, splist, sqlist, sqchange, sqdel, spuser, spulist, spuchange, spstart, sstop, spcharge, rspr, netwcmd, lpstatc, sp.lpq, sp.lprm and the links spadd, spchange, spconn, spdel, spdisconn, sphalt, spinter, spnok, spok, spstat, spstop, srelease, ssuspend
  • The set-user-id bit is what lets an ordinary user reach the socket and the shared memory, both of which are private to spooler. The command therefore runs as spooler, and decides what the caller may do by looking their real user id up in the user permissions file
  • The xt- prefixed administration tools (xt-backup, xt-checklic, xt-triallic, xt-vwrite, xt-ripc, xt-setspdir, xt-cjlist, xt-cplist, xt-spuconv) are plain 755 and are meant to be run by root

Help and message files:

-rw-r--r--  spooler  daemon  /usr/share/xi/rest.help
-rw-r--r--  spooler  daemon  /usr/share/xi/spq.help
-rw-r--r--  spooler  daemon  /usr/share/xi/int-config
  • Permissions: 644 - these must be readable by every user
  • They may be owned by another account provided they stay readable

Configuration and licence:

-rw-r--r--  root     root    /etc/xi/textconfig
-rw-r--r--  root     root    /usr/libexec/xi/.xitext.lic
  • Both are 644 and owned by root, written by the installation
  • The licence file must stay readable: the scheduler checks it before it drops to the spooler account, but the user commands read it as spooler

Printer setup files

Each printer has a directory under the printers directory, named after the printer. Before printing, spd runs spdinit, which changes directory into it and reads .device first, then a file named after the form type, then the file called default. If the directory or every one of those files is unreadable, the job fails with a setup error rather than a permission message.

-rw-r--r--  spooler  daemon  printers/ljet/.device
-rw-r--r--  spooler  daemon  printers/ljet/default
-rw-r--r--  spooler  daemon  printers/ljet/a4
  • The printer directory is created 755 and the files 644, both owned by spooler
  • Only the spooler account has to be able to read them, so the group and world bits can be removed if a site wants the setup hidden
  • The directory itself must remain searchable by spooler, because spdinit changes into it
  • A printer help file called -Help may also be present, read by spq when a user asks for help on that printer

.device begins with a dot. A shell glob of the form printers/ljet/* does not match it, so a repair loop written that way leaves that file untouched.

Checking permissions

A check script

This script reports a verdict per item. It resolves the directories from the master configuration file, so it works on a site that has moved them and on a custom blend. It uses only POSIX shell and find, so it runs on Linux, Solaris, AIX and HP-UX alike. Run it as root.

#!/bin/sh
# Xi-Text permission check. Run as root.
# Lines marked FAIL need attention; "absent" is only a problem
# for a file you expect to exist on a running system.

CONF=/etc/xi/textconfig
SPUSER=spooler

# Read a path from the master config, falling back to the compiled-in
# default. Settings left at the default are written out commented, so
# only a site that changed one has an uncommented line.
cfg() {
    val=`sed -n "s/^[   ]*$1[   ]*=[    ]*//p" $CONF 2>/dev/null | tail -1`
    if [ -n "$val" ]
    then echo "$val"
    else echo "$2"
    fi
}

SPOOLDIR=`cfg SPOOLDIR /var/spool/xi/spd`
SPUDSOCKS=`cfg SPUDSOCKS /var/spool/xi/spuds`
SPOOLPT=`cfg SPOOLPT /var/spool/xi/printers`
SPROGDIR=`cfg SPROGDIR /usr/libexec/xi`
SPHELPDIR=`cfg SPHELPDIR /usr/share/xi`
USERPATH=`cfg USERPATH /usr/local/bin`

verdict() {
    if [ -n "$2" ]
    then echo "ok    $1"
    else echo "FAIL  $1 - $3"
    fi
}

chkdir() {
    if [ ! -d "$1" ]
    then echo "FAIL  $1 - directory missing"
         return
    fi
    verdict "$1 owner" "`find $1 -prune -user $SPUSER 2>/dev/null`" \
            "not owned by $SPUSER"
    verdict "$1 owner rwx" "`find $1 -prune -perm -700 2>/dev/null`" \
            "owner lacks rwx, scheduler will refuse to start"
}

chkfile() {
    if [ ! -f "$1" ]
    then echo "-     $1 - absent"
         return
    fi
    verdict "$1 owner" "`find $1 -prune -user $SPUSER 2>/dev/null`" \
            "not owned by $SPUSER"
    verdict "$1 mode $2" "`find $1 -prune -perm $2 2>/dev/null`" \
            "mode is not $2"
}

chksuid() {
    verdict "$1 set-user-id $2" \
            "`find $1 -prune -perm -4000 -user $2 2>/dev/null`" \
            "must be mode 4755 owned by $2"
}

echo "=== Directories ==="
chkdir $SPOOLDIR
chkdir $SPUDSOCKS
chkdir $SPOOLPT
chkdir $SPROGDIR
chkdir $SPHELPDIR

echo "=== Spool files ==="
chkfile $SPOOLDIR/spshed_jfile 600
chkfile $SPOOLDIR/spshed_pfile 600
chkfile $SPOOLDIR/spshed_reps 600
for f in $SPOOLDIR/spufile*
do
    chkfile $f 600
done

echo "=== Set-user-id programs ==="
chksuid $SPROGDIR/spshed root
chksuid $SPROGDIR/spexec root
chksuid $USERPATH/spq $SPUSER
chksuid $USERPATH/spr $SPUSER
chksuid $USERPATH/sstop $SPUSER

echo "=== Printer setup ==="
for d in $SPOOLPT/*
do
    if [ -d "$d" ]
    then chkdir $d
    fi
done

A clean run prints ok for every line. Fix any FAIL before looking further. An absent spshed_jfile on a system that has been running is a finding.

Individual checks

# Spool directory as a whole
ls -la /var/spool/xi/spd/

# The saved queues, the log and the user file
ls -l /var/spool/xi/spd/spshed_jfile
ls -l /var/spool/xi/spd/spshed_pfile
ls -l /var/spool/xi/spd/spshed_reps
ls -l /var/spool/xi/spd/spufile*

# The request sockets, present only while the spooler runs
ls -l /var/spool/xi/spuds/

# The set-user-id bits
ls -l /usr/libexec/xi/spshed
ls -l /usr/local/bin/spr

# Verify your user context
id

Fixing permissions

Stop the spooler before correcting anything in the spool directory, or the ownership of spshed and spd. The remaining files can be corrected with the spooler running; check with Xi Software Support first.

# Stop Xi-Text
sstop -y

Fixing the spool directory

Leave the group off the chown so that the account keeps whichever login group the installation gave it.

# Directory ownership and permissions
chown spooler: /var/spool/xi/spd
chmod 755 /var/spool/xi/spd

# Saved queues, log and user permissions file
chown spooler: /var/spool/xi/spd/spshed_jfile
chown spooler: /var/spool/xi/spd/spshed_pfile
chown spooler: /var/spool/xi/spd/spshed_reps
chown spooler: /var/spool/xi/spd/spufile*
chmod 600 /var/spool/xi/spd/spshed_jfile
chmod 600 /var/spool/xi/spd/spshed_pfile
chmod 600 /var/spool/xi/spd/spshed_reps
chmod 600 /var/spool/xi/spd/spufile*

# Sockets directory
chown spooler: /var/spool/xi/spuds
chmod 755 /var/spool/xi/spuds

# Spooled job data, if any is queued
chown spooler: /var/spool/xi/spd/SP* /var/spool/xi/spd/PF* /var/spool/xi/spd/ER*
chmod 400 /var/spool/xi/spd/SP*
chmod 600 /var/spool/xi/spd/PF* /var/spool/xi/spd/ER*

Fixing the program directories

# Internal programs that must be root
chown root:root /usr/libexec/xi/spshed /usr/libexec/xi/spexec \
                /usr/libexec/xi/spmdisp /usr/libexec/xi/sppwchk \
                /usr/libexec/xi/xtnetserv /usr/libexec/xi/xilp \
                /usr/libexec/xi/xtlpc /usr/libexec/xi/xtlpd \
                /usr/libexec/xi/xtlpq /usr/libexec/xi/xtlprm
chmod 4755 /usr/libexec/xi/spshed /usr/libexec/xi/spexec \
           /usr/libexec/xi/spmdisp /usr/libexec/xi/sppwchk \
           /usr/libexec/xi/xtnetserv /usr/libexec/xi/xilp \
           /usr/libexec/xi/xtlpc /usr/libexec/xi/xtlpd \
           /usr/libexec/xi/xtlpq /usr/libexec/xi/xtlprm

# The printer daemon keeps its lp group, the writer its tty group
chown spooler:lp  /usr/libexec/xi/spd
chmod 6755        /usr/libexec/xi/spd
chown spooler:tty /usr/libexec/xi/spwrite
chmod 2755        /usr/libexec/xi/spwrite

# User commands
chown spooler: /usr/local/bin/spq /usr/local/bin/spr /usr/local/bin/sstop
chmod 4755     /usr/local/bin/spq /usr/local/bin/spr /usr/local/bin/sstop

# Help files
chown spooler: /usr/share/xi/*.help /usr/share/xi/int-config
chmod 644      /usr/share/xi/*.help /usr/share/xi/int-config

Fixing the printers directory

The -R and the trailing dot are what catch the dot files; a plain * glob misses .device.

# Main directory
chown spooler: /var/spool/xi/printers
chmod 755 /var/spool/xi/printers

# Printer subdirectories and everything in them
for pdir in /var/spool/xi/printers/*/
do
    chown -R spooler: "$pdir"
    chmod 755 "$pdir"
    find "$pdir" -type f -exec chmod 644 {} \;
done

Restarting Xi-Text

# After fixing permissions
spstart

On a systemd host the packaged unit runs spstart for you, so systemctl start xitext is equivalent.

What a permission problem looks like

The scheduler will not start

Symptom: spstart returns and no spshed process is left running. The reason is in the system log, which the scheduler writes before it exits.

The messages, and what each one means:

  • "Cannot stat spool directory %s - aborting" - the directory named in the configuration does not exist
  • "Spool directory %s is owned by %u8 not %u7 - aborting" - it exists but belongs to the wrong account
  • "Spool directory %s does not have rwx permissions - mode is %o8 - aborting" - the owner is missing one of read, write or execute
  • "Subdirectory %t of %s is owned by …" and the matching permissions message - the same two tests applied to a SPOOLSUBDS numbered subdirectory
  • "Cannot change directory to spool directory" - the path is not reachable, usually because a parent directory is not searchable
  • "Could not create job save file" or "Could not create printer save file" - the directory passed its checks but spshed_jfile or spshed_pfile could not be opened

Check:

tail -20 /var/spool/xi/spd/spshed_reps
ls -ld /var/spool/xi/spd
ls -l /usr/libexec/xi/spshed

spr or spq cannot reach the queue

Symptom: spr or spq reports "Panic! Cannot read job queue. This may be due to incorrect installation. Please check that the owner of the shared memory segments are the same as the set-user id of \", or the equivalent "Cannot read printer list" message.

Cause: the command is not running as spooler. Almost always the set-user-id bit or the owner of the command has been lost - a copy, a restore from backup, or an editor that rewrote the file will do it.

Check:

ls -l /usr/local/bin/spr /usr/local/bin/spq
ls -ld /var/spool/xi/spuds

If the commands are correct, the sockets directory is the next place to look: a client that cannot reach spshed_req in it gets "Xi-Text spooler is not running" even though the scheduler is up.

Jobs queue and the printer goes to error

Symptom: jobs sit in the queue and the printer goes into an error state.

The messages:

  • "There does not appear to be a directory for the printer name %s in the printers directory %t. This must be created and accessible to \." - the printer's directory is missing or spooler cannot search into it
  • "There does not appear to be a setup file (even a possible default one of `%t') in the printer directory for printer name %s." - the directory is reachable but .device, the form-type file and default are all missing or unreadable

Check:

ls -la /var/spool/xi/printers/ljet/
ls -l /dev/lp0    # or whatever device the printer uses

The user permissions file

Symptom: "Trouble creating user file - please check Xi-Text is installed correctly", or privileges that were set with spuser do not take effect.

Check:

ls -l /var/spool/xi/spd/spufile*

There should be exactly one such file and it should be 600 owned by spooler. More than one means an upgrade left the previous version's file behind; a wrong owner means the running commands cannot rewrite it.

Printer device permissions

Printer devices must be writable by the spooler account. Ptrinstall arranges this at printer-installation time: it inspects the device, and if it is already owned by spooler it adds owner write, otherwise it offers to change the ownership or, failing that, to widen the group or world permissions.

# Serial port
ls -l /dev/ttyS0

# Parallel port
ls -l /dev/lp0

# Network printers use no device file and need no device permissions

Fixing device permissions

The tidiest fix uses the group that spd is already set-group-id to, which is lp where the system has that group:

chown root:lp /dev/ttyS0
chmod 660 /dev/ttyS0

Failing that, give the device to the spooler account:

chown spooler: /dev/ttyS0
chmod 600 /dev/ttyS0

On many systems device ownership is reset at boot, or by the device manager when the port is hot-plugged, and the change is lost. Where that happens, make the rule permanent in the platform's own device-permission mechanism rather than repeating the chown - the mechanism differs on Linux, Solaris, AIX and HP-UX, so consult that system's documentation.

Security

Set-user-id programs

Every user command is set-user-id to spooler and ten of the internal programs are set-user-id to root, as listed above. Removing the bit from any user command makes that command fail with the "Panic!" message.

The requirement, precisely:

  1. User commands in /usr/local/bin: mode 4755, owner spooler
  2. The ten root programs in /usr/libexec/xi: mode 4755, owner root
  3. spd: mode 6755, owner spooler, group lp; spwrite: mode 2755, owner spooler, group tty
  4. None of them writable by anyone but its owner

Do not:

  • Change the ten root-owned programs to be owned by spooler, or the spooler-owned ones to root - the ownership is chosen per program and both directions break something
  • Remove a set-user-id or set-group-id bit
  • Make any of them group- or world-writable
  • Copy or move these files with a tool that does not preserve modes; use chmod, chgrp and chown to correct them in place, since other operations may invalidate the licence

Spool file security

Spooled print data is readable only by the spooler account. The spool directory itself is created 755: the file names in it reveal job numbers, and a directory listing is available to any user. Setting the directory to 700 closes that, and the scheduler accepts it - which is what the Administration manual asks for.

The configuration and licence files

The master configuration file is 644 and root-owned. It can be tightened, but the user commands read it on every invocation, so the spooler account and ordinary users both need read access:

chown root:daemon /etc/xi/textconfig
chmod 644 /etc/xi/textconfig

The licence file /usr/libexec/xi/.xitext.lic is also 644 and root-owned, and is read by commands running as spooler. Do not remove its read permission, and do not edit it - use the supplied licence tools.

Moving the directories

A site can move the spool, sockets and printers directories by setting SPOOLDIR, SPUDSOCKS and SPOOLPT in /etc/xi/textconfig. There is no environment variable that overrides them; the configuration file is the only route, and the scheduler must be stopped and restarted for a change to take effect.

A custom build blend moves all of these directories at once, so a system built that way will not match the defaults quoted here even though nothing in its configuration file has been changed. Resolve the paths from the configuration file, as the check script does.

Whichever location is in use, the same requirements apply: owned by spooler, owner rwx on every directory, and the file modes above.

  • Reading the Xi-Text System Log (spshed_reps) - Reading spshed_reps, where the permission errors appear
  • Xi-Text Printer Setup Files: What They Contain and When They Are Read - What the files in a printer directory contain
  • Sizing the Xi-Text Shared Memory Queue - The shared memory segments these files back
  • Checking Xi-Batch File and Directory Permissions - The same subject for the other product, with different answers

Documentation reference

The file structure in full:

  • Xi-Text Administration Guide, chapter "Advice and Trouble Shooting", section "Files & Directories"
  • Xi-Text Administration Guide, chapter "Overview of Xi-Text Architecture"

Checking Xi-Batch File and Directory Permissions

Which spool files Xi-Batch needs, who must own them, and the errors you see when they are wrong

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

Diagnosing and Clearing the Xi-Text Printer error State

What puts a printer into error, the messages Xi-Text writes when it happens, and the one command that starts it again

All articles · Release notes · Contact support