	.title	DISKUSE - Get disk quota usage for this user
;+
;
; Author:
;	Dave Leonard, Advanced Data Management, 12-DEC-1980
;
; Revised:
;	20-may-1981, put in "day-quota-exceeded" check to not
;		cause problems with running jobs when other users login
;
; Revised:
;	dd-mmm-198y, Tom Gerhard, ADM - support group-wide quotas.
;
; Restrictions:
;	1) Only the users default disk is checked.
;
;	2) All files owned by the UIC are counted towards the usage
;	count, this means all files on [0,0] (directories for example)
;	and all lost files are counted.	Thus the user may see some
;	discrepency between the number on his directory listing and
;	what the quota usage shows.  NOTE: File headers are also counted
;	in this total.  Thus one block/file surcharge is counted.
;
;	3) There is no convient way to get a breakdown of where all
;	the contributing files are.  i.e. what directory they are on.
;
; Usage:
;	First thing to do is to set up the QUOTA files for your site.
;	See the system managers guide (chapter 6) for more info on
;	running the DISKQUOTA utility.  Set the default([000,000]), to 
;	the max allowed, or 999999 blocks for both OVERDRAFT and PERMANANT.
;	Issue a REBUILD command, and then set the OVERDRAFT limit to
;	the actual quota assigned to each UIC.
;
;	(CAUTION - you must not have any users on your system while doing
;	the REBUILD as it write-locks the pack while it's in progress!!!)
;
;	As this program requires write access to the quota file on
;	each disk it is run from, either the quota file must be writeable
;	by all users, or this program must be installed with SYSPRV
;	privilege.  If the quota file is made writeable, it is recommended
;	that SYS$SYSTEM:DISKQUOTA.EXE be protected from read and execute
;	access by WORLD.
;
; Description:
;
;  Individual quotas:
;       DISKUSE - is a routine that is run at login time.  Usually
;	by inserting the command 
;
;		$ RUN SYS$SYSTEM:DISKUSE
;
;	in the common login file for all users (typically
;	SYS$MANAGER:SYLOGIN.COM).  Its purpose is to check the
;	disk space used by the running UIC and compare
;	the space used with the number in the QUOTA.SYS file.  The 
;	QUOTA.SYS file has two entries, one for PERMANANT limit and one
;	for OVERDRAFT limit.  The scheme used here is to actually use
;	the number in the OVERDRAFT slot as the actual disk quota.  The
;	PERMANANT limit is usually set to twice the OVERDRAFT limit.
;	If the user has exceeded the disk blocks specified in the 
;	OVERDRAFT slot, his PERMANANT entry is reduced to the number
;	in the OVERDRAFT entry and the user is notified that his disk
;	usage is over the allowed limit.  This efectively lowers the boom 
;	on a user who uses a lot of temporary storage, and does not delete
;	his files after he is done.  It allows him to run up to double
;	his assigned quota before he is denied disk space.  
;
;	   At that time, when the user logs in, he is notified that he has
;	excluded his quota, and that he has one day to clean things up.
;	He will continue to get this message for one day, after that,
;	the actual diskquota will be set down to the overdraft limit.
;
;	   This scheme is considerably nicer to the user than DEC's 
;	hard and fast limits.
;
;	   If the user has logged on with excessive disk usage for more
;	than one day, he is restricted to his quota until he cleans up 
;	his directory and logs in again.  At that time, the usage is 
;	again checked, if it is below the allowed limit, (in the 
;	OVERDRAFT slot), the actual PERMANANT entry is set to twice 
;	the OVERDRAFT number and the user is notified that his quota 
;	has been lifted.
;
;  Group quotas:
;	To implement a quota for an entire UIC group, make an entry
;	for the UIC [group,0] with the overdraft set to the desired
;	quota for the group, and the permanent quota set to twice
;	the overdraft.  For each member, enter a small number in the
;	overdraft, and twice that number for permanent.  When any
;	user in the group logs in, the useage for the group is summed
;	and reported.  If the group usage is over the quota defined
;	by the entry for [group,0], both the group and individual member
;	entries are flagged as described under individual quotas.
;	Thus, one member of a group (i.e. a manager) can be given an
;	overdraft large enough so that he can continue to create and
;	extend files even though the group has exceeded it's quota.
;	NOTE that this algorithm needs to handle users with a UIC of
;	[group,0] as a special case.
;-

	.enable		sup
	.disable	global

	.library	'SYS$LIBRARY:LIB'

	$DQFDEF		; Disk Quota File block
	$FIBDEF		; File ID Block
	$IODEF		; I/O def's
	$JPIDEF		; Job Process Info
	$qiowdef
	$SSDEF

	.external	LIB$PUT_OUTPUT

	.entry	DISKUSE, ^m<r7,r8,r9,r10,r11>

	$ASSIGN_S -				; assign channel to SYS$DISK
		DEVNAM=drive, -
		CHAN=channel
	blbs	r0, 10$				; go o.k. ?
	ret
10$:
	movab	fibblk, r10
	movw	#fib$c_exa_quota, fib$w_cntrlfunc(r10)	; examine quota entry

	$getjpi_s	itmlst = items			; get [group,member]
	blbs	r0, 15$
	ret
;+
;	get date
;-
15$:
	$numtim_s -
	   timbuf = time_buf
	movzwl	time_buf+4, bindate

	movab	dqfblk, r11
	moval	acp_qio, r0
	movzwl	channel, qiow$_chan(r0)
	movl	#io$_acpcontrol, qiow$_func(r0)
	movaq	iosb, qiow$_iosb(r0)
	movaq	fibdesc, qiow$_p1(r0)
	movaq	dqfdesc, qiow$_p2(r0)
	movaq	dqfblklen, qiow$_p3(r0)
	movaq	dqfdesc, qiow$_p4(r0)
	clrq	qiow$_p5(r0)
	movw	group, DQF$L_UIC+2(r11)
	clrw	dqf$l_uic(r11)			; Check for group-wide quota
check_quota:
	$qiow_g	acp_qio
	blbc	r0, 10$
	movzwl	iosb, r0
	blbs	r0, 20$
10$:
	cmpl	r0, #ss$_nodiskquota
	bneq	15$
	tstw	dqf$l_uic(r11)			; Check for group-wide quota
	bneq	15$
;+
; No group-wide quota - specify exact member number and re-try
;-
	movw	member, dqf$l_uic(r11)
	brb	check_quota
15$:
	ret
20$:
	tstw	dqf$l_uic(r11)
	bneq	25$
	bsbw	scan_group
25$:
	$FAO_S	-
		member_hdr, -
		outlength, -
		outlinedesc, -
		P1=DQF$L_UIC+2(r11), -
		P2=DQF$L_UIC(r11), -
		P3=DQF$L_USAGE(r11), -
		P4=DQF$L_OVERDRAFT(r11)

	blbc	r0, 40$
	pushab	outlinedesc
	calls	#1, g^LIB$PUT_OUTPUT
	blbs	r0, 50$
40$:
	ret
50$:
	cmpl	DQF$L_USAGE(r11), DQF$L_OVERDRAFT(r11)	; exceede allowed ?
	bgtr	60$					; screw this guy
	ashl	#1, DQF$L_OVERDRAFT(r11), r9		; mult overdraft by 2
	cmpl	r9, DQF$L_PERMQUOTA(r11)		; all o.k. ?
	beql	51$
	movl	r9, DQF$L_PERMQUOTA(r11)		; set quota back up
	brb	70$
51$:
	ret						; usage was o.k.
60$:
	bsbw	check_over
	blbs	r0, 70$
	brw	warning
70$:
	bsbw	mod_permquota

	cmpl	#1, r8				; just give warning ?
	beql	warning
	cmpl	DQF$L_OVERDRAFT(r11), DQF$L_PERMQUOTA(r11) ; set down or up ?
	beql	down
	bsbw	print_up_msg
	ret
down:
	bsbw	print_down_msg
	ret
warning:
	bsbw	print_warning
	ret
print_up_msg:
	movl	#512, outlength
	$FAO_S	-
		upline, -				; SET UP
		outlength, -
		outlinedesc, -
		P1=DQF$L_PERMQUOTA(r11)
	brb	end_fao
print_warning:
	pushaq	readthis
	calls	#1, g^lib$put_output

	movl	#512, outlength
	$FAO_S	-
		warnline, -				; GIVE WARNING
		outlength, -
		outlinedesc, -
		P1=DQF$L_OVERDRAFT(r11)
	brb	end_fao
print_down_msg:
	pushaq	readthis
	calls	#1, g^lib$put_output

	movl	#512, outlength
	$fao_s	-
		downline, -				; SET DOWN
		outlength, -
		outlinedesc, -
		P1=DQF$L_PERMQUOTA(r11)
end_fao:
	blbc	r0, 900$
	pushab	outlinedesc
	calls	#1, g^lib$put_output
	blbc	r0, 900$
	rsb
900$:
	ret
;
; CONDITIONS:
;
; 1) If overdraft*2 is equal to permquota, then warning message has not
; started yet,  thus - give warning, and set permquota to overdraft*2+bindate
;
; 2) If overdraft*2+bindate is equal to permquota then just give warning
; message for today and don't change anything
;
; 3) If neither of the above two apply, then this guy has been over quota for
; more than one day, thus screw him now...
;
check_over:
	clrl	r8					; clear flags register
	ashl	#1, DQF$L_OVERDRAFT(r11), r9		; mult overdraft by 2
	cmpl	r9, DQF$L_PERMQUOTA(r11)		; CONDITION #1 ???
	bneq	61$					; nope...
	incl	r8					; set flag to 1
61$:
	addl2	bindate, r9				; add on date
	cmpl	r9, DQF$L_PERMQUOTA(r11)		; CONDITION #2 ???
	bneq	62$					; nope...
	clrl	r0
	rsb
62$:
	tstl	r8					; condition 1 set ?
	beql	64$					; nope
	movl	r9, DQF$L_PERMQUOTA(r11)		; set warn date
	brb	65$
64$:
	movl	DQF$L_OVERDRAFT(r11), DQF$L_PERMQUOTA(r11); set quota down
	movl	#2, r8
65$:
	movl	#1, r0
	rsb
.page
scan_group:
	clrl	fib$l_wcc(r10)
	movl	#fib$m_all_mem, fib$l_cntrlval(r10)

	clrl	group_usage
	movl	dqf$l_overdraft(r11), group_overdraft
	movl	dqf$l_permquota(r11), group_permquota
100$:
	$qiow_g	acp_qio

	blbc	r0, 110$
	movzwl	iosb, r0
	blbs	r0, 120$
110$:
	cmpl	r0, #ss$_nodiskquota
	beql	200$
	ret
120$:
	addl	dqf$l_usage(r11), group_usage
	brb	100$
190$:
	ret
200$:
	$fao_s	-
		group_hdr, -
		outlength, -
		outlinedesc, -
		p1 = dqf$l_uic+2(r11), -
		p2 = group_usage, -
		p3 = group_overdraft
	blbc	r0, 190$

	pushaq	outlinedesc
	calls	#1, g^lib$put_output
	blbc	r0, 190$

;	Determine if quota file needs modification

	cmpl	group_usage, group_overdraft
	bleq	300$
	brw	group_overquota
300$:
	mull3	#2, group_overdraft, r9
	cmpl	r9, group_permquota
	beql	raise_member

;	Group overdrawn flag was set - reset permquota
	movl	r9, dqf$l_permquota(r11)
	movl	r9, group_permquota

	clrw	dqf$l_uic(r11)
	bsbw	mod_permquota
	bsbw	print_up_msg
	brb	raise_member

group_overquota:
	clrw	dqf$l_uic(r11)
	movl	group_overdraft, dqf$l_overdraft(r11)
	movl	group_permquota, dqf$l_permquota(r11)
	bsbw	check_over
	movl	dqf$l_permquota(r11), group_permquota
	blbc	r0, 100$
	bsbw	mod_permquota
	cmpl	group_permquota, group_overdraft
	beql	lower_member
100$:
	bsbw	print_warning
	brb	raise_member

raise_member:
	movl	group_overdraft, r7
	brb	change_member
lower_member:
	bsbw	print_down_msg
	clrl	r7
change_member:
;+
; Change the member's permanent quota to the value stored in R7
;-
	movw	#fib$c_exa_quota, fib$w_cntrlfunc(r10)
	clrl	fib$l_cntrlval(r10)
	clrl	fib$l_wcc(r10)

	movw	member, dqf$l_uic(r11)
	$qiow_g	acp_qio
	blbc	r0, 900$
	movzwl	iosb, r0
	blbc	r0, 900$
	$fao_s -
	   ctrstr = member_use -
	   outlen = outlength, -
	   outbuf = outlinedesc, -
	   p1 = group -
	   p2 = member -
	   p3 = dqf$l_usage(r11)
	pushaq	outlength
	calls	#1, g^lib$put_output
	tstl	r7
	bneq	300$
	movl	dqf$l_overdraft(r11), r7
300$:
	cmpl	dqf$l_permquota(r11), r7
	beql	400$
	movl	r7, dqf$l_permquota(r11)
	bsbw	mod_permquota
400$:
900$:
	ret	

mod_permquota:
	clrl	fib$l_wcc(r10)
	movw	#fib$c_mod_quota, fib$w_cntrlfunc(r10)
	movl	#fib$m_mod_perm, fib$l_cntrlval(r10)
	$qiow_g	acp_qio
	blbc	r0, 900$
	movzwl	iosb, r0
	blbc	r0, 900$
	rsb
900$:
	ret

	.psect	data, wrt

acp_qio:	$qiow			; Define parm list for qio

group_usage:		.blkl	1
group_overdraft:	.blkl	1
group_permquota:	.blkl	1

items:
	.word	4
	.word	JPI$_GRP
	.address	group
	.address	group_len

	.word	4
	.word	JPI$_MEM
	.address	member
	.address	member_len

	.long	0

group:
	.blkl	1
group_len:
	.blkl	1
member:
	.blkl	1
member_len:
	.blkl	1

group_hdr:	.ascid	"!_Disk usage for [!3OW,*]    !UL used / !UL maximum"
member_hdr:	.ascid	"!_Disk usage for [!OB,!OB]  !UL used / !UL maximum"
member_use:	.ascid	"!_Disk usage for [!3OW,!3OW]  !UL used"

outlinedesc:
outlength:
	.long		512
	.address	outline
outline:
	.blkb	512

time_buf: .blkw	7

bindate:
	.long	0

downline:
	.ascid	"!_No files may be created until you reduce your usage to!/" -
		"!_less than !UL blocks.  This strict quota enforcement!/" -
		"!_will remain in effect until you reduce your usage."
upline:
	.ascid	"!/!_Formerly imposed disk quotas have been lifted.!/" -
		"!_Please try to curb excessive space usage."

TAB = ^x09
LF = ^x0a
CR = ^x0d

readthis: .ascid -
	<CR><LF>"***** READ THIS *****" -
	<CR><LF><TAB>"You have exceeded your maximum allowed disk usage."

warnline:.ascid -
	"!_You have one day's grace period to reduce your usage to!/" -
	"!_less than !UL blocks.  After this grace period,!/" -
	"!_quota enforcement will be instated until you reduce!/" -
	"!_your usage."

fibdesc:
	.long		FIB$C_LENGTH
	.address	fibblk

dqfdesc:
	.long		DQF$C_LENGTH
	.address	dqfblk
drive:
	.ascid	/SYS$DISK/		; drive to check usage on
channel:
	.blkw	1
iosb:
	.blkw	4
fibblk:
	.blkb	FIB$C_LENGTH
dqfblk:
	.blkb	DQF$C_LENGTH
dqfblklen:
	.address 1$
1$:
	.blkw	1

	.end	DISKUSE
