IntroductionBrian Wade VM Development 28 Oct 1998 Table of ContentsServer-Wide Initialization and Termination
IntroductionThe Reusable Server Kernel is an execution infrastructure for a CMS-based server program. It solves problems repeatedly encountered in the construction of such servers. Namely, the RSK provides assistance in all of the following areas:
Instead of consisting solely of entry points, the RSK is in fact a CMS-based server program. To use it, you attach your own business-specific logic to the RSK's execution infrastructure. When you use the RSK, you write only logic that applies to the particular business problem you are trying to solve. The RSK supplies the rest of what's necessary to let your server run well on CMS. This workbook provides supplementary, illustrative information for specific RSK topics. It is intended to be used together with the book VM/ESA Reusable Server Kernel Programmer's Guide and Reference, which is part of the VM/ESA library. Module BuildThis discussion assumes you have your text decks ready. Among your text decks should be included:
You might have supplied additional text decks that contain other parts of your server. For example, such decks might contain the code for long-running threads you create in your RSKMAIN. Build your module in usual CMS fashion. Some remarks:
Here is a sample sequence of commands. In this example, text files RSKMAIN, MYINIT, MYSERV, and MYTERM comprise the application. GLOBAL TXTLIB BKWLIB DMSPSLK DMSAMT VMMTLIB VMLIB CMSSAA LOAD RSKMAIN ( CLEAR DUP AUTO LIBE NOINV FULLMAP RLDSAVE INCLUDE MYINIT ( NOCLEAR DUP AUTO LIBE NOINV FULLMAP RLDSAVE INCLUDE MYSERV ( NOCLEAR DUP AUTO LIBE NOINV FULLMAP RLDSAVE INCLUDE MYTERM ( NOCLEAR DUP AUTO LIBE NOINV FULLMAP RLDSAVE INCLUDE VMSTART ( NOCLEAR DUP AUTO LIBE NOINV FULLMAP RLDSAVE RESET VMSTART GENMOD MYSERVER ( MAP STR When the build is done, you will have MYSERVER MODULE and a load map. Run-Time EnvironmentEach module you supply must be prepared to handle the RSK run-time environment. This environment is nothing more than a simple procedure entry and exit linkage scheme that makes it unnecessary to call CMSSTOR for save areas or automatic storage. No other traditional run-time services (exception handling, for example) are provided. Roughly, the registers are used like this:
Macro SSPRLG implements the procedure entry linkage. Some remarks:
Here is an example:
RSKMAIN CSECT ,
RSKMAIN AMODE 31
RSKMAIN RMODE ANY
STM 14,12,12(13) Save registers
LR 11,15 Establish base register
USING RSKMAIN,11
LA R0,DSASIZE Number of bytes needed
SSPRLG Get save area
LR 15,13 R15 = A(prev DSA)
LR 13,2 R13 = A(my DSA)
ST 15,4(,13) Write my back pointer
ST 13,8(,15) Write prev's fwd pointer
LM 15,2,16(15) Restore R15-R2
Procedure exit is simple as well:
L R13,4(,R13) R13 = A(prev DSA)
LA R0,DSASIZE Auto storage size
SSEPIL Release it
L R14,12(,R13) Get return address
LM R0,R12,20(R13) Restore rest of registers
BR R14 Return to caller
Do whatever you want between entry and exit, but if you call some other module, make sure R12-R15 are set up correctly. Finally, note that the run-time environment is implemented by module BKWRTE MODULE. You must have this module on one of the accessed file modes prior to starting your server. BKWRTE MODULE is a CMS/MT language environment manager. CMS loads BKWRTE as a nucleus extension just prior to starting your module and calls entry points inside BKWRTE as part of its management of the threads in your server. Each of the following actions results in CMS calling BKWRTE:
Server-Wide Initialization and TerminationModule Entry PointYour code gets control at entry point RSKMAIN. Your RSKMAIN is passed an OS Type I parameter list pointed to by R1. The parameter list contains:
So, if you need to refer to any of those data areas, you can do so. The RSK doesn't do anything with any of those parameter lists. The RSKMAIN you supply has a few jobs:
RSK APIs available to RSKMAIN:
The rest of the RSK APIs are not available because the RSK does not set up its execution environment (control blocks, vectors, etc.) until you call ssServerRun. Example
@PROCESS ENVIRONMENT(VM/ESAOS) OPT(MAX); 00001000
00002000
/*********************************************************/ 00003000
/* */ 00004000
/* sample application... appl writer just supplies */ 00005000
/* entry point RSKMAIN and we do the rest. */ 00006000
/* */ 00007000
/* note runtime environment as */ 00008000
/* illustrated by PL/X procedure options */ 00009000
/* */ 00010000
/*********************************************************/ 00011000
00012000
rskmain: procedure 00013000
( 00014000
pl_epptr, /* A(eplist) */ 00015000
pl_tpptr, /* A(tplist) */ 00016000
pl_scptr /* A(SCBLOCK) */ 00017000
) 00018000
options 00019000
( 00020000
id /* generates identifier */ 00021000
reentrant /* no static data pls */ 00022000
amode(31) /* 31-bit addresses */ 00023000
rmode(any) /* can reside anywhere */ 00024000
datareg(13) /* R13 is stack ptr */ 00025000
savearea(120) /* size of a save area */ 00026000
stack('SSPRLG ','SSEPIL ') /* hi-perf auto stg mgmt */ 00027000
); 00028000
00029000
/* passed parameters */ 00030000
declare 00031000
pl_epptr pointer(31), 00032000
pl_tpptr pointer(31), 00033000
pl_scptr pointer(31); 00034000
00035000
/***********************************************/ 00036000
/* macros */ 00037000
/***********************************************/ 00038000
00039000
?RegEqu; 00040000
%include syslib(ssplxsrv); 00041000
%include syslib(sstbind); 00042000
00043000
/***********************************************/ 00044000
/* automatic storage */ 00045000
/***********************************************/ 00046000
00047000
declare 00048000
00049000
/* work variables */ 00050000
p pointer(31), 00051000
00052000
/* return and reason codes */ 00053000
rc fixed(31), 00054000
re fixed(31); 00055000
00056000
/***********************************************/ 00057000
/* protect the RAB */ 00058000
/***********************************************/ 00059000
00060000
respecify (r12) restricted; 00061000
00062000
/******************************************************************/ 00063000
/* run our tests */ 00064000
/******************************************************************/ 00065000
00066000
/******************************************************************/ 00067000
/* do a bunch of ssServiceBind calls 00068000
/******************************************************************/ 00069000
00070000
/*******************************/ 00071000
/* bind the ECHO service */ 00072000
/*******************************/ 00073000
00074000
p = 0; 00075000
call ssServiceBind 00076000
( 00077000
rc, 00078000
re, 00079000
c_ech_myname, 00080000
length(c_ech_myname), 00081000
p, 00082000
addr(ep_ech_svthread), 00083000
addr(ep_sst_term), 00084000
ss_srv_srvtype_normal 00085000
); 00086000
if (rc^=ss_srv_rc_success ) then 00087000
return code (-1); 00088000
00089000
/*******************************/ 00090000
/* bind the SGEXER service */ 00091000
/*******************************/ 00092000
00093000
p = 0; 00094000
call ssServiceBind 00095000
( 00096000
rc, 00097000
re, 00098000
c_exr_myname, 00099000
length(c_exr_myname), 00100000
p, 00101000
addr(ep_exr_svthread), 00102000
addr(ep_sst_term), 00103000
ss_srv_srvtype_normal 00104000
); 00105000
if (rc^=ss_srv_rc_success ) then 00106000
return code (-1); 00107000
00108000
/*******************************/ 00109000
/* bind the HTTP service */ 00110000
/*******************************/ 00111000
00112000
p = 0; 00113000
call ssServiceBind 00114000
( 00115000
rc, 00116000
re, 00117000
c_htt_myname, 00118000
length(c_htt_myname), 00119000
p, 00120000
addr(ep_htt_svthread), 00121000
addr(ep_sst_term), 00122000
ss_srv_srvtype_normal 00123000
); 00124000
if (rc^=ss_srv_rc_success ) then 00125000
return code(-1); 00126000
00127000
/******************************************************************/ 00128000
/* now run the server program */ 00129000
/******************************************************************/ 00130000
00131000
call ssServerRun ( rc, re ); 00132000
00133000
/******************************************************************/ 00134000
/* return to caller */ 00135000
/******************************************************************/ 00136000
00137000
/* all done */ 00138000
return code (re); 00139000
00140000
end rskmain; 00141000
00142000
PROFILE RSKShortly after you call ssServerRun, the RSK drives Rexx program PROFILE RSK. This is the RSK's profile file, and herein is where you do the work necessary to configure your server, start services, and perform other operator commands. Entry conditions for PROFILE RSK:
You should do several things in PROFILE RSK:
Configuration ParametersThe RSK defines a number of configuration parameters that control the way it operates. These parameters fall into a few broad categories:
All of these parameters are set via the operator command CONFIG. For example, the AUTHCHECK_ family of parameters controls whether the RSK's operator command processors are to perform authorization checking on the commands they handle. These parameters are:
The formal documentation contains a table explaining all of the configuration parameters and how they are used. You should take time to review the table and become familiar with the configuration parameters and what they do. You will be able to set up a successful PROFILE RSK only if you first understand how to set these configuration variables. Example/* */ /********************************************************/ /* */ /* test profile for Reusable Server Kernel */ /* */ /* remember that when this gets control, the default */ /* command environment is ADDRESS RSK */ /* */ /* You have to customize this to your environment */ /* */ /* (C) Copyright 1998 International Business */ /* Machines, Incorporated. All Rights Reserved. */ /* */ /********************************************************/ /* parse arg what say 'Args: /'what'/' */ sampledir = 'BKW.CRADLE.TEST' /************************************************/ /* initialization things */ /************************************************/ /* grab the storage group disks */ x = diag(8,'LINK BKW 1000 1000 MW') x = diag(8,'LINK BKW 1001 1001 MW') x = diag(8,'LINK BKW 1002 1002 MW') x = diag(8,'LINK BKW 1003 1003 MW') x = diag(8,'LINK BKW 1004 1004 MW') x = diag(8,'LINK BKW 1005 1005 MW') x = diag(8,'LINK BKW 1006 1006 MW') x = diag(8,'LINK BKW 1007 1007 MW') /************************************************/ /* set names of key data files */ /************************************************/ 'CONFIG SGP_FILE SSTEST VSSSGP ' sampledir 'CONFIG UMAP_FILE SSTEST VSSUMAP ' sampledir 'CONFIG AUT_LOCATION SFS' 'CONFIG AUT_DATA_1 SSTEST VSSAUD1 ' sampledir 'CONFIG AUT_INDEX_1 SSTEST VSSAUX1 ' sampledir /* 'CONFIG AUT_LOG SSTEST VSSAUL ' sampledir 'CONFIG AUT_DATA_2 SSTEST VSSAUD2 ' sampledir 'CONFIG AUT_INDEX_2 SSTEST VSSAUX2 ' sampledir */ /************************************************/ /* set the rest of the config stuff */ /************************************************/ /* look up RSCS userid */ address command 'IDENTIFY ( LIFO' parse pull . . . . rscsid . 'CONFIG RSCS_USERID' rscsid /* set monitor data stuff */ 'CONFIG MON_PRODUCT_ID SSTEST-fnn010100' 'CONFIG MON_KERNEL_ROWS 56' 'CONFIG MON_USER_SIZE 0' /* set implicit command rerouting preferences */ 'CONFIG VM_CONSOLE ON' 'CONFIG VM_SUBCOM ON' 'CONFIG VM_MSG ON' 'CONFIG VM_SPOOL OFF' /* set unmapped userid preferences */ 'CONFIG NOMAP_APPC OFF' 'CONFIG NOMAP_IUCV OFF' 'CONFIG NOMAP_MSG OFF' 'CONFIG NOMAP_SPOOL OFF' 'CONFIG NOMAP_TCP OFF' 'CONFIG NOMAP_UDP OFF' /* set authorization memory controls */ 'CONFIG AUT_CACHE 10000' 'CONFIG AUT_FREE 1000' /* turn on authorization checking */ 'CONFIG AUTHCHECK_AUTH ON' 'CONFIG AUTHCHECK_CACHE ON' 'CONFIG AUTHCHECK_CMS ON' 'CONFIG AUTHCHECK_CONFIG ON' 'CONFIG AUTHCHECK_CP ON' 'CONFIG AUTHCHECK_ENROLL ON' 'CONFIG AUTHCHECK_LD ON' 'CONFIG AUTHCHECK_MONITOR ON' 'CONFIG AUTHCHECK_SERVER ON' 'CONFIG AUTHCHECK_SGP ON' 'CONFIG AUTHCHECK_USERID ON' 'CONFIG AUTHCHECK_WORKER ON' /************************************************/ /* run the server */ /************************************************/ 'RUNSERV' if (rc<>0) then do say 'RC='rc 'from RUNSERV' return rc end /************************************************/ /* server came up... set about making it go */ /************************************************/ /************************************/ /* start things that need to run */ /* via SUBCOM (used later herein) */ /************************************/ 'SUBCOM START APPC' 'SUBCOM START AUTH' 'SUBCOM START CACHE' 'SUBCOM START CONSOLE' 'SUBCOM START ENROLL' 'SUBCOM START IUCV' 'SUBCOM START MONITOR' 'SUBCOM START MSG' 'SUBCOM START SERVER' 'SUBCOM START SGP' 'SUBCOM START SPOOL' 'SUBCOM START TCP' 'SUBCOM START UDP' 'SUBCOM START USERID' 'SUBCOM START WORKER' /************************************/ /* start base RSK services */ /************************************/ 'CONSOLE START APPC' 'CONSOLE START AUTH' 'CONSOLE START CACHE' 'CONSOLE START CMS' 'CONSOLE START CONFIG' 'CONSOLE START CP' 'CONSOLE START ENROLL' 'CONSOLE START IUCV' 'CONSOLE START MONITOR' 'CONSOLE START MSG' 'CONSOLE START SERVER' 'CONSOLE START SGP' 'CONSOLE START SPOOL' 'CONSOLE START SUBCOM' 'CONSOLE START TCP' 'CONSOLE START UDP' 'CONSOLE START USERID' 'CONSOLE START WORKER' 'MSG START APPC' 'MSG START AUTH' 'MSG START CACHE' 'MSG START CMS' 'MSG START CONFIG' 'MSG START CONSOLE' 'MSG START CP' 'MSG START ENROLL' 'MSG START IUCV' 'MSG START MONITOR' 'MSG START SERVER' 'MSG START SGP' 'MSG START SPOOL' 'MSG START SUBCOM' 'MSG START TCP' 'MSG START USERID' 'MSG START WORKER' /* 'SPOOL START AUTH' 'SPOOL START CMS' 'SPOOL START CP' 'SPOOL START SERVER' 'SPOOL START SGP' 'SPOOL START SUBCOM' 'SPOOL START TCP' 'SPOOL START USERID' */ 'SPOOL START ECHO' /************************************/ /* start storage groups */ /************************************/ 'SGP START 0 t0 BLOCKRW NODS' 'SGP START 1 t1 BLOCKRW NODS' 'SGP START 2 t2 BLOCKRW NODS' /************************************/ /* start sample server */ /************************************/ 'ENROLL LOAD HTTPCNFG DISK 0 HTTPCNFG VSSENR' sampledir 'ENROLL LOAD HTTPMIME DISK 0 HTTPMIME VSSENR' sampledir 'CACHE CREATE HTTPFILE 32768' 'CONSOLE START ECHO' 'CONSOLE START SGEXER' 'MSG START ECHO' 'MSG START SGEXER' 'TCP START ECHO 426' 'TCP START HTTP 428' 'UDP START ECHO 526' 'UDP START HTTP 528' 'IUCV START ECHO 40' 'IUCV REPORT ON' 'APPC START ECHO GLOBAL 40 BKWG0000' 'APPC REPORT ON' 'WORKER ADD cgiserv TEST001' 'WORKER ADD cgiserv TEST002' /************************************************/ /* wait for server completion */ /************************************************/ 'WAITSERV' /************************************************/ /* do author-supplied termination logic */ /************************************************/ /* void */ return 0 Structure of A Running ServerThe RSK organizes your server into a set of CMS threads. Each thread is in its own dispatch class. The following notes apply:
Interacting with Line DriversThere are many aspects to interacting with line drivers:
Line Driver BehaviorWhen a new client arrives, the line driver either creates a thread to handle the client or takes a thread from a free pool it maintains. In either case, the thread is caused to begin running the service entry point for the service to which the client has connected. The thread runs until your service routine returns. This means that your service will probably be running on multiple threads simultaneously. Even the "mundane" line drivers, like CONSOLE, do this. Be prepared for this behavior. For example, if you used ssServiceBind to define a service called MYSERV, and if you informed the UDP/IP line driver that it was to make MYSERV available on port 992, then when a datagram arrived at port 992, the UDP line driver would arrange for a thread to call MYSERV's service entry point. This would transfer control to your code. Your service routine uses QueueReceiveBlock to learn of work needing its attention. For each received message, your code performs the appropriate action and calls QueueReceiveBlock again. Eventually it is time for the relationship with the client to end. At this point your code sends an IPC message to its line driver to inform the line driver that the relationship is terminated. Your code then returns to its caller. Entry PointsYour service is made up of three routines (entry points):
Initialization Entry PointWhen it drives your initialization routine, the RSK passes your routine the following OS Type I parameter list (R1 points to this vector):
The return code and reason code are outputs which your routine must supply. If either supplied value is non-zero, the RSK will abandon its attempt to start your service. The S-block pointer is an input. The S-block is an RSK control block providing fundamental descriptive information about the service being started. See the documentation for more information. The initialization routine's job is to perform any initialization functions that are service-specific.
Service Entry PointEach time a client arrives, the RSK drives your service entry point on a dedicated thread. The RSK builds the following parameter list for the service entry point:
As mentioned earlier, the S-block provides descriptive information about the particular service in question. The C-block provides descriptive information about the relationship between the client, the RSK, and the service thread (also called the instance). The C-block is probably the most important data structure the RSK builds for the application. It contains information crucial to the correct operation of your service, such as:
See the documentation for more information.
Termination Entry PointWhen the RSK drives your termination routine, it supplies the following parameter list:
The termination routine's job is to perform any termination functions that are service-specific. Connecting Your Service to a Line DriverYou use a line driver's START command to connect your service to a line driver. When you connect your service to a line driver, you make it possible for your service to interact with clients who send requests to your server via the line driver's transport method. Each RSK line driver supports a START command which nominates the service to be started and which gives transport-specific information about how the line driver is to prepare to be contacted by clients. Specifically,
You can issue these commands via PROFILE RSK, or by hand, or via some kind of programmable operator. Use of CMS IPC
Messages to InstanceWhen the instance gets control, its job is to wait to be told about client activity and then process whatever client activity occurred. The waiting is accomplished via QueueReceiveBlock. The following information will be helpful in constructing the call:
When QueueReceiveBlock returns, it will supply a message in the following form:
When you receive this message, you should act on the bits in the following way:
Message to Line DriverWhen the instance queues data for the client, or when the instance otherwise requires the line driver to take some action, the instance informs the line driver of this need by sending it a CMS IPC message via call to QueueSend. The message is to be built in the following way:
The message should be sent to the line driver queue as identified by the queue handle field of the C-block. If the instance is using this message to inform the line driver that it is terminating, it should return to its caller after it transmits the message. The ssClient APIsWhen the line driver informs the instance that data are waiting from the client, the instance should respond by calling ssClientDataGet. This routine has the following features:
When your instance is ready to write to the client, it should use routine ssClientDataPut. This routine has the following features:
Example
@PROCESS TITLE('BKWRST - ep_rec_svthread') 00001000
ENVIRONMENT(VM/ESAOS) OPT(MAX) FLAG(I); 00002000
00003000
/* @LIST OFF; */ 00004000
@EJECT; 00005000
00006000
*/*START OF SPECIFICATION*********************************************/ 00007000
*/* */ 00008000
*/* PROLOGUE */ 00009000
*/* */ 00010000
*/* MODULE-NAME: BKWRST */ 00011000
*/* */ 00012000
*/* DESCRIPTIVE-NAME: ep_rec_svthread */ 00013000
*/* */ 00014000
*/* COPYRIGHT = */ 00015000
GENERATE;/* 00016000
* THIS MODULE IS "RESTRICTED MATERIAL OF IBM" 00017000
* 5799-EFR (C) COPYRIGHT IBM CORP. 1991 00018000
* LICENSED MATERIALS-PROPERTY OF IBM 00019000
* SEE COPYRIGHT INSTRUCTIONS, G120-2083 00020000
* 00021000
@ENDGEN;*/ 00022000
*/* */ 00023000
*/* STATUS: VM/ESA Version 2 Release 3.0 */ 00024000
*/* */ 00025000
*/* FUNCTION: service thread for generalized line-mode service */ 00026000
*/* */ 00027000
*/* NOTES: */ 00028000
*/* DEPENDENCIES: None */ 00029000
*/* RESTRICTIONS: None */ 00030000
*/* REGISTER-CONVENTIONS: R1 - List of ptrs to the parameters */ 00031000
*/* R12 - RAB address */ 00032000
*/* R13 - dynamic storage area */ 00033000
*/* R14 - Return address */ 00034000
*/* R15 - Entry point address */ 00035000
*/* PATCH-LABEL: None */ 00036000
*/* */ 00037000
*/* MODULE-TYPE: Procedure */ 00038000
*/* PROCESSOR: PL/X */ 00039000
*/* MODULE-SIZE: refer to assembler listing */ 00040000
*/* ATTRIBUTES: MP-capable, enabled, AMODE 31, RMODE ANY */ 00041000
*/* */ 00042000
*/* ENTRY-POINT: see MODULE-NAME */ 00043000
*/* */ 00044000
*/* PURPOSE: see FUNCTION */ 00045000
*/* */ 00046000
*/* LINKAGE: */ 00047000
*/* OS Type I call, with the addition that R12 */ 00048000
*/* points to the RAB (runtime anchor block), a data structure */ 00049000
*/* used for managing the stack */ 00050000
*/* */ 00051000
*/* INPUT: See procedure statement */ 00052000
*/* */ 00053000
*/* OUTPUT: See procedure statement */ 00054000
*/* */ 00055000
*/* EXIT-NORMAL: as follows: */ 00056000
*/* */ 00057000
*/* EXIT-ERROR: as follows: */ 00058000
*/* */ 00059000
*/* EXTERNAL REFERENCES: */ 00060000
*/* */ 00061000
*/* ROUTINES: */ 00062000
*/* */ 00063000
*/* CONTROL-BLOCKS: .... */ 00064000
*/* */ 00065000
*/* CALLED-BY: ...... */ 00066000
*/* */ 00067000
*/* SUBROUTINES: None */ 00068000
*/* */ 00069000
*/* ERROR MESSAGES: None */ 00070000
*/* */ 00071000
*/* MACROS: */ 00072000
*/* */ 00073000
*/* CHANGE-ACTIVITY: */ 00074000
*/* */ 00075000
*/* OPERATION: */ 00076000
*/* */ 00077000
*/* 1) */ 00078000
*/* */ 00079000
*/* 2) */ 00080000
*/* */ 00081000
*/* */ 00082000
*/*END OF SPECIFICATION***********************************************/ 00083000
*/* A000000-999999 NEW FOR VM/ESA Version 2 Release 3.0 @VR74PVM*/ 00084000
*/********************************************************************/ 00085000
@EJECT COMPILE ASM; 00086000
00087000
ep_rec_svthread: procedure 00088000
( 00089000
pl_sblock, /* my S-block */ 00090000
pl_cblock /* my C-block */ 00091000
) 00092000
options 00093000
( 00094000
id /* generates identifier */ 00095000
reentrant /* no static data pls */ 00096000
amode(31) /* 31-bit addresses */ 00097000
rmode(any) /* can reside anywhere */ 00098000
datareg(13) /* R13 is stack ptr */ 00099000
savearea(120) /* size of a save area */ 00100000
stack('SSPRLG ','SSEPIL ') /* hi-perf auto stg mgmt */ 00101000
); 00102000
00103000
/* parameters */ 00104000
declare 00105000
pl_sblock pointer(31), 00106000
pl_cblock pointer(31); 00107000
00108000
/*******************************************/ 00109000
/* macros */ 00110000
/*******************************************/ 00111000
00112000
?RegEqu; /* registers */ 00113000
%include syslib(vmplxmtr); /* CMS bindings */ 00114000
%include syslib(vmplxipc); /* CMS bindings */ 00115000
00116000
%include syslib(ssplxmem); /* skySgpXxx bindings */ 00117000
%include syslib(ssplxsrv); /* skyService stuff */ 00118000
%include syslib(ssplxcli); /* skyService stuff */ 00119000
00120000
%include syslib(bkwgen); /* internal bindings */ 00121000
%include syslib(bkwrec); 00122000
%include syslib(bkwusi); /* I am USERID... */ 00123000
%include syslib(bkwsrv); /* ... or SERVER... */ 00124000
%include syslib(bkwcfg); /* ... or CONFIG... */ 00125000
%include syslib(bkwtcp); /* ... or TCP... */ 00126000
%include syslib(bkwaut); /* ... or AUTH... */ 00127000
%include syslib(bkwsgp); /* ... or SGP... */ 00128000
%include syslib(bkwcp); /* ... or CP... */ 00129000
%include syslib(bkwenr); /* ... or ENROLL... */ 00130000
%include syslib(bkwmon); /* ... or MONITOR... */ 00131000
%include syslib(bkwcac); /* ... or CACHE... */ 00132000
%include syslib(bkwicv); /* ... or IUCV... */ 00133000
%include syslib(bkwwrk); /* ... or WORKER... */ 00134000
%include syslib(bkwnam); /* names of things */ 00135000
00136000
/*******************************************/ 00137000
/* based variables */ 00138000
/*******************************************/ 00139000
00140000
declare 00141000
word fixed(31) based, /* a full word */ 00142000
halfword fixed(16) based, /* a halfword */ 00143000
byte fixed(8) based, /* a byte */ 00144000
chars character(*) based; /* char string */ 00145000
00146000
/*******************************************/ 00147000
/* automatic storage */ 00148000
/*******************************************/ 00149000
00150000
declare 00151000
00152000
/* anchor stuff */ 00153000
skya pointer(31), 00154000
00155000
/* my subpool */ 00156000
my_subpool char(8), /* my subpool */ 00157000
00158000
/* rc/re for CSL calls */ 00159000
rc fixed(31), /* return code */ 00160000
re fixed(31), /* reason code */ 00161000
00162000
/* preallocated message buffers */ 00163000
mb character(length(vmss_imsg)), 00164000
rb character(length(vmss_lmsg)), 00165000
00166000
/* pointers to said buffers */ 00167000
mp pointer(31), /* msg pointer */ 00168000
rp pointer(31), /* reply pointer */ 00169000
00170000
/* stuff for QueueReceiveBlock */ 00171000
ml fixed(31), /* msg length */ 00172000
ko fixed(31), /* key offset */ 00173000
kl fixed(31), /* key length */ 00174000
suid character(8), /* sender UID */ 00175000
spid fixed(31), /* sender PID */ 00176000
reptok fixed(31), /* reply token */ 00177000
00178000
/* stuff for working off commands */ 00179000
curcmdbuf char(1024), /* cmd buffer */ 00180000
curcmdptr pointer(31), /* cmd pointer */ 00181000
curcmdlen fixed(31), /* cmd length */ 00182000
00183000
/* record of whether i made any output */ 00184000
imadeoutput fixed(31), 00185000
00186000
/* work variables */ 00187000
i fixed(31), /* work variable */ 00188000
j fixed(31), /* work variable */ 00189000
k fixed(31), /* work variable */ 00190000
p pointer(31); /* work pointer */ 00191000
00192000
/*************************************************/ 00193000
/* protect runtime */ 00194000
/*************************************************/ 00195000
00196000
respecify (r12) restricted; 00197000
00198000
/*********************************************************/ 00199000
/* CODE BEGINS HERE */ 00200000
/*********************************************************/ 00201000
00202000
call ep_get_anchor ( skya ); 00203000
00204000
/* might as well do this now */ 00205000
curcmdptr = addr(curcmdbuf(5)); 00206000
00207000
/* and decide who I am */ 00208000
call choose_subpool; 00209000
00210000
/* set up buffer pointers */ 00211000
mp = addr(mb); 00212000
rp = addr(rb); 00213000
00214000
/*****************************************************************/ 00215000
/* here's the big service loop. just do a receive, using my */ 00216000
/* instance key from the C-block as the receive key. everything */ 00217000
/* i have to pay attention to will come to me as an IPC message */ 00218000
/* keyed according to my instance key. when the receive is */ 00219000
/* complete, then examine the message further to see what I */ 00220000
/* have to do with this new piece of work. */ 00221000
/*****************************************************************/ 00222000
00223000
do forever; 00224000
00225000
/* i haven't made output yet */ 00226000
imadeoutput = 0; 00227000
00228000
/****************************************************************/ 00229000
/* receive notice of more work. */ 00230000
/****************************************************************/ 00231000
00232000
/* wait for something to do */ 00233000
call QueueReceiveBlock 00234000
( 00235000
rc, /* return code */ 00236000
re, /* reason code */ 00237000
pl_cblock->vmss_cblock.vc_qh, /* queue handle */ 00238000
pl_cblock->vmss_cblock.vc_ikey, /* match key */ 00239000
length(vmss_cblock.vc_ikey), /* its length */ 00240000
0, /* timeout */ 00241000
mp->chars, /* msg buffer */ 00242000
length(mb), /* its length */ 00243000
ml, /* msg length */ 00244000
ko, /* key offset */ 00245000
kl, /* key length */ 00246000
suid, /* sender UID */ 00247000
spid, /* sender PID */ 00248000
reptok /* reply token */ 00249000
); 00250000
if (rc^=vm_ipc_success) then 00251000
goto done; 00252000
00253000
/**************************************************************/ 00254000
/* process work received. */ 00255000
/**************************************************************/ 00256000
00257000
/*******************************************************/ 00258000
/* pay attention to what my line driver is telling me */ 00259000
/* in the vi_cbits field of the message it sent. it */ 00260000
/* might be telling me that something is up with my */ 00261000
/* client and that I need to do something instead of */ 00262000
/* working off the input queue. recall these bits */ 00263000
/* are: */ 00264000
/* */ 00265000
/* vi_b_cclose client has closed connection */ 00266000
/* vi_b_aclose connection closed abnormally */ 00267000
/* vi_b_newdata there is new data on queue */ 00268000
/* vi_b_cdone client is done sending */ 00269000
/* vi_b_ldstop line driver says STOP */ 00270000
/* */ 00271000
/* if the client has closed or the connection has */ 00272000
/* closed abnormally, we're done; don't even bother */ 00273000
/* working off any pending client input. */ 00274000
/* */ 00275000
/*******************************************************/ 00276000
00277000
if ( (mp->vi_b_cclose=1) | 00278000
(mp->vi_b_aclose=1) | 00279000
(mp->vi_b_ldstop=1) ) then 00280000
goto done; 00281000
00282000
if (mp->vi_b_newdata=0) then 00283000
goto cmdsdone; 00284000
00285000
/*******************************************************/ 00286000
/* work off every whole command in the client queue. */ 00287000
/*******************************************************/ 00288000
00289000
do forever; 00290000
00291000
/**************************************************/ 00292000
/* see if there is another command in the queue. */ 00293000
/**************************************************/ 00294000
00295000
/* grab command length */ 00296000
p = addr(curcmdlen); 00297000
call ssClientDataGet 00298000
( 00299000
rc, /* return code */ 00300000
re, /* reason code */ 00301000
ss_cli_iam_instance, /* i am instance */ 00302000
pl_cblock, /* C-block ptr */ 00303000
ss_cli_method_peek, /* peek it */ 00304000
0, /* ALET */ 00305000
p->chars, /* to here */ 00306000
4, /* four bytes */ 00307000
i, /* amt given */ 00308000
j /* amt additional */ 00309000
); 00310000
if ((rc^=0) | (i<4)) then 00311000
goto cmdsdone; 00312000
00313000
/* not all here? */ 00314000
if (j < curcmdlen) then 00315000
goto cmdsdone; 00316000
00317000
/* there's enough... grab it */ 00318000
i = curcmdlen + 4; 00319000
call ssClientDataGet 00320000
( 00321000
rc, /* return code */ 00322000
re, /* reason code */ 00323000
ss_cli_iam_instance, /* i am instance */ 00324000
pl_cblock, /* C-block ptr */ 00325000
ss_cli_method_read, /* read it */ 00326000
0, /* ALET */ 00327000
curcmdbuf, /* to here */ 00328000
i, /* amount to get */ 00329000
j, /* amt given */ 00330000
k /* amt left */ 00331000
); 00332000
00333000
/*******************************************************/ 00334000
/* perform the command. do_command will use the */ 00335000
/* ssClientDataPut routine to queue output for the */ 00336000
/* client, and via return code it will signal me */ 00337000
/* whether it did so. */ 00338000
/*******************************************************/ 00339000
00340000
/* handle command */ 00341000
i = curcmdlen; 00342000
00343000
select; 00344000
00345000
/* am I AUTH? */ 00346000
when (my_subpool = c_aut_subpool) 00347000
call ep_aut_do_command 00348000
( 00349000
skya, /* big pointer */ 00350000
pl_cblock, /* my C-block */ 00351000
curcmdptr->chars, /* command buffer */ 00352000
i, /* command length */ 00353000
rc /* return code */ 00354000
); 00355000
00356000
/* am I CACHE? */ 00357000
when (my_subpool = c_csv_subpool) 00358000
call ep_csv_do_command 00359000
( 00360000
skya, /* big pointer */ 00361000
pl_cblock, /* my C-block */ 00362000
curcmdptr->chars, /* command buffer */ 00363000
i, /* command length */ 00364000
rc /* return code */ 00365000
); 00366000
00367000
/* am I CMS? */ 00368000
when (my_subpool = c_cms_subpool) 00369000
call ep_cms_do_command 00370000
( 00371000
skya, /* big pointer */ 00372000
pl_cblock, /* my C-block */ 00373000
curcmdptr->chars, /* command buffer */ 00374000
i, /* command length */ 00375000
rc /* return code */ 00376000
); 00377000
00378000
/* am I CONFIG? */ 00379000
when (my_subpool = c_cfg_subpool) 00380000
call ep_cfg_do_command 00381000
( 00382000
skya, /* big pointer */ 00383000
pl_cblock, /* my C-block */ 00384000
curcmdptr->chars, /* command buffer */ 00385000
i, /* command length */ 00386000
rc /* return code */ 00387000
); 00388000
00389000
/* am I CP? */ 00390000
when (my_subpool = c_cp_subpool) 00391000
call ep_cp_do_command 00392000
( 00393000
skya, /* big pointer */ 00394000
pl_cblock, /* my C-block */ 00395000
curcmdptr->chars, /* command buffer */ 00396000
i, /* command length */ 00397000
rc /* return code */ 00398000
); 00399000
00400000
/* am I ENROLL? */ 00401000
when (my_subpool = c_enr_subpool) 00402000
call ep_enr_do_command 00403000
( 00404000
skya, /* big pointer */ 00405000
pl_cblock, /* my C-block */ 00406000
curcmdptr->chars, /* command buffer */ 00407000
i, /* command length */ 00408000
rc /* return code */ 00409000
); 00410000
00411000
/* am I IUCV or APPC? */ 00412000
when ( (my_subpool = c_icv_subpool) | 00413000
(my_subpool = c_apc_subpool) ) 00414000
call ep_icv_do_command 00415000
( 00416000
skya, /* big pointer */ 00417000
pl_cblock, /* my C-block */ 00418000
curcmdptr->chars, /* command buffer */ 00419000
i, /* command length */ 00420000
rc /* return code */ 00421000
); 00422000
00423000
/* am I MONITOR? */ 00424000
when (my_subpool = c_msv_subpool) 00425000
call ep_mon_do_command 00426000
( 00427000
skya, /* big pointer */ 00428000
pl_cblock, /* my C-block */ 00429000
curcmdptr->chars, /* command buffer */ 00430000
i, /* command length */ 00431000
rc /* return code */ 00432000
); 00433000
00434000
/* am I SERVER? */ 00435000
when (my_subpool = c_srv_subpool) 00436000
call ep_srv_do_command 00437000
( 00438000
skya, /* big pointer */ 00439000
pl_cblock, /* my C-block */ 00440000
curcmdptr->chars, /* command buffer */ 00441000
i, /* command length */ 00442000
rc /* return code */ 00443000
); 00444000
00445000
/* am I SGP? */ 00446000
when (my_subpool = c_sgp_subpool) 00447000
call ep_sgp_do_command 00448000
( 00449000
skya, /* big pointer */ 00450000
pl_cblock, /* my C-block */ 00451000
curcmdptr->chars, /* command buffer */ 00452000
i, /* command length */ 00453000
rc /* return code */ 00454000
); 00455000
00456000
/* am I TCP or UDP? */ 00457000
when ( (my_subpool = c_tcp_subpool) | 00458000
(my_subpool = c_udp_subpool) ) 00459000
call ep_tcp_do_command 00460000
( 00461000
skya, /* big pointer */ 00462000
pl_cblock, /* my C-block */ 00463000
curcmdptr->chars, /* command buffer */ 00464000
i, /* command length */ 00465000
rc /* return code */ 00466000
); 00467000
00468000
/* am I USERID? */ 00469000
when (my_subpool = c_uid_subpool) 00470000
call ep_uid_do_command 00471000
( 00472000
skya, /* big pointer */ 00473000
pl_cblock, /* my C-block */ 00474000
curcmdptr->chars, /* command buffer */ 00475000
i, /* command length */ 00476000
rc /* return code */ 00477000
); 00478000
00479000
/* am I WORKER? */ 00480000
when (my_subpool = c_wrk_subpool) 00481000
call ep_wrk_do_command 00482000
( 00483000
skya, /* big pointer */ 00484000
pl_cblock, /* my C-block */ 00485000
curcmdptr->chars, /* command buffer */ 00486000
i, /* command length */ 00487000
rc /* return code */ 00488000
); 00489000
00490000
/* I'm not anyone? */ 00491000
otherwise 00492000
Generate ( DC X'0000' ); 00493000
00494000
end; /* select */ 00495000
00496000
/* keep track of output */ 00497000
imadeoutput = imadeoutput + rc; 00498000
00499000
end; /* do forever */ 00500000
00501000
/*******************************************************/ 00502000
/* all commands complete. if i made output, tell */ 00503000
/* the line driver. then handle whether the line */ 00504000
/* driver is telling me to stop. */ 00505000
/*******************************************************/ 00506000
00507000
cmdsdone: 00508000
00509000
/* notify driver if needed */ 00510000
if (imadeoutput^=0) then 00511000
do; 00512000
00513000
/* build message for line driver */ 00514000
rp->vl_lkey = pl_cblock->vmss_cblock.vc_lkey; 00515000
rp->vl_type = ss_srv_msgtype_linedriver; 00516000
rp->vl_ikey = pl_cblock->vmss_cblock.vc_ikey; 00517000
rp->vl_ibits = 0; 00518000
rp->vl_b_newdata = 1; 00519000
00520000
/* tell line driver i made some output */ 00521000
call QueueSend 00522000
( 00523000
rc, /* return code */ 00524000
re, /* reason code */ 00525000
pl_cblock->vmss_cblock.vc_qh, /* ld queue handle */ 00526000
rp->chars, /* message buffer */ 00527000
length(rb), /* message length */ 00528000
offset(vl_lkey), /* key offset */ 00529000
length(vl_lkey) /* key length */ 00530000
); 00531000
00532000
end; /* i made output */ 00533000
00534000
/* stop if needed */ 00535000
if (mp->vi_b_cdone=1) then 00536000
goto done; 00537000
00538000
/**************************************************************/ 00539000
/* PROCESSING OF IPC MESSAGE HAS COMPLETED */ 00540000
/**************************************************************/ 00541000
00542000
end; /* do forever */ 00543000
00544000
/*****************************************************************/ 00545000
/* end of big service loop */ 00546000
/*****************************************************************/ 00547000
00548000
done: 00549000
00550000
/* emit 'I stopped' message */ 00551000
rp->vl_lkey = pl_cblock->vmss_cblock.vc_lkey; 00552000
rp->vl_type = ss_srv_msgtype_linedriver; 00553000
rp->vl_ikey = mp->vi_ikey; 00554000
rp->vl_ibits = 0; 00555000
rp->vl_b_stopack = 1; 00556000
00557000
/* and ship it to my line driver */ 00558000
call QueueSend 00559000
( 00560000
rc, /* return code */ 00561000
re, /* reason code */ 00562000
pl_cblock->vmss_cblock.vc_qh, /* queue handle */ 00563000
rp->chars, /* message buffer */ 00564000
length(rb), /* message length */ 00565000
offset(vl_lkey), /* key offset */ 00566000
length(vl_lkey) /* key length */ 00567000
); 00568000
00569000
/* and return! */ 00570000
return code(0); 00571000
00572000
/*****************************************************************/ 00573000
/* local procedures implemented as macros */ 00574000
/*****************************************************************/ 00575000
00576000
%include syslib(plxdll); 00577000
%include syslib(bkwrcs); 00578000
00579000
end ep_rec_svthread; 00580000
00581000
AnchorsThe RSK provides a simple callable anchor facility that lets the server author set and query the value of a server-wide anchor word. This facility complements the function available through CSL routine ThreadSetUserData. The envisioned use for the RSK's facility is that early on in the life of the server, the author's code will call ssAnchorSet to install the value of the anchor word. This anchor word would probably be the address of some main control block. At other times as necessary, the author's code would call ssAnchorGet to retrieve the value of the anchor word, thereby obtaining (for example) the address of the main control block. Routine ssAnchorGet also returns the address and length of the CP APPLDATA buffer the RSK maintains for the server. The author's code can place important counters or other values into the monitor buffer and they will be collected in APPLDATA records as part of CP's monitor data processing. APIs
Example/* bindings */ %include syslib(ssplxanc); /* automatic variables */ declare rc fixed(31), re fixed(31), anchor pointer(31), monbufptr pointer(31), monbuflen fixed(31); /* to set the anchor */ call ssAnchorSet ( rc, re, anchor ); /* to retrieve the anchor and the monitor info */ call ssAnchorGet ( rc, re, anchor, monbufptr, monbuflen ); Memory ManagementThe RSK provides a callable memory API that front-ends CMS's CMSSTOR facility. This API offers the additional feature that it can allocate and release storage in VM Data Spaces. Use of VM Data Spaces is subject to any limits imposed by the server's CP directory entry (statement XCONFIG ADDRSPACE). APIs
ssMemoryCreateDSTo use the RSK's memory API with VM Data Spaces, the first step is to create a "subpool" to represent the data space. Here is an example call: %include syslib(ssplxmem); declare rc fixed(31), re fixed(31), subpool char(8), pagecount fixed(31), storkey fixed(31), optcount fixed(31), optarray(4) fixed(31), asit char(8), alet fixed(31); subpool = 'MYDSPACE'; pagecount = 256 * 16; /* 16 MB */ storkey = 0; optcount = 0; /* if we wanted to supply a DMSSPCC option array, */ /* we'd do so via the OPTCOUNT and OPTARRAY parms */ call ssMemoryCreateDS ( rc, /* return code */ re, /* reason code */ subpool, /* subpool name */ pagecount, /* num of pages */ storkey, /* storage key */ optcount, /* option count */ optarray, /* option array */ asit, /* DS ASIT */ alet /* DS ALET */ ); If the call is successful, the RSK will have created the data space and will be ready for you to allocate storage from it. Keep track of the ALET because you will need it to refer to the buffers you allocate.
ssMemoryAllocateYou can use ssMemoryAllocate to allocate and release storage in:
ssMemoryAllocate supports doubleword-aligned and page-aligned requests and also supports variably-sized allocation. %include syslib(ssplxmem); declare rc fixed(31), re fixed(31), subpool char(8), min fixed(31), max fixed(31), amtgot fixed(31), storptr pointer(31); subpool = 'MYDSPACE'; min = 32; /* at least 32 bytes */ max = 1024; /* as much as 1 KB */ call ssMemoryAllocate ( rc, /* return code */ re, /* reason code */ min, /* min needed */ max, /* max wanted */ subpool, /* subpool name */ ss_mem_align_page, /* alignment reqt */ storptr, /* storage ptr */ amtgot /* amt obtained */ );
ssMemoryReleaseThe only warning here is that you should release only storage you acquired via call to ssMemoryAllocate. If you went directly to CMSSTOR, go directly there to release. %include syslib(ssplxmem); declare rc fixed(31), re fixed(31), subpool char(8), amtgot fixed(31), storptr pointer(31); subpool = 'MYDSPACE'; call ssMemoryRelease ( rc, /* return code */ re, /* reason code */ amtgot, /* amt to release */ subpool, /* subpool name */ storptr /* storage ptr */ );
ssMemoryDeleteUse this to delete subpools you created via call to ssMemoryCreateDS or manipulated via call to ssMemoryAllocate. %include syslib(ssplxmem); declare rc fixed(31), re fixed(31), subpool char(8); subpool = 'MYDSPACE'; call ssMemoryDelete ( rc, /* return code */ re, /* reason code */ subpool /* subpool name */ ); CommandsIn front-ending CMSSTOR, the RSK keeps a cache of storage ready for giving out via ssMemoryAllocate. CMSSTOR "thinks" this storage is allocated, of course. There is a threshold governing how much storage the RSK will cache in this way. If the free storage cache grows too large, the RSK will do releases to CMSSTOR. The threshold is set via the CONFIG MEM_MAXFREE command. This threshold applies on a per-subpool basis. Refer to the formal documentation for more information. Block-oriented DASDThe RSK provides a block-oriented model for reading and writing minidisks, whether CKD or FBA. The purpose of the APIs and associated commands is to give the server access to a large, high-speed pool of persistent storage blocks. The server is free to impose whatever logical structure makes sense for the problem at hand. The minidisks read and written via this facility do not contain useful CMS file systems - that is, this facility is not able to provide access to files stored on CMS minidisks. Minidisks used by the RSK's DASD model must be formatted at 4 KB and reserved. The RSK provides no facility for performing these initialization steps automatically. The RSK organizes minidisks into sets called storage groups. Storage groups have the following properties:
Figure 1 illustrates the notion of storage groups.
The grouping of minidisks into storage groups is recorded in the file pointed to by configuration parameter SGP_FILE. When a storage group is brought online it is assigned a symbolic name. This symbolic name is how the storage group is identified in the ssSgpRead and ssSgpWrite API calls. This lets the server operator change the configuration of the storage groups without breaking programs. Beyond this there is not a lot to say about these storage groups. There exist APIs and commands to bring storage groups online and offline, and there are APIs for performing reads and writes to storage groups. APIsAll of these APIs are prototyped in SSASMSGP MACRO and SSPLXSGP COPY. APIs for performing operator functions:
APIs for performing I/O:
Various other APIs:
CommandsThere are analogs for most of the APIs, for example:
The SGP service can be attached to any of the record-oriented line drivers, and then SGP commands can be issued via that line driver. For example, if we start the SGP service through the console driver, we can then type these SGP commands on the console. Example
00001000
/* bindings */ 00002000
%include syslib(ssplxsgp); 00003000
00004000
00005000
/* variables */ 00006000
declare 00007000
rc fixed(31), 00008000
re fixed(31), 00009000
sgid char(8), 00010000
curstart fixed(31), /* zero-origin start */ 00011000
curcount fixed(31), /* num of blocks */ 00012000
bufptr pointer(31); 00013000
00014000
00015000
/* doing a write is like this... */ 00016000
call ssSgpWrite 00017000
( 00018000
rc, /* return code */ 00019000
re, /* reason code */ 00020000
sgid, /* sg name */ 00021000
curstart, /* page number */ 00022000
curcount, /* page count */ 00023000
0, /* buffer ALET */ 00024000
bufptr->chars /* buffer itself */ 00025000
); 00026000
00027000
00028000
/* reading pages is like this... */ 00029000
call ssSgpRead 00030000
( 00031000
rc, /* return code */ 00032000
re, /* reason code */ 00033000
sgid, /* sg name */ 00034000
curstart, /* page number */ 00035000
curcount, /* page count */ 00036000
0, /* buffer ALET */ 00037000
bufptr->chars /* buffer itself */ 00038000
); 00039000
00040000
File CachingThe RSK provides an API set that performs caching of CMS files read from minidisk, SFS, or BFS. Cached files are held in VM Data Spaces. The server author uses the caching capability by calling the RSK's file reading routines instead of calling the CSL (DMSOPEN, BPX1OPN) or the FS macros (FSREAD). The RSK resolves the server's read request from a VM Data Space if possible. The caching API does more than just cache file data. Recognizing that the data are likely to be consumed in a different code page and record format than that in which they were stored, the RSK lets the opener specify translation schemes that govern how the RSK will transform the file data as it loads the data into the cache. This lets frequently-accessed files' data be transformed only once. Details
APIsThere are just a few APIs.
Commands
Example
00932000
/**************************************************/ 00933000
/* set open options based on translation type. */ 00934000
/* ssCacheFileOpen handles data conversions. */ 00935000
/**************************************************/ 00936000
00937000
num_flags = 4; 00938000
flag_names(1) = ss_cac_ofn_recmethod_fs; 00939000
flag_names(2) = ss_cac_ofn_xlate; 00940000
flag_names(3) = ss_cac_ofn_recmethod_cache; 00941000
00942000
select; 00943000
00944000
/* BINARY: do it as-is */ 00945000
when ( (filtertype(1::6) = 'binary') & 00946000
(filtertypelen = 6) ) then 00947000
do; 00948000
flag_values(1) = '00000000'x; 00949000
flag_values(2) = 0; 00950000
flag_values(3) = '01000000'x; 00951000
end; 00952000
00953000
/* ASCII: it depends */ 00954000
when ( (filtertype(1::5) = 'ascii') & 00955000
(filtertypelen = 5) ) then 00956000
do; 00957000
if (fs_type = fs_sfs) then 00958000
do; 00959000
flag_values(1) = '00000000'x; 00960000
flag_values(2) = 0; 00961000
flag_values(3) = '01020D0A'x; 00962000
end; 00963000
else 00964000
do; 00965000
flag_values(1) = '01011500'x; 00966000
flag_values(2) = 0; 00967000
flag_values(3) = '01020D0A'x; 00968000
end; 00969000
end; 00970000
00971000
/* DOS: X'0D0A' markers */ 00972000
when ( (filtertype(1::3) = 'dos') & 00973000
(filtertypelen = 3) ) then 00974000
do; 00975000
flag_values(1) = '01020D0A'x; 00976000
flag_values(2) = 0; 00977000
flag_values(3) = '01020D0A'x; 00978000
end; 00979000
00980000
/* UNIX: X'0A' markers */ 00981000
when ( (filtertype(1::4) = 'unix') & 00982000
(filtertypelen = 4) ) then 00983000
do; 00984000
flag_values(1) = '01010A00'x; 00985000
flag_values(2) = 0; 00986000
flag_values(3) = '01020D0A'x; 00987000
end; 00988000
00989000
/* CMS: insert record lengths */ 00990000
when ( (filtertype(1::3) = 'cms') & 00991000
(filtertypelen = 3) ) then 00992000
do; 00993000
if (fs_type = fs_sfs) then 00994000
flag_values(1) = '00000000'x; 00995000
else 00996000
flag_values(1) = '01011500'x; 00997000
flag_values(2) = 0; 00998000
flag_values(3) = '02020000'x; 00999000
end; 01000000
01001000
/* MODULE: it depends, due to how */ 01002000
/* OPENVM PUTBFS behaves for MODULEs */ 01003000
when ( (filtertype(1::6) = 'module') & 01004000
(filtertypelen = 6) ) then 01005000
do; 01006000
if (fs_type = fs_sfs) then 01007000
do; 01008000
flag_values(1) = '00000000'x; 01009000
flag_values(2) = 0; 01010000
flag_values(3) = '02020000'x; 01011000
end; 01012000
else 01013000
do; 01014000
flag_values(1) = '00000000'x; 01015000
flag_values(2) = 0; 01016000
flag_values(3) = '01000000'x; 01017000
end; 01018000
end; 01019000
01020000
/* CGI: this is an escape for later */ 01021000
when ( (filtertype(1::3) = 'cgi') & 01022000
(filtertypelen = 3) ) then 01023000
do; 01024000
goto giveerr; 01025000
end; 01026000
01027000
/* HIDE: refuse to serve file */ 01028000
when ( (filtertype(1::4) = 'hide') & 01029000
(filtertypelen = 4) ) then 01030000
do; 01031000
goto giveerr; 01032000
end; 01033000
01034000
/* other types - 7BIT, 8BIT, EBCDIC, etc. */ 01035000
otherwise 01036000
do; 01037000
if (fs_type = fs_sfs) then 01038000
flag_values(1) = '00000000'x; 01039000
else 01040000
flag_values(1) = '01011500'x; 01041000
flag_values(2) = 1; 01042000
flag_values(3) = '01020D0A'x; 01043000
end; 01044000
01045000
end; /* end select */ 01046000
01047000
/* set up for BFS/RFS for ssCacheFileOpen */ 01048000
01049000
flag_names(4) = ss_cac_ofn_bfs; 01050000
if (fs_type = fs_bfs) then 01051000
flag_values(4) = ss_cac_ofv_yes; 01052000
else 01053000
flag_values(4) = ss_cac_ofv_no; 01054000
01055000
/**************************************************/ 01056000
/* try opening the file now. try uppercasing if */ 01057000
/* first open fails with not_found. */ 01058000
/**************************************************/ 01059000
01060000
/* 01061000
?DMSMSG 01062000
TEXT('Filespec &1 &2') 01063000
SUB(DEC,filespec_len,HEX4,(filespec,filespec_len)); 01064000
*/ 01065000
01066000
do while (fileopen=0); 01067000
01068000
ssCacheFileOpen 01069000
( 01070000
rc, /* return code */ 01071000
re, /* reason code */ 01072000
filcache, /* cache name */ 01073000
filespec, /* file name */ 01074000
filespec_len, /* name length */ 01075000
c, /* ESM data */ 01076000
0, /* none */ 01077000
num_flags, /* flag count */ 01078000
flag_names, /* flag names */ 01079000
flag_values, /* flag values */ 01080000
file_token, /* file token */ 01081000
cache_alet, /* cache ALET */ 01082000
file_addr, /* cache address */ 01083000
file_size, /* file size */ 01084000
file_update /* update time */ 01085000
); 01086000
01087000
if (rc=0) then 01088000
do; 01089000
fileopen = 1; 01090000
/* 01091000
?DMSMSG 01092000
TEXT('Date: &1') 01093000
SUB(CHAR,file_update); 01094000
*/ 01095000
end; 01096000
else 01097000
do; 01098000
01099000
/* announce error */ 01100000
?DMSMSG 01101000
TEXT('&1: RC=&2 RE=&3 from ssCacheFileOpen') 01102000
SUB(CHAR,(filespec,filespec_len),DEC,rc,DEC,re); 01103000
01104000
/* if not_found, uc and try again */ 01105000
if ( (re^=ss_cac_re_file_not_found) | 01106000
(ucfs=1) ) then 01107000
goto giveerr; 01108000
else 01109000
do; 01110000
call xlate 01111000
( 01112000
addr(uce), 01113000
addr(filespec), 01114000
filespec_len 01115000
); 01116000
ucfs = 1; 01117000
end; 01118000
01119000
end; /* open error */ 01120000
01121000
end; /* while */ 01122000
01123000
/* if we get here, the file is open */ 01124000
01125000
AuthorizationIn many situations, a server is nothing more than an access method for objects of various kinds. For example, an HTTP server is a just a server that sends files over socket connections when browsers ask for them. When servers are acting as access methods, it is sometimes appropriate for the server to impose authorization rules on the set of clients, operations, and objects. Taken together, this set of rules describes "who can do what to whom". Typically, for each object controlled by the server, there will be a small set of rules for each authorized user, each rule listing the subset of operations the user is permitted to perform. The RSK supports the construction and maintenance of such an authorization rule set by providing a rule base manager in the form of a set of APIs and operator commands. This rule base manager understands the abstract notions of user, object, and action, but the actual objects defined, users defined, and actions defined are left to the server designer to decide. The RSK's rule manager just relates users, objects, and actions, whatever they are defined to be. Unlike ESMs such as RACF, the RSK does not actually protect objects. It is expected that servers needing to protect their objects will store the rule base in the RSK's rule base manager. When clients ask to perform certain operations on objects, the server can call the RSK, supplying the object name, the client name, and a vector of proposed operations. The RSK will respond with an yea/nay vector, and the server will interpret said response and either do the client's bidding or send a negative response. SetupThe authorization rules are kept in a pair of CMS files. The pair can be stored on minidisk or in the CMS Shared File System. (Storing the files on an accessed SFS directory counts as SFS.) The setup process varies according to the storage medium you choose.
MinidiskWhen the files are on minidisk, the RSK keeps twin copies of the pair (four files in all) and keeps a log file that describes which pair is up-to-date at the moment. This scheme lets the RSK recover from an incomplete update (device failure, etc.). When you use minidisk, observe the following guidelines:
Then proceed in PROFILE RSK as illustrated in this snippet from a mythical PROFILE RSK: /* this assumes minidisk 1 is at filemode K and */ /* minidisk 2 is at filemode L */ 'CONFIG AUT_LOCATION MINIDISK' fn = 'RULEBASE' dfile = fn 'RSKAUD' xfile = fn 'RSKAUX' 'CONFIG AUT_DATA_1 ' dfile 'K' 'CONFIG AUT_INDEX_1 ' xfile 'K' 'CONFIG AUT_DATA_2 ' dfile 'L' 'CONFIG AUT_INDEX_2 ' xfile 'L' 'CONFIG AUT_LOG ' fn 'RSKAUL K'
Shared File SystemWhen the files are in SFS, the RSK uses SFS's work unit support to commit changes together. Thus, only one pair of files is needed and no log file is needed. This snippet from a mythical PROFILE RSK configures the authorization data to reside in SFS: 'CONFIG AUT_LOCATION SFS' fn = 'RULEBASE' authdir = 'MYPOOL:SERVER.AUTHDATA' 'CONFIG AUT_DATA_1 ' fn 'RSKAUD' authdir 'CONFIG AUT_INDEX_1 ' fn 'RSKAUX' authdir Users, Objects, and ActionsA user is just a 1- to 64-byte string identifying a user. An action is just a 4-byte string identifying an action. An object class is just a set of actions. The object class is identified by an eight-byte name. For example, object class FRED might be the set of actions READ, WRIT, EXEC, and ERAS. There is no hierarchy in these operations. An object is a specific item that belongs to an object class. The object is identified by a 1- to 256-byte name. For example, object My Data File might belong to object class FRED and therefore would have FRED's actions defined on it. APIs
Simple APIsThese do pretty much what you would expect.
These APIs remove objects, classes, and users from the rule base.
These APIs produce listings of various kinds.
These APIs perform various queries against the rule base.
The remaining APIs are complex enough to warrant their own sections.
ssAuthPermitUserThis API accepts an (object,user) pair as input, so as to identify the rule to be modified. It also accepts other input information describing how said rule is to be modified. The modification instructions are passed in as two arrays and a scalar. Consider first the two n-element arrays. The first array contains a list of operations defined on the object's class. The second n-element array tells whether the corresponding action is to be added to or deleted from the rule. For example, consider these two three-element arrays: op array action array -------- ------------ READ ss_aut_add_operation WRIT ss_aut_remove_operation EXEC ss_aut_add_operation This would add READ and EXEC permissions to the rule and remove WRIT permission from the rule. The scalar describes how the arrays are to be applied:
With a little thought you can see how this structure lets the current rule be edited easily. ssAuthPermitUser returns an n-element vector in response. The elements of the vector describe the results of applying the arrays:
ssAuthTestOperationsThis entry point lets you test a proposed set of operations against a rule in the rule base. The rule to be tested is identified by ordered pair (object,user). The proposal is an n-element vector of operations you believe are defined on the object. The response is an n-element vector of responses. Each element of the vector is one of the following scalars:
This can be used to test many operations simultaneously. Also, this is good for locking because writes to the rule set are disabled while the test is being conducted. Operator CommandsThese parallel the API set. They all start with AUTH.
Example
*COPY BKWCAU 00001000
00002000
/*********************************************************/ 00003000
/* */ 00004000
/* check_auth: checks to see whether things are OK. */ 00005000
/* if things are NOT OK then return with a nonzero rc. */ 00006000
/* */ 00007000
/* caller must include: */ 00008000
/* ssplxaut */ 00009000
/* ssplxsrv */ 00010000
/* */ 00011000
/* caller must declare: */ 00012000
/* rc fixed(31) */ 00013000
/* */ 00014000
/*********************************************************/ 00015000
00016000
check_auth: procedure 00017000
( 00018000
ca_cblock, /* C-block addr */ 00019000
ca_sn, /* service name */ 00020000
ca_snl, /* name length */ 00021000
ca_cw, /* check word */ 00022000
ca_op /* op type */ 00023000
); 00024000
00025000
/* parameters */ 00026000
declare 00027000
ca_cblock pointer(31), 00028000
ca_sn char(8), 00029000
ca_snl fixed(31), 00030000
ca_cw fixed(31), 00031000
ca_op char(4); 00032000
00033000
/* automatic storage */ 00034000
declare 00035000
re fixed(31), 00036000
results fixed(31), 00037000
userid_len fixed(31), 00038000
i fixed(31); 00039000
00040000
/* pretend it's OK */ 00041000
rc = 0; 00042000
00043000
/* if AUTHCHECK_LD is off or he is inside this */ 00044000
/* virtual machine, it's OK */ 00045000
00046000
if ( (ca_cw=0) | 00047000
(ca_cblock->vmss_cblock.vc_userid = '* ') ) then 00048000
return; 00049000
00050000
/*****************************************************/ 00051000
/* call to ssAuthTestOperations goes here. the call */ 00052000
/* is configured like this: */ 00053000
/* */ 00054000
/* OBJECT: name of the service being manipulated */ 00055000
/* USER: userid from C-block */ 00056000
/* ACTION: STRT or STOP */ 00057000
/* */ 00058000
/*****************************************************/ 00059000
00060000
i = 1; 00061000
do while ((ca_cblock->vmss_cblock.vc_userid(i) ^= ' ') & 00062000
(i <= 8)); 00063000
i= i + 1; 00064000
end; 00065000
00066000
userid_len = i - 1; 00067000
call ssAuthTestOperations 00068000
( 00069000
rc, /* return code */ 00070000
re, /* reason code */ 00071000
ca_cblock->vmss_cblock.vc_userid, /* userid */ 00072000
userid_len, /* its length */ 00073000
ca_sn, /* object name */ 00074000
ca_snl, /* its length */ 00075000
1, /* op count */ 00076000
ca_op, /* op name */ 00077000
results /* results */ 00078000
); 00079000
00080000
/* if call worked, check results */ 00081000
if (rc=0) then 00082000
if (results^=ss_aut_op_permitted) then 00083000
rc = 1; 00084000
00085000
/* and return to caller */ 00086000
return; 00087000
00088000
end check_auth; 00089000
00090000
EnrollmentMany servers face the problem of holding onto enrollment information describing the clients they serve. The RSK provides an enrollment API that lets the server hold onto this data easily. The RSK's enrollment engine holds onto enrollment records by keeping them in a VM Data Space. This lets the RSK hold onto hundreds of thousands of enrollment records using in-memory techniques. While this engine was designed to hold enrollment records, it is really just a simple indexed access method with a single key and no wildcarding. Feel free to put it to other uses, such as holding your server's configuration parameters. The RSK supports memory-only enrollment sets. These sets are empty each time the server starts and are never written to disk. This might be useful for tracking transient indexed sets, such as information about the set of currently-connected clients. DetailsSome notes:
APIsOperations on sets:
Operations on records:
Maintenance operations:
For example, %include syslib(ssplxenr); declare rc fixed(31), re fixed(31), setname char(8), key char(64), buffer char(512), amtgot fixed(31); /* retrieve BKW's record */ setname = 'MYDIRECT'; key = 'BKW'; call ssEnrollRecordGet ( rc, /* return code */ re, /* reason code */ setname, /* set name */ key, /* key to use */ buffer, /* output buffer */ length(buffer), /* buffer size */ amtgot /* amt returned */ ); CommandsAll of these start with ENROLL. Operations on sets:
Operations on records:
Maintenance operations:
Exampleenroll list ENROLL Name Pages Entries InUse D K ENROLL ---- ----- ------- ----- - - ENROLL HTTPCNFG 256 3 1 0 d ENROLL HTTPMIME 256 209 6 0 d RSK> cp q spaces CP ASIT STORAGE P/S ACC SPACE IDENTIFICATION CP 36F3344000000013 128M PRV R/W MPTEST:BASE CP 36F334C000000007 1M PRV R/W MPTEST:BKW_HTTPCNFG CP 36F3350000000007 1M PRV R/W MPTEST:BKW_HTTPMIME CP 36F3340000000012 128M PRV R/W MPTEST:BKW_HTTPFILE CP BKWHCP0900I RC=0 from CP. RSK> enroll reclist HTTPCNFG ENROLL LOGGING ENROLL ROOT ENROLL FSTYPE RSK> Worker MachinesCertain kinds of work are best done in isolation. Running CGI programs, for example, should be done in a way that an errant CGI can't destroy your server's ability to turn HTTP transactions. Recognizing this, the RSK supports the notion of worker machines and offers an API that lets the server distribute work among virtual machines. Design points:
APIsThere is only one API: ssWorkerAllocate. %include syslib(ssplxwrk); declare rc fixed(31), re fixed(31), icblock pointer(31), class char(8), ocount fixed(31), onames(4) fixed(31), ovals(4) fixed(31), wcblock pointer(31), connid fixed(31); /* set up classname */ class = 'MYWCLASS'; /* set up options array */ ocount = 2; /* i prefer an empty worker if possible */ onames(1) = ss_wrk_ofn_prefer_empty; ovals(1) = ss_wrk_ofv_yes; /* try all workers in pool */ onames(2) = ss_wrk_ofn_retry_count; ovals(2) = 0; /* now make the call */ call ssWorkerAllocate ( rc, /* return code */ re, /* reason code */ icblock, /* my C-block */ class, /* worker class */ ocount, /* option count */ onames, /* option names */ ovals, /* option vals */ wcblock, /* worker C-block */ connid /* connection ID */ ); /* if RC=0, watch for notifications in my line */ /* driver queue -- 'message type' field of IPC */ /* message will contain the connection ID */ CommandsThese all start with WORKER:
User ID MappingBecause the RSK lets you write a service without regard for the transport technology the client uses to reach you, it is necessary for the RSK to provide some transport-independent means of identifying clients for you. If the RSK provided no such mechanism, it would be nearly impossible for you to perform authorization checking for the objects your server manages. Said another way, because the RSK offers transport independence, a given user (person) could connect to your server over a number of different transport protocols. As the author of a transport-independent server, you would want your client to be able to access his resources with the same privileges no matter what transport technology he used to connect to you. The RSK attempts to address this problem by having all of its line drivers map transport-specific user identifiers through a mapping file. The result of the mapping -- a single-token user identifier -- is passed to you as the identifier of the user. The crux of the mapping support is a mapping file that contains rules about how userids should be mapped. Each record in the file has the following form: MAP <transport> <node> <userid> <mapped_userid>where:
You should craft
the <transport>,
<node>, and <userid>
tokens in your mapping records according to
Table 1.
Table 1. Use of Mapping Tokens
Note that LISTFILE-like wildcarding (* and %) can be used in the <transport>, <node>, and <userid> fields. For example, to match all nodes starting with GDL, one could write GDL* in the <node> field. Commands
APIsThe mapping function itself is available to you via call to ssUseridMap. (Maybe you can think of another use for this besides mapping user IDs.) Example* Userid map file (node userid validated_userid) * File is processed from top to bottom, first match wins * APPC/VM stuff (LU name, IPVMID) MAP APPC '*USERID:* BKW BKW ; Brian Wade MAP APPC '*USERID:* DRQ BKW ; Brian Wade MAP APPC '*USERID:* BKW7 BKW ; Brian Wade MAP APPC '*USERID:* WADEB BKW ; Brian Wade * TCP/IP mapping MAP TCP 9.130.57.10 * BKW MAP TCP 9.130.57.120 * BKW2 MAP TCP 9.130.79.171 * VMWEB * userid/nodeid stuff (spool, MSG, SMSG) MAP * GDLVM7 BKW BKW ; Brian Wade MAP * GDLVM7 DRQ BKW ; Brian Wade MAP * GDLVM7 BKW7 BKW ; Brian Wade MAP * GDLVM7 WADEB BKW ; Brian Wade MAP * WSCVM BKW BKW ; Brian Wade MAP * GDLVMWEB EWEBADM BKW ; Brian Wade MAP * GDLVMWEB BKW BKW ; Brian Wade MAP * GDLVM7 DONOVANM DONOVANM ; Mike Donovan * anything that falls through MAP * * * $UNKNOWN ; Everyone else Monitor DataThe RSK collects data describing the operation of its various subsystems and uses CP's APPLDATA facility to cause the collected data to accrue in CP monitor data records. The RSK uses a single monitor buffer, declared via DIAG X'DC'. (This will work only if OPTION APPLMON appears in the server's CP directory entry.) The RSK divides the monitor buffer into 72-byte fragments called monitor rows. Each monitor row contains information about a specific facet of RSK operation. After the RSK's monitor rows, there is a portion of the monitor buffer called the application area. This area is reserved for the server author's use; anything written to that buffer lands in CP APPLDATA records. The server can obtain the address of the application area by calling ssAnchorGet. The first monitor row, called the kernel row, contains enough information so that you can deduce the organization of the rest of the monitor buffer. The size of a monitor row, the number of monitor rows, and the size of the application area all appear in the kernel row. The first eight bytes of a monitor row give a generic description of the kind of information being accrued in the row. For example, the first eight bytes might be LINEDRV to indicate that information about a line driver is being accrued. Note, though, that if the first eight bytes are zero, the row is not in use and should be ignored. The second eight bytes always contain a further qualifier of some kind - in the LINEDRV example, the second qualifier is the name of the line driver. You should refer to the formal documentation for the descriptions of all of the possible kinds of monitor buffers. If you are writing a program to reduce or analyze the collected monitor records, you will want to know the following pieces of information about the monitor buffer:
CommandsAs regards the RSK's monitor data, there are a few commands you should know.
Exampleserver config SERVER Parameter Value SERVER --------- ----- (some stuff deleted) SERVER MON_PRODUCT_ID SSTEST-FNN010100 SERVER MON_USER_SIZE 32 SERVER MON_KERNEL_ROWS 55 (some stuff deleted) RSK> server monitor SERVER BKWSRV0301I Monitor buffer at 07E59000.00000F98, 55 rows, 7 free RSK> monitor display kernel MONITOR KERNEL at 07E59000.00000048 MONITOR D2C5D9D5 C5D34040 40404040 40404040 00000037 00000048 00000020 00000001 MONITOR 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 MONITOR 00000000 00000000 RSK> monitor display linedrv MONITOR LINEDRV SPOOL at 07E59558.00000048 MONITOR D3C9D5C5 C4D9E540 E2D7D6D6 D3404040 00000000 00000000 00000000 00000000 MONITOR 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 MONITOR 00000000 00000000 MONITOR LINEDRV MSG at 07E59630.00000048 MONITOR D3C9D5C5 C4D9E540 D4E2C740 40404040 00000000 00000000 00000000 00000000 MONITOR 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 MONITOR 00000000 00000000 MONITOR LINEDRV SUBCOM at 07E59708.00000048 MONITOR D3C9D5C5 C4D9E540 E2E4C2C3 D6D44040 00000000 00000020 00000000 0000023E MONITOR 00000000 000004F0 00000000 00000000 00000000 00000000 00000000 00000000 MONITOR 00000000 00000000 MONITOR LINEDRV CONSOLE at 07E597E0.00000048 MONITOR D3C9D5C5 C4D9E540 C3D6D5E2 D6D3C540 00000000 00000002 00000000 0000001D MONITOR 00000000 0000011C 00000000 00000000 00000000 00000000 00000000 00000000 MONITOR 00000000 00000000 MONITOR LINEDRV TCP at 07E59AB0.00000048 MONITOR D3C9D5C5 C4D9E540 E3C3D740 40404040 00000000 00000000 00000000 00000000 MONITOR 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 MONITOR 00000000 00000000 RSK> monitor user MONITOR User data at 07E59F78.00000020 MONITOR 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 RSK> CP SPOOL CONSOLE STOP CLOSE |