CedarConsoleImpl.mesa
Copyright Ó 1990, 1991 by Xerox Corporation. All rights reserved.
Brent Welch December 4, 1990 12:26 pm PST
Michael Plass, October 22, 1991 4:22 pm PDT
In order to divert UNIX error messages it is necessary to open a pseudo-terminal and declare it as the console using an ioctl(). Closing the pseudo-terminal reverts the console to its default (i.e. the physical console).
DIRECTORY
Ascii,
CedarConsole,
Commander,
CommanderOps,
Feedback,
IO,
Process,
PseudoTerminal,
RefText,
Rope,
UnixErrno,
UnixSysCalls,
UnixTypes;
CedarConsoleImpl: CEDAR MONITOR
IMPORTS Commander, CommanderOps, Feedback, IO, Process, PseudoTerminal, RefText, Rope, UnixErrno, UnixSysCalls
EXPORTS CedarConsole = BEGIN
ROPE: TYPE ~ Rope.ROPE;
The Console record holds the master end of the pty, which is opened by PseudoTerminal.Allocate, the slave end, which we open and must be open for things to work, and a Feedback router to which we dump the error messages. Copying from the pty to Feedback is done in the background by a detached process.
Console: TYPE ~ RECORD [
stopped: BOOL ¬ TRUE,
pty: PseudoTerminal.PseudoTerminal ¬ NIL,
slaveTty: UnixTypes.FileDescriptor,
router: Feedback.MsgRouter,
process: PROCESS
];
console: Console; -- Global record containing console state
Structures for using UNIX ioctl(). The command word cleverly encodes the size of the argument and whether it is read, write, or read-write to the system call
IOParamType: TYPE ~ MACHINE DEPENDENT {(0), void(20H), R(40H), W(80H), WR(40H+80H), (255)};
IORequest: TYPE ~ MACHINE DEPENDENT RECORD [
paramType: IOParamType,
paramBytes: BYTE,
code: CHAR,
loByte: BYTE ¬ 0
];
TIOCCONS: IORequest = [void, 0, 't, 36]; -- <sys/ttycom.h> ¬IO(t, 36)
Cmd: Commander.CommandProc ~ {
Usage:"Console on" turns on the diversion of /dev/console to Feedback
"Console off" stops the diversion. /dev/console reverts to the physical console.
arg: Rope.ROPE ~ CommanderOps.NextArgument[cmd];
SELECT TRUE FROM
Rope.Equal[arg, "on", FALSE] => Start[];
Rope.Equal[arg, "off", FALSE] => Stop[];
ENDCASE => CommanderOps.Failed[cmd.procData.doc];
};
Start: PUBLIC ENTRY PROC [] RETURNS [] = {
res: UnixTypes.SysCallResult;
IF console.pty = NIL THEN {
Allocate a name for the pty, open both ends, and turn off CR to CR-LF mapping.
console.pty ¬ PseudoTerminal.Allocate[]; -- opens master end
console.slaveTty ¬ UnixSysCalls.Open[path: CHARPtrFromRefText[Rope.ToRefText[console.pty.slaveName]], flags: [access: RDWR], mode: []];
TRUSTED {
This ioctl() declares the PseudoTerminal to be the system console.
res ¬ UnixSysCalls.IOCtl[d: console.pty.controllerFD, request: LOOPHOLE[TIOCCONS], argp: LOOPHOLE[0]];
IF res # success THEN {
err: UnixErrno.Errno = UnixErrno.GetErrno[];
Feedback.PutF[console.router, oneLiner, $Debug, "CedarConsole: Unix Ioctl TIOCCONS error = %g", IO.int[ORD[err]]];
RETURN;
};
};
Create a router and let its output go to the default place. Currently this is the message window as well as the SystemScript typescript
console.router ¬ Feedback.CreateRouter[];
[] ¬ Feedback.RegisterRouter[console.router, $Console];
console.stopped ¬ FALSE;
TRUSTED {
console.process ¬ FORK CopyConsoleToFeedback[];
Process.Detach[console.process];
};
};
};
Stop: PUBLIC ENTRY PROC [] RETURNS [] = {
IF console.pty # NIL THEN {
console.stopped ¬ TRUE;
Process.Abort[console.process];
PseudoTerminal.Close[console.pty];
[] ¬ UnixSysCalls.Close[console.slaveTty];
console.pty ¬ NIL;
};
};
CHARPtrFromRefText: PROCEDURE [refText: REF TEXT] RETURNS [UnixTypes.CHARPtr] ~ TRUSTED {
charPtr: UnixTypes.CHARPtr ~
LOOPHOLE[LOOPHOLE[refText, LONG POINTER]+SIZE[TEXT[0]]];
RETURN [charPtr];
};
CopyConsoleToFeedback: PROC [] ~{
This procedure reads from the pty into a reftext, converts that to a rope, exracts substrings that don't contain CR or LF, and then passes the substrings to Feedback
buffer: REF TEXT ~ RefText.ObtainScratch[256];
bufPtr: UnixTypes.CHARPtr ~ CHARPtrFromRefText[refText: buffer];
inputRope: Rope.ROPE;
ReadLoop: PROC [in: UnixTypes.FileDescriptor] ~ {
length: INT;
position, start: INT;
inTheMiddle: BOOL; -- dangling output?
outStyle: Feedback.MsgType;
inTheMiddle ¬ FALSE;
outStyle ¬ oneLiner;
WHILE TRUE DO {
TRUSTED { length ¬ UnixSysCalls.Read[d: in, buf: bufPtr, nBytes: buffer.maxLength]; };
IF length <= 0 THEN {
IF NOT console.stopped THEN {
err: UnixErrno.Errno = UnixErrno.GetErrno[];
Feedback.PutF[console.router, oneLiner, $Debug, "CedarConsole: Unix Read error = %g", IO.int[ORD[err]]];
};
RETURN;
};
buffer.length ¬ length; -- this fixes up the RefText so we can treat it like a Rope
inputRope ¬ Rope.FromRefText[buffer];
Loop through the Rope eliminating embedded CR and NL
start ¬ 0;
position ¬ 0;
WHILE position < length DO
IF buffer[position] = Ascii.LF OR buffer[position] = Ascii.CR THEN {
IF position - start > 0 THEN {
Feedback.Append[console.router, outStyle, $Error, Rope.Substr[base: inputRope, start: start, len: position-start]];
};
outStyle ¬ oneLiner;
start ¬ position + 1;
};
position ¬ position + 1;
ENDLOOP;
Output anything left over
IF position - start > 0 THEN {
outStyle ¬ middle ;
Feedback.Append[console.router, outStyle, $Error, Rope.Substr[base: inputRope, start: start, len: position-start]];
};
Process.CheckForAbort[];
} ENDLOOP;
};
Feedback.Append[console.router, oneLiner, $Debug, "CedarConsole on"];
ReadLoop[in: console.pty.controllerFD !
IO.Error => {
Feedback.Append[console.router, oneLiner, $Debug, "CedarConsole IO.error"];
CONTINUE;
};
ABORTED => {
Feedback.Append[console.router, oneLiner, $Debug, "CedarConsole aborted"];
CONTINUE;
};
];
Feedback.Append[console.router, oneLiner, $Debug, "CedarConsole off"];
RefText.ReleaseScratch[buffer];
};
Start code registers a Console command, which must be invoked for this program to do anything. The RawViewers.command script makes a call to "Console on"
Commander.Register[
key: "Console", proc: Cmd, doc: "Start/Stop diverting UNIX errors to Feedback, [on | off]"];
END.