!
! Connect Count Monitoring Routines.
! ------- ----- ---------- --------
! 
! These routines are intended to provide the ability to monitor
! the number of times  a given file is connected during a given time
! period.
! Each file on the system has an associated file descriptor one
! of whose fields is the connect count (CCT) which is incremented
! every time the file is connected. The CCT field is a single
! byte therefore values may range from 0 - 255. When the byte reaches
! 255 then further connections leave its value unchanged - the
! field must be reset. In essence the monitoring routines read the
! current value of CCT for the files monitored then reset the field
! back to zero.
! The monitoring programs comprise 4 external routines - CCTMON,
! ALTERCCT,CCTOUT and LOOKCCT - and an associated but separately set up
! monitor file whose name is compiled as a constant in the code.
! Each of the routines is described below along with operating
! instructions and instructions on setting up a monitor file.
! 
! 1. Overview.
!    The monitor file is a series of records, one for each file
!    monitored. These contain fields for the name of the file, its fsys
!    the date monitoring started, the value of CCT the last time it
!    was read, the accumulated CCT, and the number of 'busy days' i.e.
!    the number of days the CCT was >=255.
!    Files are added or removed from the monitor file by the routine
!    ALTERCCT. The same routine can reset counters for individual files.
!    The monitoring information is written in to the monitor file
!    by the routine CCTMON which reads and resets the CCT field in
!    the file descriptors of the files to be monitored. CCTMON detaches
!    itself automatically to run again after midnight n days hence
!    (n = 1 currently).
!    Information in the monitor file is displayed by the routine
!    CCTOUT which produces a summary of monitoring to date.
! 
! 2. Operating Instructions.
!    ALTERCCT and CCTMON will only run in the owner process
!    and require sufficient privilege to read and reset CCT
!    fields. LOOKCCT and CCTOUT will run in any process with
!    read access to the monitor file. LOOKCCT, however, requires
!    the user process to have sufficient privilege to read
!    CCT fields.
! 
!    2.1 Setting up a monitor file.
!       The routines currently expect a monitor file of 1Epage which
!       allows 127 monitor records. The filename must be
!       compiled in as a constant into the code.
!       This file can easily be set up using one of the system
!       editors and inserting 4064 characters (which will exactly
!       fill 1 Epage) then cherishing and permitting it to those users
!       allowed to read the file. If desired some extra protection
!       can be supplied by changing the file type to non-standard
!       (by using #SNAP on the 4th word of the header).
!       The file should be zeroed before use by using option 'ZAP'
!       in routine ALTERCCT.
! 
!    2.2 CCTMON.
!       This routine requires no parameters. When run it prints
!       the document number of the job it has detached. When
!       running for the first time the routine should be called
!       interactively which will reset the CCTs of the files to
!       be monitored to zero and detach a job to run next day.
! 
!    2.3 ALTERCCT.
!       This routine issues prompts for all commands and is used to
!       make alterations to the monitor file.
!       The command options are :
!       HELP,ADD,DEL,REP,RES,TERM,XRES,ZAP,? and .END
!       HELP - explains the commands
!       ADD  - adds a new member to the monitor file - will further
!              prompt for <user> and <file>. Also resets the
!              file descriptor CCT to 0.
!       DEL  - deletes a member from the monitor file - will further
!              prompt for <user> and <file>
!       REP  - replaces <user> and <file> for one member of
!              the monitor file. No resetting of counters. Should
!              be used when a new version of a file with a different
!              name (e.g. version number) is introduced. Previous
!              monitoring is thus retained. Prompts for <user> and
!              <file> to be replaced then new values of <user> and
!              <file>. See also TERM.
!       RES  - resets CCT counters to 0 and the monitor start field to
!              today's day number. Resets file descriptor CCT to 0.
!              Will further prompt for <user> and <file>.
!       TERM - updates counters for a given member for values
!              accumulated since the last run of CCTMON. Intended
!              to be used in conjunction with REP (q.v.) so that
!              CCT values on old versions of a file can be added
!              to the monitoring statistics before replacement
!              by a new version. Prompts for <user> and <file>.
!       XRES - effect is RES for all monitor file members. No <user>,
!              <file> prompts.
!       ZAP  - zeroes the entire monitor file (all information destroyed
!       ?    - prints list of current members of monitor file.
!       .END - terminates requests for another option
! 
!    2.3 CCTOUT.
!       This routine prints out a summary of the information held
!       in the monitor file in a six column table of the form:
!       User.file/ date monitoring began/number of days since monitoring
!       began/ last value of CCT read for this file/ cumulative CCT/
!       number of days when CCT>=255.
!       N.B. No privilege required.
! 
!    2.4 LOOKCCT.
!       This routine prints a table of the current values of CCT
!       for each member of the monitor file by reading the file
!       descriptors directly. No alterations are made to the
!       monitor file. The table therefore represents today's
!       values of CCT from the last run of CCTMON till the
!       moment of running the routine.
!       Requires enough privilege to read CCT fields.
! 
! Colin McCallum   June 1981.
!
!>
!
RECORDFORMAT  PF(INTEGER  DEST,SRCE,P1,P2,P3,P4,P5,P6)
RECORDFORMAT  MONF(STRING (6) USER, STRING (11) FILE, C 
   BYTEINTEGER  LASTCCT, INTEGER  CUMCCT,BUSYDAYS,MONSTART)
RECORDFORMAT  RF(INTEGER  CONAD,FTYPE,DATASTART,DATAEND)
RECORDFORMAT  DFINFOF(INTEGER  NKB,RUP,EEP,APF,USE,ARCH,  C 
      FSYS,CONSEG,CCT,CODES,  C 
      BYTEINTEGER  SP1,DAYNO,POOL,CODES2,  C 
      INTEGER  SSBYTE,STRING (6) TRAN)
EXTERNALROUTINESPEC  UDERRS(INTEGER  FLAG)
EXTERNALINTEGERFNSPEC  DFSTATUS(STRING (6) USER,STRING (11) FILE,C 
  INTEGER  FSYS,ACT,VALUE)
EXTERNALROUTINESPEC  RSTRG(STRINGNAME  S)
EXTERNALROUTINESPEC  PROMPT(STRING (15) S)
EXTERNALINTEGERFNSPEC  DSPOOL(RECORD (PF)NAME  P, INTEGER  LEN,ADR)
EXTERNALINTEGERFNSPEC  DFINFO(STRING (6) USER, STRING (11) FILE,  C 
      INTEGER  FSYS,ADR)
SYSTEMROUTINESPEC  OUTFILE(STRING (31) S, INTEGER  SIZE,MAXBYTES, C 
PROT, INTEGERNAME  CONAD,FLAG)
SYSTEMROUTINESPEC  MOVE(INTEGER  LEN,FROM,TO)
SYSTEMROUTINESPEC  PSYSMES(INTEGER  I, FLAG)
SYSTEMROUTINESPEC  CONNECT(STRING (31)S,INTEGER  ACC,MAXB,PRO, C 
RECORD (RF)NAME  RR, INTEGERNAME  FLAG)
EXTERNALSTRINGFNSPEC  DATE
EXTERNALSTRINGFNSPEC  TIME
SYSTEMINTEGERFNSPEC  PACKDATEANDTIME(STRING (8) DATE,TIME)
SYSTEMSTRING (8)FNSPEC  UNPACKDATE(INTEGER  I)
SYSTEMSTRING (8)FNSPEC  UNPACKTIME(INTEGER  I)
EXTERNALROUTINESPEC  DISCONNECT(STRING (255) S)
SYSTEMSTRINGFNSPEC  ITOS(INTEGER  I)
CONSTINTEGER  MAXREC=127
CONSTSTRING (255) HELP= C 
"
Options are:
ADD  - Add a new member
DEL  - Delete a member
REP  - Replace member name
RES  - Reset counters for a single member
"
CONSTSTRING (255) HELP2= C 
"TERM - Update counters for one member
XRES - Reset counters for all members
ZAP  - Zero the entire file (destroys all info)
?    - Print out files currently being monitored
"
CONSTSTRING (15) MONFILE="MANAGR.CCTMFILE"
OWNRECORD (MONF)ARRAYFORMAT  MONAF(0:MAXREC-1)
OWNRECORD (MONF)ARRAYNAME  TAB
OWNRECORD (DFINFOF) FINF
OWNRECORD (RF) RR
ROUTINE  KDATE(INTEGERNAME  D,M,Y,INTEGER  K)
!***********************************************************************
!*    K IS DAYS SINCE 1ST JAN 1900                                     *
!*    RETURNS D, M, Y   2 DIGIT Y ONLY                                 *
!***********************************************************************
!      %INTEGER W
!      K=K+693902;                       ! DAYS SINCE CEASARS BDAY
!      W=4*K-1
!      Y=W//146097
!      K=W-146097*Y
!      D=K//4
!      K=(4*D+3)//1461
!      D=4*D+3-1461*K
!      D=(D+4)//4
!      M=(5*D-3)//153
!      D=5*D-3-153*M
!      D=(D+5)//5
!      Y=K
      *LSS_K; *IAD_693902
      *IMY_4; *ISB_1; *IMDV_146097
      *LSS_TOS ; *IDV_4; *IMY_4; *IAD_3
      *IMDV_1461; *ST_(Y)
      *LSS_TOS ; *IAD_4; *IDV_4
      *IMY_5; *ISB_3; *IMDV_153
      *ST_(M); *LSS_TOS 
      *IAD_5; *IDV_5; *ST_(D)
      IF  M<10 THEN  M=M+3 ELSE  START 
         M=M-9
         IF  Y=99 THEN  Y = 0 ELSE  Y=Y+1
      FINISH 
END ; ! OF KDATE
INTEGERFN  DDAYNO
CONSTLONGINTEGER  JMS=X'141DD76000'
INTEGER  RES
*RRTC_0
*USH_-1
*SHS_1
*USH_1
*IDV_JMS
*STUH_B 
! *AND_255
*ST_RES
! RES=1 %IF RES=0
RESULT =RES
END ; ! OF DDAYNO
STRINGFN  MYDATE(INTEGER  DAYNO)
INTEGER  D,M,Y
STRING (3) DD,MM,YY
KDATE(D,M,Y,DAYNO)
DD=ITOS(D)
DD="0".DD IF  LENGTH(DD)=1
MM=ITOS(M)
MM="0".MM IF  LENGTH(MM)=1
YY=ITOS(Y)
YY="0".YY IF  LENGTH(YY)=1
RESULT =DD."/".MM."/".YY
END ; ! OF MYDATE
INTEGERFN  GETCCT(STRING (15) USER,FILE)
INTEGER  FLAG
! GET CCT
FLAG=DFINFO(USER,FILE,-1,ADDR(FINF))
PRINTSTRING(USER.".".FILE." ") AND  C 
UDERRS(FLAG) UNLESS  FLAG=0
RESULT =FLAG
END ; ! OF GETCCT
INTEGERFN  RESET CCT(INTEGER  I)
INTEGER  FLAG
! RESET CCT TO 0
FLAG=DFSTATUS(TAB(I)_USER,TAB(I)_FILE,-1,9,0)
PRINTSTRING(TAB(I)_USER.".".TAB(I)_FILE."   ") AND  C 
UDERRS(FLAG) UNLESS  FLAG=0
RESULT =FLAG
END ; ! OF RESET CCT
ROUTINE  AUTOJOB(STRING (255) COMMANDS, INTEGER  INTERVAL,CPU)
INTEGER  COMMAD,FLAG,DAYNO,CONAD,NEWSIZE
STRING (255) MESSAGE
RECORD (PF) P
COMMAD=ADDR(COMMANDS)
NEWSIZE=LENGTH(COMMANDS)+32
OUTFILE("S#AUTO",NEWSIZE,NEWSIZE,0,CONAD,FLAG)
IF  FLAG#0 THEN  PSYSMES(10,FLAG) AND  RETURN 
INTEGER(CONAD)=NEWSIZE
INTEGER(CONAD+4)=32
INTEGER(CONAD+12)=3
MOVE(NEWSIZE-32,COMMAD+1,CONAD+32)
DISCONNECT("S#AUTO")
DAYNO=DDAYNO+INTERVAL
MESSAGE="DOCUMENT SRCE=S#AUTO,DEST=BATCH,NAME=AUTO,AFTER=". C 
MYDATE(DAYNO).",DELIV=R.D.Eager  CUR022 CUR022,".C 
"TIME=".ITOS(CPU).",START=32". C 
",LENGTH=".ITOS(NEWSIZE)
NEWLINE
P=0
FLAG=DSPOOL(P,LENGTH(MESSAGE),ADDR(MESSAGE)+1)
UNLESS  FLAG=0 THEN  START 
   UDERRS(FLAG)
   PRINTSTRING(MESSAGE)
   PRINTSTRING("Error at or around char")
   WRITE(P_P3,1)
   NEWLINE
FINISH  ELSE  START 
   PRINTSTRING("CCTMON Batch queue ident :")
   WRITE(P_P2&X'FFFFFF',1)
   NEWLINE
FINISH 
RETURN 
END ; ! OF AUTOJOB
EXTERNALROUTINE  CCTMON(STRING (255) S)
STRING (8) TODAY,NOW
INTEGER  FLAG,TOP,I
CONNECT(MONFILE,3,0,0,RR,FLAG)
PSYSMES(8,FLAG) AND  RETURN  UNLESS  FLAG=0
TAB==ARRAY(RR_CONAD+RR_DATASTART,MONAF)
TOP=INTEGER(RR_CONAD+28)-1
FOR  I=0,1,TOP CYCLE 
! GET CCT
   FLAG=GETCCT(TAB(I)_USER,TAB(I)_FILE)
   CONTINUE  UNLESS  FLAG=0
   TAB(I)_LASTCCT=FINF_CCT
   TAB(I)_CUMCCT=TAB(I)_CUMCCT+FINF_CCT
   TAB(I)_BUSYDAYS=TAB(I)_BUSYDAYS+1 IF  FINF_CCT=255
! RESET CCT TO 0
   FLAG=RESET CCT(I)
REPEAT 
TODAY=DATE
NOW=TIME
INTEGER(RR_CONAD+16)=PACKDATEANDTIME(TODAY,NOW)
AUTOJOB("CCTMON
CCTOUT
",1,20)
DISCONNECT(MONFILE)
RETURN 
END ; ! OF CCTMON
EXTERNALROUTINE  ALTERCCT(STRING (255) S)
STRING (31) REPLY,USER,FILE
INTEGER  TOP,FLAG,FOUND,TODAYSDAYNO,I,NEXTFREE
INTEGERFN  FIND(STRING (6) USER, STRING (11) FILE)
INTEGER  I,J
J=-1
FOR  I=0,1,TOP CYCLE 
J=I AND  EXIT  IF  TAB(I)_USER=USER AND  TAB(I)_FILE=FILE
REPEAT 
RESULT =J
END ; ! OF FIND
TODAYSDAYNO=DDAYNO
CONNECT(MONFILE,3,0,0,RR,FLAG)
PSYSMES(8,FLAG) AND  RETURN  UNLESS  FLAG=0
TAB==ARRAY(RR_CONAD+RR_DATASTART,MONAF)
NEXTFREE=INTEGER(RR_CONAD+28)
TOP=NEXTFREE-1
CYCLE 
   PRINTSTRING("Valid options are:
'HELP','ADD','DEL','REP','RES','TERM','XRES','ZAP' or '?'.
Terminate with '.END'
")
   PROMPT("Option? : ")
   RSTRG(REPLY) UNTIL  REPLY="ADD" OR  REPLY="DEL" OR  REPLY=".END" C 
   OR  REPLY="ZAP" OR  REPLY="?" OR  REPLY="RES" OR  REPLY="XRES" C 
   OR  REPLY="HELP" OR  REPLY="REP" OR  REPLY="TERM"
   EXIT  IF  REPLY=".END"
   IF  REPLY="HELP" THEN  PRINTSTRING(HELP) AND  C 
   PRINTSTRING(HELP2) AND  CONTINUE 
   IF  REPLY="ZAP" THEN  START 
      TAB(I)=0 FOR  I=0,1,MAXREC-1
      INTEGER(RR_CONAD+28)=0
      NEXTFREE=0
      TOP=-1
      CONTINUE 
   FINISH 
   IF  REPLY="XRES" THEN  START 
   IF  TOP<0 THEN  PRINTSTRING("*Table empty*
") AND  CONTINUE 
      FOR  I=0,1,TOP CYCLE 
         TAB(I)_MONSTART=TODAYSDAYNO
         TAB(I)_LASTCCT=0
         TAB(I)_CUMCCT=0
         TAB(I)_BUSYDAYS=0
         FLAG=RESET CCT(I)
         PRINTSTRING(TAB(I)_USER.".".TAB(I)_FILE." reset
") IF  FLAG=0
      REPEAT 
      CONTINUE 
   FINISH 
   IF  REPLY="?" THEN  START 
      PRINTSTRING(TAB(I)_USER.".".TAB(I)_FILE."
")    FOR  I=0,1,TOP
      CONTINUE 
   FINISH 
   IF  REPLY="ADD" AND  NEXTFREE=MAXREC THEN  PRINTSTRING("*Table full*
") AND  CONTINUE 
   IF  (REPLY="DEL" OR  REPLY="RES") AND  TOP<0 THEN  C 
   PRINTSTRING("*Table empty*
") AND  CONTINUE 
   PROMPT("User: ")
   RSTRG(USER) UNTIL  LENGTH(USER)=6
   PROMPT("File: ")
   RSTRG(FILE)
   FLAG=GETCCT(USER,FILE); ! DOES IT EXIST?
   CONTINUE  UNLESS  FLAG=0 OR  REPLY="REP"
   FOUND=FIND(USER,FILE)
   IF  REPLY="ADD" THEN  START 
      IF  FOUND>=0 THEN  PRINTSTRING(USER.".".FILE." already in table
")    AND  CONTINUE 
      TAB(NEXTFREE)=0
      TAB(NEXTFREE)_USER=USER
      TAB(NEXTFREE)_FILE=FILE
      TAB(NEXTFREE)_MONSTART=TODAYSDAYNO
      TOP=NEXTFREE
      FLAG=RESET CCT(NEXTFREE)
      CONTINUE  UNLESS  FLAG=0
      NEXTFREE=NEXTFREE+1
      INTEGER(RR_CONAD+28)=NEXTFREE
      PRINTSTRING(USER.".".FILE." added to CCT monitor table
")
      CONTINUE 
   FINISH 
      ! MUST BE DELETING OR RESETTING 1 MEM
   IF  FOUND=-1 THEN  PRINTSTRING(USER.".".FILE." not in table
")    AND  CONTINUE 
   IF  REPLY="DEL" THEN  START 
      ! CLOSE UP TABLE
      TAB(FOUND)=TAB(FOUND+1) AND  FOUND=FOUND+1 WHILE  FOUND<TOP
      TAB(TOP)=0
      NEXTFREE=TOP
      TOP=TOP-1
      INTEGER(RR_CONAD+28)=NEXTFREE
      PRINTSTRING(USER.".".FILE." removed from CCT monitor list
")
      CONTINUE 
   FINISH 
   IF  REPLY="RES" THEN  START 
      FLAG=RESET CCT(FOUND)
      CONTINUE  UNLESS  FLAG=0
      TAB(FOUND)_MONSTART=TODAYSDAYNO
      TAB(FOUND)_LASTCCT=0
      TAB(FOUND)_CUMCCT=0
      TAB(FOUND)_BUSYDAYS=0
      PRINTSTRING(USER.".".FILE." reset
")
      CONTINUE 
   FINISH 
   IF  REPLY="REP" THEN  START 
      PRINTSTRING("To be replaced by:
")
      PROMPT("User: ")
      RSTRG(USER) UNTIL  LENGTH(USER)=6
      PROMPT("File: ")
      RSTRG(FILE)
      FLAG=GETCCT(USER,FILE); ! DOES IT EXIST?
      CONTINUE  UNLESS  FLAG=0
      TAB(FOUND)_USER=USER
      TAB(FOUND)_FILE=FILE
      PRINTSTRING("Replacement complete
")
   FINISH 
   IF  REPLY="TERM" THEN  START 
      TAB(FOUND)_LASTCCT=FINF_CCT
      TAB(FOUND)_CUMCCT=TAB(FOUND)_CUMCCT+FINF_CCT
      TAB(FOUND)_BUSYDAYS=TAB(FOUND)_BUSYDAYS+1 IF  FINF_CCT=255
   ! RESET CCT TO 0
      FLAG=RESET CCT(FOUND)
      PRINTSTRING("Terminated O.K.
")
   FINISH 
REPEAT 
DISCONNECT(MONFILE)
RETURN 
END ; ! OF ALTERCCT
EXTERNALROUTINE  CCTOUT(STRING (255) S)
INTEGER  TOP,FLAG,I,TODAYSDAYNO
TODAYSDAYNO=DDAYNO
CONNECT(MONFILE,1,0,0,RR,FLAG)
PSYSMES(8,FLAG) AND  RETURN  UNLESS  FLAG=0
TAB==ARRAY(RR_CONAD+RR_DATASTART,MONAF)
TOP=INTEGER(RR_CONAD+28)-1
PRINTSTRING("CCT monitor file has no entries
") AND  RETURN  UNLESS  TOP>=0
PRINTSTRING("Monitoring file last updated - ".  C 
UNPACKDATE(INTEGER(RR_CONAD+16))." at ". C 
UNPACKTIME(INTEGER(RR_CONAD+16))."
")
PRINTSTRING( C 
"Monitor file         Monstart    Days  Last CCT     Cum CCT   >=255
")
FOR  I=0,1,TOP CYCLE 
   PRINTSTRING(TAB(I)_USER.".".TAB(I)_FILE)
   SPACES(14-LENGTH(TAB(I)_FILE))
   PRINTSTRING(MYDATE(TAB(I)_MONSTART))
   WRITE(TODAYSDAYNO-TAB(I)_MONSTART,7)
   WRITE(TAB(I)_LASTCCT,8)
   WRITE(TAB(I)_CUMCCT,11)
   WRITE(TAB(I)_BUSYDAYS,7)
   NEWLINE
REPEAT 
DISCONNECT(MONFILE)
RETURN 
END ; ! OF CCTOUT
EXTERNALROUTINE  LOOKCCT(STRING (255) S)
EXTERNALSTRINGFNSPEC  DATE
EXTERNALSTRINGFNSPEC  TIME
INTEGER  TOP,FLAG,I
PRINTSTRING("LOOKCCT at ".TIME." on ".DATE."

")
CONNECT(MONFILE,1,0,0,RR,FLAG)
PSYSMES(8,FLAG) AND  RETURN  UNLESS  FLAG=0
TAB==ARRAY(RR_CONAD+RR_DATASTART,MONAF)
TOP=INTEGER(RR_CONAD+28)-1
PRINTSTRING("CCT monitor file has no entries
") AND  RETURN  UNLESS  TOP>=0
PRINTSTRING( C 
"Monitor file           Current CCT
")
FOR  I=0,1,TOP CYCLE 
! GET CCT
   FLAG=GETCCT(TAB(I)_USER,TAB(I)_FILE)
   IF  FLAG=0 THEN  PRINTSTRING(TAB(I)_USER.".".TAB(I)_FILE." ") C 
   ELSE  CONTINUE 
   SPACES(14-LENGTH(TAB(I)_FILE))
   WRITE(FINF_CCT,8)
   NEWLINE
REPEAT 
DISCONNECT(MONFILE)
RETURN 
END ; ! OF LOOKCCT
ENDOFFILE