.TITLE QREPORT - Report disk quota usage for current default disk .IDENT /V1.0/ ;****************************************************************************** ; QREPORT was written for use with the DISKUSE utility found on the ; fall 83 DECUS tape. QREPORT produces a report showing disk utilization for ; the current default disk family. This report includes the following ; information; ; ; UIC ; Username (from UAF) ; Blocks used ; Percentage of base quota used ; Base quota (Overdraft) ; Extended quota (Permanent) ; ; The username is read from the UAF using RMS ISAM. If multiple users ; have the same UIC, the first user (alphabetically) is reported as the owner. ; ; If a UIC cannot be found in the UAF, the user name will be printed as ; '????????????'. ; ; The report is broken up into UIC groups with the exception of the first ; page, which reports those UIC's having unlimited quotas (signified on our ; system by having a base quota of 999999). Group totals and grand totals are ; given. Grand totals do not include the totals for the unlimited quota UIC's. ; ; NOTE: This program assumes that you are using the DISKUSE program ; found on the Fall 1983 DECUS tape. DISKUSE treats the overdraft quota as the ; base quota and treats the permanent quota as your extended quota. QREPORT ; labels the two quotas as if you are using DISKUSE. You can use this program ; without DISKUSE if you change the follwing lines; ; ; MOVL DQF$L_OVERDRAFT(R11),R2 ; ***** BASE QUOTA ***** ; MOVL DQF$L_PERMQUOTA(R11),R5 ; ***** EXTENDED QUOTA ***** ; ; TO ; ; MOVL DQF$L_OVERDRAFT(R11),R5 ; ***** EXTENDED QUOTA ***** ; MOVL DQF$L_PERMQUOTA(R11),R2 ; ***** BASE QUOTA ***** ; ; NOTE: Use of this program requires a system UIC, BYPASS or SYSPRV. It ; is not a good idea to install it with BYPASS or SYSPRV. ; ; NOTE: QREPORT has not been tested under VMS V4.0 and may not run ; because of the expanded UIC size in V4.0 and possible changes to the UAF. ; ; Compile and link thusly; ; MACRO QREPORT ; LINK QREPORT,SYS$SYSTEM:SYS.STB/SELECTIVE ;****************************************************************************** .LIBRARY /SYS$LIBRARY:LIB/ $FABDEF ; define FAB $RABDEF ; define RAB $RMSDEF ; define RMS status codes $DQFDEF ; define Disk Quota File block $FIBDEF ; define File ID Block $IODEF ; define I/O def's $SSDEF ; define SS definitions $PCBDEF ; define Process Control Block $UAFDEF ; define User Authorization File ; ; macros ; ;MACRO ERRORCK - checks R0 for error condition .MACRO ERRORCK,?L1 BLBS R0,L1 $EXIT_S CODE = R0 L1: .ENDM ERRORCK ; ; MACRO TO SET A BIT BY BIT NUMBER ; ;CALL: ; SETBIT BITNUM,FLAGWORD ; ; WHERE: ; BITNUM IS ANY VALID SOURCE OPERAND SECIFYING THE BIT ; OFFSET FROM THE FLAG BASE TO SET ; ; FLAGWORD IS ANY VALID DESTINATION OPERAND ; .MACRO SETBIT VAL,FLAG .NTYPE _$$_ VAL .IF EQ <_$$_-^X0EF> .IF NDF VAL BBSS S^#VAL,FLAG,.+1 .IFF .IF LT BISB #<1@VAL>,FLAG .IFF BBSS #VAL,FLAG,.+1 .ENDC .ENDC .IFF BBSS VAL,FLAG,.+1 .ENDC .ENDM SETBIT RETRY_RLK = 2 ; number of retries if record is locked SLEEP_RLK = 75 ; MS to sleep before retrying a GET UAF$_NORMAL = 1 ; normal completion on UAF read UAF$_INVUSR = -2 ; user not found in UAF .PSECT QREPORT_LOCAL_DATA,WRT,PIC,NOEXE,NOSHR,LONG,GBL ; ;--- ;+++ ; Usage Message ; FAOLINE: .ASCID ' [!OB,!OB] !AS !9SL !3SL% !9SL !9SL ' HEADLINE: .ASCID ' UIC USER USED % OF BASE '- 'BASE QUOTA EXTENDED QUOTA' FFLINE: .ASCID ' !^ ' TOT1LINE: .ASCID '!/ Total !9SL ' TOT2LINE: .ASCID '!/ Total !9SL !3SL% !9SL '- '!9SL ' TOT3LINE: .ASCID '!/!/Grand Total !9SL !3SL% !9SL '- '!9SL' ; ; $ FAO stuff ; ; DO NOT REARRANGE -- contains string descriptors FAODESC: .WORD 80 .WORD 0 .LONG FAOBUF FAOBUF: .BLKB 80 ; ; DISK block stuff ; ; DO NOT REARRANGE -- contains string descriptors DRIVE: .ASCID /SYS$DISK/ DKNAME: .LONG 63 DKADDR: .LONG DK DK: .BLKB 63 DKCHAN: .WORD 0 ; ; $ ACP CONTROL stuff ; FIBDESC: ; FIB descriptor .WORD FIB$C_LENGTH ; length .WORD 0 ; not used .ADDRESS FIBBLK ; address of fib block FIBBLK: .BLKB FIB$C_LENGTH ; an FIB DQFDESC: ; disk quota file descriptor .WORD DQF$C_LENGTH ; length .WORD 0 ; not used .ADDRESS DQFBLK ; address of dqf block DQFBLK: .BLKB DQF$C_LENGTH ; a DQF block (only need one) IOSB: .BLKW 4 ; I/O status block .ADDRESS GRPBLK ; group valid table GRPBLK: .BLKB ^O377 ; with 377 (octal) entries TOTAL_USED: .BLKL 1 ; group blocks used TOTAL_PERM: .BLKL 1 ; group blocks perm quota TOTAL_EXTD: .BLKL 1 ; group blocks extended quota GRAND_USED: .BLKL 1 ; system blocks used GRAND_PERM: .BLKL 1 ; system blocks perm quota GRAND_EXTD: .BLKL 1 ; system blocks extended quota QRP$Q_UAFREC: ; desc for UAF record .LONG UAF$K_LENGTH,QRP$G_UAFREC QRP$G_UAFREC: ; UAF record buffer .BLKB UAF$K_LENGTH ; space for record NAME_DESC: .LONG 12 ; user name descriptor .ADDRESS QRP$G_UAFREC QMARKS: ; default user name .ASCII /????????????/ ; ; UAF file name string ; UAFNAME: .ASCII /SYSUAF/ ; UAF file name UAFSIZE=.-UAFNAME DEFNAME: .ASCII /SYS$SYSTEM:.DAT/ ; UAF location and extension DEFSIZE=.-DEFNAME WAKEDELTA: ; time to sleep before retry .LONG -10*1000*SLEEP_RLK,-1 UIC: .BLKL 1 ; UIC to look-up SAVESP: .BLKL 1 ; place to stash Stack Pointer SAVER6: .BLKL 1 ; place top stash R6 SAVER7: .BLKL 1 ; place to stash R7 .PSECT QREPORT_CODE,EXE,PIC,NOWRT,SHR,LONG .ENTRY QREPORT, ^M<> ;****************************************************************************** ; Qreport has 2 passes. The first pass prints out those UIC's having ; unlimited (999999) base quotas and sets a flag in an array indicating which ; groups are valid. ; Pass 2 uses the array to determine which groups to print. This allows ; the report to be in order by group number without having to sort. ;****************************************************************************** START: ; open UAF MOVAB -FAB$K_BLN(SP),R6 ; allocate a FAB in the stack MOVAB -RAB$K_BLN(R6),R7 ; then a RAB MOVL R7,SP ; now create the space MOVC5 #0,(SP),#0,#FAB$K_BLN+RAB$K_BLN,(SP) ; zero FAB and RAB ASSUME FAB$B_BLN EQ FAB$B_BID+1 ; check for byte adjacency MOVW #FAB$K_BLN@8!FAB$C_BID,- ; set block length and block ID- FAB$B_BID(R6) ; values into proper fields of FAB MOVAL UAFNAME,FAB$L_FNA(R6) ; insert file name address MOVB #UAFSIZE,FAB$B_FNS(R6) ; insert file name size MOVAL DEFNAME,FAB$L_DNA(R6) ; insert default file name address MOVB #DEFSIZE,FAB$B_DNS(R6) ; insert default name size SETBIT FAB$V_GET,FAB$B_FAC(R6) ; get access only MOVB #FAB$M_GET!FAB$M_PUT!- ; share all operations FAB$M_UPD!FAB$M_DEL,FAB$B_SHR(R6) MOVB #FAB$C_IDX,FAB$B_ORG(R6) ; indexed organization MOVB #FAB$C_VAR,FAB$B_RFM(R6) ; variable length records ASSUME RAB$B_BLN EQ RAB$B_BID+1 ; check for byte adjacency MOVW #RAB$K_BLN@8!RAB$C_BID,- ; set block length and block ID- RAB$B_BID(R7) ; values into proper fields of RAB MOVB #RAB$C_KEY,RAB$B_RAC(R7) ; keyed access SETBIT RAB$V_NLK,RAB$L_ROP(R7) ; don't lock anbody's record MOVB #10,RAB$B_MBC(R7) ; multi-block reads MOVB #1,RAB$B_KRF(R7) ; use uic key (key 1) MOVAQ QRP$Q_UAFREC,R5 ; get user descriptor address MOVW (R5),RAB$W_USZ(R7) ; user buffer size MOVL 4(R5),R5 ; save buffer address in R5 MOVL R5,RAB$L_UBF(R7) ; put user buffer address in rab MOVAL UIC,RAB$L_KBF(R7) ; stash address of key MOVB #4,RAB$B_KSZ(R7) ; stash size of key MOVAL (R6),RAB$L_FAB(R7) ; insert FAB address ; Open the File and connect RAB $OPEN FAB=(R6) BLBS R0,14$ ; skip err checking if no err CMPL R0,#RMS$_SNE ; If sharing is not enabled try BEQL 10$ ; accessing it without sharing. CMPL R0,#RMS$_SPE ; The same goes for insufficient BEQL 10$ ; dynamic memory problems. CMPL R0,#RMS$_DME BEQL 10$ ERRORCK ; exit on error 10$: CLRB FAB$B_SHR(R6) ; clear share flag $OPEN FAB=(R6) ERRORCK ; exit on error 14$: $CONNECT RAB=(R7) ; connect RAB to FAB ERRORCK ; exit on error MOVL R6,SAVER6 ; stash R6 MOVL R7,SAVER7 ; stash R7 ; UAF is now open, begin processing MOVW #80,FAODESC ; set fao length to 80 $FAO_S - ; write header line CTRSTR = HEADLINE, - ; control string OUTLEN = FAODESC, - ; output length OUTBUF = FAODESC ; output descriptor ERRORCK ; exit on error PUSHAL FAODESC ; push fao output descriptor on stack CALLS #1,G^LIB$PUT_OUTPUT ; output to sys$output ; assign channel to users default disk $ASSIGN_S - ; assign channel to SYS$DISK DEVNAM=DRIVE, - CHAN=DKCHAN ERRORCK ; exit on error $TRNLOG_S - ; translate SYS$DISK LOGNAM=DRIVE,- ; -- we assume only one translation RSLLEN=DKNAME,- ; -- will be necessary. This RSLBUF=DKNAME ; -- can be risky. ERRORCK ; everything OK? MOVAB GRPBLK,R10 ; set r10 up to index grpblk MOVAB FIBBLK, R0 ; stash fib address in r0 MOVW #FIB$C_EXA_QUOTA, FIB$W_CNTRLFUNC(R0) ; examine quota entry BISB2 #FIB$M_ALL_MEM,FIB$L_CNTRLVAL(R0) ; we will be wildcarding BISB2 #FIB$M_ALL_GRP,FIB$L_CNTRLVAL(R0) ; group and member MOVL #0,FIB$L_WCC(R0) ; MOVAB DQFBLK, R11 ; R11 is not scratch anymore!!! MOVW #0, DQF$L_UIC(R11) ; wildcard member MOVW #0, DQF$L_UIC+2(R11) ; ... and group READ_QUOTA_1: MOVAB DQFDESC,R0 ; stash quota descriptor in R0 $QIOW_S - ; do the read CHAN=DKCHAN, - ; disk channel FUNC=#IO$_ACPCONTROL, - ; ACP function IOSB=IOSB, - ; IO status block P1=FIBDESC, - ; FIB descriptor P2=#DQFDESC, - ; Disk Quota Descriptor-IN P4=#DQFDESC ; Disk Quota Descriptor-OUT ERRORCK ; exit on error MOVZWL IOSB,R0 ; get IOSB BLBS R0,GOTO_CALC_1 ; if 1, then continue BRB HANDLE_ERRS_1 ; go handle recoverable errors GOTO_CALC_1: BRW CALC_USAGE_1 HANDLE_ERRS_1: CMPL R0,#SS$_NODISKQUOTA ; ... compare to NODISKQUOTA BEQLU PASS_DONE_1 ; we are done with pass 1 ERRORCK ; exit on error PASS_DONE_1: MOVL TOTAL_USED, R9 MOVW #80,FAODESC ; set fao length to 80 $FAO_S - ; format ascii output CTRSTR = TOT1LINE, - ; control string OUTLEN = FAODESC, - ; output length OUTBUF = FAODESC, - ; output descriptor P1=R9 ERRORCK ; exit on error PUSHAL FAODESC CALLS #1,G^LIB$PUT_OUTPUT MOVL #0, TOTAL_USED BRW SETUP_READ_QUOTA_2 ; go to next pass CALC_USAGE_1: CVTWL DQF$L_UIC+2(r11),r4 ; group # CMPW #0,R4 ; is this [0,0]? BEQL READ_NEXT_1 ; yes, so skip it MOVL DQF$L_USAGE(R11),R1 ; get blocks in use MOVL DQF$L_OVERDRAFT(R11),R2 ; ***** BASE QUOTA ***** MOVL DQF$L_PERMQUOTA(R11),R5 ; ***** EXTENDED QUOTA ***** CMPL #^D999999,R2 ; do they have unlimited quota? BNEQ SET_GRPBLK_1 ; no, skip print, set grpblk ADDL2 R1, TOTAL_USED ; add to total used CVTLF R1, R6 ; setup for percent calculation CVTLF R2, R7 DIVF3 R7, R6, R8 ; get % of quota used MULF3 #^F100, R8, R6 CVTFL R6, R7 BRB PRINT_USAGE_1 SET_GRPBLK_1: MOVB #1,(R10)[R4] ; set group flag in grpblk READ_NEXT_1: BRW READ_QUOTA_1 PRINT_USAGE_1: BSBW SEARCH_UAF ; search uaf for user name MOVW #80,FAODESC ; set fao length to 80 $FAO_S - ; format ascii output CTRSTR = FAOLINE, - ; control string OUTLEN = FAODESC, - ; output length OUTBUF = FAODESC, - ; output descriptor P1=DQF$L_UIC+2(R11), - ; group P2=DQF$L_UIC(R11), - ; member P3=#NAME_DESC,- ; user name P4=R1,- ; blocks used P5=R7,- ; % of base quota used P6=R2,- ; base quota P7=R5 ; extended quota ERRORCK ; exit on error PUSHAL FAODESC CALLS #1,G^LIB$PUT_OUTPUT BRW READ_QUOTA_1 SETUP_READ_QUOTA_2: MOVAB FIBBLK, R0 MOVW #FIB$C_EXA_QUOTA, FIB$W_CNTRLFUNC(R0) ; examine quota entry BICB2 #FIB$M_ALL_GRP,FIB$L_CNTRLVAL(R0) ; BISB2 #FIB$M_ALL_MEM,FIB$L_CNTRLVAL(R0) ; MOVAB DQFBLK, R11 ; R11 is not scratch anymore!!! MOVL #0,R4 ; set group pointer to zero BRB GET_NEXT_GROUP_2 ALL_DONE_2: BRW DONE GET_NEXT_GROUP_2: INCL R4 ; increment group pointer CMPL #^O400,R4 ; are we done? BEQLU ALL_DONE_2 ; yes, so get out CMPB #0,(R10)[R4] ; do we have any members in grp BEQL GET_NEXT_GROUP_2 ; no, so check next group MOVAB FIBBLK, R0 MOVL #0,FIB$L_WCC(R0) MOVW R4, DQF$L_UIC+2(R11) ; set group MOVW #0, DQF$L_UIC(R11) ; ... wildcard member MOVW #80,FAODESC ; set fao length to 80 $FAO_S - ; format ascii output CTRSTR = FFLINE, - ; control string OUTLEN = FAODESC, - ; output length OUTBUF = FAODESC ; output descriptor ERRORCK ; exit on error PUSHAL FAODESC CALLS #1,G^LIB$PUT_OUTPUT MOVW #80,FAODESC ; set fao length to 80 $FAO_S - ; format ascii output CTRSTR = HEADLINE, - ; control string OUTLEN = FAODESC, - ; output length OUTBUF = FAODESC ; output descriptor ERRORCK ; exit on error PUSHAL FAODESC CALLS #1,G^LIB$PUT_OUTPUT READ_QUOTA_2: MOVAB DQFDESC,R0 $QIOW_S - ; do the read CHAN=DKCHAN, - ; disk channel FUNC=#IO$_ACPCONTROL, - ; ACP function IOSB=IOSB, - ; IO status block P1=FIBDESC, - ; FIB descriptor P2=#DQFDESC, - ; Disk Quota Descriptor-IN P4=#DQFDESC ; Disk Quota Descriptor-OUT ERRORCK ; exit on error MOVZWL IOSB,R0 ; get IOSB BLBS R0,GOTO_CALC_2 ; if 1, then continue BRB HANDLE_ERRS_2 GOTO_CALC_2: BRW CALC_USAGE_2 HANDLE_ERRS_2: CMPL R0,#SS$_NODISKQUOTA ; ... compare to NODISKQUOTA BEQLU GOTO_GET_NEXT_2 ; ... and get the next group ERRORCK ; exit on error GOTO_GET_NEXT_2: CVTLF TOTAL_USED, R6 CVTLF TOTAL_PERM, R7 DIVF3 R7, R6, R8 ; get % of quota used MULF3 #^F100, R8, R6 CVTFL R6, R7 MOVL TOTAL_USED, R9 MOVL TOTAL_PERM, R8 MOVL TOTAL_EXTD, R6 MOVW #80,FAODESC ; set fao length to 80 $FAO_S - ; format ascii output CTRSTR = TOT2LINE, - ; control string OUTLEN = FAODESC, - ; output length OUTBUF = FAODESC, - ; output descriptor P1=R9, - P2=R7, - P3=R8, - P4=R6 ERRORCK ;exit on error PUSHAL FAODESC CALLS #1,G^LIB$PUT_OUTPUT MOVL #0, TOTAL_USED MOVL #0, TOTAL_PERM MOVL #0, TOTAL_EXTD BRW GET_NEXT_GROUP_2 ; go to next group GET_NEXT_MEMBER_2: BRW READ_QUOTA_2 CALC_USAGE_2: MOVL DQF$L_USAGE(R11),R1 ; get blocks in use MOVL DQF$L_OVERDRAFT(R11),R2 ; and permanent quota CMPL #^D999999,R2 ; do they have unlimited quota? BEQL GET_NEXT_MEMBER_2 ; yes, skip print MOVL DQF$L_PERMQUOTA(R11),R5 ; and extended quota ADDL2 R1, TOTAL_USED ADDL2 R1, GRAND_USED ADDL2 R2, TOTAL_PERM ADDL2 R2, GRAND_PERM ADDL2 R5, TOTAL_EXTD ADDL2 R5, GRAND_EXTD CVTLF R1, R6 CVTLF R2, R7 DIVF3 R7, R6, R8 ; get % of quota used MULF3 #^F100, R8, R6 CVTFL R6, R7 BSBW SEARCH_UAF ; search uaf for username MOVW #80,FAODESC ; set fao length to 80 $FAO_S - ; format ascii output CTRSTR = FAOLINE, - ; control string OUTLEN = FAODESC, - ; output length OUTBUF = FAODESC, - ; output descriptor P1=DQF$L_UIC+2(R11), - ; group P2=DQF$L_UIC(R11), - ; member P3=#NAME_DESC, - ; user name P4=R1,- ; blocks used P5=R7,- ; % of base quota used P6=R2,- ; base quota P7=R5 ; extended quota ERRORCK ; exit on error PUSHAL FAODESC CALLS #1,G^LIB$PUT_OUTPUT BRW READ_QUOTA_2 DONE: CVTLF GRAND_USED, R6 CVTLF GRAND_PERM, R7 DIVF3 R7, R6, R8 ; get % of quota used MULF3 #^F100, R8, R6 CVTFL R6, R7 MOVL GRAND_USED, R9 MOVL GRAND_PERM, R8 MOVL GRAND_EXTD, R6 MOVW #80,FAODESC ; set fao length to 80 $FAO_S - ; format ascii output CTRSTR = TOT3LINE, - ; control string OUTLEN = FAODESC, - ; output length OUTBUF = FAODESC, - ; output descriptor P1=R9, - P2=R7, - P3=R8, - P4=R6 ERRORCK ; exit on error PUSHAL FAODESC CALLS #1,G^LIB$PUT_OUTPUT MOVZWL #SS$_NORMAL,R0 ; clear out R0 $DASSGN_S CHAN=DKCHAN ; deassign disk ERRORCK ; exit on error MOVL SAVER6,R6 ; restore R6 MOVL SAVER7,R7 ; restore R7 $DISCONNECT RAB=(R7) ; disconnect UAF rab $CLOSE FAB=(R6) ; close UAF $EXIT_S CODE=R0 ; exit on error SEARCH_UAF: MOVL DQF$L_UIC(R11),UIC ; load uic to scratch area PUSHR #^M ; save the registers MOVL SP,SAVESP ; save the Stack Pointer MOVL SAVER6,R6 ; restore R6 MOVL SAVER7,R7 ; restore R7 MOVC #12,QMARKS,QRP$G_UAFREC ; move the question marks in MOVL #RETRY_RLK,R3 ; prepare for retries BRB 20$ 10$: $SCHDWK_S DAYTIM=WAKEDELTA ; before retrying schedule wake up BLBC R0,20$ $HIBER_S ; if that worked we can take a nap 20$: $GET RAB=(R7) ; try to get the record CMPL R0,#RMS$_RLK ; was it locked? BNEQ 30$ ; no, continue SOBGEQ R3,10$ ; yes, try again BRB 25$ ; record locked, default user to ?????? 30$: CMPL R0,#RMS$_RNF ; was the record found? BEQL 25$ ; no, but act like it was ERRORCK ; exit if other errors 25$: MOVL SAVESP,SP ; restore Stack Pointer POPR #^M ; restore registers RSB ; return .END QREPORT