From: heinrich@yvax.byu.edu (Ed Heinrich  - The LOKI Group, Inc.)
Newsgroups: comp.os.vms
Subject: re: Getting notified when a process terminates?
Date: 14 Aug 92 09:45:02 -0700
Distribution: world
Organization: Brigham Young University

In article <1992Aug13.082015.100@ipact.com>, lakia@ipact.com writes:
> In article <9208110358.AA01660@uu3.psi.com>, leichter@lrw.com (Jerry Leichter) writes:
>>
>> 	Say... does anyone out there know what a "Termination Mailbox" is?  I
>> 	have an application whereby I'll want a single process to "monitor" a
>> 	group of other processes to measure how long they've been online for
>>rest deleted....
>>
> One could also tell all authors of the programs you are interested
> in monitoring to set up exit handlers.  These exit handlers
> could send a mail message to a permenant mail box that your
> monitor could be looking at.   This however will not work
> if someone does a delete process  (such as the DCL STOP command).
> However, I used a function available for user written system
> services that allow the system service to be called on
> process rundown, regardless if the process was deleted, or
> exited.
> [...]
> 
> Earl Lakia			Email:  LAKIA@IPACT.COM
> IPACT Inc.			Voice:  219-464-7212
> 260 South Campbell		FAX:    219-462-5387
> Valparaiso, IN   46383
> 


Several years ago I also was faced with the neeed to know when an image
had terminated.  The processes were created as detached processses
by another process, which also needed this information and employed
the termination mailbox method. I had written several privileged-mode
routines and had created them as user-written system services, to make
modifications to those routines transparent to the users and to
avoid granting CMKRNL privileges to every process on the system that
needed access to these routines.  One of the nice side-effects of 
user-written system services is that there is a mechanism provided
that allows for these services to receive notification when an image
is being rundown, specifically so they can clean up after themselves,
i.e., return non-paged pool, etc.  As one of the necessary steps to
creating a user-written system service, you must create a change mode
vector.  The change mode vector contains relative offsets to the
user-defined executive and kernel mode change mode dispatchers and
also allows the user to specify an image rundown routine and an
RMS rundown routine.  When the image activator executes the image, it
notices the change mode vector and loads the address of the rundown
handler into CTL$GL_USRUNDWN.  As part of image rundown, VMS checks this
cell and issues a JSB to the address contained in it, (normally simply
a RSB instruction).  Your user-defined rundown code is then executed,
called via a JSB, NOT A CALLx instruction!  

The beauty of doing it this way, unlike some other proposals that 
have been posted, is that other than relinking the images that you
wish to track so they link against the privileged image containing the
change mode vector, NO modifications need be made to the target images.


In SYS$EXAMPLES is a template that can be used for writing user-written
system services.  (USSDISP.MAR, I think).  You can edit this file, I
think it contains an example of a rundown handler, and have it handle
your requirements.  I have enclosed part of a rundown handler I wrote,
past my signature, along with the shell of the symbiont process that handled 
the mailbox message sent to it from the rundown handler.  I last worked on 
this under V5.2 so I cannot be ABSOLUTELY certain that it works 100% on V5.5
but don't see anything in it that should have broken.  Also, I edited 
it quite a bit to remove superflous (to your question) code so it is not
complete.


						- Ed -

*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
Ed Heinrich
The LOKI Group, Inc.		(801)-576-0730
HEINRICH@yvax.byu.EDU
*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*


;
;
;	This vector is used by the image activator to connect the
;	privileged shareable image to the VMS change mode dispatcher.
;	The offsets in the vector are self-relative to enable the
;	construction of position independent images.  The system
;	version number will be used by the image activator to verify 
;	that this shareable image was linked with the symbol table for
;	the current system.
;
	.Weak	SYS$K_VERSION

	.Psect	PRIV_SERVICES	PAGE,VEC,PIC,NOWRT,EXE

	.Long	PLV$C_TYP_CMOD		; Vector type = change mode dispatcher
	.Long	SYS$K_VERSION		; Identify system version
	.Long	KERNEL_DISPATCH-.	; Offset to kernel mode dispatcher
	.Long	EXEC_DISPATCH-.		; Offset to executive mode dispatcher
***>>>	.Long	PRIV_RUNDOWN-.		; Offset to user rundown service
	.Long	0			; Reserved.
	.Long	0			; No RMS dispatcher
	.Long	0			; Address check - PIC image




;
;	This Psect contains local data definitions for CSIPRVRTL
;
$PRCDEF					; Process options

RNDDEF					; Define RUNDOWN mnemonics

	.Page
	.Sbttl	PUREDATA	Non-writeable Data
	.Psect	PUREDATA	NOWRT,NOEXE,PIC,REL,LONG

RUNDOWN_MAILBOX:
	.Ascii	/RUNDOWN_MBX/		; Mailbox to assign to
RUNDOWN_MBXLEN = . - RUNDOWN_MAILBOX	; Let assembler compute length

	.Align	Long
RUNDOWN_DESC:				; Ascid descriptor for
	.Ascid	/RUNDOWN/		; Name of rundown image


	.Page
	.Sbttl	PRIV_RUNDOWN		User Rundown Service
;+
;
; FUNCTIONAL DESCRIPTION:
;	This routine is invoked from within the kernel mode VMS system
;	service that performs image rundown.  It is invoked before any
;	system run down functions are performed.  It preserves all work
;	registers, as it must, disables system service failure exceptions
;	so we don't do undesireable things while in here, assigns a
;	channel to the RUNDOWN mailbox, fires a QIO off to RUNDOWN with
;	the PID of the current image, and deassigns the channel to the
;	RUNDOWN mailbox.
;
; NOTES:
;	We check in this routine if RUNDOWN is the process that is executing
;	the PRIV_RUNDOWN code so that we do not get into any funny states,
;	i.e., attempting to perform the QIO, which will wait forever for
;	the special kernel mode AST that cannot be delivered since the
;	DELPEN bit is set in the process header.
;	Note that the asynchronous form of $QIO must be used to send messages
;	to the RUNDOWN mailbox in order to avoid the situation where RUNDOWN
;	is not running and a process is attempting to queue a mailbox message
;	to it.  The QIOW form will force the image / process that is being
;	run down to wait forever since the I/O will never complete.
;
; ENTRY FORMAT:
;	JSB	PRIV_RUNDOWN	- invoked by VMS routine SYS$RUNDWN
;	Entered in KERNEL mode at IPL 0 and must exit at IPL 0.
;
; INPUT PARAMETERS:
;	R4	Current PCB address.  (Therefore R4 must be preserved).
;	R7	Access mode parameter to $RUNDWN maximized with previous mode.
;	AP 	Argument pointer existing when the $RUNDWN service was invoked.
;     4(AP) 	Access mode parameter to $RUNDWN
;
; SIDE EFFECTS:
;	A mailbox message is sent to RUNDOWN.
;	A link ABORT message is sent on any channels assigned to DECnet.
;
;-
	.Page
	.Align	Long			; For a little speed at execution time
PRIV_RUNDOWN::				; The transfer address

	PUSHL	R1			; Preserve work registers upon entry
	PUSHL	R2			; So everything else works correctly
	PUSHL	R3			; When we exit and return control to
	PUSHL	R5			; SYS$RUNDWN
	PUSHL	R6			; R6 used for disconnect data
	PUSHL	R7			; R7 is used for flag field
	PUSHL	R8			; Save R8 used for length

	$SETSFM_S enbflg = #0		; No system service failure exceptions

	MOVZBL	#<RND_M_LOCK>, R7	; Always set LOCK flag
	MOVAB	RUNDOWN_DESC + 8, R5	; Address of process name
	MOVAB	G^CTL$GL_IMGHDRBF, R2	; Image activator's image header buffer
	MOVL	0(R2), R2		; Get address of image header buffer
	MOVL	4(R2), R2		; R2 address of image file descriptor
	MOVQ	IFD$Q_CURPROG(R2), R2	; Get image name descriptor
	MOVZWL	R2, R2			; Get size as word
	LOCC	#^A/]/, R2, (R3)	; Find directory delimiter
	BEQL	0$			; Insure we found one
	ADDL3	#1, R1, R3		; Save it for later use
	LOCC	#^A/./, R0, (R1)	; Find '.' for extension
	BEQL	0$			; Not found - exit from RUNDOWN routine
	SUBL3	R3, R1, R2		; Compute length of image name
	MOVL	R2, R8			; Save length			  EAH779
	MOVL	R3, R6			; And address			  EAH779
	MOVZWL	RUNDOWN_DESC, R1	; Get length of process name
	CMPL	R1, R2			; Does size of image name match?
	BNEQ	200$			; No then not what we're lookin' for

	PUSHL	R2			; Save registers needed in next check
	PUSHL	R3			; If this one fails
	CMPC3	R1, (R5), (R3)		; See if this is the RUNDOWN process
	POPL	R3			; Before going any futher restore R3
	POPL	R2			; And R2
	TSTL	R0			; See if we had a match
	BNEQ	200$			; If this is not RUNDOWN then continue
	MOVZBL	#SS$_NORMAL, R0		; Say success to $RUNDWN
	BRW	NO_OPERATION		; If it is RUNDOWN then exit routine

	.Align	Long
    0$:	BRB	600$			; Branch helper (Like hamburger helper!)
;
;	Here we did a bunch of other things, looking for explicit
;	image names and creating other processes if we found them, etc.
;	That code has been removed to avoid complicating the example.

  200$:
;	R2	Length of current image name
;	R3	Address of current image name
;
	[...]

  600$:	PUSHAB	RUNDOWN_MAILBOX		; Build an ASCID descriptor on stack
	PUSHL	#RUNDOWN_MBXLEN		; For SYS$ASSIGN system service
	MOVAL	-(SP), R2		; A longword for channel number
	$ASSIGN_S -			; Invoke assign channel system service
		chan	=  (R2), -	; To get a channel for the mailbox to
		devnam	= 4(R2)		; The symbiont rundown process

	BLBS	R0, 800$		; Continue on success
	BRW	RUNDOWN_EXIT		; Give up on error

	.Align	Long
  800$:	PUSHL	R7			; Set flag bits for RUNDOWN.EXE
	PUSHL	PCB$L_PID(R4)		; Copy PID to stack
	MOVAL	(SP), R3		; R3 is now pointer to PID (Buffer arg)
	$QIO_S	chan	= (R2),       -	; Channel to write it to
		func	= #IO$_WRITEVBLK, -
		p1	= (R3),       -	; Buffer address
		p2	= #RND_C_REQUIRED
					; No error recovery so don't check 
	ADDL2	#RND_C_REQUIRED, SP	; Give back work space

	$DASSGN_S -			; Deassign the channel
		chan	= (R2)		; Used to send to symbiont rundown image
;
;	If we still have an open DECnet channel then that means the
;	process did not terminate the link on purpose.  We want to
;	let the remote process know that this was a LINKABORT, not
;	a planned termination.  (Contrary to the documentation, DECnet
;	does NOT automatically do this!)
;
;	Now look for DECnet channels and if found clean up for them
;	ever so nicely.  Note that the following code can be deleted
;	someday when DEC does this for us in SYSRUNDWN.
;
CHECK_CHAN:
	MOVPSL	R7			; Get PSL to work register
	PUSHL	R5
	EXTZV	#PSL$V_PRVMOD, #PSL$S_PRVMOD, R7, R7
					; Extract previous mode
	MOVZWL	@#CTL$GW_CHINDX, R5	; Get maximum channel index
	BEQL	2100$			; No channels assigned, leave now
	MNEGL	R5, R5			; Convert to negative offset
;
;	Build descriptor for optional data sent in disconnect message
;
	SUBL2	R8, SP			; Subtract size of process name
	DECL	SP			; Include count field
	MOVL	SP, R1			; Save address of stack
	MOVL	R1, R2			; Save it in work register
	MOVB	R8, (R2)+		; Make it counted string
	MOVL	R8, R0			; Copy length to use as index

  900$:	MOVB	(R6)+, (R2)+		; Include process name text
	SOBGTR	R0, 900$		; Do all bytes in it

	PUSHL	R1			; Build descriptor on stack
	PUSHL	R8			; To send optional data
	INCL	(SP)			; In ABORT message
	MOVL	SP, R6			; So remote knows who broke link
	
	ASSUME	CCB$B_STS+1 EQ CCB$B_AMOD 
 1000$:	MOVL	G^CTL$GL_CCBBASE, R2	; Get CCB base address for this channel
	MOVAB	(R2)[R5], R1		; Get CCB address for this channel
	MOVL	CCB$L_WIND(R1), R0	; Do we have a XWB?
	BEQL	2000$			; No then can't be DECnet
	MOVL	CCB$L_UCB(R1), R0	; Grab UCB address from CCB
	BBC	#DEV$V_NET, UCB$L_DEVCHAR(R0), 2000$
					; Check for DECnet type of device
	MNEGL	R5, -(SP)		; Get back channel number from offset
	CALLS	#1, G^SYS$CANCEL	; Cancel any and all outstanding I/O

	MNEGL	R5, R0			; Convert offset back to channel number
	$QIO_S	chan 	= R0, -		; Send the "correct" message!
		func	= #<IO$_DEACCESS!IO$M_ABORT>, -
		p2	= R6		; Include optional data

 2000$:	ADDL2	#CCB$C_LENGTH, R5	; Point at next channel
	BLSS	1000$			; Process next channel
	CMPL	(SP)+, (SP)+		; Throw away descriptor	
	ADDL2	R8, SP			; Fix up stack
	INCL	SP			; Including count field

 2100$:	POPL	R5
;
;		End of code to issue ABORT on DECnet
;

RUNDOWN_EXIT:
	ADDL2	#12, SP			; Restore stack

NO_OPERATION:
	POPL	R8			; Restore R8
	POPL	R7			; Get back R7
	POPL	R6			; Restore R6
	POPL	R5			; Restore saved R5
	POPL	R3			; Restore R3
	POPL	R2			; And R2
	POPL	R1			; And R1
	RSB				; Return from user run down



The following is the code, minus all the "real" rundown activity, that
was contained in the rundown symbiont.


	.Title	RUNDOWN		Image Rundown Handler
	.Ident	/V1.0-000/	; EAH974
	.Sbttl	DESCRIPTION	Program Description
;+
;
; PROGRAM:
;	RUNDOWN.MAR
;
; AUTHOR:			CREATION DATE: 
;	Edward A. Heinrich		03-Jul-1985
;
; ABSTRACT:
;	RUNDOWN is the image run down handler.  It receives a mailbox
;	message from an exiting image and performs clean up on CSI resources
;	associated with that image.
;
; ENVIRONMENT:
;	User mode code
;
; INPUTS:
;	Mailbox message from exiting image
;
;-
RNDDEF					; Define RUNDOWN mnemonics

$PRCDEF					; Process options
$PSLDEF					; PSL definitions

	.Page
	.Sbttl	PUREDATA	Non-writeable Data
	.Psect	PUREDATA	NOWRT,NOEXE,PIC,REL,LONG

RUNDOWN_MBX_PRMFLG:
	.Long	1			; Put in system table

RUNDOWN_MBX_ACMODE:
	.Long	PSL$C_KERNEL!PSL$C_USER	; Type of mailbox access allowed

RUNDOWN_MBX_NAME:
	.Ascid	/RUNDOWN_MBX/		; Mailbox name in Ascii


	.Page
	.Sbttl	IMPUREDATA	Writeable Data
	.Psect	IMPUREDATA	  WRT,NOEXE,PIC,REL,QUAD

IOSB:	.Quad	0			; General purpose IOSB

RUNDOWN_MBX_BUFQUO:
	.Long	8192			; Buffer quota for RUNDOWN_MBX

RUNDOWN_MBX_SIZE:
	.Long	RND_C_MBXSIZE		; Size of mailbox buffer

RUNDOWN_MBX_CHAN:
	.Word	0			; Word to receive mailbox channel

RUNDOWN_MBX_BUFFER:
	.Blkb	RND_C_MBXSIZE		; Buffer for mailbox I/O


	.Page
	.Sbttl	RUNDOWN		Main-line Code
	.Psect	RUNDOWN_CODE	NOWRT,  EXE,PIC,REL,LONG

	.Entry	RUNDOWN, 0		; Take your mark, get set, Go!
;
;	We begin this little program by creating a mailbox for RUNDOWN
;
	$CREMBX_S -	
		prmflg	= RUNDOWN_MBX_PRMFLG,	-
		chan	= RUNDOWN_MBX_CHAN, 	-
		maxmsg	= RUNDOWN_MBX_SIZE, 	-
		bufquo	= RUNDOWN_MBX_BUFQUO,	-
		acmode	= RUNDOWN_MBX_ACMODE,	-
		lognam	= RUNDOWN_MBX_NAME
					; Specify all relevant parameters
	BLBS	R0, 200$		; Continue if successful
	BRW	ERROR_HANDLER		; Go to error routine otherwise
;
;	Queue a read I/O request to the RUNDOWN mailbox.
;	We specify an AST address so we can simply hibernate once the
;	QIO returns and wait until we receive a message before doing
;	any more work.
;
	.Align	Long
200$:	$QIO_S	chan	= RUNDOWN_MBX_CHAN,	-
		func	= #IO$_READVBLK,	-
		iosb	= IOSB,			-
		astadr	= RUNDOWN_AST,		-
		p1	= RUNDOWN_MBX_BUFFER,	-
		p2	= #RND_C_MBXSIZE; Queue asynchronous read

	BLBC	R0, ERROR_HANDLER	; Check for errors on QIO

400$:	$HIBER_S			; Go catch up on your sleep
	BRB	400$			; In case of superious wakeups

	.Align	Long
ERROR_HANDLER:


	RET				; Exit program


	.Page
	.Sbttl	RUNDOWN_AST	AST For Actual Processing
;+
;
; FUNCTIONAL DESCRIPTION:
;	This routine is executed when a mailbox message is received.
;	It calls all the routines necessary to perform image run down.
;
; INPUTS:
;	None
;
; IMPLICIT INPUTS:
;	Mailbox message in RUNDOWN_MBX_BUFFER
;
; OUTPUTS:
;	None
;
; SIDE EFFECTS:
;	Image currently executing in process identified by PID in mailbox
;	message is run down for CSI resources.
;
;-
	.Align	Long
	.Entry	RUNDOWN_AST, 0		; No need to preserve any registers
;
;	Get PID of the image to rundown in R6
;
	MOVL	RUNDOWN_MBX_BUFFER+RND_L_PID, R6
;
;	Do whatever processing is necessary here ...
;


;
;	Issue QIO to the mailbox so we can receive another message and run 
;	down the next exiting image.
;
QUE_IO:	$QIO_S	chan	= RUNDOWN_MBX_CHAN,	-
		func	= #IO$_READVBLK,	-
		iosb	= IOSB,			-
		astadr	= RUNDOWN_AST,		-
		p1	= RUNDOWN_MBX_BUFFER,	-
		p2	= #RND_C_MBXSIZE

	RET				; Dismiss AST 


	.End	RUNDOWN			; That's all there is

Newsgroups: comp.os.vms