|
OpenVMS Programming Concepts Manual
30.8.2.4 $GETDTI and $SETDTI
This example shows the use of $GETDTI on recovery to determine the
final state of a transaction. $SETDTI is used to remove the resource
manager from the transaction.
/* Recover the state of a prepared resource after a failure */
RecoverString(...) {
int status;
IOSB iosb;
uint context = 0; /* context from $GETDTI */
int retlen;
Int state; /* transaction state */
DTIRECDEF dti;
ITMLST3_DECL (search, 1);
ITMLST3_ITEM (search, 0, DTI$_SEARCH_RESOLVED_STATE,
DTI$S_TRANSACTION_INFORMATION, &dti, 0);
DTI$S_TRANSACTION_INFORMATION, &dti, 0);
ITMLST3_END (search);
ITMLST3_DECL (result, 1);
ITMLST3_ITEM (result, 0, DTI$_TRANSACTION_INFORMATION,
DTI$S_TRANSACTION_INFORMATION, &dti, &retlen);
ITMLST3_END (result);
/* get final state of transaction */
dti.dti$b_part_name_len = 0; /* no RM name specified */
CopyUid((uint *) dti.dti$t_tid, pTaos->stringBuf.tid);
status = sys$getdtiw(pTaos->efn, DDTM$M_FULL_STATE, &iosb, NULL, 0,
pTaos->tmLogId, &context, &search, &result);
if (SUCCESS(status))
status = iosb.iosb$w_status;
if (SUCCESS(status))
state = dti.dti$b_state;
/* treat forgotten TID as presumed abort */
if (status == SS$_NOSUCHTID) {
state = DTI$K_ABORTED;
status = SS$_NORMAL;
}
if (SUCCESS(status)) {
switch (state) {
case DTI$K_COMMITTED:
/* Make update permanent and visible here.
* Set status on error. */
break;
case DTI$K_ABORTED:
/* Undo the update here. Set status on error. */
break;
}
}
if (SUCCESS(status)) {
/* allow DECdtm to remove this RM from the transaction */
status = sys$setdtiw(pTaos->efn, 0, &iosb, NULL, 0, &context
DTI$K_DELETE_RM_NAME, &result);
}
}
|
30.8.3 BLISS Program Examaple
The following BLISS program demonstrates how a simple resource manager
may perform recovery following a system failure. In the example, a
$GETDTI is executed on behalf of a remote node (MYNODE) specifying a
transaction identifier, named resource manager, participant log
identifier and transaction manager log identifier.
When the $GETDTI finishes processing, the recovery logic in the
resource manager performs its own recovery and issues a $SETDTI to
remove the resource manager name from the transaction.
MODULE RECOVER_TRANSACTION (MAIN=MAIN)=
BEGIN
LIBRARY'SYS$LIBRARY:STARLET';
FORWARD ROUTINE
MAIN,
AST_COMPLETION_ROUTINE : NOVALUE;
ROUTINE MAIN =
BEGIN
OWN
STATUS
: LONG UNSIGNED,
IOSB
: VECTOR [4,WORD],
SEARCH_CONTEXT
: LONG UNSIGNED
INITIAL (0),
PART_LOG_ID
: $BBLOCK [DTI$S_PART_LOG_ID]
INITIAL (REP DTI$S_PART_LOG_ID OF BYTE (0)),
TM_LOG_ID
: $BBLOCK [DTI$S_PART_LOG_ID]
INITIAL (REP DTI$S_PART_LOG_ID OF BYTE (0)),
TID
: $BBLOCK [DTI$S_TID]
INITIAL (REP DTI$S_TID OF BYTE (0)),
SEARCH_LIST
: $ITMLST_DECL (ITEMS=2),
ITEM_LIST
: $ITMLST_DECL (ITEMS=1),
TRANS_INFO
: $BBLOCK [DTI$S_TRANSACTION_INFORMATION];
BIND
SEARCH_NODE_NAME = UPLIT (%ASCII'MYNODE'),
RESOURCE_MANAGER = UPLIT (%ASCII'FRED');
LITERAL
SEARCH_NODE_NAME_LENGTH = %CHARCOUNT ('MYNODE'),
RESOURCE_MANAGER_LENGTH = %CHARCOUNT ('FRED');
! Resource manager opens recovery log and reads first resolved
! recovery record. The information in the recovery record
! should contain the transaction identifier, resource manager
! log identifier and transaction manager log identifier. This
! information is written into the transaction information
! record.
CH$MOVE (DTI$S_TID,
TID,
TRANS_INFO [DTI$T_TID]);
CH$MOVE (DTI$S_PART_LOG_ID,
PART_LOG_ID,
TRANS_INFO [DTI$T_PART_LOG_ID]);
CH$MOVE (RESOURCE_MANAGER_LENGTH,
.RESOURCE_MANAGER,
TRANS_INFO [DTI$T_PART_NAME]);
TRANS_INFO [DTI$B_PART_NAME_LEN] = RESOURCE_MANAGER_LENGTH;
! The search item list is initialized with a node
! name and transaction information record.
$ITMLST_INIT (ITMLST=SEARCH_LIST,
(ITMCOD=DTI$_SEARCH_AS_NODE,
BUFADR=.SEARCH_NODE_NAME,
BUFSIZ=SEARCH_NODE_NAME_LENGTH),
(ITMCOD=DTI$_SEARCH_RESOLVED_STATE,
BUFADR=TRANS_INFO,
BUFSIZ=DTI$S_TRANSACTION_INFORMATION));
! The item list is initialized to return a transaction
! information record containing the resolved state of the
! transaction.
! transaction.
$ITMLST_INIT (ITMLST=ITEM_LIST,
(ITMCOD=DTI$_TRANSACTION_INFORMATION,
BUFADR=TRANS_INFO,
BUFSIZ=DTI$S_TRANSACTION_INFORMATION));
! A $GETDTI is now performed to return the state of the
! transaction and the node name.
STATUS = $GETDTIW (EFN=10,
FLAGS=DDTM$M_FULL_STATE,
IOSB=IOSB,
ASTADR=AST_COMPLETION_ROUTINE,
ASTPRM=0,
CONTXT=SEARCH_CONTEXT,
LOG_ID=TM_LOG_ID,
SEARCH=SEARCH_LIST,
ITMLST=ITEM_LIST);
! If the transaction was committed then perform resource manager
! recovery and then delete the resource manager from the
! transaction.
IF .TRANS_INFO [DTI$B_STATE] EQLU DTI$K_COMMITTED THEN
STATUS = $SETDTIW (EFN=10,
FLAGS=0,
IOSB=IOSB,
ASTADR=AST_COMPLETION_ROUTINE,
ASTPRM=0,
CONTXT=SEARCH_CONTEXT,
FUNC=DTI$K_DELETE_RM_NAME,
ITMLST=ITEM_LIST);
RETURN .STATUS
END;
ROUTINE AST_COMPLETION_ROUTINE (ASTPRM : LONG UNSIGNED) : NOVALUE =
BEGIN
RETURN;
END;
END
ELUDOM
|
Chapter 31 Creating User-Written System Services
This chapter describes how to create user-written system services. It
contains the following sections:
Section 31.1 describes privileged routines and privileged shareable
images.
Section 31.2 describes how to write a privileged routine.
Section 31.3 describes how to create a privileged shareable image on
VAX systems.
Section 31.4 describes how to create a privileged shareable image on
Alpha systems.
31.1 Overview
Your application may contain certain routines that perform privileged
functions, called user-written system services. To
create these routines, put them in a privileged shareable image.
User-mode routines in other modules can call the routines in the
privileged shareable image to perform functions in a more privileged
mode.
You create a privileged shareable image as you would any other
shareable image, using the /SHAREABLE qualifier with the linker. (For
more information about how to create a shareable image, see the
OpenVMS Linker Utility Manual.) However, because a call to a routine in a more
privileged mode must be vectored through the system service dispatch
routine, you must perform some additional steps. The following steps
outline the basic procedure. Section 31.3 provides more detail about
requirements specific to VAX systems. Section 31.4 describes the
necessary steps for Alpha systems.
- Create the source file. The source file for a privileged shareable
image contains the routines that perform privileged functions. In
addition, because user-written system services are called using the
system service dispatcher, you must include a privileged library vector
(PLV) in your shareable image. A PLV is an operating-system-defined
data structure that communicates the location of the privileged
routines to the operating system.
On VAX systems, the PLV contains
the addresses of dispatch routines for each access mode used in the
image. You must write these dispatch routines and include them in your
shareable image. Section 31.3.1 provides more information. On Alpha
systems, you list the names of the privileged routines in the PLV,
sorted by access mode. You do not need to create dispatch routines; the
image activator creates them for you automatically. Section 31.2
provides guidelines for creating privileged routines.
- Compile or assemble the source file.
- Create the shareable image. You create a privileged shareable image
as you would any other shareable image: by specifying the /SHAREABLE
qualifier to the LINK command. Note, however, that creating privileged
shareable images has some additional requirements. The following list
summarizes these requirements. See the OpenVMS Linker Utility Manual for additional
information about linker qualifiers and options.
- Declare the privileged routine entry points as universal symbols.
Privileged shareable images use the same mechanisms to declare
universal symbols as other shareable images: transfer vectors on VAX
and symbol vectors on Alpha systems. However, because calls to
user-written system services must be vectored through the system
service dispatcher, you must use extensions to these mechanisms for
privileged shareable images. Section 31.3.3 describes how to declare a
universal symbol in a VAX privileged shareable image. Section 31.4.2
describes how to declare a universal symbol in an Alpha system
privileged shareable image.
- Prevent the linker from processing the system default shareable
image library, SYS$LIBRARY:IMAGELIB.OLB, by specifying the /NOSYSSHR
linker qualifier. Otherwise, the linker processes this library by
default.
- Protect the shareable image from user-mode access by specifying the
/PROTECT linker qualifier. If you want to protect only certain portions
of the shareable image, instead of the entire image, use the PROTECT=
linker option.
- Set the VEC attribute of the program section containing the PLV by
using the PSECT_ATTR= linker option. Modules written in MACRO can
specify this attribute in the .PSECT directive. The PLV must appear in
a program section with the VEC attribute set.
- Set the shareable image identification numbers using the GSMATCH=
option.
If your privileged application requires that you link against the
system executive, see the OpenVMS Linker Utility Manual for more information.
- Install the privileged shareable image as a protected permanent
global section. Privileged shareable images must be installed to be
available to nonprivileged programs. The following procedure is
recommended:
- Move the privileged shareable image to a protected directory, such
as SYS$SHARE.
- Invoke the Install utility, specifying the /PROTECT, /OPEN, and
/SHARED qualifiers. You can also specify the /HEADER_RESIDENT
qualifier. The following entry could be used to install a user-written
system service whose image name is MY_PRIV_SHARE:
$ INSTALL
INSTALL> ADD SYS$SHARE:MY_PRIV_SHARE/PROTECT/OPEN/SHARED/HEADER_RES
|
To use a privileged shareable image, you include it in a link operation
as you would any other shareable image: specifying the shareable image
in a linker options file with the /SHAREABLE qualifier appended to the
file specification to identify it as a shareable image.
31.2 Writing a Privileged Routine (User-Written System Service)
On both VAX systems and Alpha systems, the routines that implement
user-written system services must enable any privileges they need that
the nonprivileged user of the user-written system service lacks. The
user-written system service must also disable any such privileges
before the nonprivileged user receives control again. To enable or
disable a set of privileges, use the Set Privileges ($SETPRV) system
service. The following example shows the operator (OPER) and physical
I/O (PHY_IO) privileges being enabled. (Any code executing in executive
or kernel mode is granted an implicit SETPRV privilege so it can enable
any privileges it needs.)
PRVMSK: .LONG <1@PRV$V_OPER>!<1@PRV$V_PHY_IO> ;OPER and PHY_IO
.LONG 0 ;quadword mask required. No bits set in
;high-order longword for these privileges.
.
.
.
$SETPRV_S ENBFLG=#1,- ;1=enable, 0=disable
PRVADR=PRVMSK ;Identifies the privileges
|
When you design your system service, you must carefully define the
boundaries between the protected subsystem and the user who calls the
service. A protected image has privileges to perform tasks on its own
behalf. When your image performs tasks on behalf of users, you must
ensure that your image performs only those tasks the users could not
have done on their own. Always keep the following coding principles in
mind:
- Keep privileges off, and turn them on only when necessary.
- Make sure privileges are off on all exit paths. When you perform a
task for the user, operate in user mode whenever possible and operate
at all times with the user's privileges, identity, and so on. Make sure
that operating in an inner mode does not give you any special
privileges with respect to the operation being performed. Resume a
privileged state only when you are about to resume operation on your
own behalf.
- If user input can affect an operation executed with privilege, you
have to carefully validate the input. Never pass user parameters
directly to an operation executed in an inner mode or with privilege.
When designing your program, keep in mind that the inner modes
implicitly provide a user with the system privileges SETPRV, CMKRNL,
SYSNAM, and SYSLCK. (See the OpenVMS Guide to System Security for descriptions.)
- As a protected image, your program does not have the entire
operating system programming environment at its disposal. Unless a
module has the prefix SYS$ or EXE$, you must avoid calling it from an
inner mode. In particular, do not call LIB$GET_VM or LIB$RET_VM from an
inner mode. You can call OpenVMS RMS routines from executive mode but
not from kernel mode.
On VAX systems, Version 5.4 or later of the
operating system, any OpenVMS RMS files that were opened with privilege
from an inner mode can be left open during user execution; however,
this is not acceptable on earlier versions of the operating system.
- Never make subroutine calls to other shareable images from kernel
or executive mode.
- When a protected subsystem opens a file on its own behalf, it
should specify executive-mode logical names only by naming executive
mode explicitly in the FAB$V_LNM_MODE subfield of the file access block
(FAB). This prevents a user's logical name from redirecting a file
specification.
On VAX systems, refer to SYS$EXAMPLES:USSDISP.MAR and USSTEST.MAR for
listings of modules in a user-written system service and of a module
that calls the user-written system service.
On Alpha systems, for C examples refer to SYS$EXAMPLES:UWSS.C and
SYS$EXAMPLES:UWSS_TEST.C.
31.3 Creating a Privileged Shareable Image (VAX Only)
On VAX systems, you must create dispatch routines that transfer control
to the privileged routines in your shareable image. You then put the
addresses of these dispatch routines in a privileged library vector
(PLV). Section 31.3.1 describes how to create a dispatch routine.
Section 31.3.2 describes how to create a PLV.
31.3.1 Creating User-Written Dispatch Routines on VAX Systems
On VAX systems, you must create kernel-mode and executive-mode
dispatching routines that transfer control to the routine entry points.
You must supply one dispatch routine for all your kernel mode routines
and a separate routine for all the executive mode routines. The
dispatcher is usually written using the CASE construct, with each
routine identified by a code number. Make sure that the identification
code you use in the dispatch routine and the code specified in the
transfer vector identify the same routine.
The image activator, when it activates a privileged shareable image,
obtains the addresses of the dispatch routines from the PLV and stores
these addresses at a location known to the system service dispatcher.
When a call to a privileged routine is initiated by a CHME or CHMK
instruction, the system service dispatcher attempts to match the code
number with a system service code. If there is no match, it transfers
control to the location where the image activator has stored the
address of your dispatch routines.
A dispatch routine must validate the CHMK or CHME operand
identification code number, handling any invalid operands. In addition,
the dispatching routine must transfer control to the appropriate
routine for each identification code if the user-written system service
contains functionally separate coding segments. The CASE instruction in
VAX MACRO or a computed GOTO-type statement in a high-level language
provides a convenient mechanism for determining where to transfer
control.
Note
Users of your privileged shareable image must specify the same code
number to identify a privileged routine as you used to identify it in
the dispatch routine. Users specify the code number in their CHMK or
CHME instruction. See Section 31.3.3 for information about transfer
vectors.
|
In your source file, a dispatch routine must precede the routines that
implement the user-written system service.
Example 31-1 illustrates a sample dispatching routine, taken from the
sample privileged shareable image in SYS$EXAMPLES named USSDISP.MAR.
Example 31-1 Sample Dispatching Routine |
KERNEL_DISPATCH:: ; Entry to dispatcher
MOVAB W^-KCODE_BASE(R0),R1 ; Normalize dispatch code value
BLSS KNOTME ; Branch if code value too low
CMPW R1,#KERNEL_COUNTER ; Check high limit
BGEQU KNOTME ; Branch if out of range
;
; The dispatch code has now been verified as being handled by this dispatcher,
; now the argument list will be probed and the required number of arguments
; verified.
;
MOVZBL W^KERNEL_NARG[R1],R1 ; Get required argument count
MOVAL @#4[R1],R1 ; Compute byte count including argcount
IFNORD R1,(AP),KACCVIO ; Branch if arglist not readable
CMPB (AP),W^<KERNEL_NARG-KCODE_BASE>[R0] ; Check for required number
BLSSU KINSFARG ; of arguments
MOVL FP,SP ; Reset stack for service routine
CASEW R0,- ; Case on change mode
.
.
.
|
31.3.2 Creating a PLV on VAX Systems
On VAX systems, a call to a privileged routine goes to the transfer
vector that executes a change mode instruction (CHMx)
specifying the identification code of the privileged routine as the
operand to the instruction. The operating system routes the change mode
instruction to the system service dispatch routine, which attempts to
locate the system service with the code specified. Because the code is
a negative number, the system service dispatcher drops through its list
of known services and transfers control to a user-written dispatch
routine, if any have been specified.
The image activator has already placed at this location the address of
whatever user-written dispatch routines it found in the privileged
shareable image's PLV when it activated the PLV. The dispatch routine
transfers control to the routine in the shareable image identified by
the code. (You must ensure that the code used in the transfer vector
and the code specified in the dispatch routine both identify the same
routine.) Figure 31-1 illustrates this flow of control.
Figure 31-1 Flow of Control Accessing a Privileged Routine on
VAX Systems
Figure 31-2 shows the components of the PLV in VAX shareable images.
Figure 31-2 Components of the Privileged Library Vector on VAX
Systems
Table 31-1 describes each field in the PLV on a VAX processor,
including the symbolic names the operating system defines to access
each field. These names are defined by the $PLVDEF macro in
SYS$LIBRARY:STARLET.MLB.
Table 31-1 Components of the VAX Privileged Library Vector
Component |
Symbol |
Description |
Vector type code
|
PLV$L_TYPE
|
Identifies the type of vector. For PLVs, you must specify the symbolic
constant defined by the operating system, PLV$C_TYP_CMOD, which
identifies a privileged library vector.
|
Kernel-mode dispatcher
|
PLV$L_KERNEL
|
Contains the address of the user-supplied kernel-mode dispatching
routine if your privileged library contains routines that run in kernel
mode. The address is expressed as an offset relative to the start of
the data structure (self-relative pointer). A value of 0 indicates that
a kernel-mode dispatcher does not exist.
|
Executive-mode dispatcher
|
PLV$L_EXEC
|
Contains the address of the user-supplied executive-mode dispatching
routine if your privileged library contains routines that run in
executive mode. The address is expressed as an offset relative to the
start of the data structure (self-relative pointer). A value of 0
indicates that a kernel-mode dispatcher does not exist.
|
User-supplied rundown routine
|
PLV$L_USRUNDWN
|
Contains the address of a user-supplied rundown routine that performs
image-specific cleanup and resource deallocation if your privileged
library contains such a routine. When the image linked against the
user-written system service is run down by the system, this run-time
routine is invoked. Unlike exit handlers, the routine is always called
when a process or image exits. (The image rundown code calls this
routine with a JSB instruction; it returns with an RSB instruction
called in kernel mode at IPL 0.)
|
RMS dispatcher
|
PLV$L_RMS
|
Contains the address of a user-supplied dispatcher for OpenVMS RMS
services. A value of 0 indicates that a user-supplied OpenVMS RMS
dispatcher does not exist. Only one user-written system service should
specify the OpenVMS RMS vector, because only the last value is used.
This field is intended for use only by Compaq.
|
Address check
|
PLV$L_CHECK
|
Contains a value to verify that a user-written system service that is
not position independent is located at the proper virtual address. If
the image is position independent, this field should contain a zero. If
the image is not position independent, this field should contain its
own address.
|
Example 31-2 illustrates how the sample privileged shareable image in
SYS$EXAMPLES assigns values to the PLV.
Example 31-2 Assigning Values to a PLV on a
VAX System |
.PAGE
$PLVDEF ; Define PLV fields
.SBTTL Change Mode Dispatcher Vector Block
(1) .PSECT USER_SERVICES,PAGE,VEC,PIC,NOWRT,EXE
(2) .LONG PLV$C_TYP_CMOD ; Set type of vector to change mode
.LONG 0 ; Reserved
.LONG KERNEL_DISPATCH-. ; Offset to kernel mode dispatcher
.LONG EXEC_DISPATCH-. ; Offset to executive mode dispatcher
.LONG USER_RUNDOWN-. ; Offset to user rundown service
.LONG 0 ; Reserved.
.LONG 0 ; No RMS dispatcher
.LONG 0 ; Address check - PIC image
|
|