HP OpenVMS Systems Documentation

Content starts here

OpenVMS Utility Routines Manual

Previous Contents Index

Chapter 16
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 following:

  • 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 device

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 calls routines
  • 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 routines:

  • 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 print symbiont.
  • 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 print symbiont
  • 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 Chapter 17.

All of these routines are contained in a shareable image with the file specification SYS$SHARE:SMBSRVSHR.EXE.

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 conditions occurs:

  • A print execution queue is started (from the stopped state) and no symbiont process is running the image specified with the START/QUEUE command.
    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 streams.

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 simultaneously.

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 symbiont process.

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 PRINT command.

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 /DEVICE)
  • 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 and /NOTE)
  • 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 these functions.

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 complete.

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:

  1. The symbiont receives the print request from the job controller and stores it in a message buffer.
  2. The symbiont searches its list of input routines and selects the first input routine that is applicable to the print task.
  3. The input routine returns a data record to the symbiont's input buffer or in a buffer supplied by the input routine.
  4. Data in the input buffer is moved to the symbiont's output buffer by the formatting routines, which format it in the process.
  5. Data in the output buffer is sent to the printing device by the output routine.
  6. 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.
  7. 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 Control

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 record.

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.

  1. 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.
  2. 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 function.
    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.
  3. 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.
  4. 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 PSM$PRINT completes.
  5. Construct the modified symbiont. This involves compiling your routines, then linking them.
  6. 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 symbiont routine:

  • 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:
    • PSM$_FLUSH
    • PSM$_EOF
  • 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 level.
  • 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.
    The symbiont 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 trailer pages.
    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 appropriate.
    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 argument.
    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:
    1. 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 fixed-length string).
    2. Initialize the field DSC$A_POINTER with the address of the buffer that contains the data record.
    3. Initialize the field DSC$W_LENGTH with the length, in bytes, of the data record.

    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 corresponding code.

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 input routine.

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. 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:

Code Action Taken by the Input Routine
PSM$K_OPEN An OPEN request. When the main input routine receives this request code, it does the following:
  1. Opens the input file.
  2. Stores information about the input file.
  3. 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.

PSM$K_READ 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.
PSM$K_CLOSE A CLOSE request. When the main input routine receives this request, it closes the input file.

Previous Next Contents Index