	PROGRAM MON_MODEM
C*****************************************************************************
C* PROGRAM FUNCTIONS:
C*	This program will monitor the use of modems and stop and process
C*	which has a modem allocated and has been idle for more than 10 
C*	minutes.  This will reduce phone bills.
C*
C* PROGRAM OPTIONS:
C*	None.
C*
C* DATE DEVELOPED:
C*	24-APR-86
C*
C* PROGRAM MODIFICATIONS:
C*
C*	AUTHOR   T. MOORE
C*	DATE      2-JUN-86
C*	VERSION  1-B
C*
C*	PROGRAM CHANGES:
C*		Modify to write all user's allocating modems to a data file
C*		
C******************************************************************************
C****************************************************************************
C

	INTEGER	MAX_PORT,NUM_PORTS
C
C  MAX_PORT IS CURRENTLY THE MAXIMUM NUMBER OF PORTS AVAILABLE TO ALLOCATE
C  FOR FULL MODEM USE.
C
	PARAMETER (MAX_PORT = 10)

	LOGICAL	MORE_RECORDS

	INCLUDE 'TOOLDIR:TOLLIB.TLB(FOR-MODEM-PORT-TABLE)'

C**************************************************************************
C**************************************************************************
C  MAIN PROCEDURE SEGMENT
C**************************************************************************
C**************************************************************************

C
C Any system error encountered will cause ABNORM_ERR to be called.  This
C routine will write out an error message and TERMINATE the program.
C Therefore, no checks will be made anywhere in the program to continue 
C if there is NO error.
C
	MORE_RECORDS = .TRUE.
	CALL OPEN_AUTODIAL_DEV
C
C  Populate the PORT data table with available port names
C
	CALL READ_PORT_DAT (PORT_TABLE,MAX_PORT,NUM_PORTS)
	CALL CLOSE_AUTODIAL_DEV
	CALL MONITOR_MODEM (PORT_TABLE,MAX_PORT,NUM_PORTS)
	STOP
	END



C************************************************************************
C************************************************************************
C 		*				*
C 		*	SUBROUTINE SECTION	*
C		*				*
C************************************************************************
C************************************************************************


C*******
C*******
	SUBROUTINE OPEN_AUTODIAL_DEV
C*******
C*******

	INTEGER*4 FILE_STATUS
	CHARACTER*30 FILE_NAME

	OPEN (1,FILE='TOOLDIR:AUTODIAL_PORT.DAT',STATUS='OLD',
     *		IOSTAT=FILE_STATUS,ERR=1000,READONLY,SHARED)

	GOTO 5000
C
1000	FILE_NAME = 'TOOLDIR:AUTODIAL_PORT.DAT'
	CALL FILE_ERROR (FILE_STATUS,FILE_NAME)
	GOTO 5000
C
5000	RETURN
	END




C*******
C*******
	SUBROUTINE OPEN_MDM_STOP
C*******
C*******

	INTEGER*4 FILE_STATUS
	CHARACTER*30 FILE_NAME


	OPEN (2,FILE='SYS$MANAGER:MDM_STOP.DAT',STATUS='UNKNOWN',
     *		IOSTAT=FILE_STATUS,ERR=1000,ACCESS='APPEND',
     *		SHARED)

	GOTO 5000
C
1000	FILE_NAME = 'TOOLDIR:MDM_STOP.DAT'
	CALL FILE_ERROR (FILE_STATUS,FILE_NAME)
	GOTO 5000

C
5000	RETURN
	END




C******
C******
	SUBROUTINE OPEN_TRACK_FILE
C*******
C*******

	INTEGER*4 FILE_STATUS
	CHARACTER*30 FILE_NAME

C
C RECL = unit = longwords (default for unformatted (direct access) files
C
	OPEN (3,FILE='SYS$MANAGER:ALLOC_MDM.DAT',STATUS='UNKNOWN'
     *  ,	ORGANIZATION='INDEXED',ACCESS='KEYED'
     *	,	IOSTAT=FILE_STATUS,ERR=1000,SHARED,FORM='UNFORMATTED'
     *  ,	KEY=(1:38:CHARACTER),RECL=20,RECORDTYPE='FIXED'
     *  ,	CARRIAGECONTROL='LIST')


	GOTO 5000
C
1000	FILE_NAME = 'TOOLDIR:ALLOC_MDM.DAT'
	CALL FILE_ERROR (FILE_STATUS,FILE_NAME)
	GOTO 5000
C
5000	RETURN
	END


C*******
C*******
	SUBROUTINE CLOSE_AUTODIAL_DEV	
C*******
C*******

	INTEGER*4 FILE_STATUS
	CHARACTER*30 FILE_NAME

	CLOSE (1,ERR=1000,IOSTAT=FILE_STATUS)
	GOTO 5000
C
1000	FILE_NAME = 'TOOLDIR:AUTODIAL_PORT.DAT'
	CALL FILE_ERROR (FILE_STATUS,FILE_NAME)
	GOTO 5000
C
5000	RETURN
	END




C*******
C*******
	SUBROUTINE CLOSE_MDM_STOP	
C*******
C*******

	INTEGER*4 FILE_STATUS
	CHARACTER*30 FILE_NAME

	CLOSE (2,ERR=1000,IOSTAT=FILE_STATUS)
	GOTO 5000
C
1000	FILE_NAME = 'TOOLDIR:MDM_STOP.DAT'
	CALL FILE_ERROR (FILE_STATUS,FILE_NAME)
	GOTO 5000
C
5000	RETURN
	END



C*******
C*******
	SUBROUTINE CLOSE_TRACK_FILE
C*******
C*******

	INTEGER*4 FILE_STATUS
	CHARACTER*30 FILE_NAME

	CLOSE (3,ERR=1000,IOSTAT=FILE_STATUS)
	GOTO 5000
C
1000	FILE_NAME = 'TOOLDIR:ALLOC_MDM.DAT'
	CALL FILE_ERROR (FILE_STATUS,FILE_NAME)
	GOTO 5000
C
5000	RETURN
	END



C*******
C*******
	SUBROUTINE READ_PORT_DAT(PORT_TABLE,MAX_PORT,NUM_PORTS)
C*******
C*******

	INTEGER MAX_PORT,NUM_PORTS
	INTEGER*4 FILE_STATUS
	CHARACTER*30 FILE_NAME

	INCLUDE 'TOOLDIR:TOLLIB.TLB(FOR-MODEM-PORT-TABLE)'

	READ(1,10000,ERR=1000,END=9999,IOSTAT=FILE_STATUS) 
     *	    (PORT_TABLE(I).NAME,I=1,MAX_PORT)
C
	GOTO 9999
1000	FILE_NAME = 'TOOLDIR:AUTODIAL_PORT.DAT'
	CALL FILE_ERROR(FILE_STATUS,FILE_NAME)
	GOTO 9999
C
9999	NUM_PORTS = I - 1
	RETURN
10000	FORMAT(A4)
	END

C******
C******
	SUBROUTINE WRITE_USRSTOP(PORT_TABLE,MAX_PORT,I)
C******
C******

	INCLUDE		'TOOLDIR:TOLLIB.TLB(FOR-MODEM-PORT-TABLE)'
	INCLUDE 	'TOOLDIR:TOLLIB.TLB(MODEM-TRACKING-RECORD)'
	INCLUDE 	'($JPIDEF)'

	INTEGER		MAX_PORT,I
	INTEGER*4	LIB_STATUS,SYS_STATUS,
     *			LIB$GETJPI,LIB$WAIT,LIB$DATE_TIME
	INTEGER*4	FILE_STATUS
	CHARACTER*23	DATE_STRING
	CHARACTER*12	JPI_USERNAME
	CHARACTER*30	FILE_NAME
	CHARACTER*3	ERROR_TYPE


	CALL OPEN_MDM_STOP
C
C Determine the time and write the logout message to the data file
C
	LIB_STATUS = LIB$DATE_TIME(%DESCR(DATE_STRING))
	IF (.NOT. LIB_STATUS) THEN
	    ERROR_TYPE = 'SYS'
	    CALL ABNORM_ERR(LIB_STATUS,ERROR_TYPE)
	END IF
	SYS_STATUS = LIB$GETJPI(JPI$_USERNAME,
     *			PORT_TABLE(I).OWNER_PID,,
     *			,%DESCR(JPI_USERNAME),)
	IF (.NOT. SYS_STATUS) THEN
	    ERROR_TYPE = 'SYS'
	    CALL ABNORM_ERR(SYS_STATUS,ERROR_TYPE)
	END IF
	WRITE (2,10000,IOSTAT=FILE_STATUS,ERR=1000) 
     *	    JPI_USERNAME,PORT_TABLE(I).NAME,DATE_STRING
	CALL CLOSE_MDM_STOP
	TRACK_RECORD.USERNAME     = PORT_TABLE(I).USERNAME
	TRACK_RECORD.ALLOC_TIME   = PORT_TABLE(I).ALLOC_DATE_TIME
	TRACK_RECORD.DEALLOC_TIME = DATE_STRING
	TRACK_RECORD.TERMINATE_SYM= 'TERMINATED'
	CALL UPDATE_TRACK_REC(TRACK_RECORD)
	GOTO 9999
C
1000	FILE_NAME = 'TOOLDIR:MDM_STOP.DAT'
	CALL FILE_ERROR(FILE_STATUS,FILE_NAME)
	GOTO 9999
C
9999	RETURN
10000	FORMAT(' Username ',A12,' logged out of ',A4,' at ',A23)
	END


C******
C******
	SUBROUTINE CREATE_TRACK_REC(PORT_TABLE,MAX_PORT,I,DATE_STRING)
C******
C******

	INCLUDE 	'TOOLDIR:TOLLIB.TLB(MODEM-TRACKING-RECORD)'
	INCLUDE 	'TOOLDIR:TOLLIB.TLB(FOR-MODEM-PORT-TABLE)'

	INTEGER		MAX_PORT,I
	INTEGER*4	FILE_STATUS
	CHARACTER*23	DATE_STRING
	CHARACTER*30    FILE_NAME

	TRACK_RECORD.USERNAME     = PORT_TABLE(I).USERNAME
	TRACK_RECORD.ALLOC_TIME   = PORT_TABLE(I).ALLOC_DATE_TIME
	TRACK_RECORD.ALLOC_DEV    = PORT_TABLE(I).NAME
	TRACK_RECORD.DEALLOC_TIME = ' '
	TRACK_RECORD.TERMINATE_SYM= ' '
	CALL OPEN_TRACK_FILE
	WRITE(3,ERR=1000,IOSTAT=FILE_STATUS) TRACK_RECORD
	CALL CLOSE_TRACK_FILE
	GOTO 9999
C
1000	FILE_NAME = 'TOOLDIR:ALLOC_MDM.DAT'
	CALL FILE_ERROR(FILE_STATUS,FILE_NAME)
	GOTO 9999
C
9999	RETURN
	END


C******
C******
	SUBROUTINE UPDATE_TRACK_REC(TRACK_RECORD)
C******
C******

	INCLUDE 	'TOOLDIR:TOLLIB.TLB(MODEM-TRACKING-RECORD)'

	INTEGER*4	FILE_STATUS
	CHARACTER*30    FILE_NAME
	CHARACTER*23	DATE_TIME_TMP
	CHARACTER*13	TERMIN_TMP
	CHARACTER*38	TRACK_KEY

C
C Save the deallocation time and (if there) termination flag--because the
C read operation will destroy these values currently in TRACK_RECORD.
C
	DATE_TIME_TMP = TRACK_RECORD.DEALLOC_TIME
	TERMIN_TMP = TRACK_RECORD.TERMINATE_SYM
	CALL OPEN_TRACK_FILE
	TRACK_KEY = TRACK_RECORD.USERNAME // TRACK_RECORD.ALLOC_TIME
	READ(3,ERR=1000,IOSTAT=FILE_STATUS,KEY=TRACK_KEY) TRACK_RECORD
	TRACK_RECORD.DEALLOC_TIME = DATE_TIME_TMP
	TRACK_RECORD.TERMINATE_SYM = TERMIN_TMP
	REWRITE(3,ERR=1000,IOSTAT=FILE_STATUS) TRACK_RECORD
	CALL CLOSE_TRACK_FILE
	GOTO 9999
C
1000	FILE_NAME = 'TOOLDIR:ALLOC_MDM.DAT'
	CALL FILE_ERROR(FILE_STATUS,FILE_NAME)
	GOTO 9999
C
9999	RETURN
	END



C*******
C*******
	SUBROUTINE MONITOR_MODEM(PORT_TABLE,MAX_PORT,NUM_PORTS)
C
C  This subroutine will check allocated modem ports for time in use
C  If the time in use exceeds MAX_IDLE without an increase in process
C  CPU time, the modem is not being used and the user will be logged off
C
C*******
C*******

	INCLUDE 	'($DVIDEF)'
	INCLUDE 	'($JPIDEF)'
	LOGICAL 	HNFO
	  PARAMETER 	(HNFO = .TRUE.)
	CHARACTER*3	ERROR_TYPE
	REAL*4		MAX_HIBER
	  PARAMETER	(MAX_HIBER = 60.0)
	INTEGER*4 	OWN_PID
	INTEGER 	MAX_PORT,NUM_PORTS,MAX_CYCLE
	  PARAMETER	(MAX_CYCLE=10)
	INTEGER*4	USER_CPU_TIME,LIB_STATUS,SYS_STATUS,
     *			LIB$GETJPI,LIB$GETDVI,SYS$DELPRC,LIB$WAIT

	INCLUDE 'TOOLDIR:TOLLIB.TLB(FOR-MODEM-PORT-TABLE)'
C
C Perform this cycle forever.  This job should always be running, unless
C the system is down.
C
	DO WHILE (HNFO)
C
C Determine which ports are allocated, save the PID of the owner
C
	    DO I=1,NUM_PORTS
		LIB_STATUS = LIB$GETDVI(DVI$_PID,,
     *					%DESCR(PORT_TABLE(I).NAME),
     *					OWN_PID,,)
		IF (.NOT. LIB_STATUS) THEN 
		    ERROR_TYPE = 'SYS'
		    CALL ABNORM_ERR(LIB_STATUS)
		END IF
C
C If the present owner of the modem is SYSTEM (0) but the owner in the
C database is not system, a user deallocated the modem.  Call a routine
C to write this information to the TRACKING file and purge entry in the 
C modem database.
C
		IF ((OWN_PID .EQ. 0) .AND. 
     *		    (PORT_TABLE(I).OWNER_PID .NE. 0)) 
     *			CALL DEALLOC_MODEM(PORT_TABLE,MAX_PORT,I)

C
C If there is an owner of the modem but the database does not show an owner
C the owner allocated the modem since the last update of the port database.
C Call routine to insert the owner information in the database.
C
		IF ((OWN_PID .NE.0) .AND.
     *		    (PORT_TABLE(I).OWNER_PID .EQ. 0))
     *			CALL ALLOC_MODEM(PORT_TABLE,MAX_PORT,I,OWN_PID)
C
C If the present owner of the modem is NOT system, but is different than
C the owner in the database, the previous owner deallocated the device and
C another user allocated it during the time this program was hibernating.
C Call a routine to deallocate the old user and then a routine to put the
C new owner in the database.
C
		IF ((OWN_PID .NE. 0) .AND.
     *		    (PORT_TABLE(I).OWNER_PID .NE. OWN_PID)) THEN
     			CALL DEALLOC_MODEM(PORT_TABLE,MAX_PORT,I)
			CALL ALLOC_MODEM(PORT_TABLE,MAX_PORT,I,OWN_PID)
		END IF
C
C If the system (0) owns the modem and the database shows system (0) owns
C the modem, do nothing.
C
	    END DO

C 
C Determine how much CPU time the owner of the modem has used and compare
C it to the last recorded time, if it has not increased in IDLE_MAX cycles
C (5 minutes between cycles) TERMINATE their process.
C
	    DO I = 1,NUM_PORTS
		IF (PORT_TABLE(I).ALLOCATED) THEN
C
C Get the used CPU time from the user owning the modem.
C
		    SYS_STATUS = LIB$GETJPI(JPI$_CPUTIM,
     *					  PORT_TABLE(I).OWNER_PID,,
     *					  USER_CPU_TIME,,)
		    IF (.NOT. SYS_STATUS) THEN
			ERROR_TYPE = 'SYS'
			CALL ABNORM_ERR(SYS_STATUS,ERROR_TYPE)
		    END IF
C
C If the cpu time has increased, the user is performing work at the
C modem, place this new cpu time in the modem table and zero the modem
C table idle count.
C
		    IF (PORT_TABLE(I).CPU_TIME .NE. USER_CPU_TIME) THEN
			PORT_TABLE(I).CPU_TIME = USER_CPU_TIME
			PORT_TABLE(I).IDLE_COUNT = 0
C
C If the cpu time has not increased, the user may have forgotten to log
C out of the modem and it is needlessly running up phone bills, If this
C inactive time has been encountered for MAX_CYCLE cyles of this program check,
C log the user out.
		    ELSE
			PORT_TABLE(I).IDLE_COUNT = 
     *			    PORT_TABLE(I).IDLE_COUNT + 1
			IF (PORT_TABLE(I).IDLE_COUNT .EQ. MAX_CYCLE) THEN
			    CALL WRITE_USRSTOP(PORT_TABLE,MAX_PORT,I)
			    SYS_STATUS = 
     *				SYS$DELPRC(PORT_TABLE(I).OWNER_PID,)
			    IF (.NOT. SYS_STATUS) THEN
				ERROR_TYPE = 'SYS'
     				CALL ABNORM_ERR(SYS_STATUS,ERROR_TYPE)
			    END IF
			    PORT_TABLE(I).ALLOCATED  = .FALSE.
			    PORT_TABLE(I).CPU_TIME   = 0
			    PORT_TABLE(I).OWNER_PID  = 0
			    PORT_TABLE(I).IDLE_COUNT = 0
			    PORT_TABLE(I).USERNAME  = ' '
			    PORT_TABLE(I).ALLOC_DATE_TIME= ' '

			END IF
		    END IF
		END IF
	    END DO
C
C Call library routine to hibernate until the next cycle.
C MAX_HIBER = the length of time to hibernate.
C	    
	    SYS_STATUS = LIB$WAIT(MAX_HIBER)
	    IF (.NOT. SYS_STATUS) THEN
		ERROR_TYPE = 'SYS'
		CALL ABNORM_ERR(SYS_STATUS,ERROR_TYPE)
	    END IF
	END DO
	RETURN
	END


C*******
C*******
	SUBROUTINE FILE_ERROR (FILE_STATUS,FILE_NAME)
C*******
C*******

	INTEGER*4 	FILE_STATUS
	CHARACTER*3	ERROR_TYPE
	CHARACTER*30 	FILE_NAME

	WRITE (6,10000) FILE_NAME,FILE_STATUS
10000	FORMAT (' ERROR on file ',A20,' file status = ',I10)
	ERROR_TYPE = 'FIL'
	CALL ABNORM_ERR(FILE_STATUS,ERROR_TYPE)
	RETURN 
	END


C*****
C*****
	SUBROUTINE ALLOC_MODEM(PORT_TABLE,MAX_PORT,I,OWN_PID)

C This routine will be called when a new user allocates a modem.  It will 
C write the allocation information into the TRACKING file and put the user
C information into the PORT database.
C
	INCLUDE		'TOOLDIR:TOLLIB.TLB(FOR-MODEM-PORT-TABLE)'
	INCLUDE 	'($JPIDEF)'

	INTEGER		MAX_PORT,I
	INTEGER*4	LIB_STATUS,SYS_STATUS,
     *			LIB$GETJPI,LIB$DATE_TIME,OWN_PID
	CHARACTER*23	DATE_STRING
	CHARACTER*12	JPI_USERNAME
	CHARACTER*3	ERROR_TYPE

	PORT_TABLE(I).ALLOCATED = .TRUE.
	PORT_TABLE(I).OWNER_PID = OWN_PID	
	PORT_TABLE(I).CPU_TIME 	= 0
	PORT_TABLE(I).IDLE_COUNT= 0
C
C Return the username of the process which had allocated the modem
C
	SYS_STATUS = LIB$GETJPI(JPI$_USERNAME,
     *			PORT_TABLE(I).OWNER_PID,,
     *			,%DESCR(JPI_USERNAME),)
	IF (.NOT. SYS_STATUS) THEN
	    ERROR_TYPE = 'SYS'
	    CALL ABNORM_ERR(SYS_STATUS,ERROR_TYPE)
	END IF
C
C Get time stamp to write out to the allocation data file
C
	LIB_STATUS = LIB$DATE_TIME(%DESCR(DATE_STRING))
	IF (.NOT. LIB_STATUS) THEN
	    ERROR_TYPE = 'SYS'
	    CALL ABNORM_ERR(LIB_STATUS,ERROR_TYPE)
	END IF
	PORT_TABLE(I).USERNAME  = JPI_USERNAME
	PORT_TABLE(I).ALLOC_DATE_TIME= DATE_STRING
	CALL CREATE_TRACK_REC(PORT_TABLE,MAX_PORT,I,DATE_STRING)
	RETURN
	END

C******
C******
	SUBROUTINE DEALLOC_MODEM(PORT_TABLE,MAX_PORT,I)

C This routine will be called when a user deallocates a modem.  It will
C write the deallocation info in the TRACKING database and purge the old 
C owner from the PORT database.
C
C******
C******

	INCLUDE 	'TOOLDIR:TOLLIB.TLB(MODEM-TRACKING-RECORD)'
	INCLUDE 	'TOOLDIR:TOLLIB.TLB(FOR-MODEM-PORT-TABLE)'

	INTEGER		MAX_PORT,I
	INTEGER*4	LIB_STATUS,LIB$DATE_TIME
	CHARACTER*23	DATE_STRING
	CHARACTER*3	ERROR_TYPE

C
C Get time stamp to write out to the allocation data file
C
	LIB_STATUS = LIB$DATE_TIME(%DESCR(DATE_STRING))
	IF (.NOT. LIB_STATUS) THEN
	    ERROR_TYPE = 'SYS'
	    CALL ABNORM_ERR(LIB_STATUS,ERROR_TYPE)
	END IF

	TRACK_RECORD.USERNAME     = PORT_TABLE(I).USERNAME
	TRACK_RECORD.ALLOC_TIME   = PORT_TABLE(I).ALLOC_DATE_TIME
	TRACK_RECORD.DEALLOC_TIME = DATE_STRING
	TRACK_RECORD.TERMINATE_SYM= ' '
	CALL UPDATE_TRACK_REC(TRACK_RECORD)
	PORT_TABLE(I).ALLOCATED  = .FALSE.
	PORT_TABLE(I).OWNER_PID  = 0
	PORT_TABLE(I).USERNAME   = ' '
	PORT_TABLE(I).ALLOC_DATE_TIME = ' '
	PORT_TABLE(I).CPU_TIME   = 0
	PORT_TABLE(I).IDLE_COUNT = 0
	RETURN
	END


C******
C******
	SUBROUTINE ABNORM_ERR(STATUS,ERROR_TYPE)
C
C This subroutine will notify systems personnel of an error and that this
C program has terminated.
C
C******
C******

	INTEGER*4	STATUS,LIB_STATUS,LIB$SPAWN

	CHARACTER*32	MAIL_COMMAND1
	   PARAMETER (MAIL_COMMAND1 = 'MAIL/SUBJ="PROGRAM MON_MODEM HAS')
	CHARACTER*34    MAIL_COMMAND2
	   PARAMETER (MAIL_COMMAND2 = ' ABNORMALLY TERMINATED, STATUS = ')
	CHARACTER*5	MAIL_FILE
	   PARAMETER (MAIL_FILE = '" NL:')
	CHARACTER*23	MAIL_TO
	   PARAMETER (MAIL_TO = ' OPERATOR,SYSTEM')
	CHARACTER*6	MAIL_STATUS
	CHARACTER*100	COMMAND
	CHARACTER*3	ERROR_TYPE


	WRITE(MAIL_STATUS,10000) STATUS
10000	FORMAT(I6)
	COMMAND = MAIL_COMMAND1//MAIL_COMMAND2//'('//ERROR_TYPE//')'
     *		  //MAIL_STATUS//MAIL_FILE//MAIL_TO
	LIB_STATUS = LIB$SPAWN(%DESCR(COMMAND),,,,,,,,,,,)	
	IF (.NOT. LIB_STATUS) WRITE (6,7) LIB_STATUS
7	FORMAT(' ERROR ON MAIL SPAWN COMMAND.  STATUS= ',I10)
	CALL LIB$SIGNAL(%VAL(LIB_STATUS))
	STOP
	END
