Trailing-Edge
-
PDP-10 Archives
-
CFS_TSU04_19910205_1of1
-
update/t20src/acjdec.mac
There are 9 other files named acjdec.mac in the archive. Click here to see a list.
;RIP:<7.UTILITIES>ACJDEC.MAC.1248 4-Apr-89 20:14:57, Edit by GSCOTT
;(125) Sweep log file cache at time that system is going to be shutdown.
;RIP:<7.UTILITIES>ACJDEC.MAC.1235 30-Mar-89 11:59:54, Edit by GSCOTT
;(124) Substitute "*" in log file name for the current time.
;RIP:<7.UTILITIES>ACJDEC.MAC.1230 30-Mar-89 10:57:16, Edit by GSCOTT
;(123) Read LSN ACCESS.CONTROL files for feeble users.
;RIP:<7.UTILITIES>ACJDEC.MAC.1227 29-Mar-89 15:33:03, Edit by GSCOTT
;(122) Small log file fixes, cause entry into MDDT to sweep the log file cache.
;RIP:<7.UTILITIES>ACJDEC.MAC.1215 29-Mar-89 11:52:00, Edit by GSCOTT
;(121) Check for user deletion of access.control file to flush cache.
;RIP:<7.UTILITIES>ACJDEC.MAC.1210 29-Mar-89 01:44:11, Edit by GSCOTT
;(120) Write summary info to log file when closing it.
;RIP:<7.UTILITIES>ACJDEC.MAC.1193 29-Mar-89 00:16:04, Edit by GSCOTT
;(116) Fix log file line counter.
;RIP:<7.UTILITIES>ACJDEC.MAC.1183 28-Mar-89 23:31:06, Edit by GSCOTT
;(115) Look for filename first in cache flush of access.control files.
;RIP:<7.UTILITIES>ACJDEC.MAC.1178 28-Mar-89 22:38:19, Edit by GSCOTT
;(114) Add counter for cache flushes.
;RIP:<7.UTILITIES>ACJDEC.MAC.1159 28-Mar-89 10:50:17, Edit by GSCOTT
;(113) Log file cache sweep interval of zero disables the cache.
;RIP:<7.UTILITIES>ACJDEC.MAC.1157 28-Mar-89 10:43:02, Edit by GSCOTT
;(112) Make the log buffer three pages.
;RIP:<7.UTILITIES>ACJDEC.MAC.1155 24-Mar-89 02:13:10, Edit by GSCOTT
;(111) Implement the access control cache.
;RIP:<7.UTILITIES>ACJDEC.MAC.1083 21-Mar-89 17:20:03, Edit by GSCOTT
;(110) Sweep log file cache if reading log file, get new log file if renaming.
;RIP:<7.UTILITIES>ACJDEC.MAC.1070 20-Mar-89 13:31:36, Edit by GSCOTT
;(107) Implement the log file cache.
;RIP:<7.UTILITIES>ACJDEC.MAC.1042 2-Feb-89 10:23:25, Edit by GSCOTT
;(101) Don't output cap mask if job 0 in LOGSTA routine.
;RIP:<7.UTILITIES>ACJDEC.MAC.1040 1-Feb-89 11:38:29, Edit by GSCOTT
;(100) Bug in TAKCHK broke TAKE command lines over half the buffer size.
;RIP:<7.UTILITIES>ACJDEC.MAC.1039 31-Jan-89 18:23:01, Edit by GSCOTT
;(77) Make the log files secure when writing them.
;RIP:<7.UTILITIES>ACJDEC.MAC.1037 30-Jan-89 10:54:49, Edit by GSCOTT
;(75) Don't output bad information in spy file trailer.
;RIP:<7.UTILITIES>ACJDEC.MAC.1036 30-Jan-89 10:22:08, Edit by GSCOTT
;(74) Kill all inferior forks when crashing.
;RIP:<7.UTILITIES>ACJDEC.MAC.1033 30-Jan-89 10:02:23, Edit by GSCOTT
;(73) Output spy filename to log file if we can't open the spy file.
;RIP:<7.UTILITIES>ACJDEC.MAC.1031 26-Jan-89 11:20:21, Edit by GSCOTT
;(72) Add NOSECURE keyword to ACCESS.CONTROL
;RIP:<7.UTILITIES>ACJDEC.MAC.1030 26-Jan-89 10:07:01, Edit by GSCOTT
;(71) Add DENY-CTY and LOGIN-CTY support.
;RIP:<7.UTILITIES>ACJDEC.MAC.1028 24-Jan-89 17:01:52, Edit by GSCOTT
;(67) Make job information blocks, clean up attach and login policy code.
;RIP:<7.UTILITIES>ACJDEC.MAC.1010 19-Jan-89 22:50:33, Edit by GSCOTT
;(66) Fill text displayed by the SHOW command.
;RIP:<7.UTILITIES>ACJDEC.MAC.999 19-Jan-89 21:21:06, Edit by GSCOTT
;(65) Fill command lines output by the WRITE command.
;RIP:<7.UTILITIES>ACJDEC.MAC.996 19-Jan-89 17:46:14, Edit by GSCOTT
;(64) Improve logging of illegal requests.
;RIP:<7.UTILITIES>ACJDEC.MAC.985 19-Jan-89 13:28:48, Edit by GSCOTT
;(63) Add support for user functions.
;RIP:<7.UTILITIES>ACJDEC.MAC.982 12-Jan-89 23:12:28, Edit by GSCOTT
;(62) Ignore increment mount counts for ACJ just to be sure.
;RIP:<7.UTILITIES>ACJDEC.MAC.981 12-Jan-89 22:00:36, Edit by GSCOTT
;(60) Remove extra definition of RSKP, use MACREL's instead.
;RIP:<7.UTILITIES>ACJDEC.MAC.967 3-Jan-89 15:20:00, Edit by GSCOTT
;(51) Update copyright date.
;RIP:<7.UTILITIES>ACJDEC.MAC.966 3-Jan-89 15:18:48, Edit by GSCOTT
;(50) Add output in log file of number requests failed.
;RIP:<7.UTILITIES>ACJDEC.MAC.964 3-Jan-89 14:37:02, Edit by GSCOTT
;(47) Log more information for CRDIRs.
;RIP:<7.UTILITIES>ACJDEC.MAC.962 29-Dec-88 10:48:50, Edit by GSCOTT
;(45) Change policy to allow and log any secure operation if no ACCESS.CONTROL.
;RIP:<7.UTILITIES>ACJDEC.MAC.961 20-Dec-88 10:05:24, Edit by GSCOTT
;(41) Problem with previous edit (extra comma in BYTE 7 statement).
;RIP:<7.UTILITIES>ACJDEC.MAC.956 16-Dec-88 10:56:47, Edit by GSCOTT
;(40) Paginate the logging file.
;RIP:<7.UTILITIES>ACJDEC.MAC.955 14-Dec-88 18:50:16, Edit by GSCOTT
;(37) Add LOGIN-xxxx keywords to user profile.
;RIP:<7.UTILITIES>ACJDEC.MAC.950 14-Dec-88 17:56:36, Edit by GSCOTT
;(36) Restart ourselves if under job 0.
;RIP:<7.UTILITIES>ACJDEC.MAC.949 13-Dec-88 11:17:19, Edit by GSCOTT
;(35) Put username first in logging for easier reading.
;RIP:<7.UTILITIES>ACJDEC.MAC.948 12-Dec-88 14:04:11, Edit by GSCOTT
;(34) Add invisible bit when looking for ACCESS.CONTROL.
;RIP:<7.UTILITIES>ACJDEC.MAC.946 7-Dec-88 15:46:55, Edit by GSCOTT
;(32) In WRITE command only send keywords that differ from default enable bits.
;RIP:<7.UTILITIES>ACJDEC.MAC.940 7-Dec-88 01:33:04, Edit by GSCOTT
;(30) Implement DENY-xxx keywords for functions.
;RIP:<7.UTILITIES>ACJDEC.MAC.935 6-Dec-88 21:35:15, Edit by GSCOTT
;(27) Implement ALL as keyword in ACCESS.CONTROL, clean up here and there.
;RIP:<7.UTILITIES>ACJDEC.MAC.913 6-Dec-88 19:02:28, Edit by GSCOTT
;(26) Implement SET PRIME-TIME-BEGIN and SET PRIME-TIME-END
;RIP:<7.UTILITIES>ACJDEC.MAC.906 6-Dec-88 18:01:00, Edit by GSCOTT
;(25) Check for no functions enabled in SAVE command.
;RIP:<7.UTILITIES>ACJDEC.MAC.902 6-Dec-88 17:55:53, Edit by GSCOTT
;(24) Use TIME rather than HPTIM for uptime to prevent overflows after 4 days.
;RIP:<7.UTILITIES>ACJDEC.MAC.892 3-Dec-88 02:52:35, Edit by GSCOTT
;(23) OTIME routine needs to handle times in the range of days.
;RIP:<7.UTILITIES>ACJDEC.MAC.891 1-Dec-88 11:08:20, Edit by GSCOTT
;(22) Wrong AC tested in FINDIT after call to WTBLUK.
;RIP:<7.UTILITIES>ACJDEC.MAC.888 30-Nov-88 13:35:18, Edit by GSCOTT
;(21) Don't use TEXTBU in HDRLOG routine as logging routines could be using it.
;RIP:<7.UTILITIES>ACJDEC.MAC.883 30-Nov-88 10:05:27, Edit by GSCOTT
;(17) Add POLICY keyword.
;RIP:<7.UTILITIES>ACJDEC.MAC.880 29-Nov-88 17:34:19, Edit by GSCOTT
;(16) Move ACCESS.CONTROL code here, allow hyphen at end of line.
;RIP:<7.UTILITIES>ACJDEC.MAC.879 28-Nov-88 09:43:58, Edit by GSCOTT
;(14) Ignore errors from SPRIW.
;RIP:<7.UTILITIES>ACJDEC.MAC.872 22-Nov-88 20:27:01, Edit by GSCOTT
;(13) Support wild username specifications.
;RIP:<7.UTILITIES>ACJDEC.MAC.827 22-Nov-88 11:09:42, Edit by GSCOTT
;(12) Calculation of next midnight time was flawed. Again.
;RIP:<7.UTILITIES>ACJDEC.MAC.817 21-Nov-88 21:12:02, Edit by GSCOTT
;(11) Another log file bug, smashed AC in NEWLOG.
;RIP:<7.UTILITIES>ACJDEC.MAC.811 21-Nov-88 15:12:38, Edit by GSCOTT
;(7) Statistics should be sent to log file.
;RIP:<7.UTILITIES>ACJDEC.MAC.807 21-Nov-88 14:29:59, Edit by GSCOTT
;(6) Repair setting timer interrupt in midnight routine.
;RIP:<7.UTILITIES>ACJDEC.MAC.801 20-Nov-88 22:53:48, Edit by GSCOTT
;(4) Suppress created symbol on ERSKP, support log file switch at midnight.
;RIP:<7.UTILITIES>ACJDEC.MAC.781 20-Nov-88 14:37:06, Edit by GSCOTT
;(3) Send JSYS error messages to log file if we have a log file JFN.
;RIP:<7.UTILITIES>ACJDEC.MAC.770 20-Nov-88 12:24:13, Edit by GSCOTT
;(2) Fix problem with DISFNC.
;RIP:<GSCOTT>ACJDEC.MAC.768 20-Nov-88 12:04:50, Edit by GSCOTT
;(1) Creation.
; COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1988, 1989.
; ALL RIGHTS RESERVED.
;
; THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
; ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE
; INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER
; COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
; OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY
; TRANSFERRED.
;
; THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE
; AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT
; CORPORATION.
;
; DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS
; SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
TITLE ACJDEC - Access Control Facility Profile and Policy Driver
SUBTTL Gregory A. Scott
Subttl Table of Contents
; Table of Contents for ACJDEC
;
; Section Page
;
;
; 1. General Comments . . . . . . . . . . . . . . . . . . . 5
; 2. Definitions
; 2.1 Environment . . . . . . . . . . . . . . . . . 6
; 2.2 Version and Entry Vector . . . . . . . . . . . 7
; 2.3 Storage
; 2.3.1 Low Segment Pages . . . . . . . . . . . 8
; 2.3.2 Low Segment Writable . . . . . . . . . . 9
; 2.3.3 High Segment Writable . . . . . . . . . 13
; 2.4 Interrupt System . . . . . . . . . . . . . . . 14
; 2.5 Command GTJFN Blocks . . . . . . . . . . . . . 16
; 2.6 Command State Block . . . . . . . . . . . . . 18
; 2.7 Command Tables . . . . . . . . . . . . . . . . 19
; 2.8 Set Command Tables . . . . . . . . . . . . . . 20
; 3. Commands
; 3.1 Initialization . . . . . . . . . . . . . . . . 21
; 3.2 Top Level . . . . . . . . . . . . . . . . . . 22
; 3.3 Disable Command . . . . . . . . . . . . . . . 23
; 3.4 Enable Command . . . . . . . . . . . . . . . . 24
; 3.5 Help Command . . . . . . . . . . . . . . . . . 25
; 3.6 Save Command . . . . . . . . . . . . . . . . . 26
; 3.7 Set Command . . . . . . . . . . . . . . . . . 27
; 3.7.1 Access Log File . . . . . . . . . . . . 28
; 3.7.2 Log File Cache Sweep Interval . . . . . 29
; 3.7.3 Prime Time . . . . . . . . . . . . . . . 30
; 3.7.4 Spy Check Interval . . . . . . . . . . . 31
; 3.7.5 Spy Log Directory . . . . . . . . . . . 32
; 3.8 Show Command . . . . . . . . . . . . . . . . . 33
; 3.8.1 Show Functions . . . . . . . . . . . . . 34
; 3.8.2 Show Settings . . . . . . . . . . . . . 36
; 3.8.3 Show User . . . . . . . . . . . . . . . 37
; 3.8.4 Show Text On Terminal . . . . . . . . . 40
; 3.9 Take Command . . . . . . . . . . . . . . . . . 41
; 3.10 User Command . . . . . . . . . . . . . . . . . 44
; 3.11 Write Command . . . . . . . . . . . . . . . . 47
; 3.11.1 Write Settings . . . . . . . . . . . . . 49
; 3.11.2 Write User Profiles . . . . . . . . . . 50
; 3.11.3 Write Function Profiles . . . . . . . . 51
; 3.11.4 File Header . . . . . . . . . . . . . . 52
; 3.11.5 Fill and Write Line to File . . . . . . 53
; 3.11.6 Open/Close File . . . . . . . . . . . . 54
; 3.12 Command Subroutines . . . . . . . . . . . . . 55
Subttl Table of Contents (page 2)
; Table of Contents for ACJDEC
;
; Section Page
;
;
; 4. Access Control
; 4.1 Initialization . . . . . . . . . . . . . . . . 56
; 4.1.1 Capabilities and Interrupts . . . . . . 57
; 4.1.2 Configuration . . . . . . . . . . . . . 58
; 4.1.3 Access Control Functions . . . . . . . . 59
; 4.2 Processing Loop . . . . . . . . . . . . . . . 61
; 4.2.1 Find Function Profile . . . . . . . . . 62
; 4.2.2 Find User Profile . . . . . . . . . . . 63
; 4.2.3 Check Request . . . . . . . . . . . . . 64
; 4.2.4 Wait for failure . . . . . . . . . . . . 65
; 4.2.5 Log Request . . . . . . . . . . . . . . 66
; 4.3 Subroutines
; 4.3.1 Wild TBLUK Routine . . . . . . . . . . . 67
; 4.3.2 Get User Information . . . . . . . . . . 68
; 5. Logging Routines
; 5.1 Midnight Timer Routines . . . . . . . . . . . 72
; 5.2 System Shutdown Time Routines . . . . . . . . 73
; 5.3 Initialization of Log File . . . . . . . . . . 75
; 5.4 Send Text to Log File . . . . . . . . . . . . 78
; 5.5 Log File Cached Write . . . . . . . . . . . . 79
; 5.6 Log File Cache Sweep Interrupts . . . . . . . 80
; 5.7 Log File Cache Sweep . . . . . . . . . . . . . 81
; 5.8 Open/Close/Checkpoint Log File . . . . . . . . 82
; 5.9 New Page for Log File . . . . . . . . . . . . 83
; 5.10 Statistics Logging . . . . . . . . . . . . . . 84
; 5.11 Start Logging a Request . . . . . . . . . . . 87
; 6. Spy on Intruder . . . . . . . . . . . . . . . . . . . 88
; 6.1 Start a Spy Fork . . . . . . . . . . . . . . . 89
; 6.2 Kill Spy Fork . . . . . . . . . . . . . . . . 91
; 6.3 Start a Spy Fork
; 6.3.1 Get a Spy File . . . . . . . . . . . . . 92
; 6.3.2 Get a PTY . . . . . . . . . . . . . . . 94
; 6.4 Spy Fork
; 6.4.1 Initialization . . . . . . . . . . . . . 95
; 6.4.2 Main Loop . . . . . . . . . . . . . . . 96
; 6.4.3 Spy File I/O Routines . . . . . . . . . 97
; 6.4.4 Error Recovery and Termination . . . . . 98
; 6.4.5 Timer Interrupts . . . . . . . . . . . . 99
; 6.4.6 Setup Spy Link . . . . . . . . . . . . . 100
; 6.5 Spy File Header/Trailer . . . . . . . . . . . 101
; 6.6 Inferior Fork Termination Interrupt . . . . . 102
Subttl Table of Contents (page 3)
; Table of Contents for ACJDEC
;
; Section Page
;
;
; 7. Secure Files . . . . . . . . . . . . . . . . . . . . . 103
; 7.1 Find File's Entry . . . . . . . . . . . . . . 104
; 7.2 Check Desired Access . . . . . . . . . . . . . 105
; 7.3 Access Keywords . . . . . . . . . . . . . . . 106
; 7.4 Access Control Cache . . . . . . . . . . . . . 107
; 7.4.1 Open File . . . . . . . . . . . . . . . 108
; 7.4.1.1 Split Filename . . . . . . . . . . 109
; 7.4.1.2 Cache Find . . . . . . . . . . . . 110
; 7.4.1.3 Cache Stale Check . . . . . . . . 111
; 7.4.1.4 Cached Open . . . . . . . . . . . 112
; 7.4.1.5 Cached Input Setup . . . . . . . . 114
; 7.4.2 Close File
; 7.4.2.1 Read Finished . . . . . . . . . . 115
; 7.4.2.2 Flush Cache . . . . . . . . . . . 116
; 7.5 Action for Successful Access . . . . . . . . . 117
; 7.6 Read Line from File . . . . . . . . . . . . . 119
; 7.7 Read Character from File . . . . . . . . . . . 120
; 7.8 Read Character from Line Buffer . . . . . . . 121
; 7.9 Read Field from Line Buffer . . . . . . . . . 122
; 8. Subroutines
; 8.1 Simulate STCMP . . . . . . . . . . . . . . . . 123
; 8.2 Simulate SOUT . . . . . . . . . . . . . . . . 124
; 9. Output Subroutines
; 9.1 Output Information about Job . . . . . . . . . 125
; 9.2 Output Username/Device/Filename . . . . . . . 126
; 9.3 Output Capability Mask . . . . . . . . . . . . 127
; 9.4 Small Output Routines . . . . . . . . . . . . 128
; 9.5 Output Sixbit Word . . . . . . . . . . . . . . 129
; 9.6 Output Numbers . . . . . . . . . . . . . . . . 130
; 9.7 Output Floating Point Numbers . . . . . . . . 132
; 9.8 Output Millisecond Times . . . . . . . . . . . 133
; 9.9 Output Standard Date/Time . . . . . . . . . . 135
; 9.10 Output JSYS Error Message . . . . . . . . . . 136
; 10. Error Handler
; 10.1 Error Messages . . . . . . . . . . . . . . . . 137
; 10.2 Panic and Control-C Interrupt . . . . . . . . 138
; 10.3 Crash Handler . . . . . . . . . . . . . . . . 139
; 11. End of ACJDEC . . . . . . . . . . . . . . . . . . . . 142
SUBTTL General Comments
COMMENT ~
This Access Control Facility operates in two phases. In the first ("profile")
phase, commands are entered to set up a database of which GETOK functions are
desired and which users need special treatment. In the second ("policy") phase
the program implements the policy specified in the first phase by running as
the system access control facility.
This is the ACJDEC module. This module of the access control facility
implements the initial phase (profile phase). After the policy profile data
has been specified, a command generates a runnable ACJ.EXE which implements the
policy.
The ACJDEC module also contains the core Access Control Facility policy
program, which gets access control requests from the monitor and implements the
policy specified in the profile phase. This module also contains subroutines
called by this module in the profile and policy phases. These subroutines are
also called by the ACJUSR module.
The design of the program allows site specific policy implementations and
access control functions to be changed in the ACJUSR module. It is expected
that any site should not have to change any code in the ACJDEC module,
particularly in the core access control code. The ACJSYM module contains all
symbols that need to be shared between ACJDEC and ACJUSR.
This program was written in November 1988 by Gregory A. Scott, Digital
Equipment Corporation, Marlboro, Massachusetts.
Future enhancements to consider:
Write logfiles with date and/or time included in filespec.
Create some kind of idle job killer functionality.
~
SUBTTL Definitions -- Environment
;Normalize MACRO, load TOPS-20 standard definitions, define ACs, get MACREL.
SALL ;Clean listing
.DIREC FLBLST ;First line binary only
SEARCH MONSYM ;Get the usual monitor symbols
SEARCH MACSYM ;Get the usual macros
STDAC. ;Get the usual ACs
;ACJ specific initialization.
SEARCH ACJSYM ;Get our symbols
LOHIGH ;Tell me we need two segments for this
LOWCD ;Start off in low memory
SUBTTL Definitions -- Version and Entry Vector
;Set copyright.
.CPYRT <<1989>> ;Use the usual copyright macro there
;Define the entry vector
EV: JRST START ;Normal start
JRST START ;Reenter start
EXP VACJ ;Version
EVLEN==.-EV ;Length of entry vector
SUBTTL Definitions -- Storage -- Low Segment Pages
;[107] This section defines pages allocated in the low segment.
;[107] Pages 0-77 and 400-477 are reserved for code.
FLPAGE==000 ;[107] First page allocated to low code
LLPAGE==077 ;[107] Last page allocated to low code
FHPAGE==400 ;[107] First page allocated to high code
LHPAGE==477 ;[107] Last page allocated to high code
APAGE==LLPAGE+1 ;[111] Start at page after low code
;[107] Define a macro to allocate pages in memory.
DEFINE ALLOCP (ASIZE,SYMBPG,SYMBBU,SYMBLP),< ;[107]
IFL APAGE-<LHPAGE+1>,< ;[111] If current free page in low sen
IFGE <APAGE+ASIZE>-FHPAGE,< ;[111] and not enough space for block
APAGE==LHPAGE+1>> ;[111] Switch to high sec
IFG <APAGE+ASIZE-1>-777,< ;[111] No more memory?
PRINTX ? Too much buffer space allocated
PASS2 ;[107] Punt
END ;[107] and get out of here
> ;[107] End of IFE APAGE-LLPAGE
IFNB <SYMBPG>,<SYMBPG==APAGE> ;[120] Define first page number if needed
IFNB <SYMBLP>,<SYMBLP==APAGE+ASIZE-1> ;[120] Last page of buffer
SYMBBU=APAGE_PGSFT ;[121] Address of page map buffer
APAGE==APAGE+ASIZE ;[111] Point to next free page
> ;[107] End of DEFINE ALLOCP
;[111] First allocate pages for the access control file cache buffers.
DEFINE ALLOCC(A),<ALLOCP (SCACHE,<CBXF'A>,<CBX'A>,<CBXL'A>)> ;[111]
......==0 ;[111] Start with zero
REPEAT NCACHE,< ;[111] For each cache entry
ALLOCC(\......) ;[111] Allocate the pages for it
......==......+1> ;[111] and count to next entry
;Now allocate some other page aligned space.
ALLOCP (LOGBPC,LOGBFP,LOGBUF,LOGBLP) ;[112] Log file cache buffer
ALLOCP (1,SECOPG,SECOBU,) ;[120] Map access.control overflow pages here
ALLOCP (1,,TEXTBU,) ;[120] Place to put text into
ALLOCP (1,,HEADBU,) ;[120] Place to make header text into
SUBTTL Definitions -- Storage -- Low Segment Writable
LOWCD ;Low segment code
;Misc storage.
STACK: BLOCK PLEN ;Program stack
TEXTBP: BLOCK 1 ;Pointer into TEXTBU
;Storage used in statistics gathering.
NALLOW: BLOCK 1 ;Number of GETOKs allowed
NDENY: BLOCK 1 ;Number of GETOKs denied
NFAIL: BLOCK 1 ;Number of requests that failed
NHIT: BLOCK 1 ;[111] Number of access control cache hits
NMISS: BLOCK 1 ;[111] Number of access control cache misses
NFLUSH: BLOCK 1 ;[114] Number of access control cache flushes
RUNTIM: BLOCK 1 ;Program initial run time
PEOPLE: BLOCK 1 ;Program initial connect time
;Storage used in error reporting and crashing.
ERRBUF: BLOCK ^D400/5 ;Place to make error strings
ERRADR: BLOCK 1 ;Address of error string to print
LASERR: BLOCK 1 ;Last error at time of crash
BUGACS: BLOCK 20 ;ACs at time of crash
BUGPDL: BLOCK BUGLEN ;Stack for the crash
BUGFIL: BLOCK ^D<7+4+6+6+7+4+1>/5 ;Place to build filename
;Configuration information
TTYPTY: BLOCK 1 ;TTY number of first PTY
MAXPTY: BLOCK 1 ;Number of PTYs
CTYLNO: BLOCK 1 ;Line number of the CTY
OURNAM: BLOCK 2 ;Place to keep our ASCIZ node name
OPRUNO: BLOCK 1 ;User number of OPERATOR
OURUNO: BLOCK 1 ;User number of runner of profile generator
OURJOB: BLOCK 1 ;Our job number (0 or global job number)
LOGFIL: BLOCK ^D<40*5>/5 ;Log file name
PRIMEB: BLOCK 1 ;Time that prime time begins
PRIMEE: BLOCK 1 ;Time that prime time ends
;Storage used for access control housekeeping.
ARGBLK: BLOCK ARGLEN ;RCVOK argument block
USRSTR: BLOCK ^D<40*2>/5 ;Store a username here
TODCLK: BLOCK 1 ;[107] Uptime at the time of the last request
;Job information blocks
JIBLK: BLOCK JISIZ ;GETJI information for source job
CJBLK: BLOCK JISIZ ;Controlling job information block
TJBLK: BLOCK JISIZ ;Target job information block
;Storage used in remembering user profile.
;NOTE: Offset in right half of USRKEY is the index into USRPRO.
; Relative address in USRKEY is the index into USRNUM.
USRTBL: BLOCK 1 ;TBLUK table of [ASCIZ/user/],,profile offset
USRKEY: BLOCK NUSERS ;Data for USRTBL
USRSTG: BLOCK NUSERS*<USRCHR/5> ;ASCIZ username strings
USRPRO: BLOCK NUSERS ;Profile word for each user in table
;Storage used in writing the log file.
LOGLIN: BLOCK 1 ;Place to keep number of lines on this page
LOGPAG: BLOCK 1 ;Place to keep page number of this log file
LOGJFN: BLOCK 1 ;Place to keep the log file JFN
LOGPTR: BLOCK 1 ;[107] Pointer into log buffer
LOGCNT: BLOCK 1 ;[107] Count of freespace remaining in LOGBUF
LOGINT: EXP -1 ;[113] Interval in seconds between sweeps
LOGFNA: BLOCK ^D<5*40>/5 ;[110] Currently open log file name
LOGFTI: BLOCK ^D<2+1+3+1+4+1+2+1+2+1+2+1+1>/5 ;[124] Time used in log filename
LOGHSY: BLOCK 1 ;[125] Time that system is expected to HSYS
;Storage used in intruder spy facility.
SPYSLD: BLOCK ^D160/5 ;Spy log directory
SPYFIL: BLOCK ^D160/5 ;Place to build temp string for filename
SPYINT: BLOCK 1 ;Interval between spy fork checks
SPYFWZ==. ;First word to zero
SPYFRK: BLOCK NSPYS ;Save inferior handles here
SPYJOB: BLOCK NSPYS ;Job fork is watching
SPYUSR: BLOCK NSPYS ;Usernumber that fork is monitoring
SPYJFN: BLOCK NSPYS ;Log file JFN,,PTY JFN
SPYPDL: BLOCK NSPYS*SPLEN ;Stacks for the spy forks
SPYLWZ==.-1 ;Last word to zero
;Storage used in ACCESS.CONTROL routines.
SECGTJ: BLOCK .GJNOD+1 ;Place to build long form GTJFN
SECDIR: BLOCK ^D<40*3>/5 ;[111] Place to build "str:<directory>"
SECFNA: BLOCK ^D<40*5>/5 ;Place to build "file.type.gen"
SECFNV: BLOCK ^D<40*5>/5 ;[115] Place to build "file.type"
SECWRD: BLOCK ^D<40*5>/5 ;Place to read a field into
SECUSR: BLOCK ^D<40*2>/5 ;[115] Place to construct username
SECLIN: BLOCK SECCPL ;Place to read in the access control line
SECPMP: BLOCK 1 ;Page of file that is mapped now
SECPCT: BLOCK 1 ;Count of pages left to map
SECBCT: BLOCK 1 ;Byte count of mapped data area
SECBPT: BLOCK 1 ;Pointer into mapped data area
;[111] Access control cache blocks
DEFINE ALLOCC(A),<
XLIST ;[121] Turn listing off
PHASE 0 ;[111] Start definition of cache block
CBXJFN:!BLOCK 1 ;[111] JFN of this file
CBXCTL:!BLOCK 1 ;[111] FBCTL word of file
CBXPTR:!POINT 7,CBX'A ;[111] Byte pointer to read cached buffer
CBXPAG:!XWD .FHSLF,CBXF'A ;[111] Page number of the buffer
CBXMAP:!BLOCK 1 ;[111] Page count of cached file pages
CBXPCT:!BLOCK 1 ;[111] Page count of entire file
CBXRTI:!BLOCK 1 ;[111] Uptime at time that file was referenced
CBXMTI:!BLOCK 1 ;[111] Uptime at time that file was mapped
CBXDIR:!BLOCK ^D<6+1+1+<39*2>+1+1>/5 ;[111] Directory where file lives
CBXFIL:!BLOCK ^D<6+1+1+<39*2>+1+6+1+7+1>/5 ;[115] str:<dir>access.control
CBXSIZ:! ;[111] Define size of the block
DEPHASE ;[111] Get back to normal addressing
LIST ;[121] Turn listing on
> ;[111] End of DEFINE ALLOCC
CBPOOL: ......==0 ;[111] Start with zero
REPEAT NCACHE,< ;[111] For each cache entry
ALLOCC(\......) ;[111] Allocate the storage for it
......==......+1> ;[111] and count to next entry
SUBTTL Definitions -- Storage -- High Segment Writable
HIGHCD ;High segment code/data
;Storage for command parsing
CMDBUF: BLOCK <CBUFSZ==100> ;Command buffer
ATMBUF: BLOCK <ABUFSZ==40> ;Atom buffer
GTJBLK: BLOCK .GJATR+1 ;Long form GTJFN buffer
CSBLOK: BLOCK .CMGJB+1 ;Command state block
CMTADB: BLOCK 3 ;Block to parse time into
LINWID: BLOCK 1 ;Width of the terminal for showing text
TAKJFN: BLOCK 1 ;Take file JFN
PRSJFN: BLOCK 1 ;JFN used while parsing
SUBTTL Definitions -- Interrupt System
LOWCD ;Low segment code
;First create macro to assign each channel we desire.
;If channel not specified assign one from the range 0-6.
DEFINE ASSCHN(LEV,CHN,ADR,NAM),< ;Level, channel, routine, symbol
ONCHNS==ONCHNS!1B<^O<CHN>> ;Count this channel as one to enable
IFNB <CHN>,< ;If channel specified
CHAN'CHN==<LEV,,ADR> ;Construct this symbol for later
IFNB <NAM>,<NAM==CHN> ;Name it if desired
> ;End of IFNB <CHN>
IFB <CHN>,< ;If we want to assign one
ASSCHN(LEV,\FRECHN,ADR,NAM) ;Get one assigned and named
FRECHN==FRECHN+1 ;Count up one channel
IFE FRECHN-.ICAOV,< ;[125] If channel 0-5 now in use
FRECHN==.ICNXP+1 ;[125] Jump over panics to chan 23
> ;[125] End of IFE FRECHN-.ICAOV
> ;End of IFB <CHN>
> ;End of DEFINE ASSCHN
FRECHN==0 ;Start assigning with channel zero
ONCHNS==0 ;Start with no channels enabled
;Macro to grow the channel table from definitions set with ASSCHN.
DEFINE CHNGEN,< ;Macro to expand channel table
DEFINE PLANTC(NUM),< ;Macro to generate CHNTAB entry
XLIST ;Stop listing momentarily
IFNDEF CHAN'NUM,<EXP 0> ;Zero if no assignment
IFDEF CHAN'NUM,<EXP CHAN'NUM> ;Plant one channel table entry
LIST ;Resume listing now
......==......+1 ;Ratchet the channel number by one
> ;End of DEFINE PLANTC
......==0 ;Starting at channel 0
REPEAT ^D36,<PLANTC (\......)> ;Plant each channel
> ;End of DEFINE CHNGEN
;Assign panic channels first.
ASSCHN(1,\.ICPOV,PANIC) ;PDL overflows to PANIC
ASSCHN(1,\.ICDAE,PANIC) ;Data errors to PANIC
ASSCHN(1,\.ICQTA,PANIC) ;Disk full to PANIC
ASSCHN(1,\.ICILI,PANIC) ;Ill inst to PANIC
ASSCHN(1,\.ICIRD,PANIC) ;Ill mem read to PANIC
ASSCHN(1,\.ICIWR,PANIC) ;Ill mem write to PANIC
ASSCHN(1,\.ICMSE,PANIC) ;Sys resources exhausted to PANIC
;Assign other channels as needed.
ASSCHN(1,,CNTRLC,CCCHAN) ;Control-C trap on same as panic channel
ASSCHN(2,,MIDNIT,MDCHAN) ;Midnight interrupts
ASSCHN(2,,INTLFF,LFCHAN) ;[107] Log file cache sweeps
ASSCHN(2,,INTHSY,HFCHAN) ;[125] Hsys interrupt for log file sweep
ASSCHN(3,\.ICIFT,FRKTRM) ;Inferior fork termination interrupt
ASSCHN(3,,TIMINT,TICHAN) ;TIMER% interrupt (inferior fork use only)
;Make CHNTAB.
CHNTAB: CHNGEN ;Generate channel table
;Set the level table here.
LEVTAB: EXP LEV1PC ;Location to save level 1 PC return address
EXP LEV2PC ;Location to save level 2 PC return address
EXP LEV3PC ;Location to save level 3 PC return address
LEV1PC: BLOCK 1 ;Level 1 interrupt return PC
LEV2PC: BLOCK 1 ;Level 2 interrupt return PC
LEV3PC: BLOCK 1 ;Level 3 interrupt return PC
SUBTTL Definitions -- Command GTJFN Blocks
HIGHCD ;Back to high segment only code
;Save command GTJFN block
SAVGTJ: GJ%FOU!GJ%MSG ;(.GJGEN) Flags and generation
XWD .NULIO,.NULIO ;(.GJSRC) JFNs
0 ;(.GJDEV) Default device
0 ;(.GJDIR) Default directory
POINT 7,[ASCIZ/ACJ/] ;(.GJNAM) Default file
POINT 7,[ASCIZ/EXE/] ;(.GJEXT) Default type
0 ;(.GJPRO) Default protection
0 ;(.GJACT) Default account
0 ;(.GJJFN) Specified JFN
0 ;(.GJF2) Additional flags
;Set Access-log-file command GTJFN block
ALFGTJ: GJ%OFG ;(.GJGEN) Flags and generation parse only
XWD .NULIO,.NULIO ;(.GJSRC) Jfns
0 ;(.GJDEV) Default device
0 ;(.GJDIR) Default directory
0 ;(.GJNAM) Default file
0 ;(.GJEXT) Default type
0 ;(.GJPRO) Default protection
0 ;(.GJACT) Default account
0 ;(.GJJFN) Specified JFN
G1%SLN ;(.GJF2) Additional flags
;Set Spy-log-directory command GTJFN block
SLDGTJ: GJ%OFG ;(.GJGEN) Flags and generation parse only
XWD .NULIO,.NULIO ;(.GJSRC) Jfns
0 ;(.GJDEV) Default device
0 ;(.GJDIR) Default directory
0 ;(.GJNAM) Default file
0 ;(.GJEXT) Default type
0 ;(.GJPRO) Default protection
0 ;(.GJACT) Default account
0 ;(.GJJFN) Specified JFN
G1%SLN ;(.GJF2) Additional flags
;Take command GTJFN block
TAKGTJ: GJ%OLD ;(.GJGEN) Flags and generation
XWD .NULIO,.NULIO ;(.GJSRC) Jfns
0 ;(.GJDEV) Default device
0 ;(.GJDIR) Default directory
POINT 7,[ASCIZ/ACJPROFILE/] ;(.GJNAM) Default file
POINT 7,[ASCIZ/CMD/] ;(.GJEXT) Default type
0 ;(.GJPRO) Default protection
0 ;(.GJACT) Default account
0 ;(.GJJFN) Specified JFN
G1%IIN ;(.GJF2) Additional flags
;Write command GTJFN block
WRIGTJ: GJ%FOU!GJ%MSG ;(.GJGEN) Flags and generation
XWD .NULIO,.NULIO ;(.GJSRC) Jfns
0 ;(.GJDEV) Default device
0 ;(.GJDIR) Default directory
POINT 7,[ASCIZ/ACJPROFILE/] ;(.GJNAM) Default file
POINT 7,[ASCIZ/CMD/] ;(.GJEXT) Default type
0 ;(.GJPRO) Default protection
0 ;(.GJACT) Default account
0 ;(.GJJFN) Specified JFN
0 ;(.GJF2) Additional flags
SUBTTL Definitions -- Command State Block
;Command state block template.
CSBTPL: EXP COM2 ;(.CMFLG) Reparse at COM2
XWD .PRIIN,.PRIOU ;(.CMIOJ) Input and output JFNs
POINT 7,[EXP ASCII "ACJDE",<BYTE(7)"C",76,0>] ;(.CMRTY) Ptr to prompt
POINT 7,CMDBUF ;(.CMBFP) Pointer to start of buffer
POINT 7,CMDBUF ;(.CMPTR) Pointer to next input
EXP 5*CBUFSZ-1 ;(.CMCNT) Count of space remaining after .CMPTR
EXP 0 ;(.CMINC) Number of unparsed chars after .CMPTR
POINT 7,ATMBUF ;(.CMABP) Atom buffer pointer
EXP 5*ABUFSZ-1 ;(.CMABC) Atom buffer size in characters
EXP GTJBLK ;(.CMGJB) Address of long form GTJFN block
;Break mask for user names when they are being read by CMKEY.
USRBRK: EXP USRB0.,USRB1.,USRB2.,USRB3.
SUBTTL Definitions -- Command Tables
;Command table - must be in alphabetical order
; One entry for each command in the table
; CMND(name,help,routine,noflag)
DEFINE COMGEN<
XLIST
CMND(DISABLE,<(function) ALL|name>,DODISA)
CMND(ENABLE,<(function) ALL|name [profile]>,DOENAB)
CMND(HELP,<(message)>,DOHELP)
CMND(SAVE,<(program in) ACJ.EXE>,DOSAVE)
CMND(SET,<(mode) keywords>,DOSET)
CMND(SHOW,<ALL|FUNCTION [f]|SETTING [s]|USER [u]>,DOSHOW)
CMND(TAKE,<(commands from) acjprofile.cmd.0>,DOTAKE)
CMND(USER,<name [profile]>,DOUSER)
CMND(WRITE,<(commands to) acjprofile.cmd.-1>,DOWRIT)
LIST
>
;Command table suitable for use from COMND.
DEFINE CMND(A,B,C)<
XWD [ASCIZ/A/],C
>
CMDTBL: TBEGIN ;Insert table header
COMGEN ;Generate top level keywords
TEND ;Compute number of commands for header
;Table of keywords for HELP command.
DEFINE CMND(A,B,C)<
[ASCIZ\ A 'B'
\]
>
COMHLP: COMGEN ;Generate help text
HLPNUM==.-COMHLP ;Set number of entries
SUBTTL Definitions -- Set Command Tables
;Define a table of SET command keywords for setting and display
DEFINE SETGEN,<
XLIST
SETFUN(ACCESS-LOG-FILE,<Access Control log file>,<[-1,,LOGFIL]>,ISOUT,SETALF)
SETFUN(LOG-FILE-CACHE-SWEEP-INTERVAL,<Log file cache sweep interval in seconds>,LOGINT,ODEC,SETLFI) ;[107]
SETFUN(PRIME-TIME-BEGIN,<Prime time begin>,PRIMEB,OTOD,SETPTB)
SETFUN(PRIME-TIME-END,<Prime time end>,PRIMEE,OTOD,SETPTB)
SETFUN(SPY-CHECK-INTERVAL,<Spy check interval in seconds>,SPYINT,ODEC,SETSCI)
SETFUN(SPY-LOG-DIRECTORY,<Spy log directory and file>,<[-1,,SPYSLD]>,ISOUT,SETSLD)
LIST
> ;End of define SETGEN
;Table for SHOW SETTINGS command.
DEFINE SETFUN(A,B,C,D,E),<XWD [ASCIZ/B is set to /],D>
SETSHT: SETGEN ;Generate table of show text adr,,show routine
SETNUM==.-SETSHT ;Compute load number in table
;Table for command keywords and storing the data.
DEFINE SETFUN(A,B,C,D,E),<TENTRY(A,E)>
SETTBL: TBEGIN ;Table header
SETKEY: SETGEN ;Generate table
TEND ;End of commands table
DEFINE SETFUN(A,B,C,D,E),<EXP C>
SETDAT: SETGEN ;Generate table of data to show
;Define a microtable used to string "SETTINGS" onto command keyword lists.
SHOTBL: TBEGIN ;Plant the header
TENTRY(ALL,DOSHOA) ;SHOW ALL
TENTRY(FUNCTION,DOSHOF) ;SHOW FUNCTION function
TENTRY(SETTINGS,DOSHOS) ;SHOW SETTINGS
TENTRY(USER,DOSHOU) ;SHOW USER user
TEND ;Count that one keyword
;Define a microtable used to string "ALL" onto command keyword lists.
ALLTBL: TBEGIN ;Plant the header
ALLKEY: TENTRY(ALL,0) ;Just one keyword and that is all
TEND ;Count that one keyword
SUBTTL Commands -- Initialization
HIGHCD
;Here to start up the configuration phase of the program
START: RESET% ;The world
SETZ F, ;Load default flags
MOVE P,[IOWD PLEN,STACK] ;Load stack pointer
MOVE T1,[XWD CSBTPL,CSBLOK] ;Get BLT pointer for command state block
BLT T1,CSBLOK+.CMGJB ;Move it to the command state block
SETZM TAKJFN ;Not in a take file (any more)
SETZM PRSJFN ;Not filename parsing (any more)
MOVEI T1,NUSERS ;Load size of user table
SKIPN USRTBL ;Is the user table already set up?
MOVEM T1,USRTBL ;Save size of user table in the user table
MOVEI T1,SPYDCI ;Load default spy interval
SKIPN SPYINT ;Is one set?
MOVEM T1,SPYINT ;Nope, set default
MOVEI T1,LOGDCI ;[107] Load default spy interval
SKIPGE LOGINT ;[113] Is one set (-1 at startup)?
MOVEM T1,LOGINT ;[107] Nope, set default
HRROI T1,LOGFIL ;Point to log file area
HRROI T2,[ASCIZ/SYSTEM:LOGFILE.LOG/] ;Load default filespec
SKIPN LOGFIL ;Is log file spec set up?
CALL ISOUT ;(T1,T2/T1) Set it up now
HRROI T1,SPYSLD ;Point to spy log file area
HRROI T2,[ASCIZ/SYSTEM:ACJ-SPY/] ;Load default filespec
SKIPN SPYSLD ;Is spy log file spec set up?
CALL ISOUT ;(T1,T2/T1) Set it up now
MOVEI T1,PRIMDB ;Load default begin time
SKIPN PRIMEB ;Prime time begin set?
MOVEM T1,PRIMEB ;No, set it now
MOVEI T1,PRIMDE ;Load default end time
SKIPN PRIMEE ;Prime time end set?
MOVEM T1,PRIMEE ;No, set it now
GJINF% ;Get this job's information
MOVEM T1,OURUNO ;Save our user number for later
MOVEI T1,.PRIIN ;Load primary terminal
MOVEI T2,.MORLW ;Read line width
MTOPR% ;Get the terminal line width
ERSKP. ;Skip if error
CAIGE T3,^D40 ;At least 40?
MOVEI T3,^D40 ;Don't look bad if width zero
MOVEM T3,LINWID ;Save this here for later use
; JRST COM1 ;Start scanning commands
SUBTTL Commands -- Top Level
;Here when ready to read a command from the terminal.
COM1: MOVEI T2,[FLDDB. .CMINI] ;Get init function
CALL COMANE ;(T2/T1,T2,T3) Do it
;See if we are doing a TAKE file and if so process another line out of it.
CALL TAKCHK ;(/) Read a line from the TAKE file
;Here on a reparse
COM2: SKIPE T1,PRSJFN ;Is there a parse JFN?
RLJFN% ;Yes, release it please
ERNOP. ;Ignore errors
SETZM PRSJFN ;No parse JFN any more
MOVE P,[IOWD PLEN,STACK] ;Load stack pointer
MOVEI T2,[FLDDB. .CMKEY,,CMDTBL,<a command,>] ;Point to commands
CALL COMANE ;(T2/T1,T2,T3) Get a command
HRRZ T2,(T2) ;Get dispatch address
CALL (T2) ;(/) Do it
JRST COM1 ;Loop for more commands
SUBTTL Commands -- Disable Command
;DISABLE (function) ALL|name
DODISA: NOISE (function) ;Set noise words in front of the user
MOVEI T2,[FLDDB. .CMKEY,,FUNTBL,<a function,>,,[
FLDDB. .CMKEY,CM%SDH,ALLTBL,<ALL for all functions>]]
CALL COMANE ;(T2/T1-T3) Get a command
HRRZ P3,(T2) ;Get the function code (or 0 for all)
MOVEI P1,-FUNKEY(T2) ;Get address of enable bits
CALL CONFIR ;(/) Confirm that please
;Single function specified, disable it and return.
JUMPE P3,DODIS3 ;Jump if ALL specified
HRRZS FUNCTB(P1) ;Zero the left half of this entry
RET ;Return
;All functions specified, disable all of them and return.
DODIS3: MOVE T1,FUNAOB ;Load -number,,0
DO. ;For each function
HRRZS FUNCTB(T1) ;Zero an entry into the table
AOBJN T1,TOP. ;Loop for all of them
OD. ;End of clearing loop
RET ;Return to get more commands
SUBTTL Commands -- Enable Command
;ENABLE (function) ALL|name switch
DOENAB: NOISE (function) ;Set noise words in front of the user
MOVEI T2,[FLDDB. .CMKEY,,FUNTBL,<a function,>,,[
FLDDB. .CMKEY,CM%SDH,ALLTBL,<ALL for all functions>]]
CALL COMANE ;(T2/T1-T3) Get a command
HRRZ P3,(T2) ;Get the function code (or 0 for all)
MOVEI P1,-FUNKEY(T2) ;Get offset for this function bits
MOVE P2,ENADEF ;Load default bits to set in the mode word
;Parse profile keywords after function name.
DOENA1: MOVEI T2,[FLDDB. .CMCFM,,,,,[
FLDDB. .CMKEY,,ENATBL,<a profile keyword,>]]
CALL COMANE ;(T2/T1-T3) Parse a switch or confirm
LDB T3,[POINTR .CMFNP(T3),CM%FNC] ;Load the function parsed
CAIN T3,.CMCFM ;Was it the confirm?
JRST DOENA2 ;Yep, it certainly was this time
HRRZ T2,(T2) ;Load the address of the word with bits in it
SKIPN T2,(T2) ;Skip if there is a bit to set
IFSKP. ;There was a bit to set
TDO P2,T2 ;Set the bit please
JRST DOENA1 ;Loop until confirm seen
ENDIF. ;Otherwise it must be "NO", parse keyword
MOVEI T2,[FLDDB. .CMKEY,,ENANOT,<a profile keyword,>]
CALL COMANE ;(T2/T1,T2,T3) Get the thing parsed
HRRZ T2,(T2) ;Load address where the bits are
TDZ P2,(T2) ;Clear specified bit
JRST DOENA1 ;Loop until confirm seen
;Bits for this function are now in P2, if single function set them and return.
DOENA2: JUMPE P3,DOENA3 ;Loop for all of them if ALL specified
HLLM P2,FUNCTB(P1) ;Set bits the word where bits go today
RET ;Return happily to the caller
;All functions specified, set each one of them with bits in P2 and return.
DOENA3: MOVE T1,FUNAOB ;Load number of -functions,,0
DOENA4: HLLM P2,FUNCTB(T1) ;Zero an entry into the table
AOBJN T1,DOENA4 ;Loop for all of them
RET ;Return to get more commands
SUBTTL Commands -- Help Command
;HELP (message)
DOHELP: NOISE (message) ;Parse noise word por favor
CALL CONFIR ;(/) Confirm the command, maybe log or echo it
HRROI T1,TEXTBU ;Point to text buffer
CALL OCRLF ;(T1/T1) Start with crlf
HRROI T2,[VERSIO] ;Point to version string
CALL ISOUT ;(T1,T2/T1) Append version of this program
HRROI T2,HLPTXT ;Point to text
CALL ISOUT ;(T1,T2/T1) Append that help text next
CALL PTEXT ;(/) Print all of that on the terminal
MOVSI T3,-HLPNUM ;Get number of elements in table
HELPLP: HRRO T1,COMHLP(T3) ;Get pointer to help text
PSOUT% ;Tell that one
AOBJN T3,HELPLP ;Loop for all commands
CALLRET PCRLF ;(/) Output extra crlf and return
HLPTXT: ASCIZ/ commands:
/
SUBTTL Commands -- Save Command
;SAVE (program in) ACJ.EXE
DOSAVE: NOISE (program in) ;Mumble
;See if there is any functions enabled.
MOVE T1,FUNAOB ;Load -number,,0
MOVX T2,FU%ENA ;Load enable bits to test
DO. ;For each function
TDNE T2,FUNCTB(T1) ;Is this function enabled?
EXIT. ;Get out, there is at least one enabled
AOBJN T1,TOP. ;Loop for all of them
EMSG <No functions enabled> ;This isn't making sense
RET ;Return now
OD. ;So, there is at least function enabled
;Parse the rest of the command.
MOVE T1,[XWD SAVGTJ,GTJBLK] ;Make BLT pointer for source GTJFN
BLT T1,GTJBLK+.GJF2 ;Copy GTJFN block over there
MOVEI T2,[FLDDB. .CMFIL,CM%SDH,,<filename to save into>]
CALL COMANE ;(T1/T1-T3) Get filename
MOVEM T2,PRSJFN ;Save JFN for a sec
CALL CONFIR ;(/) Confirm that please
;Save ourselves as a runnable access control policy program.
HRROI T1,TEXTBU ;Point to text buffer
CALL OSPACE ;(T1/T1) Start with a space please
MOVE T2,PRSJFN ;Load the JFN today
CALL OJFNS ;(T1,T2/T1) Send the saved filename
HRROI T2,[ASCIZ/ Saved
/] ;Label the preceeding filename
CALL ISOUT ;(T1,T2/T1) Send that along as the end
MOVEI T1,ASTART ;Load new start address
HRRM T1,EV ;Save as new start address
IFE DBUGSW,HRRM T1,EV+1 ; and as reenter address
MOVE T1,PRSJFN ;Load the JFN back
SETZM PRSJFN ;Don't try to release the JFN later
HRLI T1,.FHSLF ;This fork
MOVX T2,SS%CPY!SS%RD!SS%EXE!FLD(-SAVCNT,SS%NNP)!FLD(0,SS%FPN)
SSAVE% ;Save our image
JSERRO (<Cannot create image>,,R) ;Owie!
CALL PTEXT ;(/) Output the "saved" message
HALTF% ;Halt the ACJ fork
JRST .-1 ;Don't allow continue
SUBTTL Commands -- Set Command
;SET keyword value
DOSET: MOVEI T2,[FLDDB. .CMKEY,,SETTBL,<item to set,>]
CALL COMANE ;(T2/T1-T3) Get a set command
MOVEI P1,-SETKEY(T2) ;Get offset for this function bits
HRRZ T2,(T2) ;Get dispatch address
CALLRET (T2) ;(/) Do it and return
SUBTTL Commands -- Set Command -- Access Log File
;SET ACCESS-LOG-FILE str:<dir>file.typ
SETALF: MOVE T1,[XWD ALFGTJ,GTJBLK] ;Make BLT pointer for source GTJFN
BLT T1,GTJBLK+.GJF2 ;Copy GTJFN block over there
MOVEI T2,[FLDDB. .CMFIL,CM%SDH,,<filespec for access control logging>,SYSTEM:LOGFILE.LOG]
CALL COMANE ;(T1/T1,T2,T3) Get filename
MOVEM T2,PRSJFN ;Save JFN for a sec
CALL CONFIR ;(/) Confirm that please
HRROI T1,LOGFIL ;Point to log directory area
MOVE T2,PRSJFN ;Load the JFN back, COM2 will release it
MOVX T3,JS%PAF!FLD(.JSAOF,JS%DEV)!FLD(.JSAOF,JS%DIR)!FLD(.JSAOF,JS%NAM)!FLD(.JSAOF,JS%TYP)
JFNS% ;Make a string out of that
JSERRO (<Can't make access control filespec string>)
RET ;Return to get more commands
SUBTTL Commands -- Set Command -- Log File Cache Sweep Interval
;SET LOG-FILE-CACHE-INTERVAL n (seconds)
SETLFI: NOISE (seconds) ;[107] Mumble
MOVEI T2,[FLDDB. .CMNUM,CM%SDH,^D10,<cache sweep interval in seconds>] ;[107]
CALL COMANE ;[107] (T2/T1-T3) Parse that number
SKIPL P1,T2 ;[113] Skip if greater than zero or zero
IFSKP. ;[107] If zero or less
EMSG <Interval must be non-negative number> ;[113] Owie
RET ;[107] Return now
ENDIF. ;[107] End of error code
CALL CONFIR ;[107] (/) Confirm that command
MOVEM P1,LOGINT ;[107] Save log file interval time
RET ;[107] and return
SUBTTL Commands -- Set Command -- Prime Time
;SET PRIMT-TIME-BEGIN hh:mm
;SET PRIME-TIME-END hh:mm
SETPTB: MOVEI T2,[FLDDB. .CMTAD,CM%SDH,CM%ITM!CM%NCI!CMTADB,<time in form hh:mm>]
CALL COMANE ;(T2/T1,T2,T3) Parse that time
CALL CONFIR ;(/) Confirm that
MOVE T2,CMTADB+2 ;Load the time in seconds since midnight
HRRZM T2,@SETDAT(P1) ;Save the time
RET ; and return
SUBTTL Commands -- Set Command -- Spy Check Interval
;SET SPY-CHECK-INTERVAL n (seconds)
SETSCI: NOISE (seconds) ;Mumble
MOVEI T2,[FLDDB. .CMNUM,CM%SDH,^D10,<spy interval in seconds>]
CALL COMANE ;(T2/T1-T3) Parse that number
SKIPLE P1,T2 ;Skip if greater than zero
IFSKP. ;If zero or less
EMSG <Interval must be positive number>
RET ;Return now
ENDIF. ;End of error code
CALL CONFIR ;(/) Confirm that command
MOVEM P1,SPYINT ;Save spy interval time
RET ; and return
SUBTTL Commands -- Set Command -- Spy Log Directory
;SET SPY-LOG-DIRECTORY str:<dir>file
SETSLD: MOVE T1,[XWD SLDGTJ,GTJBLK] ;Make BLT pointer for source GTJFN
BLT T1,GTJBLK+.GJF2 ;Copy GTJFN block over there
MOVEI T2,[FLDDB. .CMFIL,CM%SDH,,<directory to save spy logs into>,SYSTEM:ACJ-SPY]
CALL COMANE ;(T1/T1-T3) Get filename
MOVEM T2,PRSJFN ;Save JFN for a sec
CALL CONFIR ;(/) Confirm that please
HRROI T1,SPYSLD ;Point to spy log directory area
MOVE T2,PRSJFN ;Load the JFN back, COM2 will release it
MOVX T3,JS%PAF!FLD(.JSAOF,JS%DEV)!FLD(.JSAOF,JS%DIR)!FLD(.JSAOF,JS%NAM)
JFNS% ;Make a string out of that
JSERRO (<Can't make spy log filespec string>)
RET ;Return to get more commands
SUBTTL Commands -- Show Command
;SHOW ALL|FUNCTION [ALL|fun]|SETTING [ALL|setting]|USER [ALL|user]
DOSHOW: MOVEI T2,[FLDDB. .CMKEY,,SHOTBL,<item to show,>,ALL] ;Parse keyword
CALL COMANE ;(T2/T1-T3) Parse that please
HRRZ T2,(T2) ;Load address to dispatch to
CALLRET (T2) ;(/) Perform show function and return
;Here for SHOW ALL, confirm it first. Show all possible things.
;Returns +1 always with all possible things showed.
DOSHOA: CALL CONFIR ;(/) Confirm this event
CALL SHOSEA ;(/) Show settings all
CALL SHOFUA ;(/) Show functions all
CALLRET SHOUSA ;(/) Show user all and return
SUBTTL Commands -- Show Command -- Show Functions
;Here for SHOW FUNCTION ALL|function.
DOSHOF: MOVEI T2,[FLDDB. .CMKEY,,FUNTBL,<a function,>,ALL,[
FLDDB. .CMKEY,,ALLTBL,<ALL for all functions>]]
CALL COMANE ;(T2/T1,T2,T3) Parse that
DMOVE P2,T2 ;Save entry from table and which adr used
CALL CONFIR ;(T1/T1) Confirm it
HRRZ T2,(P2) ;Get rh of returned keyword entry
JUMPE T2,SHOFUA ;(/) Use routine to perform ALL
MOVEI P1,-FUNKEY(P2) ;Load offset for this function
MOVE T2,FUNCTB(P1) ;Load the function enable bits
TXNN T2,FU%ENA ;Is it enabled?
SKIPA T2,[-1,,[ASCIZ/ Disabled function /]] ;Disabled function
HRROI T2,[ASCIZ/ Enabled function /] ;Enabled function
; CALLRET SHOFUN ;(T2,P1/) Yes, a function, send it and return
;Here to show information about a single function.
;Call with
; T2/ pointer to ASCIZ starting text
; P1/ function offset (into FUNKEY, etc.)
;Returns +1 always
SHOFUN: HRROI T1,HEADBU ;Point to text buffer buffer
CALL ISOUT ;(T1,T2/T1) Start off with this
HLRO T2,FUNKEY(P1) ;Load name of this function string
CALL ISOUT ;(T1,T2/T1) Append function name string
MOVE T4,ENAAOB ;Load -n,,0 where n is the count of ENATBL
DO. ;Loop through elements in table
HRRZ T3,ENAKEY(T4) ;Point to flag word
MOVE T3,(T3) ;Get that word with the bits in it
TDNN T3,FUNCTB(P1) ;Is this bit lit for this function?
IFSKP. ;Yes, tell me about this one
HRROI T2,[ASCIZ/, /] ;Load seperator
CALL ISOUT ;(T1,T2/T1) Append that
HLRO T2,ENASHO(T4) ;Point to string
CALL ISOUT ;(T1,T2/T1) Append that
ENDIF. ;End of output code
AOBJN T4,TOP. ;Loop for all function profile bits
OD. ;End of loop
CALL OCRLF ;(T1/T1) Append in a crlf
CALLRET SHOWIT ;(/) Output all of that and return
;Show all enabled and disabled functions.
;Returns +1 always.
SHOFUA: HRROI P3,[ASCIZ/The following functions are enabled:
/] ;Start the list off right with this text
MOVE P4,[TXNN T3,FU%ENA] ;Load instruction to execute
CALL SHOFU2 ;(T1,P3,P4/) Show all of the enabled ones
HRROI P3,[ASCIZ/The following functions are disabled:
/] ;Load initial text
MOVE P4,[TXNE T3,FU%ENA] ;Load instruction to execute
; CALLRET SHOFU2 ;(T1,P3,P4/) Show all of the disabled ones
;Worker routine for SHOFUA, shows all enabled or disabled functions.
;Call with
; P3/ pointer to identifier string
; P4/ TXN% T3,FU%ENA
;Returns +1 always, P1 and P3 smashed
SHOFU2: MOVE P1,FUNAOB ;Load -functions,,0
DO. ;Loop for all functions
MOVE T3,FUNCTB(P1) ;Load enable bits from the table
XCT P4 ;Skip if we should show this one
IFSKP. ;Yes, show this one
MOVE T2,P3 ;Load the next prepending text
HRROI P3,[ASCIZ/ /] ;Point to tab for next one
CALL SHOFUN ;(T2,P1/) Show this function
ENDIF. ;End of Missouri (show me) code
AOBJN P1,TOP. ;Loop for all function
OD. ;End of for all functions loop
RET ; and return when done
SUBTTL Commands -- Show Command -- Show Settings
;Here for SHOW SETTINGS.
DOSHOS: MOVEI T2,[FLDDB. .CMKEY,,SETTBL,<a setting,>,ALL,[
FLDDB. .CMKEY,,ALLTBL,<ALL for all settings>]]
CALL COMANE ;(T2/T1,T2,T3) Parse that
DMOVE P2,T2 ;Save entry from table and which adr used
CALL CONFIR ;(T1/T1) Confirm it
HRRZ T2,(P2) ;Get rh of returned keyword entry
JUMPE T2,SHOSEA ;(/) If ALL then use special routine
MOVEI P1,-SETKEY(P2) ;Load offset of entry from table
HRROI P3,[ASCIZ/ /] ;Load pointer to just a space string
; CALLRET SHOSET ;(P1,P3/) Show settings and return
;Here to show program settings.
;Call with P1/ offset to SETTBL P3/ string for beginning of each line
;Returns +1 always.
SHOSET: HRROI T1,HEADBU ;Point to text buffer buffer
MOVE T2,P3 ;Load initial text first
CALL ISOUT ;(T1,T2/T1) Send it first
HLRO T2,SETSHT(P1) ;Load text to print first
CALL ISOUT ;(T1,T2/T1) Send that
HRRZ T3,SETSHT(P1) ;Load address of routine to call
MOVE T2,@SETDAT(P1) ;Get the data itself to pass to show routine
CALL (T3) ;(T1,T2/T1) Show this item
CALL OCRLF ;(T1/T1) Append a CRLF to all of that
CALLRET SHOWIT ;(/) Show all of that and return
;Here to show all program settings.
;Returns +1 always.
SHOSEA: HRROI T1,[ASCIZ/The following program settings are in effect:
/] ;Load the header
PSOUT% ;Type it on terminal
HRROI P3,[ASCIZ/ /] ;Load pointer to a tab
MOVSI P1,-SETNUM ;Load -ive things to show,,0
DO. ;Loop for all things to show
CALL SHOSET ;(P1,P3/) Send one to terminal
AOBJN P1,TOP. ;Loop for all of them
OD. ;End of loop
RET ;Return
SUBTTL Commands -- Show Command -- Show User
;Here for SHOW USER ALL|wildusername.
DOSHOU: MOVEI T2,[FLDDB. .CMKEY,,USRTBL,<a user profile,>,ALL,[
FLDDB. .CMKEY,CM%SDH,ALLTBL,<ALL for all user profiles>]]
CALL COMANE ;(T2/T1,T2,T3) Parse that
DMOVE P2,T2 ;Save entry from table and which adr used
CALL CONFIR ;(T1/T1) Confirm it
HRRZ T2,(P2) ;Get rh of returned keyword entry
JUMPE T2,SHOUSA ;(/) If it was ALL use special routine
MOVEI P1,-USRKEY(P2) ;Load offset of entry from table
HRROI T2,[ASCIZ/ /] ;Load prepending text
; CALLRET SHOUSR ;(T2,P1/) Do just this user please
;Here to display information about a single user.
;Call with T2/ prepending text, P1/ offset into user table.
;Returns +1 always.
SHOUSR: HRROI T1,HEADBU ;Point to text buffer
CALL ISOUT ;(T1,T2/T1) Start with prepended text
HRROI T2,[ASCIZ/User /] ;Label the next string
CALL ISOUT ;(T1,T2/T1) Start off with this
HLRO T2,USRKEY(P1) ;Load pointer to username string
CALL ISOUT ;(T1,T2/T1) Append username string
MOVE T4,USEAOB ;Load -n,,0
DO. ;Loop through elements in table
HRRZ T2,USESHO(T4) ;Get dispatch address
CALL (T2) ;(T1,T3,P1/T1,P1) Output that
AOBJN T4,TOP. ;Loop for all function profile bits
OD. ;End of loop
CALL OCRLF ;(T1/T1) Append in a crlf
CALLRET SHOWIT ;(/) Print that and return
;Here to show user profile that happens to be a bit.
;Output "keyword" if bit (in data word) is lit in profile.
;Call with T1/ output pointer, T4/ USExxx offset, P1/ USRxxx offset
;Returns +1 always with T1/ updated
SHOBIT: HRRZ T3,USEKEY(T4) ;Load address of this keyword's flag word
HRRZ T2,USRKEY(P1) ;Load offset into profile table
MOVE T2,USRPRO(T2) ;Load user profile bits into T3
XOR T2,USEDEF ;Set not-default bits to 1
TDNN T2,(T3) ;Does this user not have default?
RET ;Default, return now
HRROI T2,[ASCIZ/, /] ;Load seperator
CALL ISOUT ;(T1,T2/T1) Append that
HRRZ T2,USRKEY(P1) ;Have to say something, load profile offset
MOVE T2,USRPRO(T2) ;Load user profile bits
TDNE T2,(T3) ;Skip if the bit is not lit
IFSKP. ;If bit is off
HRROI T2,[ASCIZ/no /] ;Load no keyword
CALL ISOUT ;(T1,T2/T1) Just say no
ENDIF. ;End of no code
HLRO T2,USESHO(T4) ;Point to string
CALLRET ISOUT ;(T1,T2/T1) Append that and return
;Here to show user profile that happens to be a decimal number.
;Output "keyword n", data word is byte pointer with 0 address.
;Call with T1/ output pointer, T4/ USExxx offset, P1/ USRxxx offset
;Returns +1 always with T1/ updated
SHODEC: HRRZ T2,USEKEY(T4) ;Load address of byte pointer
MOVE T2,(T2) ;Get bp into T2
HRRZ T3,USRKEY(P1) ;Load offset into profile table
HRRI T2,USRPRO(T3) ;Load user profile bits address into T3
LDB T3,T2 ;Get the value
JUMPE T3,R ;Return if zero
HRROI T2,[ASCIZ/, /] ;Load seperator
CALL ISOUT ;(T1,T2/T1) Append that
HLRO T2,USESHO(T4) ;Point to string
CALL ISOUT ;(T1,T2/T1) Append that
CALL OSPACE ;(T1/T1) A space next please
MOVE T2,T3 ;Reload the value
CALLRET ODEC ;(T1,T2/T1) Output that and return
;Here when SHOW USER ALL command.
;Returns +1 always.
SHOUSA: HLRZ P1,USRTBL ;Point to user table
JUMPE P1,R ;Return if no user defined
IMUL P1,[XWD -1,0] ;Make -users,,0
HRROI P3,[ASCIZ/The following user profiles are defined:
/] ;Label following
DO. ;Loop to show user profiles
MOVE T2,P3 ;Load the prependin text
HRROI P3,[ASCIZ/ /] ;Load a tab for the next one
CALL SHOUSR ;(T2,P1/) Show user profile
AOBJN P1,TOP. ;Loop for all of them
OD. ;End of loop for each user profile
RET ;Return
SUBTTL Commands -- Show Command -- Show Text On Terminal
;Here to display show text on terminal based on its line width.
;Call with HEADBU/ ASCIZ text
;Returns +1 always.
;ACs: T1/ destination pointer (TEXTBU)
; T2/ source pointer (HEADBU)
; T3/ current character
; T4/ space available on this line
; Q1/ copy of T1 at last space character
; Q2/ copy of T2 at last space character
SHOWIT: SAVEAC <Q1,Q2> ;Preserve these two ACs
MOVE T1,[POINT 7,TEXTBU] ;Point to destination buffer
MOVE T2,[POINT 7,HEADBU] ;Point to source buffer
SHOWI1: MOVE T4,LINWID ;Load maximum characters per line
SHOWI2: ILDB T3,T2 ;Load a source byte
IDPB T3,T1 ;Store it in destination
JUMPE T3,PTEXT ;Publish if a null seen
CAIE T3,.CHLFD ;Is it a linefeed
CAIN T3,.CHCRT ; or a return?
JRST SHOWI1 ;Yes, reset line counter and continue
CAIN T3,"," ;Is it a comma?
DMOVEM T1,Q1 ;Yes, remember where the last comma was
CAIN T3,.CHTAB ;Is it a tab?
SUBI T4,7 ;Yes, account for it as 8 positions always
SOJG T4,SHOWI2 ;Loop for all characters on the line
MOVE T1,Q1 ;Reload destination pointer after space char
HRROI T2,[BYTE(7).CHCRT,.CHLFD,.CHTAB,.CHTAB,0] ;Text is cr lf tab tab
CALL ISOUT ;(T1,T2/T1) Break the line here
MOVE T2,Q2 ;Reload source pointer
MOVE T4,LINWID ;Reload the terminal width
SUBI T4,^D16 ; account for those two tabs
JRST SHOWI2 ; and reenter the loop
SUBTTL Commands -- Take Command
;TAKE (commands from) file.typ
DOTAKE: SKIPN TAKJFN ;Are we in a TAKE now?
IFSKP. ;Yes
EMSG (Nested TAKE commands are illegal)
JRST TAKEOF ;Abort this take command
ENDIF. ;That's all
NOISE (commands from file) ;Mumble
MOVE T1,[XWD TAKGTJ,GTJBLK] ;Make BLT pointer for source GTJFN
BLT T1,GTJBLK+.GJF2 ;Copy GTJFN block over there
MOVEI T2,[FLDDB. .CMFIL,CM%SDH,,<take filename>]
CALL COMANE ;(T1/T1-T3) Get filename
MOVEM T2,PRSJFN ;Save JFN
CALL CONFIR ;(/) Confirm that command
;Open up the file, set the I/o JFNs and so forth, and then return.
MOVE T1,PRSJFN ;Load JFN again
MOVX T2,<FLD(7,OF%BSZ)!OF%RD> ;Read 7-bit bytes
OPENF% ;Pry it open
ERJMP COMERR ;Error, return
SETZM PRSJFN ;Don't release the JFN now
MOVEM T1,TAKJFN ;Reload that JFN please
MOVE T1,TAKGTJ+.GJSRC ;Load .NULIO,,.NULIO JFNs
MOVEM T1,CSBLOK+.CMIOJ ;That is the input JFN now
RET ;Return for all commands
;Routine to call after call to .CMINI function to process take file.
;Returns +1 always
TAKCHK: SKIPN T1,TAKJFN ;Do we have a take JFN?
RET ;Nope, return now
HRROI T2,CMDBUF ;Point to command buffer
MOVEI T3,<CBUFSZ*5>-1 ;Load characters we can supply to buffer
MOVEI T4,.CHLFD ;Load terminating character
;Loop reading one command from the file. Check for hyphen at end of line.
TAKCH1: SIN% ;String INput
ERJMP TAKEOF ;Check for EOF if error
CAILE T3,<CBUFSZ*5>-4 ;Have at least 3 characters been read?
JRST TAKCH5 ;Nope, no continuation possible
MOVNI Q1,3 ;Backup by this many bytes
ADJBP Q1,T2 ;Point back three
ILDB Q2,Q1 ;Get character two back
CAIE Q2,"-" ;Hyphen?
JRST TAKCH5 ;No, cannot be continuation then
ILDB Q2,Q1 ;Get the next character
CAIN Q2,.CHCRT ;Was it a return?
JRST TAKCH1 ;Yes, get the next line also please
;Entire command line has been read now, set up CSB, echo command, and return.
TAKCH5: MOVEI T4,<CBUFSZ*5>-1 ;Load mas possible characters transferred
SUB T4,T3 ;Compute number stored in buffer
MOVEM T4,CSBLOK+.CMINC ;Save that as number of unparsed characters
MOVEI T3,0 ;Load a null character
IDPB T3,T2 ;Insure a null at end of text string
MOVE T1,CSBLOK+.CMRTY ;Load the pointer to prompt string
PSOUT% ;Send that to the terminal please
HRROI T1,CMDBUF ;Point to command buffer again
PSOUT% ;Send it to the terminal
RET ;Back in the saddle again
;Come here when error reading from take file. If IOX4 it must be the end of
;the take file, otherwise give error message. Then close the TAKE file and go
;to COM1 to start getting commands from the terminal.
TAKEOF: CALL GETERR ;(/T2) Get last error code into T2
CAIE T2,IOX4 ;Is it end of file on take command?
OJSERR (<Error reading command file>) ;Nope, mumble about error instead
HRROI T1,TEXTBU ;Point to text buffer
HRROI T2,[ASCIZ/[End of /] ;Point bracket and start of message
CALL ISOUT ;(T1,T2/T1) Send that along
MOVE T2,TAKJFN ;Reload the JFN
CALL OJFNS ;(T1,T2/T1) Send along the filename
HRROI T2,[ASCIZ/]
/] ;Point to bracket cr lf string
CALL ISOUT ;(T1,T2/T1) Send that along
CALL PTEXT ;(/) Send all of that to terminal
CALL TAKCLS ;(/) Close out the take file JFN
JRST COM1 ;Restart command
;Here to close TAKE file.
;Returns +1 always.
TAKCLS: SKIPN T1,TAKJFN ;Reload the file's JFN
RET ;None there
CLOSF% ;Close it
ERCAL TAKCL3 ;Maybe it wasn't open
SETZM TAKJFN ;No more JFN
MOVE T1,CSBTPL+.CMIOJ ;Load the primary input JFN
MOVEM T1,CSBLOK+.CMIOJ ;That is the input JFN now
RET
TAKCL3: MOVE T1,TAKJFN ;Reload the JFN
RLJFN% ;Release it
ERNOP. ;HFO?
RET ;Return to above
SUBTTL Commands -- User Command
;USER name profile
;AC usage in this routine
; P2/ accumulated profile
; P3/ offset to USExxx tables
DOUSER: MOVEI T2,[FLDDB. .CMUSR,CM%SDH,,<Username to set profile for>,,[
FLDBK. .CMFLD,CM%SDH,,<Wild user specification to set profile for>,,USRBRK]]
CALL COMANE ;(T2/T1-T3) Parse username field
LDB T3,[POINTR .CMFNP(T3),CM%FNC] ;Load the function parsed
HRROI T1,USRSTR ;Place to keep user string
CAIE T3,.CMFLD ;Was it the field parse?
IFSKP. ;Yes
HRROI T2,ATMBUF ;Point to atom buffer source
CALL ISOUT ;(T1,T2/T1) Copy the user name down there
ELSE. ;Otherwise it was the username parse
CALL ODIRST ;(T1,T2/T1) Send username in there
ENDIF. ;End of field/username parse code
MOVE P2,USEDEF ;Load the default profile for a user
;Get this user's profile.
DOUSE1: MOVEI T2,[FLDDB. .CMCFM,,,,,[
FLDDB. .CMKEY,,USETBL,<a user profile keyword,>]]
CALL COMANE ;(T2/T1-T3) Parse a keyword or confirm
LDB T3,[POINTR .CMFNP(T3),CM%FNC] ;Load the function parsed
CAIN T3,.CMCFM ;Was it the confirm?
JRST DOUSE2 ;Yep, it certainly was this time
MOVEI P3,-USEKEY(T2) ;Load offset for USExxx tables
CALL @USEPRS(P3) ;(P2,P3/P2) Parse this field
JRST DOUSE1 ;Loop for more keywords or confirm
;Here when we are ready to store this user's profile.
DOUSE2: HLRZ T4,USRTBL ;Load entry count of table
IMULI T4,USRCHR/5 ;Compute offset into username storage area
HRROI T1,USRSTG(T4) ;Point to storage area
HRROI T2,USRSTR ;Point to atom buffer string we copied earlier
CALL ISOUT ;(T1,T2/T1) Send that as the user name string
;See if user already in table.
MOVEI T1,USRTBL ;See if this user is in the table
HRROI T2,USRSTG(T4) ;Load the address of the string
TBLUK% ;Is the user in the table already?
ERJMP DOUSE3 ;Nope
TXNE T2,TL%EXM ;Exact match for user in table?
JRST DOUSE4 ;Yep
;Add user to table.
DOUSE3: MOVEI T1,USRTBL ;Point to user table
HLRZ T2,USRTBL ;Get the offset again
HRLI T2,USRSTG(T4) ;Get address of string,,offset
TBADD% ;Add it to the table
JSERRO (<Cannot add user to table>)
;Set profile bits of user, entry address is now in T1 (from TBLUK or TBADD).
DOUSE4: HRRZ T1,(T1) ;Load the entry offset for this user
MOVEM P2,USRPRO(T1) ;Store new profile word
RET ;Return for more commands
;Here to parse format field that is just a bit.
;"NO keyword" clears the bit an "keyword" sets the bit.
;Call with P2/ accumulated format bits, P3/ offset to USExxx
;Returns +1 always with P2/ updated bits
PRSBIT: HRRZ T2,USEKEY(P3) ;Load the address of the word with bits in it
SKIPN T2,(T2) ;Skip if there is a bit to set
IFSKP. ;There was a bit to set
TDO P2,T2 ;Set the bit please
RET ;Loop until confirm seen
ENDIF. ;Otherwise it must be "NO", parse keyword
MOVEI T2,[FLDDB. .CMKEY,,USENOT,<a user profile keyword,>]
CALL COMANE ;(T2/T1,T2,T3) Get the thing parsed
HRRZ T2,(T2) ;Load address where the bits are
TDZ P2,(T2) ;Clear specified bit
RET ;Return to loop until confirm seen
;Here when parsing a keyword followed by a number.
;Call with P2/ accumulated format bits, P3/ offset to USExxx
;Returns +1 always with P2/ updated bits
PRSDEC: MOVEI T2,[FLDDB. .CMNUM,,^D10] ;Get a decimal number
CALL COMANE ;(T2/T1,T2,T3) Parse that
HRRZ T1,USEKEY(P3) ;Load address of data word
MOVE T1,(T1) ;Load the data which is the byte pointer
TXO T1,P2 ;Address to store in is P2
DPB T2,T1 ;Store the value
RET ;Return for more
SUBTTL Commands -- Write Command
;WRITE (commands to) acjprofile.com.-1
DOWRIT: NOISE (commands to) ;Mumble about this
MOVE T1,[XWD WRIGTJ,GTJBLK] ;Make BLT pointer for source GTJFN
BLT T1,GTJBLK+.GJF2 ;Copy GTJFN block over there
MOVEI T2,[FLDDB. .CMFIL,CM%SDH,,<filename to write profile to>]
CALL COMANE ;(T1/T1-T3) Get filename
MOVEM T2,PRSJFN ;Save JFN
CALL CONFIR ;(/) Confirm that command
;Open the file and write a header to it.
MOVE P2,PRSJFN ;Reload the JFN we parsed
SETZM PRSJFN ;Don't let anyone else try to release it
CALL WRIOPN ;(P2/) Open the file
CALLRET WRICLS ;(P2/) Error, return
CALL WRIHDR ;(P2/) Construct header text in TEXTBU
CALL WRITXT ;(P2/) Write that to the file
;Write program settings.
MOVSI P1,-SETNUM ;Load -ive things to show,,0
DO. ;Loop for all things to show
CALL WRISET ;(P1/) Send one line information to text buffer
CALL WRITXT ;(P2/) Send that to the file
AOBJN P1,TOP. ;Loop for all items
OD. ;End of loop
;Write function settings.
MOVE P1,FUNAOB ;Load number of functions to do
DO. ;Loop for each function
CALL WRIFUN ;(P1/) Show this function to text buffer
CALL WRITXT ;(P2/) Send the text buffer to the file
AOBJN P1,TOP. ;Loop for all function
OD. ;End of for each function loop
;Write user profiles.
HLRZ P1,USRTBL ;Point to user table
IFN. P1 ;If there are userd defined today
IMUL P1,[XWD -1,0] ;Make -users,,0
DO. ;Loop for each user
CALL WRIUSR ;(P1/) Show user profile to buffer
CALL WRITXT ;(P2/) Send that text to the file
AOBJN P1,TOP. ;Loop for all of them
OD. ;End of short loop
ENDIF. ;End of user write code
CALLRET WRICLS ;(P2/) Close JFN and return
SUBTTL Commands -- Write Command -- Write Settings
;Here to write all program settings.
;Returns +1 always with TEXTBU filled.
WRISET: HRROI T1,TEXTBU ;Point to text buffer
HRROI T2,[ASCIZ/Set /] ;Load initial text
CALL ISOUT ;(T1,T2/T1) Send that
HLRO T2,SETKEY(P1) ;Load text to print first
CALL ISOUT ;(T1,T2/T1) Send that
CALL OSPACE ;(T1/T1) Space it out
HRRZ T3,SETSHT(P1) ;Load address of routine to call
MOVE T2,@SETDAT(P1) ;Get the data itself to pass to show routine
CALL (T3) ;(T1,T2/T1) Show this item
CALLRET OCRLF ;(T1/T1) Append a CRLF to all of that
SUBTTL Commands -- Write Command -- Write User Profiles
;Here to display information about a single user, P1/ offset into USRTBL.
;Returns +1 always with TEXTBU set up.
WRIUSR: HRROI T1,TEXTBU ;Point to text buffer
HRROI T2,[ASCIZ/User /] ;Label the next string
CALL ISOUT ;(T1,T2/T1) Start off with this
HLRO T2,USRKEY(P1) ;Load username string
CALL ISOUT ;(T1,T2/T1) Append username string
MOVE T4,USEAOB ;Load AOB pointer to user profile table (-n,,0)
DO. ;Loop through elements in table
CALL @USEWRI(T4) ;(T1,T4,P1/T1,T4,P1) Call routine to write
AOBJN T4,TOP. ;Loop for all function profile bits
OD. ;End of loop
CALLRET OCRLF ;(T1/T1) Append in a crlf and return
;Here to write user profile that happens to be a bit.
;Call with T1/ string pointer, T4/ USEKEY offset, P1/ USRKEY offset
;Returns +1 always, T1/ updated
WRIBIT: HRRZ T3,USEKEY(T4) ;Load address of the flags mask for this bit
HRRZ T2,USRKEY(P1) ;Load offset into profile table
MOVE T2,USRPRO(T2) ;Load user profile
XOR T2,USEDEF ;Set non-default bit setting to 1
TDNN T2,(T3) ;Is the bit not the default setting?
RET ;Bit is default setting, say nothing
CALL OSPACE ;(T1/T1) Output a space
HRRZ T2,USRKEY(P1) ;Have to say something, load profile offset
MOVE T2,USRPRO(T2) ;Load user profile bits
TDNE T2,(T3) ;Skip if the bit is not lit
IFSKP. ;If bit is off
HRROI T2,[ASCIZ/NO#/] ;Load NO keyword
CALL ISOUT ;(T1,T2/T1) Just say no
ENDIF. ;End of no code
HLRO T2,USEKEY(T4) ;Point to string containing keyword
CALLRET ISOUT ;(T1,T2/T1) Append that keyword and return
;Here to write user profile that happens to be a decimal number.
;Call with T1/ string pointer, T4/ USEKEY offset, P1/ USRKEY offset
;Returns +1 always, T1/ updated
WRIDEC: HRRZ T2,USEKEY(T4) ;Get address of the data
MOVE T2,(T2) ;Get the byte pointer which is the data word
HRRZ T3,USRKEY(P1) ;Load offset into profile table
HRRI T2,USRPRO(T3) ;Load user profile address
LDB T3,T2 ;Get the data
JUMPE T3,R ;Return if its zero
CALL OSPACE ;(T1/T1) Space first
HLRO T2,USEKEY(T4) ;Point to string containing keyword
CALL ISOUT ;(T1,T2/T1) Append that keyword
CALL OSPACE ;(T1/T1) Output a space next
MOVE T2,T3 ;Reload the value
CALLRET ODEC ;(T1,T2/T1) Make it decimal and return
SUBTTL Commands -- Write Command -- Write Function Profiles
;Here to show information about a single function, P1/ function offset
;Returns +1 always with TEXTBU set up.
WRIFUN: HRROI T1,TEXTBU ;Point to text buffer
MOVE T3,FUNCTB(P1) ;Load bits for this function
HRROI T2,[ASCIZ/Enable /] ;Assume enabled
TXNN T3,FU%ENA ;Skip if this is true
HRROI T2,[ASCIZ/Disable /] ;The function is disabled
CALL ISOUT ;(T1,T2/T1) Start off with this
HLRO T2,FUNKEY(P1) ;Load function name string
CALL ISOUT ;(T1,T2/T1) Append function name string
TXNN T3,FU%ENA ;Is this function enabled?
CALLRET OCRLF ;(T1/T1) Nope, output crlf and return now
MOVE T4,ENAAOB ;Load -n,,0
DO. ;Loop through elements in table
HRRZ T3,ENAKEY(T4) ;Point to flag word
MOVE T3,(T3) ;Get that word with the bits in it
MOVE T2,FUNCTB(P1) ;Load the profile word for this bit
XOR T2,ENADEF ;Set not equal bits to one
TDNN T2,T3 ;Skip if the bit is not set to default value
IFSKP. ;Yes, need keyword output
CALL OSPACE ;(T1/T1) Output a space
HRROI T2,[ASCIZ/NO#/] ;Load NO keyword
TDNN T3,FUNCTB(P1) ;Skip if the bit is lit
CALL ISOUT ;(T1,T2/T1) Just say no
HLRO T2,ENAKEY(T4) ;Point to keyword string
CALL ISOUT ;(T1,T2/T1) Append that
ENDIF. ;OK, done with that one
AOBJN T4,TOP. ;Loop for all function profile bits
OD. ;End of loop
CALLRET OCRLF ;(T1/T1) Append in a crlf
SUBTTL Commands -- Write Command -- File Header
;Here to write a header to the file for later use.
;Call with P2/ file jfn
;Returns +1 always, TEXTBU/ header
WRIHDR: HRROI T1,TEXTBU ;Point to text buffer
HRROI T2,[ASCIZ/! /] ;Start with header string
CALL ISOUT ;(T1,T2/T1) Start with this please
HRROI T2,[VERSIO] ;Point to version string
CALL ISOUT ;(T1,T2/T1) Append that text next
HRROI T2,[ASCIZ/ profile written by /] ;Label the user who did this
CALL ISOUT ;(T1,T2/T1) Start with this please
MOVE T2,OURUNO ;Load our user number
CALL ODIRST ;(T1,T2/T1) Send that text next
HRROI T2,[ASCIZ/ at /] ;Label the time
CALL ISOUT ;(T1,T2/T1) Append that text next
CALL OODTIN ;(T1/T1) Send current date and time
CALLRET OCRLF ;(T1/T1) Output a crlf and return
SUBTTL Commands -- Write Command -- Fill and Write Line to File
;Here to send command in text buffer to the file, makes 79 character lines.
;Changes "#" to spaces that are not broken across lines.
;Call with
; P2/ JFN
; TEXTBU/ ASCIZ text
;Returns +1 always.
;ACs: T1/ destination pointer (HEADBU)
; T2/ source pointer (TEXTBU)
; T3/ current character
; T4/ space available on this line
; Q1/ copy of T1 at last space character
; Q2/ copy of T2 at last space character
WRITXT: SAVEAC <Q1,Q2> ;Get some more scratch storage
MOVE T1,[POINT 7,HEADBU] ;Point to destination buffer
MOVE T2,[POINT 7,TEXTBU] ;Point to source buffer
WRITX1: MOVEI T4,WRICPL ;Load maximum characters per line
WRITX2: ILDB T3,T2 ;Load a source byte
CAIE T3,"#" ;Nonbreakable space?
SKIPA CX,T3 ;Nope, use origional character
MOVEI CX," " ;Yes it is, make it a space please
IDPB CX,T1 ;Store it in destination
JUMPE T3,WRITX5 ;Get out if a null seen
CAIE T3,.CHCRT ;Is it a return
CAIN T3,.CHLFD ; or linefeed?
JRST WRITX1 ;Yes, reset line counter and continue
CAIN T3," " ;Is it a space?
DMOVEM T1,Q1 ;Yes, remember where the last space was
SOJG T4,WRITX2 ;Loop for all characters on the line
MOVE T1,Q1 ;Reload destination pointer after space char
HRROI T2,[BYTE(7)"-",.CHCRT,.CHLFD,.CHTAB,0] ;Point to dash cr lf tab
CALL ISOUT ;(T1.T2/T1) Break the line here
MOVE T2,Q2 ;Reload source pointer
MOVEI T4,WRICPL-^D8 ; and reload the character counter
JRST WRITX2 ; and reenter the loop
WRITX5: MOVE T1,P2 ;Load JFN of the file
HRROI T2,HEADBU ;Point to the text buffer
SETZB T3,T4 ;Terminate on a null please
SOUT% ;Send that to the file
JSERRO (<Can't write file>,<CALL WRICLS>,COM1) ;Owie
RET ;Return to caller
SUBTTL Commands -- Write Command -- Open/Close File
;Here to open the file for writing all profile commands into.
;Call with P2/ JFN
;Returns +1 if error
;Returns +2 if success
WRIOPN: MOVE T1,P2 ;Load JFN
MOVX T2,<FLD(7,OF%BSZ)!OF%WR> ;Write 7-bit bytes
OPENF% ;Pry it open
JSERRO (<Can't open file to write>,,R) ;Error, punt and return +1
RETSKP ;Skip return
;Here to close the file we are writing
;Call with P2/ JFN
;Returns +1 always with file closed.
WRICLS: MOVE T1,P2 ;Load the JFN again
CLOSF% ;Close it please
ERSKP. ;Skip if error
RET ;Return
MOVE T1,P2 ;Load the JFN back
RLJFN% ;Release it now
ERNOP. ;Well I tried
RET ;Return
SUBTTL Commands -- Command Subroutines
;Here to parse something using COMND JSYS.
;Call with T2/ address of command function block chain
;Returns +1 if no parse
;Returns +2 if parsed OK
COMAND: MOVEI T1,CSBLOK ;Point to command state block
COMND% ;Parse that function please
ERJMP COMAN3 ;Owie if error!
TXNN T1,CM%NOP ;Error during confirm parse?
AOS (P) ;Nope, give skip return
RET ;Nope, return OK
COMAN3: CALL GETERR ;(/T2) Get last error code
CAIN T2,IOX4 ;Is it "End of file reached"?
JRST COM1 ;Yes, handle it by going to COM1
RET ;No, return
;Call CONFIR to parse a confirm, echo command if in take file.
;Returns +1 always, goes to COMERR if there is a problem.
CONFIR: MOVEI T2,[FLDDB. .CMCFM] ;Point to confirm function
; CALLRET COMANE ;(T2/T1-T3) Get the function done and return
;Here to perform a COMND JSYS function and go to COMERR if error.
;Call with T2/ function block
;Returns +1 always (goes to COMERR if there is a problem).
COMANE: CALL COMAND ;(T2/T1-T3) Do the function
JRST COMERR ;Give error message
RET ;Return to caller
;Here when some kind of command error.
COMERR: OJSERR (<Command error>) ;Nope, an owie instead of EOF
JRST COM1 ;Reset stack and continue parsing commands
SUBTTL Access Control -- Initialization
LOWCD ;Switch back to low seg
ASTART: RESET% ;Init the world again
MOVX F,FL%ACJ!FL%NOI ;Running as the ACJ now, no ints for now
MOVE P,[IOWD PLEN,STACK] ;Load stack pointer
SETZM LOGJFN ;No log file because of reset above
SETZM SPYFWZ ;Zero first word to zero
MOVE T1,[SPYFWZ,,SPYFWZ+1] ;Load BLT pointer to clear
BLT T1,SPYLWZ ;Clear storage (including old fork handles!)
;Call various initialization routines.
CALL INICAP ;(/) Turn on capabilities
CALL INICON ;(/) Get configuration information
CALL NEWLOG ;(/) Get a new log file JFN and put stuff in it
CALL INIMID ;(/) Set interrupt for midnight
CALL INILFF ;(/) Set interrupt for log file cache sweeps
CALL DEFFNC ;(/) Get defaults for all functions
CALL ENAFNC ;(/) Enable the access control functions
JRST MAIN ;Start processing access control requests
SUBTTL Access Control -- Initialization -- Capabilities and Interrupts
;Here to enable capabilities and PI system.
INICAP: MOVEI T1,.FHSLF ;Read this fork's capabilities
RPCAP% ;Read Process CAPabilities
TRNN T2,SC%WHL!SC%OPR ;Must be able to set wheel or operator today
BUG(HLT,NEP,<Not enough privs available>) ;Crash
MOVE T3,T2 ;Enable all capabilities
EPCAP% ;Enable Process CAPabilities
ERJMP [BUG(HLT,CEP,<Can't enable privs>)] ;Crash
MOVEI T1,.FHSLF ;For this fork
MOVE T2,[LEVTAB,,CHNTAB] ;Point to level and channel table
SIR% ;Set the interrupt table addresses
MOVX T2,ONCHNS ;For these channels
AIC% ;Activate Interrupt Channels
EIR% ;Enable interrupt system
MOVX T1,<.TICCC,,CCCHAN> ;Load code for control c and control c channel
ATI% ;Attach terminal interrupt character
JSERRO (<Could not enable control-C trapping>)
MOVEI T1,.MSIIC ;Load ignore increment mount count function
MSTR% ;Now we don't have to mount structures
JSERRO (<Could not ignore increment mount counts>)
RET ;Return
SUBTTL Access Control -- Initialization -- Configuration
;Get configuration information today.
INICON: GJINF% ;Get job information for us
MOVEM T1,OURUNO ;Save our user number
MOVEM T3,OURJOB ;Save our job number
MOVEI T1,.NDGLN ;Function to read local node name
MOVEI T2,T3 ;Argument block address
HRROI T3,OURNAM ;Point to storage
NODE% ;Get our local node name
ERNOP. ;What?
MOVX T1,RC%EMO ;Exact match only
HRROI T2,[ASCIZ/OPERATOR/] ;For this user
RCUSR% ;Get operator user number
ERSKP. ;[112] Skip if error
MOVEM T3,OPRUNO ;Save operator user number
MOVE T1,[SIXBIT/PTYPAR/] ;Name of table telling how many PTYs
SYSGT% ;Get number of PTYs,, TTY number of first PTY
HRRZM T1,TTYPTY ;Put TTY number of first PTY here
HLRZM T1,MAXPTY ;Put number of PTYs here
MOVX T1,<1,,.LOGDE> ;Get LOGDES+1
GETAB% ; which is the designator for job 0 output
IFNJE. ;If no error getting it
TXZ T1,.TTDES ;Make it just a line number
MOVEM T1,CTYLNO ; and save it as the CTY line number
ENDIF. ;End of GETAB worked code
RET ;Return for more work
SUBTTL Access Control -- Initialization -- Access Control Functions
;Here to enable trapping of access control functions that are enabled by the
;access control profile phase. All access control functions listed in our
;table are enabled (if FU%ENA) after disabling all functions.
;Returns +1 always.
ENAFNC: CALL DISFNC ;(/) First clear all enabled functions
MOVE T4,FUNAOB ;Load -ive count of functions,,0
DO. ;Loop for all functions
MOVEI T1,.SFSOK ;Set GETOK function
MOVE T2,FUNTMO(T4) ;Load default for this
TXO T2,SF%EOK ;Light the enable bit for this function
HRR T2,FUNKEY(T4) ;Get function code
TXNE T2,.GOUSR ;Is it a user function code?
HRRI T2,.GOUSR ;Yes, make it just 1B18 please
MOVE T3,FUNCTB(T4) ;Load enable bits
TXNE T3,FU%ENA ;Enable this function?
SMON% ;Yes, enable the function
IFNJE. ;If no JSYS error
TXO T3,FU%GOK ;Record we are now fully awake for the 1st time
ELSE. ;If a JSYS error
TXZ T3,FU%GOK ;Then we didn't enable GETOKs for this one
HRROI T1,TEXTBU ;Point to usual text place this rainy afternoon
HRROI T2,[ASCIZ/Can't enable /] ;Start out mess
CALL ISOUT ;(T1,T2/T1) Start with that string
HLRO T2,FUNKEY(T4) ;Point to text describing function
CALL ISOUT ;Send that along next please
HRROI T2,[ASCIZ/: /] ;Start out mess
CALL ISOUT ;(T1,T2/T1) Start with that string
CALL GETERR ;(/T2) Get last error
HRROI CX,TEXTBU ;Point to text buffer now
CAIE T2,SMONX2 ;"Invalid SMON function" is OK
CALL JSERR1 ;(CX/) Print the error message
ENDIF. ;End of that testing
MOVEM T3,FUNCTB(T4) ;Store the updated function bits
AOBJN T4,TOP. ;Loop for all functions in the table
OD. ;End of function enable loop
RET ;Return to caller with functions enabled
;Here to disable all access control functions we enabled. Called when we crash
;and before enabling any functions.
;Returns +1 always.
DISFNC: MOVE T4,FUNAOB ;Load -ive count of functions,,0
DO. ;Loop for all functions we know about
MOVEI T1,.SFSOK ;Reload the SMON GETOK function code
HLL T2,FUNTMO(T4) ;Get the default action for this function
HRR T2,FUNKEY(T4) ;Get function code
TXNE T2,.GOUSR ;Is it a user function code?
HRRI T2,.GOUSR ;Yes, make it just 1B18 please
MOVE T3,FUNCTB(T4) ;Load enable bits for this function
TXZE T3,FU%GOK ;Were we enabled for this function?
SMON% ;Yes, disable the function
ERNOP. ;Ignore all errors for now
MOVEM T3,FUNCTB(T4) ;Store the bit back please
AOBJN T4,TOP. ;Loop for all functions in the table
OD. ;End of loop
RET ;Return to caller
;Here to remember the GETOK function settings for all functions on startup.
;Returns +1 always with FUNTMO set up
DEFFNC: MOVE T4,FUNAOB ;Load the -n,,0 for the function table
DO. ;For every function that we know today
MOVEI T1,.SFSOK ;Load the function for SMON
HRRZ T2,FUNKEY(T4) ;Load the function code
TXNE T2,.GOUSR ;Is it a user function code?
HRRI T2,.GOUSR ;Yes, make it just 1B18 please
TMON% ;Test MONitor
ERSKP. ;If error, function probably not in monitor
TDZA T2,[^-SF%DOK] ;Keep just the interesting bit for later use
MOVX T2,SF%DOK ;Allow whatever this is if TMON failed
MOVEM T2,FUNTMO(T4) ;Save the flag for resetting later
AOBJN T4,TOP. ;Do this for all of them
OD. ;End of loop to get them
RET ;Return now
SUBTTL Access Control -- Processing Loop
;This is the main processing loop for access control.
MAIN: TXZ F,FL%DEN!FL%UNU!FL%FAI!FL%NOI ;Clear flags set per request, OKINT
SETZM ARGBLK ;Clear first word of argument block
MOVE T1,[XWD ARGBLK,ARGBLK+1] ;Load BLT pointer to block
BLT T1,ARGBLK+20 ;Clear only first twenty words today
MOVEI T1,ARGBLK ;Get address of answer block
MOVEI T2,ARGLEN ;and length of block
RCVOK% ;Get next function to check/log
ERJMP [BUG(HLT,NRA,<Could not receive access requests>)]
TXO F,FL%NOI ;We got one, set noint flag
TIME% ;[107] Get system uptime
MOVEM T1,TODCLK ;[107] Save this for later use
CALL FINDUS ;(/P3,P4) Try to find the job and user profile
CALL FINDIT ;(/P1,P2,T2,T3) Try to find the request
CAIA ;Skip if illegal request with T2 and T3 setup
CALL REQUES ;(P1,P2,P3,P4/T2,T3,Q1,P2) Perform the checking
SKIPE T2 ;Are we allowing the thing?
AOSA NDENY ;Nope, count the deny
AOS NALLOW ;Count the allow
MOVE T1,ARGBLK+.RCRQN ;Get the request number
GIVOK% ;Allow or deny request
ERJMP [BUG(HLT,FOK,<Failed to give OK>)] ;Hell is freezin' over
CALL WAITFO ;(Q1,P1,P2,P3,P4/) Wait only if needed today
CALL LOGREQ ;(Q1,P1,P2,P3,P4/) Log this request or not
TXZE F,FL%NLF ;Do we need a new log file?
CALL NEWLOG ;(/) Yes, get a new log file
SKIPLE LOGINT ;[113] Is the log file cache disabled?
TXZE F,FL%SLF ;[107] Do we need to sweep log file cache?
CALL SWPLOG ;[107] (/) Yes, sweep the log file cache now
JRST MAIN ;Loop for more requests
SUBTTL Access Control -- Processing Loop -- Find Function Profile
;Here to find the function offset for this request.
;Returns +1 if error, log text filled in and
; T2/ error code
; T3/ error string
; P1/ 0 to indicate no profile found
; P2/ contents of ENADEF (default bits for a function profile)
;Returns +2 if found and should be processed, with
; P1/ offset into function tables
; P2/ FUNCTB bits from tables
FINDIT: HLRZ T1,ARGBLK+.RCFCJ ;Get function code to look for
MOVE P1,FUNAOB ;Load number of functions built with
DO. ;Looping to find the function code
MOVE P2,FUNCTB(P1) ;Load bits for this function
CAMN T1,FUNCOD(P1) ;Match this one
RETSKP ;Yes, return +2
AOBJN P1,TOP. ;Loop for all of them
OD. ;Uh oh, it wasn't found
;Request is not found and is therefore illegal and will be denied.
HRROI T2,[ASCIZ/illegal/] ;Load identification string
CALL LOGSTA ;(T2/T1) Start a log file entry
HRROI T2,[ASCIZ/, code /] ;Label for following number
HLRZ T3,ARGBLK+.RCFCJ ;Get function code to report
CALL OLOCT ;(T1,T2,T3/T1) Send that to logging text
MOVEM T1,TEXTBP ;Save the pointer to to text buffer
MOVEI T1,ERRILR ;Not found, illegal request
HRROI T2,[ASCIZ/Unexpected request for access - denied/]
SETZ P1, ;Indicate that none found, no logging
MOVE P2,ENADEF ;Load default bits (hopefully logging)
TXO F,FL%DEN ;Deny flag
RET ;Deny whatever it was
SUBTTL Access Control -- Processing Loop -- Find User Profile
;Try to find user profile for this request.
;Returns +1 always with USRSTR and JIBLK set up and
; P3/ offset for user profile tables
; P4/ USRPRO profile bits for this user
FINDUS: SETZ P3, ;Load default which is user not found in table
MOVE P4,USEDEF ;Load default bits to allow logins at least
;Fill in JIBLK which contains all information about the job for later checking.
HRRZ T1,ARGBLK+.RCFCJ ;Get the job number
MOVEI T4,JIBLK ;Point to job info block
CALL GETINF ;(T1,T4/T4) Get job information
BUG(HLT,GIJ,<Can't get information on job>)
HLRZ T1,ARGBLK+.RCFCJ ;Load the function again
MOVE T3,ARGBLK+.RCARA ;Point to the supplied argument block address
MOVE T3,.GELUN(T3) ;Get user number for login
CAIN T1,.GOLOG ;Is it the login function?
MOVEM T3,JIBLK+.JIUNO ;Yes, fix up GETJI argument block
;Find out the username, returning default P2 and P3 if error or illegal.
HRROI T1,USRSTR ;Point to user string storage area
MOVE T2,JIBLK+.JIUNO ;Load user number not from ARGBLK mind you
CALL ODIRST ;(T1,T2/T1,T2) Send that to the username string
TLNN T2,-1 ;Not logged in or unknown user?
RET ;Yes, return +1 now with P3 and P4 defaulted
;Find user in our database, returning default P2 and P3 if not found.
MOVEI T1,USRTBL ;Point to the usr keyword table
HRROI T2,USRSTR ;Point to string to compare against
CALL WTBLUK ;(T1,T2/T1,T2,T3) Do a wild TBLUK function
TXNE T2,TL%NOM ;No match?
RET ;Return P3 and P4 as defaults
HRRZ P3,(T1) ;Return the offset into USRPRO and such tables
MOVE P4,USRPRO(P3) ;Reload the profile bits
RET ;Skip return with P1 through P4 set up
SUBTTL Access Control -- Processing Loop -- Check Request
;Here to check on the request, called with
; P1/ offset into function tables
; P2/ FUNCTB bits from tables
; P3/ offset for user profile tables
; P4/ USRPRO profile bits for this user
;Returns +1 always, T2 and T3/ set up for GIVOK
REQUES: HLRO T2,FUNLOG(P1) ;Point to name of this function
CALL LOGSTA ;(T2/T1) Start filling in log information
MOVE Q1,ARGBLK+.RCARA ;Point to argument block
HRRZ T2,FUNLOG(P1) ;Load address of the routine
CALL (T2) ;(T1,Q1,P1,P2,P3,P4/T1) Fill in the log text
MOVEM T1,TEXTBP ;Store updated pointer
TXNE P2,FU%POL ;Enforce policy for this function?
IFSKP. ;No, just allow (or deny) always
SETZB T2,T3 ;Assume we will allow this
MOVE T4,FUNTMO(P1) ;Load TMON bits for this function
TXNE T4,SF%DOK ;Should we allow this?
RET ;Yes, allow the function
TXO F,FL%DEN ;Light the deny bit please
MOVEI T2,400000 ;Deny the request
HRROI T3,[ASCIZ/Denied by access control facility/]
RET ;Deny the request
ENDIF. ;Otherwise call routine to test policy
CALL @FUNTST(P1) ;(Q1,P1,P2,P3,P4/T2,T3) Should be allowed?
IFSKP. ;Skip means yes
CALL USRPOL ;(Q1,P1,P2,P3,P4/T2,T3) Should be allowed?
ANSKP. ;Skip means yes
SETZB T2,T3 ;Yes, allow the request
RET ; and return
ENDIF. ;Deny the request, insure T2 and T3 are OK
TRNE T2,400000 ;Is this a legal error code?
TLNE T2,-1 ;Cannot have bits in left half
MOVEI T2,ERRAEC ;Illegal access control code
TLC T3,-1 ;Check the left half
TLCE T3,-1 ;Is it -1,,address?
HRROI T3,[ASCIZ/Denied by access control facility/] ;Generic message
RET ;Return ready for GIVOK JSYS
SUBTTL Access Control -- Processing Loop -- Wait for failure
;Here to perform special checks after a request has been disposed of.
;Called with
; Q1/ ARGBLK+.RCARA ("user" argument block for .Gxxxx offsets)
; P1/ offset into function tables
; P2/ FUNCTB bits from tables (FU%xxx)
; P3/ offset for user profile tables
; P4/ USRPRO profile bits for this user (US%xxx)
;Returns +1 always.
WAITFO: HLRZ T1,ARGBLK+.RCFCJ ;Get function code
;Check for login or attach complete.
CAIE T1,.GOLOG ;Was it a login ?
CAIN T1,.GOATJ ; or an attach?
IFNSK. ;Yes
MOVEI T1,^D1000 ;Wait a sec...
DISMS% ; for the job to LOGIN or not
HRRZ T1,ARGBLK+.RCFCJ ;Get this job number
HRROI T2,T4 ;Want one word returned in T4
MOVEI T3,.JIUNO ;Job's user number
GETJI% ;Get it
ERNOP. ;Ignore error for now
TRNE T4,-1 ;Did user get logged in?
RET ;So then return
TXO F,FL%FAI ;Nope, failed
AOS NFAIL ;Count the failed attempt
RET ; and then return
ENDIF. ;It wasn't a login or attach
;[122] Check for functions that must cause a log file sweep.
CAIN T1,.GOHSY ;[125] HSYS setting?
CALLRET HSYLOG ;[125] (Q1/) Please set a timer interrupt then
CAIN T1,.GOMDD ;[122] Is it a MDDT entry?
TXO F,FL%SLF ;[122] Yes, sweep the log file
;Other special waits should go here.
RET ;Return
SUBTTL Access Control -- Processing Loop -- Log Request
;Here to possibly send the request to the log file.
;Call with P2/ function bits
LOGREQ: MOVE T1,TEXTBP ;Point to text buffer today please
HRROI T2,[ASCIZ/ [Denied]/] ;Load denied indicator
TXNE F,FL%DEN ;Denied?
CALL ISOUT ;(T1,T2/T1) Yes book 'em
HRROI T2,[ASCIZ/ [Unusual]/] ;Load unusual indicator
TXNE F,FL%UNU ;Unusual?
CALL ISOUT ;(T1,T2/T1) Yes tell me so
HRROI T2,[ASCIZ/ [Failed]/] ;Load failed indicator
TXNE F,FL%FAI ;Failed?
CALL ISOUT ;(T1,T2/T1) Yes the unfortunate consequences
CALL OCRLF ;(T1/T1) Append a CRLF to all of that stuff
CALLRET USRLOG ;(P1,P2,P3,P4/) Log if desired
SUBTTL Access Control -- Subroutines -- Wild TBLUK Routine
;Routine to do a wild TBLUK function.
;Call with TBLUK% ACs:
; T1/ address of table
; T2/ byte pointer to string to be compared with strings in table
;Returns +1 always
; T1/ address of entry that matches or would have
; T2/ Recognition flags (TL%NOM and TL%EXM being the interesting ones)
WTBLUK: SAVEAC <Q1,Q2,Q3> ;Save ACs to save calling arguments in
DMOVE Q1,T1 ;Copy the ACs over to the Qs
TBLUK% ;Look this user up
ERNOP. ;Never ITRAPs, but be careful anyway
TXNE T2,TL%EXM ;Exact match?
RET ;Yes, return T1, T2, T3
MOVEI Q3,(T1) ;Save address to be returned if no match
MOVEI T4,(T1) ;Load address returned by TBLUK%
DO. ;Start looping through the table backwards
MOVEI T1,.WLSTR ;Load wild string match function
HLRO T2,(T4) ;Point to the ASCIZ user argument
HRROI T3,USRSTR ;Point to username to compare against
WILD% ;Try and match this one
ERNOP. ;Never is supposed to ITRAP, but ya never know
IFN. T1 ;If no match
CAIE T4,1(Q1) ;Was this the last entry in the table?
SOJA T4,TOP. ;Nope, keep looking
MOVEI T1,(Q3) ;Return address where the TBLUK wanted it to be
MOVX T2,TL%NOM ;Return no match bit
RET ;Return +1 always
ENDIF. ;Otherwise it must have matched
OD. ; so fall out of the loop
MOVEI T1,(T4) ;Point to the entry that matched today
MOVX T2,TL%EXM ;Return exact match flag for caller
RET ;Return +1 always
SUBTTL Access Control -- Subroutines -- Get User Information
;Here to find out all about a job.
;Call with T1/ job, T4/ address of our GETJI block
;Returns +1 always if GETJI% error
;Returns +2 if no error in the GETJI%, and with following:
; GETJI block, NTINFB+.NWTTF, NTBLK, and MORSPW set up, T4 preserved
GETINF: SAVEAC <Q1,Q2,Q3> ;Save the Qs
MOVEI T2,(T4) ;Point to start of block
HRLI T2,-<.JIMAX+1> ;Load size of this block
SETZ T3, ;All info
GETJI% ;Get info on this job
ERJMP R ;Return +1 if error here
;Got basic job info, now get job origin information.
SETZM NTBLK(T4) ;Insure first work is zero for local lines
MOVX T1,<NW%NNT>B17 ;Load default which is non network terminal
MOVEM T1,NTINFB+.NWTTF(T4) ;Save this in case NTINF JSYS fails
SETZM MORSPW(T4) ;Clear returned speed word
;Danger Will Robinson! Since NTINF% will give you a free null at the end of
;the origin string if it is a LAT terminal in older (6.1 and early 7.0)
;monitors, we cannot just write the string to our output pointer. We have
;write to it NTBLK instead and copy it to the output pointer later.
HRROI T1,NTBLK(T4) ;Point to place to store the string
MOVEM T1,NTINFB+.NWNNP(T4) ;Save output pointer
MOVE T2,.JIJNO(T4) ;Load job number
MOVEM T2,NTINFB+.NWLIN(T4) ;Save job number for NTINF
DMOVE T1,[EXP .NWNU1+1,.NWRRH] ;Load size of arg block and arg type
DMOVEM T1,NTINFB+.NWABC(T4) ;Save size of the block and arg block type
MOVEI T1,NTINFB(T4) ;Load address of information block
NTINF% ;Get information on this user
ERJMP GETIN1 ;If error, assume local terminal
;Check result, if non network terminal we say nothing.
MOVE T2,NTINFB+.NWTTF(T4) ;Load returned type and flags
LDB T3,[POINT 9,NTINFB+.NWTTF(T4),17] ;(no symbol for this field)
CAIE T3,NW%NNT ;Non network terminal?
JRST GETIN2 ;Nope, a network terminal
; JRST GETIN1 ;Fall through if non network terminal
;Here if the line appears to be a non network (front end) terminal.
GETIN1: SKIPGE .JICPJ(T4) ;Is this a PTY job?
SKIPGE T1,.JITNO(T4) ; or is this a detached job?
RETSKP ;Yes, return now
TXO T1,.TTDES ;Make designator out of terminal number
MOVEI T2,.MORSP ;Read terminal speed word
MTOPR% ;Read terminal speed
ERJMP RSKP ;Return now if error
MOVEM T3,MORSPW(T4) ;Return to the user for later
RETSKP ;Now return
;Here if the line appears to be a network terminal. If name found copy it to
;the output pointer. If name not known, but network type is, return a properly
;formatted number.
GETIN2: MOVEI T1,NTBLK(T4) ;Point to place where we stored the string
HRLI T1,(POINT 7) ;Make a pointer to that please
TXNE T2,NW%NNN ;No name known for this network terminal?
JRST GETIN3 ;Yes, print a number instead
MOVE T3,T1 ;Get current pointer
DO. ;Find the end of that name string
ILDB T2,T3 ;Load a character
JUMPN T2,TOP. ;Loop if not a null
OD. ;End of null search loop
MOVNI T1,1 ;Load a -1
ADJBP T1,T3 ;Back up one so we can write over the null
JRST GETIN7 ;Nope, name known, add type next
;Here when network name not known.
GETIN3: CAIN T3,NW%TCP ;Unknown TCP host connection?
JRST GETIN4 ;Yes, do it
CAIN T3,NW%DNA ;Unknown DECnet host connection?
JRST GETIN5 ;Yes, handle it
CAIN T3,NW%LAT ;Unknown LAT connection?
JRST GETIN6 ;Yes
RET ;None of those return with T1 unchanged
;Here if its a TCP connection, output "0.0.0.0".
GETIN4: CALL OSPACE ;(T1/T1) Send a space along next
MOVEI Q1,4 ;Load number of octets to print
MOVE Q2,[POINT 8,NTINFB+.NWNNU(T4),3] ;Make ILDB pointer to data
DO. ;For each octet
ILDB T2,Q2 ;Load a TCP octet
CALL ODEC ;(T1,T2/T1) Output a octet in decimal
SOJG Q1,GETIN7 ;(T1/T1) Output connection type
HRROI T2,[ASCIZ/./] ;Point to a dot
CALL ISOUT ;(T1,T2/T1) Append that to the string
JRST TOP. ;Loop for all octets
OD. ;End of TCP loop
;Here if its a DECnet connection, output "0.0".
GETIN5: CALL OSPACE ;(T1/T1) Send a space along next
LDB T2,[POINT 6,NTINFB+.NWNNU(T4),25] ;Get area of DECnet node number
CALL ODEC ;(T1,T2/T1) Print it
HRROI T2,[ASCIZ/./] ;Load a pointer to a dot
CALL ISOUT ;(T1,T2/T1) Output the dot next
LDB T2,[POINT 10,NTINFB+.NWNNU(T4),35] ;Get node number part
CALL ODEC ;(T1,T2/T1) Output that and return
CALLRET GETIN7 ;(T1/T1) Output connection type and return
;Here if it is a LAT connection, output "00-00-00-00-00-00".
GETIN6: CALL OSPACE ;(T1/T1) Send a space along next
MOVEI Q1,6 ;Load number of hex bytes to print
MOVE Q2,[POINT 8,NTINFB+.NWNNU(T4)] ;Make ILDB pointer to data
MOVX T3,NO%LFL!NO%ZRO!FLD(2,NO%COL)!FLD(^D16,NO%RDX) ;Hex output
DO. ;For each octet
ILDB T2,Q2 ;Load a TCP octet
NOUT% ;Output that hex digit
ERNOP. ;Ignore errors for now
SOJG Q1,GETIN7 ;(T1/T1) Output connection type and retutn
HRROI T2,[ASCIZ/./] ;Point to a dot
CALL ISOUT ;(T1,T2/T1) Append that to the string
JRST TOP. ;Loop for all octets
OD. ;End of TCP loop
;Here to output type of connection from the table.
GETIN7: HRRZ T2,NTINFB+.NWTTF(T4) ;(no symbol for this field) Get line type
MOVSI Q1,-OORSIZ ;Load number of things in table,,0
DO. ;Loop for things in table
HRRZ Q2,OORTAB(Q1) ;Load type of connection from table
CAMN Q2,T2 ;Match the type we want to hear about?
EXIT. ;Yes
AOBJN Q1,TOP. ;Loop for all of them
RETSKP ;Unknown type
OD. ;End of loop
HLRO T2,OORTAB(Q1) ;Match, load address of text
CALL ISOUT ;(T1,T2/T1) Add that in
RETSKP ; and then skip return
;Table of network connection types for above code.
OORTAB: TENTRY (<(NRT)>,NW%MC) ;MCB (NRT) terminal
TENTRY (<(TCP)>,NW%TV) ;TVT (TCP) terminal
TENTRY (<(CTM)>,NW%CH) ;CTERM terminal
TENTRY (<(LAT)>,NW%LH) ;LAT terminal
OORSIZ==.-OORTAB ;Make size of table
SUBTTL Logging Routines -- Midnight Timer Routines
;Here on a timer interrupt at midnight to start a new log file. See if we can
;do it now and if so do it, otherwise set a flag and do it later.
;Returns +1 always.
MIDNIT: CALL MIDHAN ;(/) Handle the midnight interrupt
DEBRK% ;Dismiss from interrupt
MIDHAN: SAVEAC <T1,T2,T3,T4,Q1,Q2,Q3> ;Save all of the suspect ACs
TXNE F,FL%NOI ;Are we NOINT?
TXOA F,FL%NLF ;Yes, remember to do log file stuff later
CALL NEWLOG ;(/) OKINT, get a new log file now please
; CALLRET INIMID ;(/) Get back here again please at midnight
;Here to set the timer interrupt that goes off each night at midnight.
;Returns +1 always, T1-T4 smashed.
INIMID: SETO T2, ;The time is now
SETZ T4, ;No flags for this please
ODCNV% ;Get all seperate pieces of time
JSERRO (<ODCNV failure>,,R) ;This will never happen I hope
HRRI T4,0 ;Load time of midnight today
IDCNV% ;Convert midnight today back to internal format
JSERRO (<IDCNV failed>,,R) ;This should never happen
ADD T2,[1,,0] ;Get midnight tomorrow
MOVX T1,<.FHSLF,,.TIMDT> ;Set interrupt at particular time
MOVEI T3,MDCHAN ;Load channel number to interrupt on
TIMER% ;Set the interrupt
JSERRO (<Failed to set midnight timer>) ;Too many timer blocks?
RET ;Just return +1 no matter what happens
SUBTTL Logging Routines -- System Shutdown Time Routines
;[125] Here when ACJ is initializing to get any possible HSYS time.
;Sets a TIMER for the time specified that the system will go down.
;Returns +1 always.
INIHSY: MOVEI T1,.DWNTI ;[125] Load GETAB table for down time
GETAB% ;[125] Get this from the monitor
SETZ T1, ;[125] Assume no shutdown if that failed
MOVE T2,T1 ;[125] Copy time to T2
CALLRET HSSLOG ;[125] (T2/) Set a timer for that time please
;[125] Here when a GOHSY GETOK function has been processed.
;Call with Q1/ ARGBLK+.RCARA ("user" argument block for .Gxxxx offsets).
;Returns +1 always.
HSYLOG: MOVE T2,.GESDT(Q1) ;[125] Load the shutdown time specified
; CALLRET HSSLOG ;[125] (T2/) Set a timer interrupt at that time
;[125] Here to set a timer because we think the system will be shutdown.
;Call with T2/ time that the system is expected to be shutdown
;Returns +1 always.
HSSLOG: MOVEI T3,HFCHAN ;[125] Load channel number to interrupt on
SKIPN LOGHSY ;[125] Do we have a timer set now?
IFSKP. ;[125] Yes, we do in fact
EXCH T2,LOGHSY ;[125] Save new time, get old time
MOVX T1,<.FHSLF,,.TIMDD> ;[125] Get function for delete and this fork
TIMER% ;[125] Remove the old interrupt time
ERNOP. ;[125] Ignore error for now
MOVE T2,LOGHSY ;[125] Get new time in T2 again
ENDIF. ;[125] End of timer set previously code
MOVEM T2,LOGHSY ;[125] Save time of expected HSYS
JUMPE T2,R ;[125] Return now if shutdown canceled
GTAD% ;[125] Get current date time
ADDI T1,<<2,,0>/^D<60*60*24>> ;[125] Get time two seconds from now
CAMGE T1,T2 ;[125] Shutdown time is in the next 2 seconds?
IFSKP. ;[125] Yes, don't set a timer
SETZM LOGHSY ;[125] Not expecting an interrupt now
TXO F,FL%NLF ;[125] Get a new log file now
RET ;[125] and then return
ENDIF. ;[125] End of shutdown in the near future code
MOVX T1,<.FHSLF,,.TIMDT> ;[125] Set interrupt at time in T2
TIMER% ;[125] Yes, please set the interrupt
ERNOP. ;[125] Not that important today anyway
RET ;[125] Just return +1 no matter what happens
;[125] Here when timer interrupt goes off to get a new log file. Does not get
;a new log file if we get the timer interrupt when we don't expect to get one.
;Does the work now if OKINT, sets flag to do it later if NOINT.
INTHSY: SKIPE LOGHSY ;[125] Expecting system to go down?
CALL HSYINT ;[125] (/) Yes, call routine to do the work
DEBRK% ;[125] Return from interrupt
HSYINT: SAVEAC <T1,T2,T3,T4,Q1,Q2,Q3> ;[125] Save all of the suspect ACs
TXNE F,FL%NOI ;[125] Are we NOINT?
TXOA F,FL%NLF ;[125] Yes, remember to do log file stuff later
CALL NEWLOG ;[125] (/) A new broom sweeps the best
RET ;[125] Return from interrupt
SUBTTL Logging Routines -- Initialization of Log File
;Routine to get a JFN on a new log file.
;Returns +1 always.
NEWLOG: CALL CLOLOG ;(/) First try and close any old file
CALL NAMLOG ;[124] (/) Get a new log file name
;[107] Open a JFN up and then get the resulting filename for later use.
MOVX T1,GJ%SHT!GJ%FOU ;Load new log file bits
HRROI T2,LOGFNA ;[124] Point to log file spec
GTJFN% ;Try getting new file JFN
JSERRO (<Can't get JFN for log file>,<SETZ T1,>) ;Owie, return T1/ 0
MOVEM T1,LOGJFN ;[107] Save the JFN (zero if none)
JUMPE T1,R ;[107] Return now if no log file JFN
MOVE T2,T1 ;[110] Copy JFN of the log file
HRROI T1,LOGFNA ;[110] Point to log file area
MOVX T3,JS%SPC ;[110] We want the entire filespec por favor
JFNS% ;[110] Get the filename we are using
ERNOP. ;[110] Ignore errors for now
;[107] Open up the log file, set pointers to it, write header, and return.
CALL OPNLOG ;[107] (/) Open up that log file now
CALL NPTLOG ;[107] (/) Set up new log file cache pointers
CALL SECLOG ;(T1/T1) Make the log file secure
SETZM LOGLIN ;We have written no lines to this file
SETZM LOGPAG ;We have written no pages to this file
CALLRET HDRLOG ;[107] (/) Get a new header on the log file
;Local routine for NEWLOG to make a log file secure, call with T1/ JFN
;Returns +1 always.
SECLOG: SKIPN T1,LOGJFN ;[124] Reload the JFN of the log file
RET ;[124] Log file not open now
TXO T1,CF%NUD!FLD(.FBCTL,CF%DSP) ;Load the FBCTL word
MOVX T2,FB%SEC ;The secure bit mask
MOVX T3,FB%SEC ;We want the secure bit to be set
CHFDB% ;Set the file secure
ERNOP. ;Don't care about errors
RET ;Return +1 always
;[124] Local routine for NEWLOG to create a new log file name.
;The log filename is made by by moving the LOGFIL string into LOGFNA,
;replacing the character "*" with a string to output the time.
;Returns +1 always, LOGFNA set up.
NAMLOG: SETZM LOGFTI ;[124] Be sure to get a fresh time if needed
MOVE T4,[POINT 7,LOGFIL] ;[124] Point to the log file name
MOVE T1,[POINT 7,LOGFNA] ;[124] Point to place to build log file spec
DO. ;[124] Loop for all characters in the filename
ILDB T3,T4 ;[124] Load a character for the log file
CAIE T3,"*" ;[124] Is it the wild thing?
IFSKP. ;[124] Yes, substitute the time
SKIPN LOGFTI ;[124] Any time string set up?
CALL FNTLOG ;[124] (/) Make the time string
HRROI T2,LOGFTI ;[124] Point to formatted time
CALL ISOUT ;[124] (T1,T2/T1) Store the time
ELSE. ;[124] Otherwise it was not wild
IDPB T3,T1 ;[124] Not wild, store that character
ENDIF. ;[124] End of wild/nonwild check
JUMPN T3,TOP. ;[124] Loop until input null seen
OD. ;[124] End of loop for characters
RET ;[124] Return to caller with LOGFNA set up
;[124] Local routine called only from NAMLOG.
;Call to create time string "year-month-day-hours-minutes-seconds" in LOGFTI.
;Returns +1 always with LOGFTI filled.
FNTLOG: SAVEAC <T1,T2,T3,T4,Q1,Q2,Q3> ;[124] Save the Ts and Qs
SETO T2, ;[124] Load time of now
SETZ T4, ;[124] No particular options
ODCNV% ;[124] Get seperate numbers for time and so on
ERJMP R ;[124] This will not fail today
HRRZ Q3,T2 ;[124] Copy month to Q3
HLRZ Q2,T3 ;[124] Copy day to Q2
HRRZ Q1,T4 ;[124] Copy seconds since midnight to Q1
HRROI T1,LOGFTI ;[124] Point to place to build the time
HLRZ T2,T2 ;[124] Load the year
CALL ODEC ;[124] (T1,T2/T1) Send the year first
HRRZ T2,Q3 ;[124] Load numeric month
CALL FNTLON ;[124] (T1,T2/T1) Send hyphen and month
HRRZ T2,Q2 ;[124] Load the numeric day
CALL FNTLON ;[124] (T1,T2/T1) Send hyphen and day
IDIVI Q1,^D60*^D60 ;[124] Get hours in Q1,
IDIVI Q2,^D60 ;[124] minutes in Q2, seconds in Q3
MOVE T2,Q1 ;[124] Load hours
CALL FNTLON ;[124] (T1,T2/T1) Send hyphen and hours
MOVE T2,Q2 ;[124] Load the minutes
CALL FNTLON ;[124] (T1,T2/T1) Send hyphen and minutes
MOVE T2,Q3 ;[124] Load the seconds
CALL FNTLON ;[124] (T1,T2/T1) Send hyphen and seconds
MOVEI T2,0 ;[124] Load a null
IDPB T2,T1 ;[124] and store it there
RET ;[124] Return
;[124] Local routine called only from FNTLOG.
;Stores a hyphen followed by two ASCII digits from a binary number in T2.
;Returns +1 always.
FNTLON: MOVEI T3,"-" ;[124] Load hyphen
IDPB T3,T1 ;[124] store hyphen first
IDIVI T2,^D10 ;[124] Get the two digits
ADDI T2,"0" ;[124] Convert tens digit to ASCIZ
ADDI T3,"0" ;[124] And the ones digit to ASCIZ
IDPB T2,T1 ;[124] Store the tens digit
IDPB T3,T1 ;[124] and the ones digit
RET ;[124] Return to caller
SUBTTL Logging Routines -- Send Text to Log File
;Routine to send text to the log file, used for all logging of GETOK functions.
;Call with T1/ pointer to ASCIZ text.
;Returns +1 always, string written
SENLOG: SAVEAC Q1 ;Save an AC
MOVEM T1,Q1 ;Save the pointer to the string please
MOVE T1,LOGLIN ;[120] Skip if no lines printed on this page
CAIL T1,PAGLEN ;[120] Lines sent, over maximum number on page?
CALL HDRLOG ;[120] (/) Send header to log file
MOVE T1,Q1 ;[107] Restore pointer to string
CALLRET SOULOG ;[107] (T2/) Write that string to the log file
;[116] Here to write header and statistic information to the log file.
;Returns +1 always
HDRLOG: HRROI T1,HEADBU ;[116] Point to the header buffer
CALL NPGLOG ;[116] (T1/T1) Output first line of header
CALL STALOG ;[116] (T1/T1) Output second through nth lines
CALL OCRLF ;[116] (T1/T1) Repeat myself
HRROI T1,HEADBU ;[116] Point to text buffer
CALLRET SOULOG ;[116] (T1/) Send all of that to log file
;[120] Here to write summary information to the log file.
;Returns +1 always.
SUMLOG: MOVE T1,LOGLIN ;[120] Load lines printed
CAILE T1,PAGLEN-5 ;[120] Is there enough room on this page?
CALLRET HDRLOG ;[120] (/) Do a new header instead
HRROI T1,HEADBU ;[120] Point to the header buffer
CALL OCRLF ;[120] (/) First we want a CRLF output
CALL STALOG ;[120] (T1/T1) Output second through nth lines
HRROI T1,HEADBU ;[120] Point to text buffer
CALLRET SOULOG ;[120] (T1/) Send all of that to log file
SUBTTL Logging Routines -- Log File Cached Write
;[107] Routine to set up for new log file buffer.
;Uses CX so that no real ACs are damaged.
;Returns +1 always with pointers and counts reset.
NPTLOG: MOVEI CX,<<1+LOGBLP-LOGBFP>*5*1000>-1 ;[107] Load number of characters
MOVEM CX,LOGCNT ;[107] and save it here
MOVE CX,[POINT 7,LOGBUF] ;[107] Point to the log buffer
MOVEM CX,LOGPTR ;[107] Save that there
SETZM LOGBUF ;[107] Insure first word zero for checks
RET ;[107] Return always
;[107] Routine to do a SOUT to the log file.
;Note: this routine is called ONLY from SENLOG, HDRLOG, and SUMLOG.
;Call with T1/ pointer to ASCIZ text.
;Returns +1 always, string written (to cache or real file).
SOULOG: TLC T1,-1 ;[107] Complement left half
TLCN T1,-1 ;[107] Was the left half -1?
HRLI T1,(Point 7) ;[107] Yes, make it a byte pointer
DO. ;[107] Loop for entire string
ILDB T2,T1 ;[107] Load a character from the file
CAIN T2,.CHLFD ;[116] Is it a line feed?
AOS LOGLIN ;[116] Count this line as sent to log
JUMPE T2,ENDLP. ;[107] Get out if a null seen
CALL CHRLOG ;[107] (T2/) Send character to log file
LOOP. ;[107] Loop for all of them
OD. ;[107] End of copy loop
MOVE T1,LOGPTR ;[107] Load the pointer
IDPB T2,T1 ;[107] Store null to bind off string
RET ;[107] and return
;[107] Routine to store a log file character in log file cache buffer.
;Call with T2/ character.
;Returns +1 always.
CHRLOG: SOSLE LOGCNT ;[107] Is there space for that character?
IFSKP. ;[107] Nope
SETZ CX, ;[107] Load a null
IDPB CX,LOGPTR ;[107] and bind off the end of the buffer
CALL SWPLOG ;[107] (/) Sweep the log file out to disk
TXO F,FL%SLF ;[107] Perform another sweep later please
CALLRET CHRLOG ;[107] (/) Now output the character and return
ENDIF. ;[107] End of no more room code
IDPB T2,LOGPTR ;[107] Save the character in buffer
RET ;[107] and return to caller
SUBTTL Logging Routines -- Log File Cache Sweep Interrupts
;[107] Here on a interrupt to log file sweep every LOGINT seconds.
;Returns +1 always with log file cache swept.
INTLFF: CALL LFFHAN ;[107] (/) Handle the interrupt
DEBRK% ;[107] Dismiss from interrupt
LFFHAN: SAVEAC <T1,T2,T3,T4,Q1,Q2,Q3> ;[107] Save all of the suspect ACs
TXNE F,FL%NOI ;[107] Are we NOINT?
TXOA F,FL%SLF ;[107] Yes, remember to do log file stuff later
CALL SWPLOG ;[107] (/) Sweep out the log file cache please
; CALLRET INILFF ;[107] (/) Get back here again in LOGINT secs
;[107] Here to set the timer interrupt that goes off to sweep log file cache.
;Returns +1 always, T1-T4 smashed.
INILFF: MOVX T1,<.FHSLF,,.TIMEL> ;[107] Set interrupt at elapsed time
SKIPG T2,LOGINT ;[113] Load interval for log file updates
RET ;[113] If zero cache is disabled
IMULI T2,^D1000 ;[107] and convert it to milliseconds
MOVEI T3,LFCHAN ;[107] Load channel number to interrupt on
TIMER% ;[107] Set the interrupt
JSERRO (<Failed to set log file timer>) ;[107] Too many timer blocks?
RET ;[107] Just return +1 no matter what happens
SUBTTL Logging Routines -- Log File Cache Sweep
;[107] Routine to sweep contents of log file to disk.
;Returns +1 always with LOGBUF copied to log file.
SWPLOG: SKIPE LOGBUF ;[107] Is there anything to write or
SKIPN LOGJFN ;[107] any JFN on the log file?
RET ;[107] Nope, get out now
SAVEAC <T1,T2,T3,T4> ;[107] Save the temps as they are really needed
HRROI T2,LOGBUF ;[107] Point to the log buffer
SETZB T3,T4 ;[107] Terminate on a null
SKIPE T1,LOGJFN ;[107] Load the JFN for the log file
SOUT% ;[107] Send that text to the log file
IFJER. ;[107] If there was a problem
OJSERR (<Error writing to log file>) ;[107] Mumble about the error
HRROI T1,LOGBUF ;[107] Reload pointer to string
PSOUT% ;[107] Send to console terminal
ENDIF. ;[107] End of log file write problem code
CALL NPTLOG ;[107] (/) Set up new pointers and so on
; CALLRET CKPLOG ;[107] (/) Try closing and reopening log file
SUBTTL Logging Routines -- Open/Close/Checkpoint Log File
;[107] Routine to checkpoint the log file.
;Called only from SWPLOG, closes and then reopens the log file.
;Returns +1 always
CKPLOG: SKIPN T1,LOGJFN ;Get JFN, skip if there is one
CALLRET NEWLOG ;[107] (/) None there, try for a new one
TXO T1,CO%NRJ ;Keep the JFN but close the file
CLOSF% ;Yes, close the log file
ERCAL NEWLOG ;(/T1) Oh no! Holy OFN, something smells bad
; CALLRET OPNLOG ;[107] (/) Reopen the log file
;[107] Routine to open the log file.
;Returns +1 always.
OPNLOG: SKIPN T1,LOGJFN ;[107] Skip if a log file JFN assigned today
RET ;[107] No log file to open
MOVX T2,FLD(7,OF%BSZ)!OF%APP ;[107] 7 bit append mode
OPENF% ;[107] Open the jfn for append
JSERRO (<Can't open log file>,<CALL CLOLOX>) ;[107] Report error
RET ;[107] Return
;Here to close the log file JFN.
;Returns +1 always with log file closed.
CLOLOG: SKIPN LOGJFN ;[120] Anything to do?
RET ;[120] Nope, return now
CALL SUMLOG ;[120] (/) Send summary to log file
CALL SWPLOG ;[107] (/) Sweep log file cache
CLOLOX: SKIPE T1,LOGJFN ;[107] Load the log file JFN, skip if none
CLOSF% ;Shake rattle and roll
ERCAL RLSLOG ;Try to release the JFN if error
SETZM LOGJFN ;No longer a JFN to worry about
CALLRET NPTLOG ;[110] (/) Don't worry be happy
RLSLOG: MOVE T1,LOGJFN ;Load the JFN again
RLJFN% ;Release it today
ERNOP. ;Really don't care if this fails
RET ;In any case we tried to dump the JFN
SUBTTL Logging Routines -- New Page for Log File
;[116] Local routine to output first line of new page header.
;Call with T1/ output pointer
;Returns+1 always, T1/ updated pointer
NPGLOG: SETZM LOGLIN ;[116] Clear number of lines this page
HRROI T2,[BYTE(7).CHFFD,.CHCRT,.CHLFD,.CHTAB,0] ;Form feed crlf tab
CALL ISOUT ;(T1,T2/T1) Start with that
HRROI T2,[VERSIO] ;Point to my name and version
CALL ISOUT ;(T1,T2/T1) Send that out
HRROI T2,[ASCIZ/ on /] ;Label the node
CALL ISOUT ;(T1,T2/T1) Append that text next
HRROI T2,OURNAM ;Point to this node's name
CALL ISOUT ;(T1,T2/T1) Append that text next
HRROI T2,[ASCIZ/, /] ;Load the little seperator
CALL ISOUT ;(T1,T2/T1) Append that text next
SETOB T2,T3 ;The time is now, long format please
CALL OODTI1 ;(T1,T2,T3/T1) Do the ODTIM JSYS
AOS T3,LOGPAG ;Get the page number we are writing today
HRROI T2,[ASCIZ/, page /] ;Label for page number
CALL OLDEC ;(T1,T2,T3/T1) Output that label and number
CALLRET OCRLF ;[116] (T1/T1) Output a crlf and return
SUBTTL Logging Routines -- Statistics Logging
;[116] Local routine to output statistics, second through nth header lines.
;Call with T1/ output pointer
;Returns+1 always, T1/ updated pointer
STALOG: SKIPN NALLOW ;[116] [111] Any allowed?
SKIPE NDENY ;[111] Any denied?
IFNSK. ;[111] Yes to either
HRROI T2,[ASCIZ/ Allowed /] ;Space out for rest of text
CALL ISOUT ;Append that label
MOVE T2,NALLOW ;Load number allowed
HRROI T3,[ASCIZ/ request/] ;Label number
CALL OPLURA ;(T1,T2,T3/T1) Output number and label
HRROI T2,[ASCIZ/, denied /] ;Label for next number
CALL ISOUT ;Append that label
MOVE T2,NDENY ;Load number allowed
HRROI T3,[ASCIZ/ request/] ;Label number
CALL OPLURA ;(T1,T2,T3/T1) Output number and label
HRROI T2,[ASCIZ/, /] ;Label for next number
CALL ISOUT ;Append that label
MOVE T2,NFAIL ;Load number failed
HRROI T3,[ASCIZ/ request/] ;Label number
CALL OPLURA ;(T1,T2,T3/T1) Output number and label
HRROI T2,[ASCIZ/ failed
/] ;[111]
CALL ISOUT ;[111] Append that label
ENDIF. ;[111] That's all of the allow deny code
;[111] Output access control hit, misses, and ratio.
SKIPN NMISS ;[111] We will always have at least 1 miss
IFSKP. ;[111] Yes
MOVE T3,NHIT ;[113] Load (100*hits/hits+misses)
IMULI T3,^D100 ;[113] Make into percentage (times millisecs)
MOVE T2,NMISS ;[113] Load misses
ADD T2,NHIT ;[113] Count hits
IDIV T3,T2 ;[113] Compute percentage
HRROI T2,[ASCIZ/ Cache /] ;[113] What hit percentage
CALL OLDEC ;[113] (T1,T2/T1) Send hit percentage
HRROI T2,[ASCIZ/%, hit /] ;[113] Seperate hits
MOVE T3,NHIT ;[111] Load hits
CALL OLDEC ;[113] (T1,T2/T1) Send hits
HRROI T2,[ASCIZ/, missed /] ;[111] Seperate misses
MOVE T3,NMISS ;[111] Load misses
CALL OLDEC ;[111] (T1,T2/T1) Send misses along
HRROI T2,[ASCIZ/, flushed /] ;[113] Seperate flishes
MOVE T3,NFLUSH ;[113] Load flushes
CALL OLDEC ;[113] (T1,T2/T1) Send flushes
CALL OCRLF ;[111] (T1/T1) and a crlf
ENDIF. ;[111] End of access control cache display
;Get and save elapsed runtime and connect time.
SAVEAC <Q1,Q2> ;Save a couple of ACs today
MOVEM T1,Q2 ;[113] Save current pointer to text string
MOVEI T1,.HPRNT ;Get our runtime
HPTIM% ;In high precsion units
JSERRO (<HPTIM failure to get runtime>,<SETZ T1,>) ;Big owie
SKIPN RUNTIM ;Is there any runtime set?
MOVEM T1,RUNTIM ;Nope, set it now
MOVE Q1,T1 ;Copy that to T1
SUB Q1,RUNTIM ;Get our elapsed runtime
TIME% ;Get system uptime in milliseconds
SKIPN PEOPLE ;Any elapsed people time
MOVEM T1,PEOPLE ;Nope, store initial value
EXCH Q2,T1 ;[113] Swap uptime for saved pointer
SUB Q2,PEOPLE ;Get our elapsed people time
IFN. Q2 ;If there has been elapsed connect time
HRROI T2,[ASCIZ/ Used /] ;[111] Label for next number
CALL ISOUT ;Append that label
MOVE T2,Q1 ;Load run time
CALL OTIMEH ;(T1,T2/T1) Output time in HPTIM units
HRROI T2,[ASCIZ/ in /] ;In
CALL ISOUT ;Append that tiny string
MOVE T2,Q2 ;Load people time in milliseconds
CALL OTIME ;(T1,T2/T1) Output elapsed people time
CALL OCRLF ;(T1/T1) Output a CRLF
ELSE. ;Otherwise we are running for first time
HRROI T2,[ASCIZ/ ACJ restart
/] ;Load the pointer to message
CALL ISOUT ;(T1,T2/T1) Send that along please
ENDIF. ;[111] Done with logging time
RET ;[116] Done
SUBTTL Logging Routines -- Start Logging a Request
;Here to start out the log file text which is kept in TEXTBU until sending it
;to the log file. This routine should be called with
; JIBLK/ info about job in question
; USRSTR/ username string for the request
; T2/ pointer to function description string
;Returns +1 always, TEXTBP and T1/ pointer to log buffer
LOGSTA: SAVEAC <Q1> ;Place to keep text pointer
HRROI T1,TEXTBU ;Point to text buffer first of all
MOVEM T2,Q1 ;Save the pointer to text
SETO T2, ;Want current time
MOVX T3,OT%NDA ;No date
CALL OODTI1 ;(T1,T2,T3/T1) Do the ODTIM JSYS
CALL OSPACE ;(T1/T1) Append in a single space
HRROI T2,USRSTR ;Point to the username in question
CALL ISOUT ;(T1,T2/T1) Send username string to log file
CALL OSPACE ;(T1/T1) Append in a single space
MOVE T2,Q1 ;Load pointer to entry type name
CALL ISOUT ;(T1,T2/T1) Send name to log file
MOVEI T4,JIBLK ;Point to the GETJI block we made for this job
CALL OGETJ1 ;(T1,T4/T1) Output non-username job information
HRRZ T4,ARGBLK+.RCCAP ;Get capability mask enabled
SKIPE JIBLK+.JIJNO ;Is it job 0?
CALL OCAPAB ;(T1,T4/T1) Asciify caps if not job 0
MOVEM T1,TEXTBP ;Store the byte pointer for later
RET ;Return +1 with T1/ pointer
SUBTTL Spy on Intruder
;In order to spy on intruders (an excellent technique to discover unwanted
;tourists) the SPYON routine was stolen from another ACJ. This intruder spy
;facility puts the user's output to a file.
;For each intruder there is a fork in a BIN%/BIN% loop between a PTY TLINKed to
;the intruder's terminal and the suspected intruder's job. The fork runs in
;ACJ's address space so that it can be started quickly, and is started sharing
;adress space with the top level ACJ fork. Each fork has its own acs of course
;but also its own private stack. The private fork's stack and the ACs are the
;only storage the subfork writes into. Every 10 seconds it takes a TIMER
;interrupt to see if the job has moved to another terminal, logged out, or
;typed BREAK (it always redoes the spy link in case the user typed BREAK).
;After the spy fork halts, the superior fork notices on a fork termination
;interrupt and kills any spy forks that smell bad (halt or error halt status).
;AC usage in the spy on intruder fork startup code and in the spy fork:
; P1/ User number
; P2/ Job number
; P3/ Log file JFN
; P4/ PTY designator
; P5/ PTY's TTY I/O designator
;Define a little macro to catch spy fork errors and punt the spying session.
;This is done because the fork by design does not touch any memory that the
;superior ACJ main fork might be using.
DEFINE ERSPY(TEXT),<
ERJMP [ JSP T1,SPYERR
ASCIZ/ ACJ Spy: TEXT/]
>
DEFINE SPYER(TEXT),<
JRST [ JSP T1,SPYERR
ASCIZ/ ACJ Spy: TEXT/]
>
SUBTTL Spy on Intruder -- Start a Spy Fork
;Here to start spying on a suspected attacker.
;Call with
; T1/ user number to spy on
; T2/ job number to spy on
;Returns +1 Already logging or can't log
;Returns +2 Spy fork started
;AC usage in this routine, all of which are passed to inferior fork:
; Q1/ inferior fork's stack pointer
; Q2/ inferior fork's offset in SPYxxx tables
; P1/ User number
; P2/ Job number
; P3/ Log file JFN
; P4/ PTY designator
; P5/ PTY's TTY I/O designator
SPYON: SAVEAC <P1,P2,P3,P4,P5,Q1,Q2> ;Save the perms
DMOVE P1,T1 ;Copy user number to P1, job number to P2
SETZ Q2, ;Indicate no free slot found yet
MOVSI T4,-NSPYS ;Get number of forks we can have
DO. ;Search job/user table
CAME P2,SPYJOB(T4) ;Does the job match?
IFSKP. ;Yes
CAMN P1,SPYUSR(T4) ;Does the user match?
RET ;Yes, just return +1 for now
EXIT. ;Same job, not same user, start another spy
ENDIF. ;Job didn't match
SKIPE SPYFRK(T4) ;Is this slot used?
IFSKP. ;No, we have found a free slot then
SKIPN Q2 ;Do we have a free slot yet?
MOVE Q2,T4 ;Nope, we do now though
ENDIF. ;End of free slot code
AOBJN T4,TOP. ;Loop for all possible forks we are running
OD. ;End of search loop
JUMPE Q2,R ;Return if no fork slot available in table
;Create stack pointer for inferior fork.
MOVEI Q1,(Q2) ;Load which spy fork this is
IMULI Q1,SPLEN ;Point to proper stack start address
ADDI Q1,SPYPDL ;Make offset into address of stack
HRLI Q1,-SPLEN ;Make -n,,0
;Open up the spy log file.
CALL SPYGTJ ;(Q1,P2/P3) Get JFN on spy file
JRST SPYPNT ;(Q2/) Punt if error
HRLM P3,SPYJFN(Q2) ;Save log file JFN
;Get a PTY and remember which one.
CALL GETPTY ;(/T1,T2) Try to get a PTY
JRST SPYPNT ;(Q2/) Couldn't, punt JFN and return
DMOVE P4,T1 ;Save PTY JFN and TTY designator for fork
HRRM T1,SPYJFN(Q2) ;Save PTY JFN here too
;Create fork with our caps, our ACs, our map, starting it at SPYSTA.
MOVX T1,CR%CAP!CR%ACS!CR%MAP!CR%ST!FLD(SPYSTA,CR%PCV) ;All implemented
SETZ T2, ;Indicate that fork should get copy of our ACs
CFORK% ;Create a fork
IFJER. ;If error
CAIE T1,CFRKX3 ;No more forks?
OJSERR (<Can't create fork>,,SPYPNT) ;(Q2/) No, some other error
JRST SPYPNT ;(Q2/) Clean up and return
ENDIF. ;End of CFORK error recovery
;We have a bouncing baby fork, set up the SPYxxx tables for later use.
HRRZM T1,SPYFRK(Q2) ;Remember fork handle
MOVEM P1,SPYUSR(Q2) ;Save user number,
MOVEM P2,SPYJOB(Q2) ; and job that the fork is spying
RETSKP ;Spying fork running now
SUBTTL Spy on Intruder -- Kill Spy Fork
;Here to kill off the spy fork.
;Call SPYKIL with T4/ spy fork table offset
;Call SPYPNT with Q2/ spy fork table offset
;Returns +1 always fork is gone, PTY JFN released, log file closed
SPYPNT: MOVEI T4,(Q2) ;Load offset to tables
SPYKIL: MOVE T1,SPYFRK(T4) ;Load the fork handle again
KFORK% ;Kill it off now
ERNOP. ;Well at least I tried
CALL SPYTRL ;(T4/) Perform finishing functions
HLRZ T1,SPYJFN(T4) ;Load spy file JFN
SKIPE T1 ;Was there a JFN?
CLOSF% ;Yes, close it
ERNOP. ;Well, not much we can do at this point
HRRZ T1,SPYJFN(T4) ;Load PTY JFN
SKIPE T1 ;Was there a JFN?
CLOSF% ;Close that too
ERNOP. ;Crunch?
SETZM SPYFRK(T4) ;Clear table entry for fork
SETZM SPYJOB(T4) ;Clear job being spyed on
SETZM SPYUSR(T4) ;Clear user being spyed on
SETZM SPYJFN(T4) ;Clear JFN words
RET ;Return with all cleaned up
SUBTTL Spy on Intruder -- Start a Spy Fork -- Get a Spy File
;Here to get a JFN on the spy fork log file.
;Call with P2/ user number
;Returns +1 if error
;Returns +2 with spy log file open and header written, P3/ JFN
;First construct file name from "predefinedstring-OURNAM.USERNAME"
SPYGTJ: HRROI T1,SPYFIL ;Point to destination area
HRROI T2,SPYSLD ;Load initial text of filespec
CALL ISOUT ;(T1,T2/T1) Copy that string
HRROI T2,[ASCIZ/-/] ;Delimit the string
CALL ISOUT ;(T1,T2/T1) Send the delimiter
HRROI T2,OURNAM ;Point to our node name string
CALL ISOUT ;(T1,T2/T1) Append node name to filespec
HRROI T2,[ASCIZ/./] ;Point to a seperator character
CALL ISOUT ;(T1,T2/T1) Append seperator to filespec
MOVE T2,P1 ;Get user number back
CALL ODIRST ;(T1,T2/T1) Change it to up to 39 characters
CALL ISOUT ;(T1,T2/T1) Append that string in there
;Get a JFN on the file.
MOVX T1,GJ%FOU!GJ%SHT ;Load these flags
HRROI T2,SPYFIL ;Use this string
GTJFN% ;Get JFN for spying
JSERRO (<Unable to get JFN for spy file>,,SPYLFD)
MOVEI P3,(T1) ;Copy JFN for handing down to fork
TXO T1,CF%NUD!FLD(.FBCTL,CF%DSP) ;Load the FBCTL word, don't update
MOVX T2,FB%INV!FB%SEC ;Load the invisible and secure bits for mask
MOVX T3,FB%INV!FB%SEC ;Load the invisible and secure bits to set
CHFDB% ;Change file descriptor block
JSERRO (<Can't make spy file secure and invisible>,<CALL SPYLFD>)
MOVEI T1,(P3) ;Reload T1 incase previous CHFDB fails
TXO T1,CF%NUD!FLD(.FBBYV,CF%DSP) ;Load the FBBYV word and no updates
HRLI T1,.FBBYV ;We want to keep infinite generations
MOVX T2,FB%RET ;Load set retention bit
SETZ T3, ;Keep 0 generations
CHFDB% ;Change file descriptor block
JSERRO (<Can't make spy file generation retention count 0>,<CALL SPYLFD>)
CALL SPYOPN ;(P3/) Open up the file
OJSERR (<Can't open spy file>,,SPYLFD) ;Punt if errors
;Send header to spy file and then return.
CALL SPYHDR ;(P3/) Construct header and send it to file
CALL SPYCLS ;(P3/) Close spy file
OJSERR (<Can't close spy file>,,SPYLFD)
CALL SPYOPN ;(P3/) Reopen the file
OJSERR (<Can't reopen spy file>,,SPYLFD) ;Owie!
RETSKP ;Return OK
;Small local routine to append spy filename into log file.
;Called only from SPYGTJ because otherwise SPYFIL isn't set up.
;Returns +1 always with filename string sent to log file.
SPYLFD: TXO F,FL%ERR ;Indicate we are in error processing
HRROI T1,ERRBUF ;Point to error string buffer
HRROI T2,[ASCIZ/ File /] ;Space over four
CALL ISOUT ;(T1,T2/T1) Send some space along
HRROI T2,SPYFIL ;Point to the spy filename
CALL ISOUT ;(T1,T2/T1) Send that along
CALL OCRLF ;(T1/T1) and a crlf
HRROI T1,ERRBUF ;Point to error buffer again
CALL SENLOG ;(T1/) Send that to the usual log file
TXZ F,FL%ERR ;No longer in error processing
RET ;Return +1
SUBTTL Spy on Intruder -- Start a Spy Fork -- Get a PTY
;Called to get a PTY assigned to spy on intruder.
;Returns +1 always, T1/ PTY JFN, T2/ TTY designator for that PTY
GETPTY: MOVE T4,MAXPTY ;Load maximum number of PTYs on system
IMUL T4,[XWD -1,0] ;Make -n,,0
GETPT1: MOVSI T1,.DVDES!.DVPTY ;Load PTY designator value
HRRI T1,(T4) ;Make designator for particular PTY
DVCHR% ;Get characteristics of the device
ERJMP GETPT2 ;Owie
TXNN T2,DV%AV ;Is this PTY available?
JRST GETPT2 ;Nope, try next one
MOVE T2,T1 ;Load device designator
HRROI T1,SPYFIL ;Point to tempoary area
DEVST% ;Now convert the device to a string
JRST GETPT2 ;Bad, well try the next one
HRROI T2,[ASCIZ/:/] ;Point to the appropriate puctuation
CALL ISOUT ;(T1,T2/T1) Append that and a null
MOVX T1,GJ%SHT ;Load short form bit
HRROI T2,SPYFIL ;Point to string we just built
GTJFN% ;Now try to get a JFN on that PTY
ERJMP GETPT2 ;Well, try the next one then
MOVEI T3,(T1) ;Save JFN here for now
MOVX T2,FLD(7,OF%BSZ)!OF%RD!OF%WR ;Load the open bits
OPENF% ;Open the PTY for read/write
ERJMP GETPT3 ;This is got to be a problem
HRRZ T2,T4 ;Load the PTY unit number
ADD T2,TTYPTY ;Convert PTY unit to TTY unit
TXO T2,.TTDES ; and then make it a device designator
RETSKP ;Successful return with T1, T2 set up
;Here if some problem getting the PTY, release JFN and try again.
GETPT3: MOVEI T1,(T3) ;Get the JFN back
RLJFN% ;Give up the JFN
ERNOP. ;Ignore error here
;Here to try next PTY unit number.
GETPT2: AOBJN T4,GETPT1 ;Try next PTY unit
RET ;Out of units, return failure
SUBTTL Spy on Intruder -- Spy Fork -- Initialization
;This is the start of the fork that spys on intruders. It runs in ACJ's
;address space so that it can be started quickly.
;DANGER Will Robinson: the code running in the inferior fork cannot use any
;storage other than its ACs and stack! All private storage other than the ACs
;must be on the stack or in the big TRVAR at SPYSTA.
;AC usage:
; Q1/ Initial stack pointer
; P1/ User number
; P2/ Job number
; P3/ Spy file JFN
; P4/ PTY designator
; P5/ TTY designator (TTY end of PTY)
SPYSTA: MOVE P,Q1 ;Set up the stack for this fork
TRVAR <<SPYBUF,^D200/5>,<FRKLEV,3>,FRK3PC>
MOVEI T1,.FHSLF ;Load my own fork handle
MOVX T2,JP%SYS ;We are a system fork
SPRIW% ;Do this so we are fast and elude detection
ERNOP. ;Well, at least we tried today
MOVEI T2,FRK3PC ;Load level 3 PC return address
MOVEM T2,2+FRKLEV ;Save as return for level 3
SETZM 0+FRKLEV ;Clear level 1 PC return
SETZM 1+FRKLEV ;Clear level 1 PC return
HRLI T2,FRKLEV ;Load that address
HRRI T2,CHNTAB ;Make LEVTAB,,CHNTAB
SIR% ;Setup our interrupt tables
MOVX T2,1B<TICHAN> ;Load the bit for the timer channel
AIC% ;Activate interrupt channel
EIR% ;Enable interrupt system
CALL INITIM ;(/) Start timer interrupt
CALL SETSPY ;(P1,P2,P5/) Setup spy link
SPYER (<Cannot set up link to user>)
; JRST SPYLOP ;Start up main loop
SUBTTL Spy on Intruder -- Spy Fork -- Main Loop
;This is the loop that takes all of the output of the user's session to spy it.
SPYLOP: MOVE T1,P4 ;Load PTY JFN
BIN% ;Get a byte from the PTY into T2
ERNOP. ;Ignore error for the present time
MOVE T1,P3 ;Load the spy file JFN
BOUT% ;Put the character into the spy file
ERJMP SPYEND ;Punt everything if error writing to file
TXO F,FL%SIO ;Indicate the characters have been sent
JRST SPYLOP ;Loop for more characters to spy
SUBTTL Spy on Intruder -- Spy Fork -- Spy File I/O Routines
;Here to open up the spy file, called with P3/ spy file JFN
;Returns +1 if error
;Returns +2 if success
SPYOPN: MOVE T1,P3 ;Get just the JFN again, this time, all right?
MOVX T2,FLD(7,OF%BSZ)!OF%APP ;Load 7 bit append mode please today sir
OPENF% ;Pry the spy file open
ERJMP R ;Return if error
RETSKP ;Skip return, ok sir, that'll be all sir
;Here to close up the spy file, called with P3/ spy file JFN
;Returns +1 if error
;Returns +2 if success
SPYCLS: MOVE T1,P3 ;Load the JFN again
TXO T1,CO%NRJ ;Close spy file to make it real
CLOSF% ;Crunch
ERJMP R ;Return +1 if error
RETSKP ;Skip return
;Here to send a string to the spy file.
;Call with T2/ pointer to text, P3/ JFN
;Returns +1 if error
;Returns +2 if OK
SENSPY: MOVE T1,P3 ;Load the JFN of the file
SETZB T3,T4 ;Clear this so terminate on null
SOUT% ;Send that to the file
ERJMP R ;Return +1 if error
RETSKP ;Return to caller
SUBTTL Spy on Intruder -- Spy Fork -- Error Recovery and Termination
;Here to output an error message to the spy file, cleanup and halt.
;Call with T1/ address of ASCIZ text
;Kills the fork.
SPYERR: PUSH P,T1 ;Save message string
HRROI T1,SPYBUF ;Point to spy buffer
HRROI T2,[ASCIZ/
[/] ;Point to CRLF bracket
CALL ISOUT ;(T1,T2/T1) Send that along first
CALL OODTIN ;(T1/T1) Current date time
POP P,T2 ;Now send the message
TLO T2,-1 ;Make -1,,address
CALL ISOUT ;(T1,T2/T1) Send message next
HRROI T2,[ASCIZ/, /] ;Send some white space
CALL ISOUT ;(T1,T2/T1) Make it look nice
CALL OERSTR ;(T1/T1) Send last JSYS error
HRROI T2,[ASCIZ/]
/] ;Send closing bracket
CALL ISOUT ;Append all of that together
HRROI T2,SPYBUF ;Point to buffer
CALL SENSPY ;(T2,P3/) Send all of that to the log file
SKIPA T1,SPYBUF ;Uh oh
JRST SPYEND ;Now end this spying session
PSOUT% ;Send to terminal if nothing else possible
;Here when we want to end this spying session. Halts the spy fork.
SPYEND: HALTF% ;Halt the spy fork
JRST .-1 ;What?
SUBTTL Spy on Intruder -- Spy Fork -- Timer Interrupts
;Here when the spy fork wants to do a periodic check to see if job has logged
;out, changed terminals (i.e. got detached, and re-attached), or maybe typed a
;BREAK.
TIMINT: CALL SPYTIM ;(/) Handle the interrupt please
DEBRK% ;Return from interrupt
;Routine to do the work of checking things out at timer interrupts.
SPYTIM: SAVEAC <T1,T2,T3,T4> ;Save the temps for a second
TXZN F,FL%SIO ;Spy file I/O happened?
IFSKP. ;Yes
CALL SPYCLS ;(P3/) Close spy file
JRST SPYABT ;Abort this session if error
CALL SPYOPN ;(P3/) Reopen the spy file
JRST SPYABT ;Abort this session if error
ENDIF. ;End of file checkpoint code
CALL SETSPY ;(P1,P2,P5/) Fix the spy link if needed
JRST SPYABT ;Abort if failure
CALLRET INITIM ;(/) Start timer again and return
;Here to abort the spy fork by changing the interrupt return PC to SPYEND.
SPYABT: MOVEI T1,SPYEND ;Victim is gone, done logging
MOVEM T1,FRK3PC ;Save the return PC as where to return to
RET ;Return to dismiss interrupt and so on
;Setup a timer interrupt for 10 seconds from now
INITIM: MOVE T1,[.FHSLF,,.TIMEL] ;Set interrupt this fork, elapsed time
MOVE T2,SPYINT ;Load interval for spying
IMULI T2,^D1000 ;Convert seconds to milliseconds
MOVX T3,TICHAN ;Load channel for timer interrupts
TIMER% ;Have the monitor do that for us
ERSPY (<TIMER% failed>);Punt this session
RET ;Return as it all went well
SUBTTL Spy on Intruder -- Spy Fork -- Setup Spy Link
;Routine to set up spy link between our PTY and the victim's job.
;Called with
; P1/ Victim's user number
; P2/ Victim's job number
; P5/ TTY designator for our PTY
;Returns +1 no such job
;Returns +2 spy link setup (unless detached)
SETSPY: MOVE T1,P2 ;Get victim's job number
MOVE T2,[-2,,Q1] ;Get TTY and user number
MOVEI T3,.JITNO ; from the monitor on the victim
GETJI% ;Get Job Information
IFJER. ;If that failed
CAIN T1,GTJIX4 ;Did she go away?
RET ;Yes, return +1
SPYER (<GETJI% failed>)
ENDIF. ;End of error
IFN. Q2 ;If there is a user logged in
CAME P1,Q2 ;Is it the user we expect?
RET ;Nope, return +1
ENDIF. ;User not logged in or is who we expect
MOVE T1,P5 ;Get TTY end of the PTY
TXO T1,TL%ERO ;Setup spy link
SKIPGE T2,Q1 ;Load terminal of user's job
RETSKP ;Don't try TLINK if detached
TXO T2,.TTDES ;Make terminal number into designator
TLINK% ;Make link from user terminal to our PTY
ERSPY (<TLINK% failed>) ;Owie!
RETSKP ;Give +2 return
ENDTV. ;End of fork's private storage
SUBTTL Spy on Intruder -- Spy File Header/Trailer
;Here to write header shortly after the file is opened as as file is closed.
;Call with Q1/ freespace pointer
;Returns +1 always.
SPYTRL: SAVEAC <Q1,Q2,Q3> ;Need more ACs today
HRROI Q1,[ASCIZ/ end spying/] ;Here for trailer
HLRZ Q2,SPYJFN(T4) ;Load JFN to Q2
SETZ Q3, ;No job information please
JRST SPYMSG ;Send the message
SPYHDR: SAVEAC <Q1,Q2,Q3> ;Need more ACs today again
HRROI Q1,[ASCIZ/ spying on/] ;Here for header
MOVE Q2,P3 ;Copy the JFN to Q2
MOVEI Q3,JIBLK ;Point to that job's GETJI block
;Here with Q1/ message pointer and Q2/ JFN
SPYMSG: MOVE T1,TEXTBU ;Get current text pointer area
HRROI T1,1(T1) ;Point to some free space
HRROI T2,[ASCIZ/
[/] ;Point to crlf bracket
CALL ISOUT ;(T1,T2/T1) Start off
CALL OODTIN ;(T1/T1) Send time and date
CALL OSPACE ;(T1/T1) and a space
HRROI T2,[VERSIO] ;Load our version text
CALL ISOUT ;(T1,T2/T1) Send that to the line
HRROI T2,[ASCIZ/ on /] ;Label next little part
CALL ISOUT ;(T1,T2/T1) Send that to the line
HRROI T2,OURNAM ;Point to our node name
CALL ISOUT ;(T1,T2/T1) Send that to the line
MOVE T2,Q1 ;Load the mumble string
CALL ISOUT ;(T1,T2/T1) Send that
SKIPE T4,Q3 ;Telling about a job?
CALL OGETJI ;(T1,T4/T1) Tell about this job
HRROI T2,[ASCIZ/]
/] ;Load ending text
CALL ISOUT ;(T1,T2/T1) Send that to the line last
MOVE T1,Q2 ;Load the JFN of the file
MOVE T2,TEXTBU ;Get current text pointer area
HRROI T2,1(T2) ;Point to some free space
SETZB T3,T4 ;Clear this so terminate on null
SOUT% ;Send that to the file, don't use SENSPY
ERNOP. ;Return +1 if error
RET ;Return +1 always as a matter of fact
SUBTTL Spy on Intruder -- Inferior Fork Termination Interrupt
;Come here on fork termination interrupt to check for dead forks.
FRKTRM: CALL SPYTRM ;(/) Call worker routine to do the work
DEBRK% ;Return from interrupt
;Routine to look for spy forks that are halted and kill them off.
;Returns +1 always
SPYTRM: SAVEAC <T1,T2,T3,T4> ;Save those ACs on the stack
MOVSI T4,-NSPYS ;Get number of forks in table
SPYTR1: SKIPN T1,SPYFRK(T4) ;Is there a fork handle for this fork?
JRST SPYTR6 ;Nope, check next one
RFSTS% ;Get fork's status
ERJMP SPYTR2 ;If error kill it
HLRZ T2,T1 ;Load the fork status code
CAIE T2,.RFHLT ;Halted
CAIN T2,.RFFPT ; or forced termination?
SPYTR2: CALL SPYKIL ;(T4/) Yes, kill that fork please
SPYTR6: AOBJN T4,SPYTR1 ;Loop for all of forks in table
RET ;Return to sender
SUBTTL Secure Files
;This routine is called to determine if the user has access to a particular
;file that is set secure. This routine is called with the following arguments
;The format of the ACCESS.CONTROL file is as follows:
; filename access-keyword user user, access-keyword user user, ...
;The first entry that matches the filename (as determined by WILD%) is used.
;Call with:
; Q1/ ARGBLK+.RCARA
; Q2/ SF.xxx for the type of desired access
;Returns +1 if access is not allowed
;Returns +2 if access is allowed (or access control file is not found)
;ACs used in these routines:
; P1/ free
; P2/ byte pointer to current line from that file
; P3/ last character read from that file
; P4/ CBX (cache block index)
SECFIL: SAVEAC <P1,P2,P3,P4> ;Save the perms for our use today
;First try and find the file ACCESS.CONTROL in the same directory as the file
;in question. If this file is not found or is damaged, then the all secure
;operations are allowed and are logged as unusual. Change the "CALLRET SETUNU"
;to "RET" if all secure operations are to be denied if the access control file
;is not found.
CALL SECOPN ;(Q1/Q1,P4) Open up ACCESS.CONTROL
CALLRET SETUNU ;Not there, allow access but log as unusual
;Try and locate the filename in the ACCESS.CONTROL file.
CALL SECFND ;(Q1,P4/P2,P3) Locate this filename
CALLRET SECCLS ;(P4/) Not found, close the file and return +1
;Check desired access against listed allowed access.
HRROI T1,SECUSR ;Point to username build block
MOVE T2,JIBLK+.JIUNO ;Load user number in question
CALL ODIRST ;(T1,T2/T1) Get username made into string
CALL SECACC ;(Q2,P2,P3/) See if user has proper access
CALLRET SECCLS ;(P4/) Nope, close file and return +1
;Access is allowed, close the file and skip return.
CALL SECCLS ;(P4/) Yes, close file
CALL SECACT ;(Q1,Q2/) See if special action is needed
RETSKP ;Return +1 indicating the access is allowed
SUBTTL Secure Files -- Find File's Entry
;Here to try to locate an entry for the file in question in the ACCESS.CONTROL
;file. We are called with SECFNA/ "file.type.gen" and P4/ CBX
;Returns +1 if entry not found
;Returns +2 if entry found, P2/ line pointer and P3/ last character read
SECFND: CALL SECSIN ;(P4/P2,P3) Read a line in from file
RET ;If EOF return +1 (not found)
;We have a line read in, eat white space. If comment line get another line.
CALL SECSPN ;(P2/P2,P3) Eat until first non blank character
JRST SECFND ;None on that line, get another line
CAIN P3,";" ;Comment character semicolon first nonblank?
JRST SECFND ;Yes, this was a comment, get next line please
;Line has been read in, get the first field in it which should be the filename.
CALL SECFLD ;(P2,P3/P2,P3) Read a field into the word area
RET ;If EOL before field read, return +1
;We have a field read in. See if the file in question matches this entry.
MOVX T1,.WLSTR ;Load wild string match function
HRROI T2,SECWRD ;Point to the (possibly wild) entry from file
HRROI T3,SECFNA ;Point to the "file.type.gen" requested by user
WILD% ;Get the monitor's help here
ERJMP SECFND ;Should never ITRAP, but ya never know
JUMPN T1,SECFND ;Continue looping if the string didn't match
;Filename matched! Check terminator of field, it must be space or tab.
CAIE P3,.CHTAB ;Was previous field terminator a tab
CAIN P3," " ; or was it a space?
RETSKP ;Return +2 since the entry matched
JRST SECFND ;Look some more if illegal terminator
SUBTTL Secure Files -- Check Desired Access
;Called here after the entry for this file has been found to check the
;desired access, with Q2/ access code, P2 and P3 set up from SECFND.
;Returns +1 if access not allowed (not found)
;Returns +2 if access is allowed (user found)
SECACC: CALL SECFLD ;(P2,P3/P2,P3) Read in a field
RET ;If end of line return +1
;See if the keyword read is something we recognize.
MOVEI T1,SECTBL ;Point to table of keywords
HRROI T2,SECWRD ;Point to the word to match please
TBLUK% ;Look it up in our keyword table
ERJMP SECAC2 ;Shouldn't ITRAP, but if error check next one
TXNE T2,TL%NOM!TL%AMB ;No match or ambig?
JRST SECAC2 ;Yes, eat until end of line or comma seen
HRRZ T2,(T1) ;Get the value for this keyword
TDNN Q2,T2 ;Match the desired access?
JRST SECAC2 ;Nope, keep looking
CAIE P3,.CHTAB ;Was previous field terminator a tab
CAIN P3," " ; or was it a space?
JRST SECAC5 ;Yes, we have a winner on this particular entry
;The keyword didn't match or wasn't the desired access keyword, eat until ",".
SECAC2: CALL SECCHR ;(P2/P2,P3) Get the next character
RET ;Return badly if end of line
CAIE P3,"," ;Looking for a comma
JRST SECAC2 ;Keep looking until comma or end of line seen
SECAC4: CALL SECCHR ;(P2/P2,P3) Get the character after the comma
RET ;End of line? Not here, not after all of this!
JRST SECACC ;Char after comma loaded, examine next field
;We have reached the desired access keyword. See if desired user is in list.
SECAC5: CALL SECFLD ;(P2,P3/P2,P3) Read the next field in
RET ;Owie if end of line
MOVX T1,.WLSTR ;Load wild string match function
HRROI T2,SECWRD ;Point to the (possibly wild) entry from file
HRROI T3,SECUSR ;Point to the username we are concerned about
WILD% ;Get the monitor's help here
ERSKP. ;Should never ITRAP, but ya never know
JUMPE T1,RSKP ;User can do this! Our work is done
CAIN P3,"," ;Was terminator a comma?
JRST SECAC4 ;Yes keep looking on this line
CAIE P3,.CHTAB ;Was previous field terminator a tab
CAIN P3," " ; or was it a space?
JRST SECAC5 ;OK terminator, continue looping through users
RET ;Return badly if illegal terminator
SUBTTL Secure Files -- Access Keywords
;This is a TBLUK style table of access keywords and access keyword values.
;It is used when reading the keywords after the filenames in ACCESS.CONTROL.
;Lines commented out are ideas for future functions. A priv scheme must be
;thought about before implementation. There is also the possibility that the
;ACJ could cause quota or blocking problems with these same ideas.
SECTBL: TBEGIN ;Plant the header here
TENTRY (ALL,SF.ALL) ;All access (app, del, rea, ren, sec, wri)
TENTRY (APPEND,SF.APP) ;Append access (OPENF)
; TENTRY (ANNOUNCE,SF.ANN) ;Send message to user list specified (priv)
; TENTRY (CONSOLE,SF.CON) ;TTMSG to CTY when file touched (priv)
TENTRY (DELETE,SF.DEL) ;Delete access (DELF)
; TENTRY (LOG,SF.LOG) ;Log access in samestr:<user>ACCESS.LOG (priv?)
TENTRY (NOSECURE,SF.NOS) ;Clear secure access (CHFDB)
TENTRY (READ,SF.REA) ;Read access (OPENF)
TENTRY (RENAME,SF.REN) ;Rename access (RNAMF)
TENTRY (SECURE,SF.SEC) ;Set secure access (CHFDB)
TENTRY (WRITE,SF.WRI) ;Write access (OPENF)
TEND ;End of table
SUBTTL Secure Files -- Access Control Cache
;[111] The Access Control Cache maintains the last NCACHE files open read for
;secure files access control.
;The SECOPN routine is called to open an ACCESS.CONTROL file for parsing. This
;routine tries to find an entry in the cache for the directory of the filename
;passed to this routine. Routine SECOSF is called to split the given filename
;string apart into a string containing the structure and directory and other
;string containing just the file.type.version.
;Routine SECOCF is called to find a cache entry for the str:<directory> where
;the given file resides. If there is no entry for this directory, an entry is
;found by searching the cache for the first free cache block. If there is no
;free block the oldest referenced cache block which is then used for this
;opening of the file.
;The cache is organized into NCACHE non-contiguous fixed buffers in memory,
;each of SCACHE contiguous pages. A table of CBXSIZ words is used for each
;fixed buffer and contains the address of the buffer as determined at assembly
;time as well as the ASCIZ directory for which the cache is a part.
;After the cache block is idenitified, SECOCS is called to check to see that
;the entry is not "stale". Stale entries are determined by checking the time
;that the data pages were mapped. If stale entries were not flushed, changes
;in the ACCESS.CONTROL files would never be picked up. If the entry is
;considered stale the cache block is invalidated. This invalidation causes the
;file to be remapped (as if it was a new entry in the cache). This period
;should be fairly long to avoid overhead.
;Routine SECOCO is called after the stale check. This routine opens and maps
;the file if the cache block indicates that the file is not currently mapped.
;Finally, routine SECOCI is called to set up the pointers and counts needed to
;for routines that scan the ACCESS.CONTROL file.
;The cached paged are then read by the normal SECCIN routine. If the scan of
;the file causes all cached pages to be looked at, and the file was larger than
;SCACHE pages, routine SECMAP is called to map the next page in the file into
;the "overflow" buffer. Pages are then looked at one at a time until all pages
;in the file have been mapped.
;When SECCLS is called to close the file only the overflow buffer is unmapped.
;The file stays open along with its JFNs for possible future access. Routine
;SECFLU is called to flush an entry from the cache, unmapping all pages and
;releasing the JFN.
SUBTTL Secure Files -- Access Control Cache -- Open File
;Here to open the ACCESS.CONTROL file in the same directory as the secure file.
; Sets up SECDIR/ "str:<directory>" and SECFNA/ "file.type.gen"
; Finds cache entry for this file.
; If file not in cache, open and map SCACHE pages from it.
; Set up variables for reading the file.
;Called with Q1/ ARGBLK+.RCARA
;Returns +1 if file not found
;Returns +2 if file found, P4/ cache block
SECOPN: CALL SECOSF ;[111] (Q1/) Split filespec first
;Find cache entry, and try to open the file.
CALL SECOCF ;[111] (/P4) Find cache entry for this
CALL SECOCS ;[111] (P4/) Flush stale entry if needed
CALL SECOCO ;[111] (P4/) Open up the file if possible
RET ;[111] Owie, return +1
CALL SECOCI ;[111] (P4/) Setup for I/O
RETSKP ;Give the +2 return please
SUBTTL Secure Files -- Access Control Cache -- Open File -- Split Filename
;[111] Routine to split up filename strings. Sets SECDIR "str:<directory>",
;SECFNA "file.type.gen" and SECFNV "file.type"
;Called with Q1/ ARGBLK+.RCARA
;Returns +1 always with SECDIR and SECFNA and SECFNV set up.
SECOSF: MOVEI T2,.GEFIL(Q1) ;[111] Load address of the with the filename
HRLI T2,(POINT 7) ;[111] Point to the thing with a byte pointer
MOVE T1,[POINT 7,SECDIR] ;[111] Point to "str:<directory>" build area
DO. ;[111] Loop to grab the str:<dir>
ILDB T3,T2 ;[111] Load a source byte
IDPB T3,T1 ;[111] Store a byte please
CAIN T3,76 ;[111] Is it a close angly?
EXIT. ;[111] Yes, exit
JUMPN T3,TOP. ;[111] Continue looping if not end of string
RET ;[111] Return now if premature end of filename
OD. ;[111] Now we have the "str:<dir>" done
SETZ T3, ;[111] Load up a null
IDPB T3,T1 ;[111] and store it there to bind off dir
HRROI T1,SECFNA ;[111] Point to "file.type.gen" storage area
CALL ISOUT ;[111] (T1,T2/T1) Copy rest of filename there
HRROI T1,SECFNV ;[115] Point to filename area
HRROI T2,SECFNA ;[111] Load address of "file.type.gen"
CALL ISOUT ;[111] (T1,T2/T1) Copy string, ending bp in T1
DO. ;[111] Loop back from end to find first dot
SETO T2, ;[111] Load a -1 in T2
ADJBP T2,T1 ;[111] Back it up one
MOVEM T2,T1 ;[111] Store it back
LDB T2,T1 ;[111] Load character from there
CAIE T2,"." ;[111] Period?
JUMPN T2,TOP. ;[111] Not yet, go back another character
OD. ;[111] Found start of version
SETZ T2, ;[111] Load a null
DPB T2,T1 ;[111] Store it over period
RET ;[111] Return
SUBTTL Secure Files -- Access Control Cache -- Open File -- Cache Find
;[111] SECOPN routine to examine the cache and find entry for str:<directory>.
;It may return with
; 1) an existing valid cache block (cache hit)
; 2) a free cache entry (cache miss)
; 3) a reused cache entry (cache miss)
;Call with SECDIR set up.
;Returns +1 always with P4/ offset to cache block entry
SECOCF: MOVE P4,[-NCACHE,,CBPOOL] ;[111] Load number of entries in the cache
DO. ;[111] Loop for all cache blocks
SKIPN CBXJFN(P4) ;[111] Is this one free?
IFSKP. ;[111] Nope
HRROI T1,SECDIR ;[111] Point to "str:<directory>"
HRROI T2,CBXDIR(P4) ;[111] Point to filename for this entry
CALL ISTCMP ;[111] (T1,T2/T1) Compare the strings
IFE. T1 ;[111] If a match
AOS NHIT ;[111] Count as a cache hit
RET ;[111] Return
ENDIF. ;[111] End of string match code
ENDIF. ;[111] End of check
ADDI P4,CBXSIZ-1 ;[111] Point to next block -1
AOBJN P4,TOP. ;[111] Loop if more blocks to consider
OD. ;[111] End of loop to check old entries
AOS NMISS ;[111] Count a cache miss
;[111] Entry was not in the cache, locate a place for it and fill in the CB.
MOVE P4,[-NCACHE,,CBPOOL] ;[111] Now we find an empty slot or oldest
MOVX T1,.INFIN ;[111] Load a real log uptime about a year
DO. ;[111] Loop to find a free spot
SKIPN CBXJFN(P4) ;[111] Is this one free?
EXIT. ;[111] Yes, we found one
CAMG T1,CBXRTI(P4) ;[111] Is this the oldest one yet?
IFSKP. ;[111] Yes, this entry is older than last one
MOVE T1,CBXRTI(P4) ;[111] Load the time of the oldest one
MOVEI T2,(P4) ;[111] and remember which entry this was
ENDIF. ;[111] Continue
ADDI P4,CBXSIZ-1 ;[111] Point to next block -1
AOBJN P4,TOP. ;[111] Loop for each one
MOVEI P4,(T2) ;[111] Load the address of the one matching
CALL SECFLU ;[111] (P4/) Remove this entry from cache
OD. ;[111] End of loop
HRROI T1,CBXDIR(P4) ;[111] Point to filename for this entry
HRROI T2,SECDIR ;[111] Point to "str:<directory>"
CALLRET ISOUT ;[111] (T1,T2/T1) Copy the str:<dir> to CBX
SUBTTL Secure Files -- Access Control Cache -- Open File -- Cache Stale Check
;[111] SECOPN routine to check that cached entry found is not too stale to use.
;If the cache block entry specified is too old it is flushed before use.
;Call with P4/ CBX
;Returns +1 always.
SECOCS: SKIPN CBXJFN(P4) ;[111] Is this entry in use now?
RET ;[111] Nope, get out
;[111] Files are always kept in the cache we know when they change.
MOVX T1,FB%SEC ;[111] Load secure bit
TDNN T1,CBXCTL(P4) ;[111] Was this file secure when we looked?
IFSKP. ;[111] Yes
MOVX T1,FU%GOK ;[111] Load we are doing GETOKs bit
TDNE T1,GOOPNB ;[111] Can we tell a secure OPENF on this file?
RET ;[111] Yes, do nothing to interfere
ENDIF. ;[111] Let the timer take care of it
;[111] Non secure file, check for maximum map time.
MOVE T1,TODCLK ;[111] Load the uptime at last RCVOK function
SUB T1,CBXMTI(P4) ;[111] Get elapsed time that file mapped
CAILE T1,SECDCI*^D1000 ;[111] Is this entry too old?
CALL SECFLU ;[111] (P4/) Yes, abort it now and remap it
RET ;[111] Return
SUBTTL Secure Files -- Access Control Cache -- Open File -- Cached Open
;[111] SECOPN routine to open up a file that is not currently in the cache.
;Call with P4/ cache block offset
;Returns +1 if error
;Returns +2 if already open or just opened and cached file set up for reading.
SECOCO: SKIPE CBXJFN(P4) ;[111] Is there a cached entry?
RETSKP ;[111] Yes, it is all set up
;[111] Construct filename to use.
HRROI T1,CBXFIL(P4) ;[111] Point to filename area
HRROI T2,CBXDIR(P4) ;[111] Point to str:<directory>
CALL ISOUT ;[111] (T1,T2/T1) Copy that first
HRROI T2,[ASCIZ/ACCESS.CONTROL/] ;[111] Point to the filename we desire
CALL ISOUT ;[111] (T1,T2/T1) Append in the desired file
;[111] Get a JFN on the access.control file.
MOVX T1,GJ%OLD!GJ%XTN ;[111] Load old file flags long form block
MOVEM T1,SECGTJ+.GJGEN ;[111] and set in long form block
MOVX T1,G1%IIN ;[111] Include invisible files
MOVEM T1,SECGTJ+.GJF2 ;[111] and set this in second flag word
MOVEI T1,SECGTJ ;[111] Point to long form GTJFN block
MOVX T1,GJ%OLD!GJ%SHT ;[111] Load short form and old file flags
HRROI T2,CBXFIL(P4) ;[111] Point to the filename please
GTJFN% ;[111] Try to find that file
ERJMP R ;[111] Return +1 if file not found
;[111] Open up the access control file.
MOVEM T1,CBXJFN(P4) ;[111] Save the JFN
MOVX T2,OF%PDT!OF%RD!FLD(7,OF%BSZ) ;[111] Preserve dates, 7 bit, read
OPENF% ;[111] Pry that file open please
ERJMP SECFLU ;[111] (P4/) Close file and return +1 if errors
;[111] Get FBCTL word for this file and store it.
SETZM CBXCTL(P4) ;[111] Default FBCTL work to zero
MOVX T2,<1,,.FBCTL> ;[111] Just this word
MOVEI T3,CBXCTL(P4) ;[111] Poimt to block for storing this
GTFDB% ;[111] Get the file's FDB word
ERJMP SECFLU ;[111] (P4/) We must be able to get this word
;[111] Get size of file and compute how many pages to map.
MOVE T1,CBXJFN(P4) ;[111] Load the JFN for the file again
SIZEF% ;[111] Get the size of the file
ERJMP SECFLU ;[111] (P4/) If error, close it now
MOVEM T3,CBXPCT(P4) ;[111] Save file page count
CAILE T3,SCACHE ;[111] Is it over the size of the cache buffer?
MOVEI T3,SCACHE ;[111] Reduce pages to map to the size
MOVEM T3,CBXMAP(P4) ;[111] Save as mapped page count
;[111] We know all about the file, map the first SCACHE pages into cache.
HRLZ T1,CBXJFN(P4) ;[111] Load JFN of that file please
MOVE T2,CBXPAG(P4) ;[111] Load fork,,page number of cache buffer
TXO T3,PM%RD!PM%CNT ;[111] We have to read and have a count please
PMAP% ;[111] Map those pages into cache please
ERJMP SECFLU ;[111] (P4/) If error we are screwed now
;[111] Update the mapped time for this cache entry.
MOVE T1,TODCLK ;[111] Load now
MOVEM T1,CBXMTI(P4) ;[111] and save it as time of mapping
RETSKP ;[111] Return OK
SUBTTL Secure Files -- Access Control Cache -- Open File -- Cached Input Setup
;[111] SECOPN routine to set up the pointers and counts to read cached pages.
;Call with P4/ CBX
;Returns +1 always with pointers and counts set up.
SECOCI: MOVE T1,TODCLK ;[111] Load now
MOVEM T1,CBXRTI(P4) ;[111] and save it as time of reference
MOVE T1,CBXPTR(P4) ;[111] Get byte pointer to cache buffer
MOVEM T1,SECBPT ;[111] Save as byte pointer to data
MOVE T1,CBXMAP(P4) ;[111] Load number of pages mapped today
IMULI T1,PGSIZ*5 ;[111] Compute possible bytes there
MOVEM T1,SECBCT ;[111] Save as byte count
MOVE T1,CBXMAP(P4) ;[111] Load number of pages mapped today
SUBI T1,1 ;[111] Get next page to map-1 for SECMAP
MOVEM T1,SECPMP ;[111] Save the logical last page mapped
MOVE T1,CBXPCT(P4) ;[111] Reload the total size of the file
SUB T1,CBXMAP(P4) ;[111] Compute pages that are NOT cached
SKIPGE T1 ;[111] Did it fit in the cache?
SETZ T1, ;[111] Yes, load zero non cached page count
MOVEM T1,SECPCT ;[111] Save non cached page count
RET ;[111] Return
SUBTTL Secure Files -- Access Control Cache -- Close File -- Read Finished
;[111] Here when finished with ACCESS.CONTROL file. This routine just returns
;after unmapping the page used when the entire file does not fit into one
;cache buffer (greater than SPAGES pages). The cached part of the file will
;remain so it's JFN must remain as well.
;Call with P4/ CBX
;Returns +1 always
SECCLS: SETO T1, ;Load a -1 for no mapping
MOVX T2,<.FHSLF,,SECOPG> ;[111] This fork that page
MOVEI T3,0 ;Load no flags and junk
TXZE F,FL%LAC ;[111] Long access control file mapped?
PMAP% ;Unmap that stuff
ERNOP. ;[111] Ignore errors or nothing to unmap
RET ;[111] Return, that's all there is to do
SUBTTL Secure Files -- Access Control Cache -- Close File -- Flush Cache
;[111] Here when aborting use of ACCESS.CONTROL file.
;Unmaps all pages, closes JFN, frees cache block.
;Call with P4/ cache block address
;Returns +1 always.
SECFLU: CALL SECCLS ;[111] (P4/) First unmap junk page if any
SKIPN T3,CBXMAP(P4) ;[111] Load mapped page count, skip if some
IFSKP. ;[111] Pages were in fact mapped today
SETO T1, ;[111] Get ready to unmap the cache paged
MOVE T2,CBXPAG(P4) ;[111] Load page number and this fork
TXO T3,PM%CNT ;[111] We have to have a count please
PMAP% ;[111] Unmap all of those pages
ERNOP. ;[111] Forget errors
SETZM CBXMAP(P4) ;[111] Perform housekeeping
ENDIF. ;[111] End of unmap code
MOVE T1,CBXJFN(P4) ;[111] Load the JFN
CLOSF% ;[111] Close and release the JFN
ERCAL SECABR ;[111] (P4/) If that failed, try releasing it
SETZM CBXJFN(P4) ;[111] Clear this cache entry then now
AOS NFLUSH ;[115] Count a flushed one
RET ;[111] Return +1 please
SECABR: MOVE T1,CBXJFN(P4) ;[111] JFN must not be open (OPENF failed!)
RLJFN% ;[111] So release the JFN already
ERNOP. ;[111] Ignore errors this fine spring day
RET ;[111] Return +1 always
;[111] Here when wanting to abort all cache entries.
;Called from the crash routine.
;Returns +1 always.
SECCLA: MOVE P4,[-NCACHE,,CBPOOL] ;[111] For all entries in the cache
DO. ;[111] Loop through eache entry
CALL SECFLU ;[111] (P4/) Flush this entry
ADDI P4,CBXSIZ-1 ;[111] Count to next block address-1
AOBJN P4,TOP. ;[111] Loop for all cache entries
OD. ;[111] End of that loop
RET ;[111] Return to caller
SUBTTL Secure Files -- Action for Successful Access
;[110] Here to check to see if the secure file being touched is our log file.
;If a read of the log file is requested, then we set the sweep cache flag. If
;a rename of the log file is requested, then we get a new log file.
;[111] Then if a rename or a write, check each access control block to see if
;it matches the filename of one of our cached files, and if so flush entry.
;
;Call only after determining that access is allowed with
; Q1/ ARGBLK+.RCARA
; Q2/ SF.xxx for the type of desired access
;Returns +1 always.
SECACT: TXNE Q2,SF.REN!SF.REA ;[121] Is it a rename or read function?
CALL SECACL ;[121] (/) Yes, have to check log file spec
TXNE Q2,SF.REN!SF.WRI!SF.DEL ;[121] Is it a rename, write, or delete?
CALL SECACA ;[121] (/) Check for access.control file
RET ;[121] Return to caller
;[121] Local routine for SECACT to check for access.control file.
;Called here if a rename or write function was specified.
;Returns +1 always, access.control cache entry flushed if a match.
SECACA: HRROI T1,SECFNV ;[121] Point to "file.type" of the file
HRROI T2,[ASCIZ/ACCESS.CONTROL/] ;[121] Point to the usual filename
CALL ISTCMP ;[121] (T1,T2/T1) See if it is the magic one
JUMPN T1,R ;[121] If no match return now
SAVEAC <P4> ;[111] Save an AC
MOVE P4,[-NCACHE,,CBPOOL] ;[111] For all entries in the cache
DO. ;[111] Loop through eache entry
SKIPN CBXJFN(P4) ;[111] Is this entry active?
IFSKP. ;[111] Yes see if access.control file
HRROI T1,SECDIR ;[115] Load address of "str:<directory>"
HRROI T2,CBXDIR(P4) ;[115] Point to cache block str:<directory>
CALL ISTCMP ;[111] (T1,T2/T1) See if a match
SKIPN T1 ;[111] Skip if no match
CALLRET SECFLU ;[121] (P4/) Flush if touching access.control
ENDIF. ;[111] End of check for entry to flush
ADDI P4,CBXSIZ-1 ;[111] Count to next block address-1
AOBJN P4,TOP. ;[111] Loop for all cache entries
OD. ;[111] End of that loop
RET ;[121] Return to caller
;[121] Local routine for SECACT to check for manipulation of the log file.
;Called here if a rename or read function is being performed.
;Returns +1 always, with new log file or flush log file bit set.
SECACL: SKIPN LOGJFN ;[121] Log file open now?
RET ;[121] Nope, return now
HRROI T1,.GEFIL(Q1) ;[111] Point to filename
HRROI T2,LOGFNA ;[111] Point to log filename
CALL ISTCMP ;[111] (T1,T2/T1) Compare those strings now
JUMPN T1,R ;[121] Return now if not a match on filename
CAIN Q2,SF.REA ;[110] Reading the log file?
TXO F,FL%SLF ;[110] It was a match, we need to sweep
CAIN Q2,SF.REN ;[110] Rename log file?
CALL NEWLOG ;[110] (/) Yes, we need a new log file then
RET ;[121] Return to caller
SUBTTL Secure Files -- Read Line from File
;Here to read one non-comment line from ACCESS.CONTROL into the buffer.
;Called with P4/ CBX
;Returns +1 if EOF (or any other error)
;Returns +2 if line read, P2/ pointer to line, P3/ first nonblank character
SECSIN: MOVE P2,[POINT 7,SECLIN] ;Point to the line first of all
MOVEI P3,<SECCPL*5>-1 ;Load maximum characters per line today
DO. ;Loop to read in a line from the file
CALL SECCIN ;(P4/T1) Get a character from the file
EXIT. ;End the loop if not possible
CAIE T1,"-" ;Is it a hyphen?
IFSKP. ;Yes, possibly a line continuation character
CALL SECCIN ;(P4/T1) Get next character
EXIT. ;End loop if end of file
CAIE T1,.CHCRT ;Return after hyphen?
IFSKP. ;Yes, return after hyphen
CALL SECCIN ;(P4/T1) Get next character
EXIT. ;End loop if end of file
CAIN T1,.CHLFD ;Must be a line feed next
LOOP. ;Yes, it was, continue as if nothing happened
EXIT. ;Format error in file, lets get out of here now
ELSE. ;Otherwise hyphen not followed by a return
MOVEI T2,"-" ;Reload the hyphen
IDPB T2,P2 ;Store it followed by character we just read
SOJLE P3,ENDLP. ;Count this character, fall thru to store T1
ENDIF. ;End of hyphen not followed by return case
ENDIF. ;End of hyphen seen case
IDPB T1,P2 ;Store that byte please
CAIN T1,.CHLFD ;Is it a line feed?
EXIT. ;Yes, get out of here with a line read
SOJG P3,TOP. ;Loop for all possible characters
OD. ;End of loop to read line of characters
MOVEI T1,0 ;Load zero also known as the null character
IDPB T1,P2 ;Store that there to insure null on end
MOVE P2,[POINT 7,SECLIN] ;Point to line please
RETSKP ;Skip return
SUBTTL Secure Files -- Read Character from File
;Local routine to read a character from the file, call with P4/ CBX.
;Returns +1 if error
;Returns +2 if no error, T1/ character
SECCIN: SOSL SECBCT ;See if any bytes left in buffer
IFSKP. ;If none left there
CALL SECMAP ;(P4/) Map a page of the file
RET ;If no mapping possible return +1
JRST SECCIN ;Try again please
ENDIF. ;Otherwise we do not need to get more bytes
ILDB T1,SECBPT ;Load a byte from the file
MOVE CX,SECBPT ;[123] Load the byte pointer
MOVE CX,(CX) ;[123] Load the data word
TRNE CX,1B35 ;[123] Is this a LSN line?
JRST SECCIN ;[123] Yes, ignore it now
JUMPN T1,RSKP ;If a real character, skip return
JRST SECCIN ;A null was seen, check next character
;Here to map another page of the access control file. [111] We only get here
;when the cached page count is exhausted, that is the file is larger than
;SCACHE pages long, after thatn this routine is called for each remainting page
;in the file. It is felt that files over SCACHE pages are unusual, and at
;least the first SCACHE pages are cached.
;Call with P4/ CBX and SECPCT/ count of pages left to map
;Returns +1 if end of file or other problem with mapping.
;Returns +2 if file mapped, SECBPT and SECPTR set up
SECMAP: SOSGE SECPCT ;Count a page that is mapped
RET ;End of file
AOS T1,SECPMP ;Load the next page number
HRL T1,CBXJFN(P4) ;Load the JFN into the proper place
MOVX T2,<.FHSLF,,SECOPG> ;[111] This fork and this page
MOVX T3,PM%RD ;Reading only the one page today
PMAP% ;Map those pages in please
ERJMP R ;If error we are done
TXO F,FL%LAC ;[111] Indicate long access control file
MOVE T1,[POINT 7,SECOBU] ;[111] Point to proper place
MOVEM T1,SECBPT ;Store this in the correct place
MOVEI T1,PGSIZ*5 ;Load byte count of the page
MOVEM T1,SECBCT ;Save the byte count of this page
RETSKP ;Skip return
SUBTTL Secure Files -- Read Character from Line Buffer
;Here to read a character from the line. Eats embedded comments.
;Call with P2/ pointer to the line
;Returns +1 if end of line seen
;Returns +2 if not end of line, P2/ updated pointer P3/ character
SECCHR: CALL SECCH2 ;(P2/P2,P3) Load a character
RET ;End of line!
CAIE P3,"!" ;Is it a embedded comment character?
RETSKP ;Nope, return +2 now
SECCH1: CALL SECCH2 ;(P2/P2,P3) Get next character
RET ;Returns +1 at end of line
CAIE P3,"!" ;Is it the end of embedded comment?
JRST SECCH1 ;Nope, keep looking for end of comment
; CALLRET SECCH2 ;(P2/P2,P3) Yes, get next character and return
SECCH2: ILDB P3,P2 ;Load a character please
CAIE P3,.CHCRT ;Return?
CAIN P3,.CHFFD ;Form feed?
JRST SECCHR ;Yes, eat those
CAIN P3,.CHLFD ;End of line?
RET ;Yes, return +1
CAIL P3,"a" ;Is it
CAILE P3,"z" ; lowercase?
RETSKP ;Nope, return +2
SUBI P3,"a"-"A" ;Yes, convert to upper case
RETSKP ; and return +2
;Here to eat characters from pointer in P2 until a nonblank character is found.
;Call SECSPA with P2/ pointer to the line, P3/ 0 or last character read
;Call SECSPN to ignore last character read, P2/ pointer to the line
;Returns +1 if end of line
;Returns +2 if not end of line with P2/ updated pointer and P3/ character
SECSPN: CALL SECCHR ;(P2/P2,P3) Read a character
RET ;Return +1 if end of line
SECSPA: CAIE P3," " ;Was the last character read a space?
CAIN P3,.CHTAB ;Was the last character read a tab?
JRST SECSPN ;Yes, get another character then please
RETSKP ;Return +2, P3/ nonblank character
SUBTTL Secure Files -- Read Field from Line Buffer
;Here to read in a field from the access control listing file.
;Legal field characters are alphanumerics, asterisk, dot, percent.
;Call with P2/ pointer to the line, P3/ last character read
;Returns +1 if blank field (EOL or field terminator detected before field read)
;Returns +2 if non-blank field, P2/ updated, P3/ field terminator
SECFLD: CALL SECSPA ;(P2,P3/P2,P3) Eat any leading spaces
RET ;End of line
MOVE T1,[POINT 7,SECWRD] ;Point to output area (word/field to match)
SETZM SECWRD ;Make it easy to see blank fields
SECFL1: CAIL P3,"0" ;OK, well is it a character we can consider
CAILE P3,"9" ; a numeric character?
CAIN P3,"." ;Is it a hot dot? (dot is less than zero)
JRST SECFL5 ;Yes in fact this is a legal character
CAIE P3,"*" ;Is it a going to be
CAIN P3,"%" ; a wild character?
JRST SECFL5 ;Yes, proceed to store it and loop
CAIN P3,"$" ;Is it money?
JRST SECFL5 ;Yes
CAIE P3,"_" ;Is it a going to be underscore
CAIN P3,"-" ; or a hyphen?
JRST SECFL5 ;Yes, proceed to store it and loop
CAIL P3,"A" ;Is the character one that we consider
CAILE P3,"Z" ; alphabetic?
JRST SECFL6 ;Nope, it is a field terminator
;Here if non-terminator character. Store it, get next character, and loop.
SECFL5: IDPB P3,T1 ;It is a legal character, store the character
CALL SECCHR ;(P2,P2/P3) Read a non blank character
JRST SECFL6 ;End of line, check it out
JRST SECFL1 ; and loop for more of them today please sir
;Here if field terminator seen. Store a null, return +2 if non-null word read.
SECFL6: SETZ T2, ;Load a zero or null character to tie it off
IDPB T2,T1 ;Stick a null at end of the string
SKIPE SECWRD ;Here if end of line, did we store something?
AOS (P) ;Yes, skip return
RET ;Return +1 or +2
SUBTTL Subroutines -- Simulate STCMP
;[111] Here to quickly/cheaply compare to ASCIZ strings.
;Call with T1 and T2 pointing to two ASCIZ strings.
;CAUTION! This routine smashes T1 through T4!
;Returns +1 always, T1/0 if strings matched.
ISTCMP: TLC T1,-1 ;[111] Complement left half
TLCN T1,-1 ;[111] Was the left half -1?
HRLI T1,(Point 7) ;[111] Yes, make it a byte pointer
TLC T2,-1 ;[111] Complement left half
TLCN T2,-1 ;[111] Was the left half -1?
HRLI T2,(Point 7) ;[111] Yes, make it a byte pointer
DMOVE T3,T1 ;[111] Copy the pointers to T3 and T4
DO. ;[111] Loop through the characters
ILDB T2,T4 ;[111] Get character from user's filespec
ILDB T1,T3 ;[111] Get character from log filespec
CAIN T1,(T2) ;[111] Do the characters match?
JUMPN T1,TOP. ;[111] Yes, continue to loop unless null seen
OD. ;[111] End of loop, null in T1 if a match seen
RET ;[111] Return with T1 set up
SUBTTL Subroutines -- Simulate SOUT
;Here to quickly/cheaply copy ASCIZ string, insures null at end of string.
;Call with T1/ destination byte pointer, T2/ source byte pointer
;CAUTION! This routine MUST preserve all ACs except T1 and T2.
;Returns +1 always, string copied, T1 and T2 updated
ISOUT: TLC T1,-1 ;Complement left half
TLCN T1,-1 ;Was the neft half -1?
HRLI T1,(Point 7) ;Yes, make it a byte pointer
TLC T2,-1 ;Complement left half
TLCN T2,-1 ;Was the neft half -1?
HRLI T2,(Point 7) ;Yes, make it a byte pointer
ISOUT1: ILDB CX,T2 ;Load a byte
IDPB CX,T1 ;Store it
JUMPN CX,ISOUT1 ;Jump if not done
MOVNI CX,1 ;Back up the byte pointer
ADJBP CX,T1 ; by one and
MOVEM CX,T1 ; store back the byte pointer
RET ;Return
SUBTTL Output Subroutines -- Output Information about Job
;Here to output information about a particular job.
;Call with T1/ output pointer and T4/ address of GETJI block.
;Returns +1 always
OGETJI: CALL OSPACE ;(T1/T1) Output a space next please
;Output username.
MOVE T2,.JIUNO(T4) ;Load the user number
CALL ODIRST ;(T1,T2/T1) Output directory name
;Output any controlling job information next.
OGETJ1: HRROI T2,[ASCIZ/ job /] ;Label the job number, first part of data
MOVE T3,.JIJNO(T4) ;Load the job number
CALL OLDEC ;(T1,T2,T3/T1) Append in the job number
SKIPGE .JICPJ(T4) ;Is there a controlling job ?
IFSKP. ;Yes
SKIPL .JIBAT(T4) ;Is the controlling job BATCON?
IFSKP. ;Yes it is batch
HRROI T2,[ASCIZ/ batch/] ;Label it as such
CALL ISOUT ;(T1,T2/T1) Append that to the string
ELSE. ;Job is controllied and not batch
HRROI T2,[ASCIZ/ ctrl /] ;Label next field
MOVE T3,.JICPJ(T4) ;Load the controlling job again
CALL OLDEC ;(T1,T2,T3/T1) Output label and job in decimal
ENDIF. ;End of job not batch code
ENDIF. ;End of controlling job check
;Output terminal number and origin.
SKIPGE T3,.JITNO(T4) ;Have a terminal number?
IFSKP. ;Yes
HRROI T2,[ASCIZ/ TTY/] ;Label the number
CALL OLOCT ;(T1,T2,T3/T1) Append the label and octal line
SKIPN NTBLK(T4) ;Have network terminal information?
IFSKP. ;Yes, output that
CALL OSPACE ;(T1/T1) First a space
HRROI T2,NTBLK(T4) ;Point to block with name in it
CALL ISOUT ;(T1,T2/T1) Send that along next
ENDIF. ;End of network org available code
ELSE. ;We don't have a terminal number
HRROI T2,[ASCIZ/ Det/] ;Its detached
CALL ISOUT ;(T1,T2/T1) Append detached string
ENDIF. ;End of terminal number output
;Output program name.
CALL OSPACE ;(T1/T1) Output a space
SKIPN T2,.JIPNM(T4) ;Get program name
MOVE T2,.JISNM(T4) ;If none, use subsystem name
CALLRET OSIXBI ;(T1,T2/T1) Output sixbit word
SUBTTL Output Subroutines -- Output Username/Device/Filename
;Routine to do a DIRST.
;Call with T1/ output designator, T2/ user or directory number (5B2+n)
;Returns +1 always, T1/ updated pointer
; T2/ user or directory number if legal,
; 0,,error if unknown or 0,,0 if not logged in
ODIRST: TRNE T2,-1 ;Not logged in?
IFSKP. ;If not logged in
HRROI T2,[ASCIZ/not-logged-in/] ;Indicate not logged in today
CALL ISOUT ;(T1,T2/T1) Send all of that to string and ret
SETZ T2, ;Indicate not logged in
RET ;Return with T1/ updated pointer and T2/ 0
ENDIF. ;End of not logged in case
MOVE T3,T1 ;Copy pointer in case error
DIRST% ;DIRectory number to STring
ERSKP. ;Skip if error
RET ;Return to caller
EXCH T1,T3 ;Swap pointer with error code
HRROI T2,[ASCIZ/unknown/] ;Say something if failure
CALL ISOUT ;(T1,T2/T1) Return a string
MOVE T2,T3 ;Return error code in T2
RET ;Return pointer in T1
;Routine to do a DEVST.
;Call with T1/ output designator, T2/ directory number
;Returns +1 always
ODEVST: MOVE T3,T1 ;Copy designator to T3 in case of error
DEVST% ;Convert to string
ERSKP. ;Skip if error
RET ;Return if success
MOVE T1,T3 ;Reload the pointer
HRROI T2,[ASCIZ/unknown/] ;Load unknown tag
CALLRET ISOUT ;(T1,T2/T1) Send that and return
;Routine to do a JFNS.
;Call with T1/ output designator, T2/ JFN
;Returns +1 always
OJFNS: SETZ T3, ;Default format today
JFNS% ;JFN to String
JSERRO (<JFNS failure>) ;Owie
RET ;Return +1
SUBTTL Output Subroutines -- Output Capability Mask
;Here to output text for bits in capability mask
;Call with
; T1/ output pointer
; T4/ capability bits
;Returns +1 always, T1/ updated pointer, T2 and T3 smashed
OCAPAB: MOVSI T3,-CAPNUM ;Load AOBJN pointer to capabilities
DO. ;Loop for these capabilities
HLRO T2,CAPTBL(T3) ;Load ASCIZ string for this capability
TDNE T4,CAPTBL(T3) ;Is this one lit?
CALL ISOUT ;(T1,T2/T1) Yes, dump its string
AOBJN T3,TOP. ;Loop for all of them
OD. ;End of loop
RET ;Return to caller
;Table of interesting (right halfword) capabilities.
CAPTBL: TENTRY (< whl>,SC%WHL)
TENTRY (< opr>,SC%OPR)
TENTRY (< cnf>,SC%CNF)
TENTRY (< mnt>,SC%MNT)
TENTRY (< enq>,SC%ENQ)
TENTRY (< ipc>,SC%IPC)
TENTRY (< nwz>,SC%NWZ)
TENTRY (< nas>,SC%NAS)
TENTRY (< dna>,SC%DNA)
TENTRY (< ana>,SC%ANA)
TENTRY (< sem>,SC%SEM)
CAPNUM==.-CAPTBL ;Compute number in table
SUBTTL Output Subroutines -- Small Output Routines
;Here to output CRLF (PCRLF) or text buffer (PTEXT).
;Returns +1 always.
PTEXT: SKIPA T1,[XWD -1,TEXTBU] ;Point to text area and skip always
PCRLF: HRROI T1,[BYTE(7).CHCRT,.CHLFD] ;Point to crlf
PSOUT% ;Send to terminal
RET ; and return
;Here to print error buffer as an error message.
;Returns +1 always.
PERRO: HRROI T1,ERRBUF ;Point to the finished message
ESOUT% ;Output that error string to terminal
RET ;Return +1 always
;Here to append a CRLF to the string pointed to by T1.
;Returns +1 always, T1/ updated pointer.
OCRLF: HRROI T2,[BYTE(7).CHCRT,.CHLFD] ;Point to crlf
CALLRET ISOUT ;(T1,T2/T1) Append that to the string and ret
;Here to output a space or comma, insures a null after character of course.
;Call with T1/ output pointer
;Returns +1 always, T1/ updated pointer.
OCOMMA: SKIPA T2,[XWD -1,[ASCIZ/,/]] ;Load pointer to a comma and skip
OSPACE: HRROI T2,[ASCIZ/ /] ;Load pointer to a space
CALLRET ISOUT ;Send that along and return
;Small routine to check on the byte pointer in T1.
;Call with T1/ suspected pointer
;Returns +1 always T1/ real byte pointer
PCHECK: TLC T1,-1 ;Complement left half
TLCN T1,-1 ;Was the neft half -1?
HRLI T1,(Point 7) ;Yes, make it a byte pointer
RET ;Return +1 always
SUBTTL Output Subroutines -- Output Sixbit Word
;Here to output a SIXBIT word.
;Call with
; T1/ output pointer
; T2/ SIXBIT word
;Returns +1 always, T1/ updated, T2 smashed
OSIXBI: SAVEAC <T3,T4> ;Save some ACs for scratch
CALL PCHECK ;(T1/T1) Insure real byte pointer in T1
MOVE T4,[POINT 6,T2] ;Load byte pointer to string
OSIXB3: ILDB T3,T4 ;Load character
JUMPE T3,OSIXB6 ;Done if null (space) seen
ADDI T3,"A"-'A' ;Convert to ASCII
IDPB T3,T1 ;Store character
JRST OSIXB3 ;Loop for all characters
OSIXB6: MOVEM T1,T4 ;Get a copy of the current byte pointer
IDPB T3,T4 ;Store a null past the last real character
RET ; and then return
SUBTTL Output Subroutines -- Output Numbers
;Here to prepend a text string and then output a number in decimal radix.
; T1/ destination pointer
; T2/ pointer to ASCIZ text
; T3/ number to be output
;Returns +1 always, T1/ updated pointer
OLDEC: CALL ISOUT ;(T1,T2/T1) Output string (preserves T3!)
MOVE T2,T3 ;Load number to print into T2
CAME T2,[INFQUO] ;Is it +inf quota?
CALLRET ODEC ;Nope, print the number
HRROI T2,[ASCIZ/inf/] ;Load infinity string
CALLRET ISOUT ;(T1,T2/T1) Output that and return
;Here to prepend a text string and then output a number in octal radix.
; T1/ destination pointer
; T2/ pointer to ASCIZ text
; T3/ number to be output
;Returns +1 always, T1/ updated pointer
OLOCT: CALL ISOUT ;(T1,T2/T1) Send label string first
MOVE T2,T3 ;Load number
; CALLRET OOCT ;(T1,T2/T1) Send octal number and return
;Here to output number quickly (without using a JSYS to do so).
; T1/ destination pointer
; T2/ number to be output
;Returns +1 always, T1/ updated pointer, T2/ 0, T3/ smashed
OOCT: SKIPA CX,[4+4] ;Radix 8
ODEC: MOVEI CX,5+5 ;Radix 10
CALL PCHECK ;(T1/T1) Insure real byte pointer in T1
CALL ONUMB ;(T1,T2,CX/T1,T2) Call local routine for output
MOVE T3,T1 ;Copy output pointer
IDPB T2,T3 ;Store that null after the useful text
RET ; and quickly return to the caller
ONUMB: IDIVI T2,(CX) ;Extract digit from the number in proper radix
ADDI T3,"0" ;Convert that binary digit to ASCII
PUSH P,T3 ;Save this on the stack
SKIPE T2 ;Skip if we are all done
CALL ONUMB ;(T1,T2/T1) Loop for all numbers
POP P,T3 ;Restore a digit from the stack
IDPB T3,T1 ;Store that in the output pointer
RET ;Return to caller or to get another digit
;Here to output a label and number in octal halfword format such as a PPN.
;Call with
; T1/ destination pointer
; T2/ pointer to ASCIZ text
; T3/ number to be output
;Returns +1 always, T1/ updated pointer
OLPPN: CALL ISOUT ;(T1,T2/T1) Output string (preserves T3!)
MOVE T2,T3 ;Load number to print into T2
; CALLRET OPPN ;(T1,T2/T1) Output number in halfword format
;Here to output a number in octal halfword format such as a PPN.
;Call with
; T1/ destination pointer
; T2/ number to be output
;Returns +1 always, T1/ updated pointer.
OPPN: PUSH P,T2 ;Save the number
HLRZ T2,T2 ;Load the project number into the right half
CALL OOCT ;(T1,T2/T1) Send that project number out
MOVEI T2,"," ;Load a comma for the halfword seperator
IDPB T2,T1 ;Store that comma next please
POP P,T2 ;Restore the project-programmer number
ANDI T2,-1 ;Clear the left half leaving programmer number
CALLRET OOCT ;(T1,T2/T1) Output programmer and return
;Here to output number and name of it with "s" as appropriate.
;Call with
; T1/ destination
; T2/ number
; T3/ pointer to ASCIZ text
;Returns +1 always, T1/ updated pointer.
OPLURA: SAVEAC <Q1,Q2> ;Save a couple of ACs first
DMOVEM T2,Q1 ;Save the number and pointer to text
CALL ODEC ;(T1/T1,T2) Output number
MOVE T2,Q2 ;Load pointer to the text
CALL ISOUT ;(T1,T2/T1) Output that string next
SOSN Q1 ;Was the number just one?
RET ;Yep, done
HRROI T2,[ASCIZ/s/] ;Load your S up
CALLRET ISOUT ;(T1,T2/T1) Output and insure a null on end
SUBTTL Output Subroutines -- Output Floating Point Numbers
;Here to output floating point number
; T1/ destination pointer
; T2/ floating point number to be output
;Returns +1 always, T1/ updated
OFLOUT: MOVX T3,FL%ONE!FL%PNT!FLD(4,FL%RND)!FLD(3,FL%SND) ;Format bits
FLOUT% ;Output that
JSERRO (<FLOUT failure>) ;Snowballs exist in hell today
RET ;Return to sender
SUBTTL Output Subroutines -- Output Millisecond Times
;Routine to output time in the form "h:mm:ss.tt" or "n days hh:mm:ss".
;Call at OTIMEH with T1/ destination byte pointer, T2/ time in HPTIM units
;Call at OTIME with T1/ destination byte pointer, T2/ time in milliseconds
;Returns +1 always, T1/ updated pointer
OTIME: IDIV T2,[^D<24*60*60*1000>] ;Milliseconds, get days in T2, time in T3
IMULI T3,^D100 ;Convert milliseconds to HPTIM units
JRST OTIME1 ; and enter the high precision units output
OTIMEH: IDIV T2,[^D<24*60*60*1000*100>] ;HPTIM unit, get days in T2, time in T3
OTIME1: SAVEAC <P1,P2,P3,P4,P5> ;Save the Ps
;Now T1/ output pointer, T2/ days, T3/ time in HPTIM units.
MOVEM T3,P1 ;Save the time in HPTIM units for later
HRROI T3,[ASCIZ/ day/] ;Label the number as "day" or "days"
SKIPN P5,T2 ;Skip if days, load P5 with number of days
JRST OTIME2 ;No days to output today
CALL OPLURA ;(T1,T2,T3/T1) Output that
CALL OSPACE ;(T1/T1) followed by a space
;Now P1/ time, T1/ output pointer, breakup the time into its components.
OTIME2: EXCH T1,P1 ;Get time in T1, save output pointer
ADDI T1,^D500 ;Round up the hundredths of seconds
IDIVI T1,^D1000 ;Get units into hundredths of seconds
IDIV T1,[^D<100*60*60>] ;Get hours from hundreths of seconds
IDIVI T2,^D<100*60> ;Get minutes from fractional hours
IDIVI T3,^D100 ;Get seconds from fractional minutes
DMOVEM T3,P3 ;Save seconds in P3 and hundreths of secs in P4
MOVEM T2,P2 ;Save minutes in P2
EXCH T1,P1 ;Save hours in P1, get string pointer back
;Now P1/ hours, P2/ minutes, P3/ seconds, P4/ hundreths, P5/ days.
;Output hours and colon if days output or if hours are not zero.
MOVX T3,^D10 ;Load radix 10 for the hours please
MOVEI T4,":" ;Load a colon for a suffix
SKIPN T2,P1 ;Always output nonzero hours
SKIPE P5 ;If any days always output hours even if zero
CALL OTIME3 ;(T1,T2,T3,T4/T1,T3) Output hours and a colon
;Output minutes and a colon if hours output or if minutes is nonzero.
SKIPN T2,P2 ;Always output minutes if nonzero
TXNE T3,NO%LFL ;Mins zero, output mins if hours or days output
CALL OTIME3 ;(T1,T2,T3,T4/T1,T3) Output minutes and colon
;Output seconds always, suffix is a dot only if no hours output.
MOVE T2,P3 ;Load seconds as they are always output
SKIPE P5 ;If no hours were output
TDZA T4,T4 ; then no suffix will be printed
MOVEI T4,"." ;Suffix after seconds should be a hot dot
CALL OTIME3 ;(T1,T2,T3,T4/T1,T3) Output seconds and a dot
;Output hundreths with no suffix only if no days have been output.
JUMPN P5,R ;Return now if hours were output
SETZ T4, ;No seperator now
MOVE T2,P4 ;Load hundredths of seconds and fall through
;Local routine called from above to output parts of the time and a suffix.
OTIME3: NOUT% ;Output number
JSERRO (<NOUT failed>) ;Owie
SKIPE T4 ;Any seperator character?
IDPB T4,T1 ;Yes, store it now
MOVX T3,NO%LFL!NO%ZRO!FLD(2,NO%COL)!FLD(^D10,NO%RDX) ;2 for the rest
RET ;Return to above
SUBTTL Output Subroutines -- Output Standard Date/Time
;Outputs time of day from internal format, call with T1/ output byte pointer.
;Call at OODTIN with T1/ pointer, current time, suppressing columnation.
;Call at OODTIM with T1/ pointer, T2/ time, suppressing columnation.
;Call at OODTI1 with T1/ pointer, T2/ time, and T3/ time format bits.
;Returns +1 always with T1/ updated pointer.
OODTIN: SETO T2, ;The time is now
OODTIM: MOVX T3,OT%SCL ;Suppress columnation 9 days of the month
OODTI1: ODTIM% ;Zap it to terminal
JSERRO (<ODTIM failed>) ;Owie
RET ;Return
;Routine to output time of day as expressed in seconds since midnight.
;Call with T1/ pointer, T2/ time as seconds since midnight.
;Returns +1 always T1/ updated.
OTOD: SAVEAC <P1,P2> ;Save some space
CALL PCHECK ;(T1/T1) Insure real byte pointer in T1
IDIVI T2,^D60*^D60 ;Get hours in T2
IDIVI T3,^D60 ;Get minutes in T3 and seconds in T4
DMOVEM T3,P1 ;Put minutes in P1 and seconds in P2
CALL OTOD2 ;(T1,T2/T1) Use local routine for just numbers
MOVE T2,P1 ;Load the number of minutes
CALL OTOD1 ;(T1,T2/T1) Use routine sending colon and time
SKIPE T2,P2 ;Load the seconds, don't output if its zero
CALL OTOD1 ;(T1,T2/T1) Use routine sending colon and time
MOVEI T2,0 ;Load a null
IDPB T2,T1 ;Store it last
MOVNI T2,1 ;Back up the byte pointer
ADJBP T2,T1 ; by one and
MOVEM T2,T1 ; store back the byte pointer
RET ;Return
;Local routine to send time (OTOD2) or colon and time (OTOD1).
;Accepts T1/ output pointer, T2/ number.
;Returns +1 always, T1/ updated
OTOD1: MOVEI T3,":" ;Load suffix character next
IDPB T3,T1 ;Store seperator
OTOD2: IDIVI T2,^D10 ;Get the two digits
ADDI T2,"0" ;Convert to ASCII
IDPB T2,T1 ;Send that first digit out
ADDI T3,"0" ;Pump up another ASCII digit
IDPB T3,T1 ;Send that too
RET ;Return
SUBTTL Output Subroutines -- Output JSYS Error Message
;Subroutine to send along the last JSYS error
;Call with T1/ output designator
;Returns +1 always with T1/ updated
OERSTR: MOVX T2,<.FHSLF,,-1> ;This fork's last error
SETZ T3, ;No limit
ERSTR% ;Get string to error
CALLRET OERSTE ;(T1/T1) Undefined error number
JFCL ;String size out of bounds or bad designator?
RET ;Return to caller with T1 updated
OERSTE: HRROI T2,[ASCIZ/Undefined error /] ;Output label for string
CALL ISOUT ;(T1,T2/T1) Append that string too
CALL GETERR ;(/T2) Get fork's last error
MOVEI T3,4+4 ;Radix 8
NOUT% ;Don't use OOCT routine to output that number
ERNOP. ;Ignore error within error within error
RET ;Return with T1 updated
SUBTTL Error Handler -- Error Messages
;Subroutine to handle JSYS errors
;Call with CX/address of ASCIZ string
;Returns +1 always, message, error, and trailing CRLF printed
JSERR1: TXOE F,FL%ERR ;Error within an error?
RET ;Return before additional damage can occur
HRROM CX,ERRADR ;Save address of string to print
SAVEAC <T1,T2,T3,T4> ;Save some ACs today please
HRROI T1,ERRBUF ;Point to text buffer
TXNN F,FL%ACJ ;Acting like the ACJ?
IFSKP. ;Yes
HRROI T2,[ASCIZ/Access Control Facility error detected at /] ;Label
CALL ISOUT ;(T1,T2/T1) Send that
CALL OODTIN ;(T1/T1) Output date time of now
HRROI T2,[ASCIZ/
/] ;Label the next little bit
CALL ISOUT ;(T1,T2/T1) Send that and return
ENDIF. ;End of acting like the ACJ code
MOVE T2,ERRADR ;Load pointer to error text string
CALL ISOUT ;(T1,T2/T1) Start off with that text
CALL OERSTR ;(T1/T1) Append in the last error text please
CALL OCRLF ;(T1/T1) Append a CRLF to all of that
CALL PERRO ;(/) Send all of that to terminal as error mess
HRROI T1,ERRBUF ;Point to text buffer again
TXNE F,FL%ACJ ;[116] Acting like the ACJ?
CALL SENLOG ;[116] (T1/) Send to log file if possible
TXZ F,FL%ERR ;Clear error bit
RET ; and return
;Small routine to return this fork's last error in T2
;Returns +1 always, T2/ error number, T1 preserved
GETERR: PUSH P,T1 ;Save T1
MOVEI T1,.FHSLF ;Load this fork
GETER% ;Get last error in T2
TLZ T2,-1 ;Zap junk in LH
POP P,T1 ;Restore T1
RET ;Return
SUBTTL Error Handler -- Panic and Control-C Interrupt
;Here when a panic interrupt hits us. For now we just crash.
PANIC: BUG(HLT,PAN,<Panic interrupt>) ;Just say crash
;Here when a control-C seen, terminate the world by crashing.
CNTRLC: BUG(HLT,CCC,<Control-C Crash>) ;Boom
SUBTTL Error Handler -- Crash Handler
;Here when we want to crash, save all of the ACs, save ourself, then crash.
;Called by JSR BUGHLT, following is ASCIZ/crash code/ and ASCIZ/crash reason/.
BUGHLT: EXP 0 ;Called by JSR BUGHLT
;Save all the ACs, get a new stack.
MOVEM 17,BUGACS+17 ;Save all of the ACs here please
MOVEI 17,BUGACS ;Get source,,destination (0,,BUGACS)
BLT 17,BUGACS+16 ;Move all of the rest of them to memory please
MOVE P,[IOWD BUGLEN,BUGPDL] ;Load a new stack pointer
;Save last TOPS-20 error, send message to terminal about this problem.
CALL GETERR ;(/T2) Get last JSYS error code
HRRZM T2,LASERR ;Save it here
CALL FATALE ;(/) Let me tall ya sumthin ma ma ma ma mannn
;Save our image in a good place today.
HRROI T2,[ASCIZ/DMP:ACJ-/] ;Load the initial part of filename
CALL BUGSAV ;(T2/) Save ourselves
IFNSK. ;If it failed
HRROI T2,[ASCIZ/SYSTEM:ACJ-/] ;Try SYSTEM: this time
CALL BUGSAV ;(T2/) Try again
OJSERR (<Can't save crash>) ;This will be interesting debugging
ENDIF. ;We did all we could
;Kill inferiors, disable all GETOK functions, close log file.
MOVX T1,.FHINF ;Load fork handle for all inferiors
KFORK% ;Kill all of them
ERNOP. ;Ignore errors at this point
TXO F,FL%ERR ;[110] Light error in progress bit please
CALL DISFNC ;(/) Disable all functions before crashing
CALL CLOLOG ;[110] (/) Sweep and close the log file
CALL SECCLA ;[111] (/) Close all cached access.control
;Restore ACs. Halt if not running as job 0. Restart if running as job 0.
MOVSI 17,BUGACS ;Get source,,destination (BUGACS,,0)
BLT 17,16 ;Move all but one of them back from memory
MOVE 17,BUGACS+17 ;Reload the last AC
RESET% ;Dump all resources we might have
SKIPN OURJOB ;Are we running under job 0?
JRST ASTART ;For security reasons, restart ourselves
HALTF% ;Halt
JRST .-1 ; and don't continue either
;Print a message on the console about this problem, only called from BUGHLT.
;Returns +1 always.
FATALE: HRROI T1,ERRBUF ;Point to error buffer today
HRROI T2,[ASCIZ/
Access control program fatal error "/]
CALL ISOUT ;(T1,T2/T1) Send that along first
MOVE T2,BUGHLT ;Point to ASCIZ strings
HRLI T2,(Point 7) ;Make a byte pointer to it
CALL ISOUT ;(T1,T2/T1) Copy the reason code
MOVE Q1,T2 ;Save this for a little bit
HRROI T2,[ASCIZ/" (/] ;Load the next little bit
CALL ISOUT ;(T1,T2/T1) Copy the string
MOVEI T2,1(Q1) ;Get next address after null
HRLI T2,(Point 7) ;Point to rest of string
CALL ISOUT ;(T1,T2/T1) Send that along
HRROI T2,[ASCIZ/)
Last TOPS-20 error: /] ;Label next string
CALL ISOUT ;(T1,T2/T1) Send that along
CALL OERSTR ;(T1/T1) Send last JSYS error next
HRROI T2,[ASCIZ/
Access control terminated at /] ;Start the next line
CALL ISOUT ;(T1,T2/T1) Send that along also
CALL OODTIN ;(T1/T1) Output the time of now
CALL OCRLF ;(T1/T1) Append a crlf
HRROI T1,ERRBUF ;Point to error buffer again
PSOUT% ;Send to the console
RET ;Return to caller
;Here to get a JFN on a crash filename and save ourselves.
;Call with T2/ pointer to initial part of crash filename
;Returns +1 if error.
;Returns +2 if crash saved.
BUGSAV: HRROI T1,BUGFIL ;Point to place to store filename
CALL ISOUT ;(T1,T2/T1) Copy first part of string
HRRZ T2,EV+2 ;Load the edit number of the ACJ
CALL OOCT ;(T1,T2/T1) Send the OCTAL edit number next
HRROI T2,[ASCIZ/-/] ;Delimit the string
CALL ISOUT ;(T1,T2/T1) Send the delimiter
MOVE T2,BUGHLT ;Load the stop address again
HRLI T2,(Point 7) ;Make a byte pointer again
CALL ISOUT ;(T1,T2/T1) Copy the crash code next
HRROI T2,[ASCIZ/-CRASH.EXE/] ;Finish up the string
CALL ISOUT ;(T1,T2/T1) Send the file type last
MOVX T1,GJ%SHT!GJ%FOU ;Load short form and for output bits
HRROI T2,BUGFIL ;Point to file we just made up
GTJFN% ;Try to get a JFN on dump file
ERJMP R ;Return +1 for error
MOVE T4,T1 ;We got a JFN, copy it for a sec
HRROI T1,ERRBUF ;Point back to error buffer again
HRROI T2,[ASCIZ/ Access control crash saved as /] ;Load label
CALL ISOUT ;(T1,T2/T1) Start the text right
MOVE T2,T4 ;Load the JFN
CALL ISOUT ;(T1,T2/T1) Save initial text
CALL OJFNS ;(T1,T2/T1) Send the filename next
CALL OCRLF ;(T1/T1) Make it neat
MOVE T1,T4 ;Reload the JFN again
HRLI T1,.FHSLF ;Make this fork,,jfn
MOVX T2,SS%CPY!SS%RD!SS%EXE!FLD(-770,SS%NNP)!FLD(0,SS%FPN) ;Page 0-767
SSAVE% ;Save our image, closes the JFN today
ERJMP R ;Return +1 if errors today
HRROI T1,ERRBUF ;Point to error buffer
PSOUT% ;Send to console
RETSKP ;Return +2 for success
SUBTTL End of ACJDEC
;Dump literals here
LOWCD ;Get to low seg
DECLIT: XLIST ;Remove literals from listing
LIT ;Dump them here
LIST ;Resume listing
;Get globular symbols
GGLOBS
END <EVLEN,,EV>