        .title  findmaster
;;
;; This program uses the $ENQ system service to take out a null lock on the
;; resource name specified in the DCL symbol FINDMASTER_RESOURCE.  It then uses
;; the $GETLKI system service to retrieve information on the lock (in
;; particular, the lock master node for the resource).
;;								K.Parris 3/98
;; Defines the global symbol FINDMASTER_NODE to contain the name of the
;; lock master node (or "" if there is none).                   KP 4/28/98
;;
;; Author: Keith Parris  parris@encompasserve.org or keithparris@yahoo.com
;; http://www.geocities.com/keithparris/ or http://encompasserve.org/~parris/
	$lckdef		;Include definition macros...
	$lkidef		;...used for $ENQ and $GETLKI
	$syidef		;Include definitions used for $GETSYI
	$psldef		;USER/SUPER/EXEC/KERNEL mode definitions
	$libdef		;Include definition macros for LIB$ routines
;------------------------------------------------------------------------------
	.PSECT	RO_DATA	QUAD,RD,noWRT,noEXE
;;
;; Read-only data area
;;

;	.align	quad
symbol_name_res:	.ascid	"FINDMASTER_RESOURCE"	;Resource name
;	.align	quad
symbol_name_acmode:	.ascid	"FINDMASTER_ACMODE"	;Mode (USER/SUP/EXEC/KERNEL)
;	.align	quad
symbol_name_node:	.ascid	"FINDMASTER_NODE"	;Name of symbol returned
;;
;; Output strings used to format the lock information.
;;
;	.align	quad
;line1:	.ascid	"Lock master CSID: !XL"
;	.align	quad
line2:	.ascid	"Lock master node: !AS"
line2n:	.ascid	"No locks, thus no master node at this time."
;	.align	quad
;line3:	.ascid	"Lock count: !UL"
;;
;; Item List for the $GETLKI call.
;;
	.align	long
lki_itmlst:
	.word		4			;buffer length (1 longword)
	.word		LKI$_MSTCSID		;CSID of master node
	.address	lock_mstcsid		;buffer address
	.long		0			;returned length address [null,
;						;since it will always be 4...]
	.word		4               	;buffer length (1 longword)
	.word		LKI$_LCKCOUNT		;# of locks on resource
	.address	lock_count		;buffer address
	.long		0			;returned length address [null,
;						;since it will always be 4...]
	.long		0	;end of item list.
;;
;; Item List for the $GETSYI call.
;;
	.align	long
syi1_itmlst:
	.word		4			;buffer length (1 longword)
	.word		SYI$_NODE_CSID		;Return CSID of node
	.address	node_csid		;buffer address
	.long		0			;returned length address [null,
;						;since it will always be 4...]
;
	.long		0	;end of item list.
syi2_itmlst:
	.word		mstnode_length		;buffer length
	.word		SYI$_NODENAME		;Return nodename of node
	.address	mstnode			;buffer address
	.address	mstnode_len		;returned length address
;
	.long		0	;end of item list.

;------------------------------------------------------------------------------
	.PSECT	RW_DATA	QUAD,RD,WRT,noEXE
;;
;; Read-write data area
;;

	.align	quad
resnam_length = 31
resnam_desc:				; resource name descriptor
resnam_len:	.long		resnam_length	;length of string
		.address	lock_resnam	;address of buffer
lock_resnam:	.blkb		resnam_length	;resource name

	.align	quad
acmode_length = 10
acmode_desc:				; access mode descriptor
acmode_len:	.long		acmode_length	;length of string
		.address	acmode	;address of buffer
acmode:	.blkb		acmode_length	;access mode name

	.align	long
lock_acmode:	.blkl		;Access mode of lock (PSL$C_xxxx value)

	.align	quad
lksb:	.blkl	2		;lksb for $ENQW
lock_id = lksb+4		; LKSB+4 = lock id

	.align	quad
iosb:	.blkl	2		;IOSB for $GETLKI

;;
;; Buffers referenced in the $GETLKI item list.
;;
	.align	long
lock_mstcsid:	.blkl		;CSID of lock master
lock_count:	.blkl		;Number of locks on the resource

;;
;; Buffers referenced in the $GETSYI item lists.
;;
	.align	long
node_csid:	.blkl		;CSID of the node we're running on

	.align	quad
mstnode_length = 15    ; Nodename's maximum length (15 in theory, 6 in practice)
mstnode_desc:				; Nodename descriptor
mstnode_len:	.long		0		;length of string
mstnode_addr:	.address	mstnode		;address of buffer
; We reset the above length to zero, resulting in a null nodename string being
; returned, if there is no lock master node presently (i.e. if this node is
; identified as the lock master but our single lock is the only lock in
; existence, which we can tell from a lock count value of 1 from $GETLKI).
mstnode:	.blkb		15		;nodename buffer itself

;;
;; Descriptor for $FAO call.
;;
maxfao = 80			;maximum fao buffer length
	.align	quad
faodesc:			;fao descriptor
faolen:	.blkl			;fao output buffer length
	.address	faobuf	;address of buffer
faobuf:	.blkb	maxfao		;80 character buffer
;------------------------------------------------------------------------------
	.PSECT	CODE	QUAD,RD,noWRT,EXE
;;
;;
;;  Begin code
;;
;;

;;
;; status check macro
;;
.macro check_status, loc=R0, ?no_error1$
        blbs loc, no_error1$          ;branch if no error
	.if different loc, R0
        movzwl loc, R0                ;move error into R0
	.endc
        $exit_s R0                    ;exit with error
no_error1$:                           ;return to program flow
.endm check_status

;;
;; Format and Output message macro.
;;
.macro output_msg, msg_line, param1, ?end_of_msgs$
	movzbl  #maxfao,faolen
	$fao_s	ctrstr=msg_line,-	;format the message
		outlen=faolen,-
		outbuf=faodesc,-
		p1=param1
	check_status			;check status
	pushal faodesc
	calls	#1, g^lib$put_output	;output the message
	check_status			;check status
end_of_msgs$:                         ;Message is output
.endm output_msg                      ;return to program flow

;;;;===========================================================================
;;;;
;;;;
;;;; Main program
;;;;
;;;;
;;;;===========================================================================
        .entry  start,^m<>

;;
;; Grab the resource name from the DCL symbol FINDMASTER_RESOURCE
;;
;;;;	pushl	#0		; table-type-indicator, by reference [null]
	pushaw	resnam_len	; resultant-length, by reference
	pushaq	resnam_desc	; resultant-string, by descriptor
	pushaq	symbol_name_res	; symbol name, by descriptor
	calls	#3,g^LIB$GET_SYMBOL
	check_status
;;
;; Grab the lock access mode name from the DCL symbol FINDMASTER_ACMODE
;;
;;;;	pushl	#0		; table-type-indicator, by reference [null]
	pushaw	acmode_len	; resultant-length, by reference
	pushaq	acmode_desc	; resultant-string, by descriptor
	pushaq	symbol_name_acmode	; symbol name, by descriptor
	calls	#3,g^LIB$GET_SYMBOL
	check_status
;;
;; Decode the access mode lock name
;;
	movzbl	acmode,R0	; Grab the first letter of the access mode
	movl	#PSL$C_USER,lock_acmode		; Default is user mode
;	cmpb	R0,#^a'U'		; User mode?
;	beql	10$		; Yes, user mode -->
	cmpb	R0,#^a'E'		; Exec mode?
	bneq	10$		; No, not exec mode --> default to user
	movl	#PSL$C_EXEC,lock_acmode	; Exec mode lock needed
	$cmexec_s -		; Have to do $ENQ/$GETLKI in EXEC mode
		routin=lock_work,-
		arglst=#0
	check_status
	brb	50$

; We won't even try to support kernel or supervisor modes at this point
;	movl	#PSL$C_KERNEL,lock_acmode
;	cmpb	R0,#^a'E'		; Exec mode?
;	movl	#PSL$C_SUPER,lock_acmode
;	cmpb	R0,#^a'S'		; Exec mode?
10$:
	calls	#0,lock_work
	check_status
	brb	50$

50$:	; continue
;; If the lock count is 1, and the master node is this node, that means
;; our NL lock is the only lock in existence in the entire cluster at this
;; time, and so there was no lock master, in essence, at this time.
	cmpl	lock_count,#1	;Is there only a single lock (maybe just ours)?
	bneq	51$		;No --> more out there
;;
;; Find out the CSID of the node we're running on
;;
	$getsyiw_s	-
			itmlst=syi1_itmlst,-	;item list
			iosb=iosb		;IOSB
	check_status				;check status of R0
	check_status iosb			;check status of iosb

	cmpl	lock_mstcsid,node_csid	;Is this node the lock master?
	bneq	51$			;No --> report the info

;; Lock count was 1, and we're the lock master, so this resource was
;; apparently not in use in general in the cluster at this time.  Don't
;; try to report the lock mastership.
	clrw	mstnode_len		;Zero the nodename length to nullify it
	pushal	line2n
	calls	#1, g^lib$put_output	;output the "no locks" message
	check_status			;check status
	brw	59$			;Return results and exit -->

51$:
;;
;; Find out the name of the node mastering the lock tree
;;
	$getsyiw_s	csidadr=lock_mstcsid,-	;CSID
			itmlst=syi2_itmlst,-	;item list
			iosb=iosb		;IOSB
	check_status				;check status of R0
	check_status iosb			;check status of iosb
;;
;; Format and output the information on the lock.
;;
;	output_msg	line1, lock_mstcsid	;CSID message
;; Report the lock master nodename
	output_msg	line2, #mstnode_desc	;Nodename message
;; Report the count of locks on this resource
;	output_msg	line3, lock_count	;Lock count message

;; Return results in a DCL global symbol also
59$:

;;
;; Set the DCL global symbol FINDMASTER_NODE to be the nodename of the
;; resource master node (or a null string if there was none)
;;
	pushal	#LIB$K_CLI_GLOBAL_SYM	; table-type-indicator, by reference
	pushaq	mstnode_desc		; value string, by descriptor
	pushaq	symbol_name_node	; symbol name, by descriptor
	calls	#3,g^LIB$SET_SYMBOL
;;	check_status
;; Checked on exit anyway...

99$:
;;;
;;; Done.  Exit.
;;;
	$exit_s	r0				;exit and check status
;
	ret	; So Alpha macro compiler knows this routine has ended

;;
;; Take out a NL mode lock on the resource.  This routine is either simply
;; called in user mode or (for an exec-mode lock) called by $CMEXEC in
;; executive mode.
;;
        .entry  lock_work,^m<>

	$enqw_s	-
		resnam=resnam_desc,-	;resource name
		lkmode=#lck$k_nlmode,-	;lock mode: Null
		flags=#LCK$M_SYSTEM,-	;system-wide lock (not per-UIC-group)
		acmode=lock_acmode,-	;user-mode lock
		lksb=lksb		;lock status block
;	check_status			;check status of R0
	blbc	r0,lock_work_exit
;	check_status	lksb		;check status of lksb
	movzwl	lksb,r0
	blbc	r0,lock_work_exit
;;
;; Get information on the lock.  Use the lock id returned by the $ENQ call.
;;
	$getlkiw_s -
		lkidadr=lock_id,-	;id for lock we want info on
		itmlst=lki_itmlst,-	;item list
		iosb=iosb		;status block
;	check_status			;check status of R0
	blbc	r0,lock_work_exit
;	check_status	iosb		;check status of iosb
	movzwl	iosb,r0
	blbc	r0,lock_work_exit
;;
;; Dequeue the lock.
;;
	$deq_s	lkid=lock_id		;dequeue the queued lock
;	check_status			;check status of R0
lock_work_exit:
	ret

        .end start
