OpenVMS Utility Routines Manual
Print Symbiont Modification (PSM) Routines
The print symbiont modification (PSM) routines allow you to modify the
behavior of the print symbiont supplied with the operating system.
16.1 Introduction to PSM Routines
The print symbiont processes data for output to standard line printers
and printing terminals by performing the following functions:
- Reading the data from disk
- Formatting the data
- Sending the data to the printing device
- Composing separation pages (flag, burst, and trailer pages) and
inserting them into the data stream for printing
Some of the reasons for modifying the print symbiont include the
- To include additional information on the separation pages (flag,
burst, and trailer) or to format them differently
- To filter and modify the data stream sent to the printer
- To change some of the ways that the symbiont controls the printing
You might not always be able to modify the print symbiont to suit your
needs. For example, you cannot modify the:
- Symbiont's control logic or the sequence in which the symbiont
- Interface between the symbiont and the job controller
If you cannot modify the print symbiont to suit your needs, you can
write your own symbiont. However, Compaq recommends that you modify the
print symbiont rather than write your own.
The rest of this chapter contains the following information about PSM
- Section 16.2 contains an overview of the print symbiont and of
symbionts in general. It explains concepts such as "symbiont
streams"; describes the relationship between a symbiont, a device
driver, and the job controller; and gives an overview of the print
symbiont's internal logic.
This section is recommended for those
who want to either modify the print symbiont or write a new symbiont.
- Section 16.3 details the procedure for modifying the print
symbiont. It includes an overview of the entire procedure, followed by
a detailed description of each step.
- Section 16.4 contains an example of a simple modification to the
- Section 16.5 describes each PSM routine and the interface used by
the routines you substitute for the standard PSM routines.
16.2 Print Symbiont Overview
The operating system supplies two symbionts: a print symbiont, which is
an output symbiont, and a card reader, which is an
input symbiont. An output symbiont receives tasks from the job
controller, whereas an input symbiont sends jobs to the job controller.
The card reader symbiont cannot be modified. You can modify the print
symbiont, described in this section, using PSM routines.
There are two types of output symbiont: device and server. A device
symbiont processes data for output to a device, for example, a printer.
A server symbiont also processes data but not necessarily for output to
a device, for example, a symbiont that copies files across a network.
The operating system supplies no server symbionts.
16.2.1 Components of the Print Symbiont
The print symbiont includes the following major components:
- PSM routines that are used to modify the print symbiont
- Routines that implement input, format, and output services in the
- Routines that implement the internal logic of the print symbiont
The print symbiont is implemented using the Symbiont Services facility.
This facility provides communication and control between the job
controller and symbionts through a set of Symbiont/Job Controller
Interface routines (SMB routines), which are documented in
All of these routines are contained in a shareable image with the file
16.2.2 Creation of the Print Symbiont Process
The print symbiont is a device symbiont, receiving tasks from the job
controller and processing them for output to a printing device. In the
operating system, the existence of a print symbiont process is linked
to the existence of at least one print execution queue that is started.
The job controller creates the print symbiont process by calling the
$CREPRC system service; it does this whenever either of the following
- A print execution queue is started (from the stopped state) and no
symbiont process is running the image specified with the START/QUEUE
A print execution queue is started by means of the DCL
command START/QUEUE. Use the /PROCESSOR qualifier with the START/QUEUE
command to specify the name of the symbiont image that is to service an
execution queue; if you omit /PROCESSOR, then the default symbiont
image is PRTSMB.
- Currently existing symbiont processes suited to a print execution
queue cannot accept additional devices; that is, the symbionts have no
more available streams. In such a case, the job controller creates
another print symbiont process. The next section discusses symbiont
The print symbiont process runs as a detached process.
16.2.3 Symbiont Streams
A stream is a logical link between a print execution
queue and a printing device. When the queue is started (by means of
START/QUEUE), the job controller creates a stream linking the queue
with a symbiont process. Because each print execution queue has a
single associated printing device (specified with the
/ON=device qualifier in the INITIALIZE/QUEUE or START/QUEUE
command), each stream created by the job controller links a print
execution queue, a symbiont process, and the queue's associated printer.
A symbiont that can support multiple streams simultaneously (that is,
multiple print execution queues and multiple devices) is termed a
multithreaded symbiont. The job controller enforces an upper limit of
16 on the number of streams that any symbiont can service
Therefore, in the operating system environment, only one print symbiont
process is needed as long as the number of print execution queues (and
associated printers) does not exceed 16. If there are more than 16
print execution queues, the job controller creates another print
The print symbiont is, therefore, a multithreaded symbiont that can
service as many as 16 queues and devices, and you can modify it to
service any number of queues and devices as long as the number is less
than or equal to 16.
A symbiont stream is "active" when a queue is started on that
stream. The print symbiont maintains a count of active streams. It
increments this count each time a queue is started and decrements it
when a queue is stopped with the DCL command STOP/QUEUE/NEXT or
STOP/QUEUE/RESET. When the count falls to zero, the symbiont process
exits. The symbiont does not decrement the count when the queue is
paused by STOP/QUEUE.
Figure 16-1 shows the relationship of generic print queues, execution
print queues, the job controller, the print symbiont, printer device
drivers, and printers. The lines connecting the boxes denote streams.
Figure 16-1 Multithreaded Symbiont
16.2.4 Symbiont and Job Controller Functions
This section compares the roles of the symbiont and job controller in
the execution of print requests. You issue print requests using the
The job controller uses the information specified on the PRINT command
line to determine the following:
- Which queue to place the job in (/QUEUE, /REMOTE, /LOWERCASE, and
- How many copies to print (/COPIES and /JOB_COUNT)
- Scheduling constraints for the job (/PRIORITY, /AFTER, /HOLD,
/FORM, /CHARACTERISTICS, and /RESTART)
- How and whether to display the status of jobs and queues (/NOTIFY,
/OPERATOR, and /IDENTIFY)
The print symbiont, on the other hand, interprets the information
supplied with the qualifiers that specify this information:
- Whether to print file separation pages (/BURST, /FLAG, and /TRAILER)
- Information to include when printing the separation pages (/NAME
- Which pages to print (/PAGES)
- How to format the print job (/FEED, /SPACE, and /PASSALL)
- How to set up the job (/SETUP)
The print symbiont, not the job controller, performs all necessary
device-related functions. It communicates with the printing device
driver. For example, when a print execution queue is started (by means
of START/QUEUE/ON=device) and the stream is established
between the queue and the symbiont, the symbiont parses the device name
specified by the /ON qualifier in the START/QUEUE command, allocates
the device, assigns a channel to it, obtains the device
characteristics, and determines the device class. In versions of the
operating system prior to Version 4.0, the job controller performed
The print symbiont's output routine returns an error to the job
controller if the device class is neither printer nor terminal.
16.2.5 Print Symbiont Internal Logic
The job controller deals with units of work called jobs, while the
print symbiont deals with units of work called tasks. A print job can
consist of several print tasks. Thus, in the processing of a print job,
the job controller's role is to divide a print job into one or more
print tasks, which the symbiont can process. The symbiont reports the
completion of each task to the job controller, but the symbiont
contains no logic to determine that the print job as a whole is
In the processing of a print task, the symbiont performs three basic
functions: input, format, and output. The symbiont performs these
functions by calling routines to perform each function.
The following steps describe the action taken by the symbiont in
processing a task:
- The symbiont receives the print request from the job controller and
stores it in a message buffer.
- The symbiont searches its list of input routines and selects the
first input routine that is applicable to the print task.
- The input routine returns a data record to the symbiont's input
buffer or in a buffer supplied by the input routine.
- Data in the input buffer is moved to the symbiont's output buffer
by the formatting routines, which format it in the process.
- Data in the output buffer is sent to the printing device by the
- When an input routine completes execution, that is, when it has no
more input data to process, the symbiont selects another applicable
input routine. Steps 3, 4, and 5 are repeated until all applicable
input routines have executed.
- The symbiont informs the job controller that the task is complete.
Figure 16-2 illustrates the steps taken by the symbiont in the
processing of a print task.
Figure 16-2 Symbiont Execution Sequence or Flow of
As Figure 16-2 shows, most of the input routines execute in a
specified sequence. This sequence is defined by the symbiont's main
control routine. You cannot modify this main control routine; thus, you
cannot modify the sequence in which symbiont routines are called.
The input routines that do not execute in sequence are called
"demand input routines." These routines are called whenever
the service they provide is required and include the page header, page
setup, and library module input routines.
The symbiont can perform input, formatting, and output functions
asynchronously; that is, the order in which the symbiont calls the
input, formatting, and output routines can vary. For example, the
symbiont can call an input routine, which returns a record to the input
buffer; it can then call the format routine, which moves that record to
the output buffer; and then it can call the output routine to move that
data to the printing device. This sequence results in the movement of a
single data record from disk to printing device.
On the other hand, the symbiont can call the input and formatting
routines several times before calling the output routine for a single
buffer. The buffer can contain one or more formatted input records. In
some cases an output buffer might contain only a portion of an input
In this way the symbiont can store input records; then call the format
routine, which moves one of those records to the output buffer; and
finally call the output routine, which moves that data to the printing
device. Note, however, that the formatting routine must be called once
for each input record.
Similarly, the symbiont can store several formatted records before
calling the output routine to move them to the printing device.
The symbiont requires this flexibility in altering the sequence in
which input, format, and output routines are called for reasons of
efficiency (high rate of throughput) and adaptability to various system
parameters and system events.
The value specified with the call to PSM$PRINT determines the maximum
size of the symbiont's output buffer, which cannot be larger than the
value of the system parameter MAXBUF. If the buffer is very small, the
symbiont might need to call its output routine one or more times for
each record formatted. If the buffer is large, the symbiont stores
several formatted records before calling the output routine to move
them to the printing device.
16.3 Symbiont Modification Procedure
To modify the print symbiont, perform the following steps. These steps
are described in more detail in the sections that follow.
- Determine the modification needed. The modification might involve
changing the way the symbiont performs a certain function, or it might
involve adding a new function.
- Determine where to make the modification. This involves selecting a
function and determining where that function is performed within the
symbiont's execution sequence. You specify a function by calling the
PSM$REPLACE routine and specifying the code that identifies the
Some codes correspond to symbiont-supplied routines. When
you specify one of these codes, you replace that routine with your
routine. Other codes do not correspond to symbiont-supplied routines.
When you specify one of these codes, you add your routine to the set of
routines the symbiont executes. Table 16-1 lists these codes.
- Write the routine. Because the symbiont calls your routine, your
routine must have one of three call interfaces, depending on whether it
is an input, format, or output routine. See the descriptions of the
USER-INPUT-ROUTINE, USER-FORMAT-ROUTINE, and USER-OUTPUT-ROUTINE
routines, which follow the
descriptions of the PSM routines.
- Write the symbiont-initialization routine. This routine executes
when the symbiont is first activated by the job controller. It
initializes the symbiont's internal database; specifies, by calling
PSM$REPLACE, the routines you have supplied; activates the symbiont by
calling PSM$PRINT; and performs any necessary cleanup operations when
- Construct the modified symbiont. This involves compiling your
routines, then linking them.
- Integrate the modified symbiont with the system. This involves
placing the executable image in SYS$SYSTEM, identifying the symbiont
image to the job controller, and debugging the symbiont.
As mentioned previously, you identify each routine you write for the
symbiont by calling the PSM$REPLACE routine. The code
argument for this routine specifies the point within the symbiont's
execution sequence at which you want your routine to execute. You
should know which code you will use to identify your routine before you
begin to write the routine. Section 16.3.6 provides more information
about these codes.
16.3.1 Guidelines and Restrictions
The following guidelines and restrictions apply to the writing of any
- Do not use the process-permanent files identified by the logical
names SYS$INPUT, SYS$OUTPUT, SYS$ERROR, and SYS$COMMAND.
- The symbiont code should be linked against SMBSRVSHR.EXE in order
to define the following status codes:
- Do not use the system services $HIBER and $WAKE.
- The job completion (PSM$K_JOB_COMPLETION) and output (PSM$K_OUTPUT)
routines are not replaceable when using the LAT protocol option.
- Use the following two OpenVMS Run-Time Library routines for
allocation and deallocation of memory: LIB$GET_VM and LIB$FREE_VM.
- Minimize the amount of time that your routine spends executing at
AST level. The job controller sends messages to the symbiont by means
of user-mode ASTs; the symbiont cannot receive these ASTs while your
user routine is executing at AST level.
- The symbiont can call your routines at either AST level or non-AST
- If your routine returns any error-condition value (low bit clear),
the symbiont aborts the current task and notifies the job controller.
Note that, by default, an error-condition value returned during the
processing of a task causes the job controller to abort the entire job.
However, this default behavior can be overridden. See the description
of the /RETAIN qualifier of the DCL commands START/QUEUE,
INITIALIZE/QUEUE, and SET QUEUE in the OpenVMS DCL Dictionary.
stores the first error-condition value (low bit clear) returned during
the processing of a task. The symbiont's file-errors routine, an input
routine (code PSM$K_FILE_ERRORS), places the message text associated
with this condition value in the symbiont's input stream. The symbiont
prints this text at the end of the listing, immediately before the
The symbiont sends this error-condition value to the
job controller; the job controller then stores this condition value
with the job record in the job controller's queue file. The job
controller also writes this condition value in the accounting record
for the job.
If you choose to return a condition value when an
error occurs, you should choose one from the system message file. This
lets system programs access the message text associated with the
condition value. Specifically, the Accounting and SHOW/QUEUE utilities
and the job controller will be able to translate the condition value to
its corresponding message text and to display this message text as
This guideline applies to input, input-filter, and
output-filter routines, and to the symbiont's use of dynamic string
descriptors in these routines.
The simplest way for an input
routine to pass the data record to the symbiont is for it to use a
Run-Time Library string-handling routine (for example, STR$COPY_R).
These routines use dynamic string descriptors to point to the record
they have handled and to copy that record from your input buffer to the
symbiont-supplied buffer specified in the funcdesc
By default, the symbiont initializes a dynamic string
descriptor that your input routine can use to describe the data record
it returns. Specifically, the symbiont initializes the DSC$B_DTYPE
field of the string descriptor with the value DSC$K_DTYPE_T (which
indicates that the data to which the descriptor points is a string of
characters) and initializes the DSC$B_CLASS field with the value
DSC$K_CLASS_D (which indicates that the descriptor is dynamic).
Alternatively, the input routine can pass a data record to the
symbiont by providing its own buffer and passing a static string
descriptor that describes the buffer. To do this, you must redefine the
fields of the descriptor to which the funcdesc
argument points, as follows:
- Initialize the field DSC$B_CLASS with the value DSC$K_CLASS_S
(which indicates that the descriptor points to a scalar value or a
- Initialize the field DSC$A_POINTER with the address of the buffer
that contains the data record.
- Initialize the field DSC$W_LENGTH with the length, in bytes, of the
Each time the symbiont calls the routine to read some data, the
symbiont reinitializes the descriptor to make it a dynamic descriptor.
Consequently, if you want to use the descriptor as a static descriptor,
your input routine must initialize the descriptor each time it is
called to perform a reading operation.
Input-filter routines and
output-filter routines return a data record to the symbiont by means of
the func_desc_2 argument. The symbiont initializes a
descriptor for this argument the same way it does for descriptors used
by the input routine. Thus, the guidelines described for the input
routine apply to the input-filter routine and output-filter routine.
16.3.2 Writing an Input Routine
This section provides an overview of the logic used in the print
symbiont's main input routine, and it discusses the way in which the
print symbiont handles carriage-control effectors.
The print symbiont calls your input routine, supplying it with
arguments. Your routine must return arguments and condition values to
the print symbiont. For this reason, your input routine must use the
interface described in the description of the USER-INPUT-ROUTINE.
When the print symbiont calls your routine, it specifies a particular
request in the func argument. Each function has a
Your routine must provide the functions identified by the codes
PSM$K_OPEN, PSM$K_READ, and PSM$K_CLOSE. Your routine need not respond
to the other function codes, but it can if you want it to. If your
routine does not provide a function that the symbiont requests, it must
return the condition value PSM$_FUNNOTSUP to the symbiont.
The description of the func argument of the
USER-INPUT-ROUTINE describes the codes that the symbiont can send to an
See Section 16.3.5 for additional information about other function codes
used in the user-written input routine.
For each task that the symbiont processes, it calls some input routines
only once, and some more than once; it always calls some routines and
calls others only when needed.
Table 16-1 lists the codes that you can specify when you call the
PSM$REPLACE routine to identify your input routine to the symbiont. The
description of the PSM$REPLACE routine describes these routines.
188.8.131.52 Internal Logic of the Symbiont's Main Input Routine
The internal logic of the symbiont's main input routine, as described
in this section, is subject to change without notice. This logic is
summarized here. This summary is not intended as a tutorial on the
writing of a symbiont's main input routine, although it does provide
insight into such a task.
A main input routine is one that the symbiont calls to read data from
the file that is to be printed. A main input routine must perform three
sets of tasks: one set when the symbiont calls the routine with an OPEN
request, one set when the symbiont calls with a READ request, and one
set when the symbiont calls with a CLOSE request.
The following table lists the codes that identify each of these three
requests and describes the tasks that the symbiont's main input routine
performs for each request:
||Action Taken by the Input Routine
An OPEN request. When the main input routine receives this request
code, it does the following:
- Opens the input file.
- Stores information about the input file.
- Returns the type of carriage control used in the input file. If
this routine cannot open the file, it returns an error.
Note that the print symbiont's main input routine performs these tasks
when it receives the PSM$K_START_TASK function code, rather than the
PSM$K_OPEN function code.
This atypical behavior occurs because some of the information
stored by the main input routine must be available for other input
routines that execute before the main input routine. For example,
information about file attributes and record formats is needed by the
symbiont's separation-page routines, which print flag and burst pages.
Consequently, if you supply your own main input routine, some of
the information about the file being printed that appears on the
standard separation pages is not available, and the symbiont prints a
message on the separation page stating so.
The symbiont receives the file-identification number from the job
controller in the SMBMSG$K_FILE_IDENTIFICATION item of the requesting
message and uses this value rather than the file specification to open
the main input file.
A READ request. When the main input routine receives this request, it
returns the next record from the file. In addition, when the carriage
control used by the data file is PSM$K_CC_PRINT, the main input routine
returns the associated record header.
A CLOSE request. When the main input routine receives this request, it
closes the input file.