// DLSDialInServer.bcpl -- Control program for Alto DLS // Last modified July 11, 1983 12:02 PM by Taft // Last modified January 15, 1985 10:56 AM by Diebert get "DLSDriver.decl" get "Pup.decl" get "DLSControl.decl" get "AltoDefs.d" external [ // outgoing procedures DLSCommand; Connect; OpenPortForCtx; TalkToRemote; TerminalToNet; NetToTerminal NetError; Disconnect; CheckConnection; MakeBSP // Procedures defined in other parts of the DLS control program DLSCommandError; OtherPupProc; SendTelnetProtocol LeaveRemoteMode; DLSReturnFrom; DLSRemoteError; Grapevine // Procedures defined in DLSUtil GetString; Echo; Confirm; Error; Ws; Wss; Wns; TimeCallStop; Set7BitProcs GetNumber; WizardMode; OnListCheck // Procedures defined in DLS driver DLSResetOutput; DLSOutputEmpty; DLSInputIdle; ControlOut; ControlIn; DetermineDLSLineSpeed; UpdateCarrierOn; WaitForBitTimes DLSInput; DLSInput7; DLSOutput; DLSOutput7; DLSOutputTI // Procedures defined in other packages OpenLevel1Socket; CloseLevel1Socket; ReleasePBI; OpenRTPSocket; CloseRTPSocket; CreateBSPStream; CloseBSPSocket; BSPForceOutput; GetPBI;AppendStringToPup; BSPGetMark; BSPPutMark; BSPPutInterrupt; GetPartner; ExchangePorts; CompletePup InitializeContext; CallContextList; Block; Enqueue; Dequeue; Unqueue; SetTimer; TimerHasExpired; Dismiss; PutTemplate; PutNum; WriteRingBuffer // Procedures defined in operating system Gets; Puts; Endofs; Resets; Allocate; Free; AddToZone; Zero; CallSwat; MyFrame; GotoFrame; ReturnFrom; Noop; DefaultArgs; MoveBlock // incoming statics mainCtx; ctxTable; versionText; crlf; dlsWizardList dlsName @lbTable // DLS line block table CtxRunning; sysZone; lenPBI; pbiFreeQ; ndbQ; keyDsp; postedNotice; socketSequence loginServerCB; ] // --------------------------------------------------------------------------- let DLSCommand(ctx) be // --------------------------------------------------------------------------- // Does local command decoding. Returns when user hangs up. [ let type = nil let length = nil let width = nil let dlb = ctx>>CTX.dlb let soc = lv ctx>>CTX.socket dlb>>DLB.error = DLSCommandError ctx>>CTX.lineState = lineStateActive // Flag not in remote mode dlb>>DLB.timeout = 100 // 1-second timeout for DLS Gets Puts(dlb, $>) // Prompt // Dispatch on command character let char = Gets(dlb) switchon char into [ case $8: Wss(dlb, "Set line to 8 bit no parity ") if Confirm(dlb) then [ dlb>>DLB.eightBit = 1; dlb>>DLB.flowControl = 0 ] endcase case $7: Wss(dlb, "Set line to 7 bit even parity ") if Confirm(dlb) then [ dlb>>DLB.eightBit = 0; dlb>>DLB.flowControl = 1 ] endcase case 3: // Control-C means connect to default host if Connect(ctx, ctx>>CTX.host) then TalkToRemote(ctx) endcase case $B: case $b: Wss(dlb, "Bye [Confirm] ") if Confirm(dlb) then [ Disconnect(ctx) PutTemplate(dlb, "#$O off*n*l", dlb>>DLB.line) return ] endcase case $C: case $c: if Connect(ctx, 0) then TalkToRemote(ctx) endcase case $D: case $d: Wss(dlb, "Disconnect ") if Confirm(dlb) then Disconnect(ctx, 3000) endcase case $E: case $e: Wss(dlb, "Escape character currently = ") Echo(dlb, ctx>>CTX.escapeChar) Wss(dlb, " Change to ") char = Gets(dlb) if char eq $*n % char eq $*l % char eq 177B then [ Error(dlb, " Illegal escape character*n*l") // Don't allow cr, lf, delete endcase ] Echo(dlb, char); Puts(dlb, $*s) if Confirm(dlb) then ctx>>CTX.escapeChar = char endcase case $F: case $f: Wss(dlb, "Flush typein stream ") unless ctx>>CTX.socketOpen do [ Error(dlb, "? No connection open*n*l"); endcase ] if Confirm(dlb) then if BSPPutInterrupt(soc, 0, "Sync") then BSPPutMark(soc, 1) endcase case $G: case $g: Wss(dlb, "Grapevine...") if Grapevine(ctx) then TalkToRemote(ctx) endcase case $I: case $i: Wss(dlb, "Ignore escape character ") if Confirm(dlb) then [ Wss(dlb, "Number of seconds to ignore for? ") ctx>>CTX.escapeTime = GetNumber(dlb) if ctx>>CTX.escapeTime le 0 then [ ctx>>CTX.escapeTime = 0; endcase ] ctx>>CTX.escapeDisabled = 1 SetTimer(lv ctx>>CTX.escapeCharTimer, 100) ] endcase case $L: case $l: PutTemplate(dlb, "Local echoing $S ", (ctx>>CTX.localEcho? "off", "on")) if Confirm(dlb) then ctx>>CTX.localEcho = not ctx>>CTX.localEcho endcase case $P: case $p: PutTemplate(dlb, "Pad $S ", (dlb>>DLB.noPad? "on", "off")) if Confirm(dlb) then [ dlb>>DLB.noPad = not dlb>>DLB.noPad; Set7BitProcs(dlb) ] endcase case $R: case $r: Wss(dlb, "Resume connection ") unless ctx>>CTX.socketOpen do [ Error(dlb, "? No connection open*n*l"); endcase ] if Confirm(dlb) then TalkToRemote(ctx) endcase case $T: case $t: Wss(dlb, "Set terminal parameters*n*lType? ") type = GetNumber(dlb) if type ls 0 then endcase if type gr 10 % (type gr 3 & type ls 7) do [ Wss(dlb, "Bad terminal type*n*l"); endcase ] ctx>>CTX.terminalType = type Wss(dlb, "Width? ") width = GetNumber(dlb) if width ls 0 then endcase ctx>>CTX.terminalWidth = width Wss(dlb, "Length? ") length = GetNumber(dlb) if length ls 0 then endcase ctx>>CTX.terminalLength = length endcase case $V: case $v: PutTemplate(dlb, "Version: $S running on Alto $O#$O#*n*l", versionText, (ndbQ!0)>>NDB.localNet, (ndbQ!0)>>NDB.localHost) endcase case $W: case $w: if ctx>>CTX.name eq 0 do [ Wss(dlb, "Wizard mode*n*lLogin required. *n*l") endcase ] if dlb>>DLB.lineType ne ltHardwired do unless dlb>>DLB.carrierOff then [ Wss(dlb, "Wizard mode*n*lYou must break connection first!!!*n*l") endcase ] Wss(dlb, "Wizard mode [Confirm] ") if Confirm(dlb) then if OnListCheck(ctx, dlb, dlsWizardList) then WizardMode(ctx, dlb) endcase case $X: case $x: PutTemplate(dlb, "Xon/Xoff (Flow Control) $S ", (dlb>>DLB.flowControl? "off", "on")) if Confirm(dlb) then dlb>>DLB.flowControl = not dlb>>DLB.flowControl endcase case $?: Wss(dlb, "? Commands are: Bye, Connect, Disconnect, Escape, Flush, Grapevine*n*l") Wss(dlb, " Ignore escape char, Local, Pad, Resume, Terminal, Version,*n*l") Wss(dlb, " Xon/Xoff, 7 bit, 8 bit.*n*l") if ctx>>CTX.host ne 0 then PutTemplate(dlb, " Control-C = connect to $S*n*l", ctx>>CTX.host) endcase case 177B: // delete Wss(dlb, " XXX") case $*n: case $*l: case $*s: Wss(dlb, crlf) endcase default: Error(dlb, " ?*n*l", char) ] ] repeat // --------------------------------------------------------------------------- and Connect(ctx, hostName) = valof // --------------------------------------------------------------------------- [ let dlb = ctx>>CTX.dlb Wss(dlb, "Connect to: ") if ctx>>CTX.socketOpen then [ Error(dlb, "? Connection already open*n*l"); resultis false ] let frnPort = vec lenPort let string = vec 20 test hostName eq 0 ifso [ unless GetString(dlb, string, 40) resultis false hostName = string ] ifnot PutTemplate(dlb, "$S*n*l", hostName) unless GetPartner(hostName, dlb, frnPort, 0, 1) do [ Error(dlb, crlf); resultis false ] if frnPort>>Port.host eq 0 then [ Error(dlb, "Inadequate foreign port specification*n*l") resultis false ] resultis MakeBSP(frnPort, ctx) ] // --------------------------------------------------------------------------- and Disconnect(ctx, timeout) be // --------------------------------------------------------------------------- [ let dlb = ctx>>CTX.dlb Set7BitProcs(dlb) if ctx>>CTX.socketOpen then [ if ctx>>CTX.auxCtx ne 0 then [ Unqueue(mainCtx, ctx>>CTX.auxCtx) ctx>>CTX.auxCtx = 0 ] CloseBSPSocket(lv ctx>>CTX.socket, timeout) ctx>>CTX.socketOpen = false ] ] // --------------------------------------------------------------------------- and CheckConnection(ctx) = valof // --------------------------------------------------------------------------- // Returns true if the connection was open and became closed. [ if ctx>>CTX.socketOpen then test ctx>>CTX.socket.state eq stateOpen ifso if ctx>>CTX.timeout then [ Disconnect(ctx, 0) // Abort connection Wss(ctx>>CTX.dlb, " [Connection timed out]*n*l>") resultis true ] ifnot [ Disconnect(ctx, 3000) Wss(ctx>>CTX.dlb, " [Connection closed remotely]*n*l>") resultis true ] resultis false ] // --------------------------------------------------------------------------- and OpenPortForCtx(ctx, frnPort) = valof // --------------------------------------------------------------------------- [ let lclPort = vec lenPort; Zero(lclPort, lenPort) if socketSequence eq 0 % socketSequence eq socTransient then socketSequence = socketSequence+1 lclPort>>Port.socket↑1 = socketSequence lclPort>>Port.socket↑2 = ctx>>CTX.dlb>>DLB.line socketSequence = socketSequence+1 let soc = lv ctx>>CTX.socket Zero(soc, lenBSPSoc) OpenLevel1Socket(soc, lclPort, frnPort) resultis soc ] // --------------------------------------------------------------------------- and TalkToRemote(ctx) be // --------------------------------------------------------------------------- [ let dlb = ctx>>CTX.dlb test dlb>>DLB.eightBit eq 1 ifso [ dlb>>DLB.gets = DLSInput dlb>>DLB.puts = DLSOutput ] ifnot Set7BitProcs(dlb) let auxCtx = vec 150 Enqueue(mainCtx, InitializeContext(auxCtx, 150, NetToTerminal, 1)) auxCtx!3 = ctx ctx>>CTX.auxCtx = auxCtx ctx>>CTX.lineState = lineStateRemote // Flag that we are in remote mode ctx>>CTX.timeout = false ctx>>CTX.returnFrame = MyFrame() // Frame to force return to dlb>>DLB.error = DLSRemoteError dlb>>DLB.timeout = -1 // Never time out DLS Gets TerminalToNet(ctx) // Returns on escape char or error Unqueue(mainCtx, auxCtx) ctx>>CTX.auxCtx = 0 dlb>>DLB.error = DLSCommandError Wss(dlb, crlf) Set7BitProcs(dlb) ] // --------------------------------------------------------------------------- and TerminalToNet(ctx) be // --------------------------------------------------------------------------- // This is a procedure called within the line's main context. // Its task is to copy characters from terminal to net. // It returns if either the escape character is typed or the // connection is broken. The NetToTerminal process can force us // to return by subterfuge involving diddling of stack frames. [ let dlb = ctx>>CTX.dlb let soc = lv ctx>>CTX.socket let bspStr = lv soc>>BSPSoc.bspStr // Send terminal parameters. let type, length = ctx>>CTX.terminalType, ctx>>CTX.terminalLength if type eq 0 & dlb>>DLB.baud ge 1200 do [ type = 10 if ctx>>CTX.terminalLength eq 0 then length = 24 ] if type ne 0 then SendTelnetProtocol(soc, 4B, type) if length ne 0 then SendTelnetProtocol(soc, 3B, length) let width = ctx>>CTX.terminalWidth if width eq 0 then width = selecton dlb>>DLB.baud into [ // Select width by assuming terminal type on the basis of baud rate case 300: 79 // TI 700 case 1200: 80 // Tektronix 4023 default: 72 // All others (including Teletype) ] SendTelnetProtocol(soc, 2B, width) // Main loop to send terminal characters to net. // This code accumulates characters and sends them only when the line goes // idle or one second elapses from the beginning of a burst, whichever // occurs first. This attempts to maximize the number of characters per Pup // during sustained activity without introducing excessive echoing delays. [ // repeat let burstStarted = false let timer = nil [ // repeat let char = Gets(dlb) unless burstStarted do [ burstStarted = true; SetTimer(lv timer, 100) ] test ctx>>CTX.escapeDisabled ifso [ test TimerHasExpired(lv ctx>>CTX.escapeCharTimer) ifso test ctx>>CTX.escapeTime le 0 ifso [ ctx>>CTX.escapeDisabled = 0 ] ifnot [ ctx>>CTX.escapeTime = ctx>>CTX.escapeTime - 1; SetTimer(lv ctx>>CTX.escapeCharTimer, 100) ] ifnot [ ] ] ifnot [ if (char & 177b) eq ctx>>CTX.escapeChar do [ Set7BitProcs(dlb) return ] ] Puts(bspStr, char) if ctx>>CTX.localEcho then [ Puts(dlb, char) if char eq $*n then [ Puts(bspStr, $*l); Puts(dlb, $*l) ] ] if Endofs(dlb) then [ if TimerHasExpired(lv timer) then break WaitForBitTimes(dlb, 2) // wait for next char to start if it's going to if DLSInputIdle(dlb) then break ] ] repeat // sendNow if forcing due to timeout -- because the next ForceOut // is also likely to be invoked by a timeout, and one second is more // than the Pup package's timeout before requesting acknowledgment. BSPForceOutput(soc, TimerHasExpired(lv timer)) ] repeat ] // --------------------------------------------------------------------------- and NetToTerminal(ctx) be // --------------------------------------------------------------------------- // This is a separate process whose only task is to copy characters // from net to terminal. [ ctx = ctx!3 // Get main context for this line let dlb = ctx>>CTX.dlb let str = lv ctx>>CTX.socket.bspStr [ let char = Gets(str) test ctx>>CTX.syncCount eq 0 ifso Puts(dlb, char) ifnot DLSResetOutput(dlb) ] repeat ] // --------------------------------------------------------------------------- and NetError(str, ec) = valof // --------------------------------------------------------------------------- // This is the procedure called via the BSP stream error dispatch // for any abnormal condition. Note that failures may occur either // within the line's main context (TerminalToNet) or within // the auxiliary context (NetToTerminal). [ let soc = str-offset BSPSoc.bspStr/16 let ctx = soc-offset CTX.socket/16 let dlb = ctx>>CTX.dlb switchon ec into [ case ecMarkEncountered: switchon BSPGetMark(soc) into [ case 1: // Data Mark ctx>>CTX.syncCount = ctx>>CTX.syncCount-1 DLSResetOutput(dlb) while ctx>>CTX.syncCount ls 0 do Block() endcase case 5: // Timing Mark until DLSOutputEmpty(dlb) do Block() until BSPPutMark(soc, 6) loop // Timing Mark Reply ] // Unknown Mark types are ignored resultis 0 // Return a null -- the user will never notice! case ecPutsTimeout: if soc>>BSPSoc.unAckedPups eq 0 & soc>>BSPSoc.bytesPerPup ne 0 then [ if ctx eq CtxRunning then //do this only in main ctx Error(dlb, "*007") //too much typeahead, Warren resultis false ] ctx>>CTX.timeout = true //fall thru to other error cases case ecBadStateForGets: case ecBadStateForPuts: LeaveRemoteMode(ctx) ] ] // --------------------------------------------------------------------------- and MakeBSP(frnPort, ctx) = valof // --------------------------------------------------------------------------- [ let dlb = ctx>>CTX.dlb let soc = OpenPortForCtx(ctx, frnPort) let login = CheckForLogin(frnPort, ctx>>CTX.name) OpenRTPSocket(soc, 0, modeInitAndReturn, 0, OtherPupProc) let timer = nil SetTimer(lv timer, 3000) // 30 seconds [ Block() if soc>>RTPSoc.state eq stateOpen break if soc>>RTPSoc.state ne stateRFCOut % TimerHasExpired(lv timer) % dlb>>DLB.carrierOff then [ CloseRTPSocket(soc, 0) CloseLevel1Socket(soc) ctx>>CTX.socketOpen = false Wss(dlb, "*n*lFailed to connect*n*l") resultis false ] ] repeat CreateBSPStream(soc) ctx>>CTX.socketOpen = true soc>>BSPSoc.error = NetError // Own stream error procedure ctx>>CTX.syncCount = 0 if login then PutTemplate(lv soc>>BSPSoc.bspStr, "Login $S $S 1*n", ctx>>CTX.name, ctx>>CTX.password) resultis true ] // --------------------------------------------------------------------------- and CheckForLogin(frnPort, name) = valof // --------------------------------------------------------------------------- [ let sendLogin = false let soc = vec lenPupSoc let infoPort = vec lenPort MoveBlock(infoPort, frnPort, lenPort) infoPort>>Port.socket↑1 = 0 infoPort>>Port.socket↑2 = miscServicesSocket OpenLevel1Socket(soc, 0, infoPort) for i=1 to 5 do //Try five times.... [SendWherePup let p=GetPBI(soc) AppendStringToPup(p, 1, name) p>>PBI.pup.type = typeWhereUserRequest CompletePup(p) let wait=nil; SetTimer(lv wait, 200) // 2 sec Block() repeatuntil TimerHasExpired(lv wait) % soc>>PupSoc.iQ.head ne 0 p=Dequeue(lv soc>>PupSoc.iQ) if p eq 0 then loop let pup=lv p>>PBI.pup switchon p>>PBI.pup.type into [ case typeWhereUserReply: [ sendLogin = true ReleasePBI(p) break ] default: [ ReleasePBI(p) ] ] //Switchon ]SendWherePup CloseLevel1Socket(soc) resultis sendLogin ]