TITLE SMTDCN - Multiforking mail listener for DECnet SUBTTL From SMTPSV by Chris Maio, Columbia University, 21 July 194 SEARCH MACSYM,MONSYM .REQUIRE SYS:MACREL SALL ; suppress macro expansions .DIRECTIVE FLBLST ; sane listings for ASCIZ, etc. .TEXT "/NOINITIAL" ; suppress loading of JOBDAT .TEXT "SMTDCN/SAVE" ; save as SMTDCN.EXE ; Version components SMTWHO==0 ; who last edited SMTDCN (0=developers) SMTVER==6 ; SMTDCN's release version (matches monitor's) SMTMIN==1 ; SMTDCN's minor version SMTEDT==^D9 ; SMTDCN's edit version ; This version of SMTPSV manages a set of MAISER forks, and uses ; DECnet JFNs. ; ; The maximum number of simultaneous connections allowed is controlled ; by the setting of NFKS below. The maximum number of forks to allow to ; exist after they have finished (in order to cut down on MAISER startup ; overhead) is specified with MAXIDL. IFNDEF MAXIDL,MAXIDL==2 ; maximum allowable idle forks IFNDEF NFKS,NFKS==10 ; maximum simultaneous connections IFNDEF PDLLEN,PDLLEN==200 ; PDL size A=1 ; AC definitions B=2 C=3 D=4 FX=14 ; fork table index of current fork CX=16 ; Fork variables ; ; The current fork index (not a TOPS-20 fork handle) is always kept in ; FX, so indexing into the fork table is done implicitly by the following ; DEFSTRs. DEFSTR FH,FKSTAT(FX),17,18 ; TOPS-20 fork handle DEFSTR FKRUN,FKSTAT(FX),18,1 ; set if fork is currently running DEFSTR FKJFN,FKSTAT(FX),35,9 ; fork's network JFN DEFINE NOINT <CALL .NOINT> ; disable PSI DEFINE OKINT <CALL .OKINT> ; enable PSI DEFINE TMOSET (INTVL,RETAD) < SETZM CLKCNT PUSH P,[PC%USR+RETAD] POP P,CLKLOC PUSH P,[-<INTVL/5>] POP P,CLKCNT >;DEFINE TMOSET DEFINE TMOCLR < SETZM CLKCNT SETZM CLKLOC >;DEFINE TMOCLR SUBTTL Data area PC1: BLOCK 1 ; level 1 interrupt PC IN1ACS: BLOCK 20 ; level 1 AC save PC2: BLOCK 1 ; level 2 interrupt PC IN2ACS: BLOCK 20 ; level 2 AC save CLKCNT: BLOCK 1 ; clock count CLKLOC: BLOCK 1 ; clock location NRUN: BLOCK 1 ; active fork count list NFORKS: BLOCK 1 ; subfork count NJFNS: BLOCK 1 ; connection count FKSTAT: BLOCK NFKS ; fork status PDL: BLOCK PDLLEN ; stack SUBTTL Pure data CHNTAB: PHASE 0 ; interrupt channel table TIMCHN:!1,,TIMINT ; timeout BLOCK .ICIFT-. .ICIFT:!2,,FRKINT ; fork termination interrupts BLOCK ^D36-. DEPHASE LEVTAB: PC1 ; level 1 PC2 ; level 2 BLOCK 1 ; level 3 unused SUBTTL Main program ; Entry vector EVEC: JRST SMTDCN ; START address JRST SMTDCN ; REENTER address <FLD SMTWHO,VI%WHO>!<FLD SMTVER,VI%MAJ>!<FLD SMTMIN,VI%MIN>!<FLD SMTEDT,VI%EDN> EVECL==.-EVEC SMTDCN: RESET% MOVE P,[IOWD PDLLEN,PDL] MOVX A,.FHSLF ; enable all capabilities RPCAP% IORB C,B EPCAP% MOVE B,[LEVTAB,,CHNTAB] SIR% ; set up interrupt channels EIR% ; enable interrupts MOVX B,1B<TIMCHN>!1B<.ICIFT> ; channels to interrupt on AIC% ; activate the interrupt channels JSP CX,SETTIM ; start the timer SETZM FKSTAT ; clear the fork table MOVE A,[FKSTAT,,FKSTAT+1] BLT A,FKSTAT+NFKS-1 DO. MOVE A,NRUN ; get running fork count CAIL A,NFKS ; all in use? WAIT% ; yes, wait for one to complete CALL LISTEN ; listen for a connection LOOP. ; and go back for another one ENDDO. SUBTTL Interrupt routines ; Here to initialize the timer, called via JSP CX,SETTIM. Note that A,B,C ; are clobbered! SETTIM: MOVE A,[.FHSLF,,.TIMEL] ; tick the timer MOVX B,^D5000 ; every 5 seconds MOVX C,TIMCHN ; on TIMCHN channel TIMER% ERJMP .+1 JRST (CX) ;;;Here on timer interrupt TIMINT: MOVEM 17,IN1ACS+17 ; save ACs MOVEI 17,IN1ACS BLT 17,IN1ACS+16 JSP CX,SETTIM ; reinitialize the timer AOSE CLKCNT ; should time out now? IFSKP. SKIPE A,CLKLOC ; get time-out routine MOVEM A,PC1 ; set it ENDIF. MOVSI 17,IN1ACS ; restore ACs BLT 17,17 DEBRK% ; FRKINT is called on fork termination to scan the fork list to find ; any halted forks and close the corresponding connections. FRKINT: MOVEM 17,IN2ACS+17 ; save ACs MOVEI 17,IN2ACS BLT 17,IN2ACS+16 MOVE 17,IN2ACS+17 MOVE A,PC2 ; get interrupt pc location MOVE A,-1(A) ; get waiting instruction CAME A,[WAIT%] ; waiting for a free fork? IFSKP. SETONE PC%USR,PC2 ; yes, make JSYS return to user ENDIF. MOVSI FX,-NFKS ; loop through all forks DO. IFQN. FKRUN ; only "running" forks are checked LOAD A,FH ; get the fork handle RFSTS% ; get its status ERJMP STOP HRRZS B ; flush flags from PC LOAD D,RF%STS,A ; get the fork status code CAIE D,.RFHLT ; halted? CAIN D,.RFFPT ; or terminated? SOSA NRUN ; yes, one less running fork ANSKP. SETZRO FKRUN ; say fork is no longer running CAIE D,.RFHLT ; halted normally? IFSKP. MOVE A,NFORKS ; get the number of existing forks SUB A,NRUN ; subtract balance of running forks CAILE A,MAXIDL ; too many free forks? ANSKP. ELSE. LOAD A,FH ; get the fork handle back KFORK% ; zap it ERJMP STOP SOS NFORKS ; one less fork to worry about SETZRO FH ; say the fork is gone ENDIF. LOAD A,FKJFN ; get the JFN CALL CLSJFN ; close the connection SETZRO FKJFN ; delete it from the table ENDIF. AOBJN FX,TOP. ; loop if more forks to examine ENDDO. MOVSI 17,IN2ACS BLT 17,17 DEBRK% ; return from the interrupt ERJMP STOP ; CLSJFN - close the DECnet connection ; ; Accepts: ; A/ network JFN ; Returns: ; +1 Always CLSABT: TXO A,CZ%ABT ; abort the connection CLSJFN: MOVE D,A ; get a copy of the JFN TMOSET (30,CLSABT) CLOSF% ; close it IFJER. TMOCLR MOVE A,D ; get the JFN back RLJFN% ; if close failed, just release JFN ERJMP .+1 ENDIF. TMOCLR SOS NJFNS ; one less connection RET SUBTTL LISTEN - listen for a connection ; Listens for a connection on the DECnet SMTP socket and starts up a copy ; of MAISER. ; ; Returns: ; +1 open failed ; +2 open succeeded, SMTP fork started LISTEN: STKVAR <SRVJFN> ; temp ac for storing JFN DO. MOVX A,GJ%SHT ;; HRROI B,[ASCIZ/SRV:125/] ; SMTP on object 125 HRROI B,[ASCIZ/SRV:TASK.MX-LISTENER/] ; SMTP name DEC uses... GTJFN% ; get a JFN to listen on IFJER. MOVX A,^D<10*1000> ; failed, wait a bit DISMS% LOOP. ; and try again ENDIF. MOVEM A,SRVJFN ; copy the JFN to a safe register MOVX B,<OF%RD!OF%WR!<FLD ^D7,OF%BSZ>> OPENF% ; wait for a connection IFJER. MOVE A,SRVJFN ; get the JFN back RLJFN% ; throw it away ERJMP .+1 MOVX A,^D<10*1000> ; failed, wait a bit DISMS% LOOP. ; and try again ENDIF. ENDDO. DO. MOVX B,.MORLS ; read link status MTOPR% IFNJE. JXN C,<MO%CON!MO%WCC>,ENDLP. ; connected? ANDXN. C,MO%WFC ; no, waiting for connect? MOVX A,^D1000 ; yes, wait a second DISMS% MOVE A,SRVJFN ; and try again LOOP. ELSE. MOVE A,SRVJFN ; get the JFN back CLOSF% ; throw it away ERJMP .+1 RET ; print error message and return ENDIF. ENDDO. AOS NJFNS ; bump connection count CALL GETFRK ; find a free fork table entry IFNSK. MOVE A,SRVJFN HRROI B,[ASCIZ/421 Insufficient system resources; please report this /] SETZ C, SOUTR% ERJMP .+1 CALLRET CLSJFN ; close the connection and return ENDIF. MOVE A,SRVJFN ; get the JFN back STOR A,FKJFN ; save the JFN LOAD A,FH ; get the fork handle in a LOAD B,FKJFN ; get the JFN HRLS B ; B/ input JFN,,output JFN SPJFN% ; set the primary JFNs ERJMP STOP NOINT ; defer interrupts LOAD A,FH SETZ B, ; start the fork at normal entry SFRKV% ; start it ERJMP STOP SETONE FKRUN ; say the fork has been started AOS NRUN ; bump running the running fork count OKINT ; allow interrupts again RET ; return to get another fork ENDSV. SUBTTL GETFRK - allocate a fork ; Scan the fork table looking for an idle fork. If one is found, its ; index is returned, otherwise a new fork is created unless the table is ; full. ; ; Returns: ; +1 no more forks ; +2 success, fork index in FX GETFRK: MOVSI FX,-NFKS ; loop through all forks DO. IFQE. FKJFN ; fork in use? JN FH,,RSKP ; no, if fork exists we can use it AOBJN FX,TOP. ENDIF. ENDDO. ; No idle fork exists, so create one if table can hold it MOVSI FX,-NFKS DO. IFQE. FKJFN ; fork in use? HRRZS FX ; no, isolate the fork index JN FH,,RSKP ; if exists, idle fork appeared, use it MOVX A,CR%CAP ; else make one with our caps CFORK% ERJMP STOP AOS NFORKS ; bump the fork count STOR A,FH ; save the handle TXZ A,.FHSLF MOVX A,GJ%SHT!GJ%OLD HRROI B,[ASCIZ/SYSTEM:MAISER.EXE/] GTJFN% ; get a handle on the file ERJMP STOP LOAD B,FH ; get the fork handle HRL A,B ; A/ fork,,JFN GET% ; load in the file ERJMP STOP RETSKP ; return with FX set up ENDIF. AOBJN FX,TOP. ; loop if more to try ENDDO. RET ; otherwise fail SUBTTL OKINT and NOINT - turn interrupts on and off .OKINT: SAVEAC <A> MOVX A,.FHSLF EIR% ; enable interrupts RET .NOINT: SAVEAC <A> MOVX A,.FHSLF DIR% ; disable interrupts RET SUBTTL Other randomness STOP: HRROI A,[ASCIZ/SMTDCN: /] ESOUT% MOVX A,.PRIOU HRLOI B,.FHSLF ; dumb ERSTR% SETZ C, ERSTR% NOP ; undefined error number NOP ; can't happen MOVX A,^D5000 ; sleep for 5 seconds DISMS% JRST SMTDCN ; restart ; Literals ...VAR:!VAR ; generate variables (there shouldn't be any) IFN .-...VAR,<.FATAL Variables illegal in this program> ...LIT: XLIST ; save trees during LIT LIT ; generate literals LIST END EVECL,,EVEC ; The End