	.title idllb
;;; idllb - (priviledged) determine idle information for terminals
;
; revision history
;
; 02b 08Oct83 Rg  Fix a few typos.
; 02a 08Nov82 cal .Written; this routine has been coded again from
;		   scratch. Revision history after this point has
;		   no reason for existing and can be safely ingnored.
; 01b 16jul82 vp  .added .title
; 01a 12Mar82 cal .Taken from a Decus tape
;
; synopsis
;
;   status = idle(buf_tim, buf_pid, buf_siz)
;
;	buf_tim - buffer of longwords to receive idle times
;	buf_pid - buffer of longwords to receive pids
;	buf_siz - number of units to get information about
;
; Both buffers are longwords (integer*4) arrays of size buf_siz. Idle
; time and process ids are stored in the two buffers in consecutive
; elements with a zero longword in the pid buffer to mark end of the
; list. The information is ordered by terminal name.
;
; Note: Use of this routine requires the privilege CMKRNL.
;
	.library  /SYS$LIBRARY:LIB.MLB/
	$ucbdef
	$ddbdef
;
;	Useful constants
;
off_tim = 4					; offset to due time buffer
off_pid = 8					; offset to pid buffer
off_siz = 12					; offset to unit count
num_args = 3					; number of arguments

fudge = 2					; add this to exe$gl_abstim
sizeof_long = 4					; size of a longword
max_units = 128					; max units to report about
sizeof_buf = max_units * sizeof_long		; size of return buffers

	.entry	idle,^M<r2,r3,r4,r5,r6>

	cmpl	#num_args,(ap)			; need this many arguments
	beql	10$				; branch if correct
	movl	#ss$_insfarg,r0			; setup error status
	ret					; go give him the bad news

10$:	cmpl	@off_siz(ap),#max_units 	; want at least this many units
	bgequ	20$				; we'll allow his to be bigger
	movl	#ss$_ivbuflen,r0		; setup error status
	ret					; return to caller

20$:	probew  #0,#sizeof_buf,@off_tim(ap)	; see if we can write there...
	beql	30$				; bail out if not

	probew  #0,#sizeof_buf,@off_pid(ap)	; see if we can write here too
	bneq	40$				; branch if we can
30$:	movl	#ss$_accvio,r0			; setup error status
	ret					; return to caller

40$:	$lkwset_s inadr=lock			; lock routine into working set
	cmpl	#ss$_wasclr,r0			; did we lock the pages?
	beql	50$				; branch if so
	cmpl	#ss$_wasset,r0			; were they already locked?
	bneq	err				; if not, then error

50$:	$cmkrnl_s routin=idlekrn		; do the privileged stuff
	movl	r0,r6				; save status for later

	$ulwset_s inadr=lock			; unlock kernel routine
	cmpl	#ss$_wasclr,r0			; did we unlock the pages?
	beql	60$				; branch if so
	cmpl	#ss$_wasset,r0			; were they already unlocked?
	bneq	err				; if not, then error

60$:	movc3	al_cnt,bl_tim,@off_tim(ap)	; store idle time info
	movc3	al_cnt,bl_pid,@off_pid(ap)	; store pid info
	movl	r6,r0				; restore status

err:	ret					; that's all folks!

;
;	Descriptor used to lock kernel routine into our working set
;
lock:	.address	idlekrn
	.address	idlekrnend

;
; Entry point for the privileged search and slurp. The following
; registers are used:
;
;	r0 - return status
;	r1 thru r5 - scratch
;	r6 - system absolute time
;	r9 - address of due time storage area
;	r8 - address of pid storage area
;	r10 - address of current unit control block
;	r11 - address of current device data block
;
	.entry	idlekrn,^M<r6,r8,r9,r10,r11>

	movl	#bl_tim,r9			; address of due time buffer
	movl	#bl_pid,r8			; address of pid buffer

	jsb	sch$iolockr			; lock io data base for read
	dsbint	#ipl$_synch			; raise ipl while mucking...

	addl3	#fudge,exe$gl_abstim,r6		;; add fudge to abstim

	movl	ioc$gl_devlist,r11		;; ddb list in r11
	brb	jump1				;; jump into middle loop

loop1:	movl	ddb$l_link(r11),r11		;; look at next ddb
jump1:	beql	finish				;; already did last controller

	movl	ddb$l_ucb(r11),r10		;; fetch first ucb of this ddb
	brb	jump2				;; jump into innermost loop
loop2:	movl	ucb$l_link(r10),r10		;; look at next ucb
jump2:	beql	loop1				;; no more units left

	cmpb	#dc$_term,ucb$b_devclass(r10)	;; is this unit a terminal?
	bneq	loop1				;; next ddb if not

	tstl	ucb$l_pid(r10)			;; check for a process
	beql	loop2				;; skip if no process

	subl3	ucb$l_duetim(r10),r6,r2		;; idle time in r2
	bbc	#ucb$v_tim,ucb$w_sts(r10),10$	;; see if unit is timed out
	brb	20$				;; timed out so go zero time
10$:	bbs	#ucb$v_bsy,ucb$w_sts(r10),30$	;; see if unit is busy
20$:	clrl	r2				;; if true, zero idle time

30$:	movl	r2,(r9)+			;; stow idle time
	movl	ucb$l_pid(r10),(r8)+		;; stow owner pid

	cmpl	#bl_pid+sizeof_buf,r8		;; have we run out of buffer?
	bgequ	loop2				;; if not negative we're ok
	subl	#sizeof_long,r8			;; backup just a tad
	movl	#ss$_bufferovf,r0		;; setup return status
	brb	krnerr				;; and exit

finish:	movl	#ss$_normal,r0			;; setup return status
   
krnerr:	movl	r0,r6				;; store status
	jsb	sch$iounlock			;; unlock io data base
	enbint					;; restore ipl

	clrl	(r8)+				; flag end of list
	subl3	#bl_pid,r8,al_cnt		; calculate size of transfer
	movl	r6,r0				; restore status
	ret					; back to user mode

bl_tim:	.blkl	sizeof_buf			; storage for due time
bl_pid:	.blkl	sizeof_buf			; storage for pid

al_cnt:	.long	0				; size of transfer

idlekrnend:					; end of locked memory

	.end
