/// <summary> /// Initializes a new instance of a <see cref="TerminalEmulator"/> using the specified font to display text. /// </summary> public TerminalEmulator() { var options = new TerminalOptions(); options.LFlag = PtyConstants.ICANON | PtyConstants.ECHO; options.C_cc[PtyConstants.VERASE] = (byte)'\b'; options.C_cc[PtyConstants.VEOL] = (byte)'\r'; options.C_cc[PtyConstants.VEOL2] = (byte)'\n'; PseudoTerminal.CreatePair(out _master, out _slave, options); _stdout = new StreamWriter(_master); _stdin = new StreamReader(_master); _stdout.AutoFlush = true; HasFocusedChanged += (o, a) => { if (IsFocused) { _cursorOn = true; _cursorAnim = 0; Invalidate(true); } }; }
/// <inheritdoc/> public ServerResponseType HandleMessage(Backend backend, ServerMessageType message, string session, BinaryReader datareader, BinaryWriter datawriter) { PseudoTerminal _master = null; PseudoTerminal _slave = null; var options = new TerminalOptions(); options.LFlag = PtyConstants.ICANON; options.C_cc[PtyConstants.VERASE] = (byte)'\b'; options.C_cc[PtyConstants.VEOL] = (byte)'\r'; options.C_cc[PtyConstants.VEOL2] = (byte)'\n'; PseudoTerminal.CreatePair(out _master, out _slave, options); int slaveRemoteId = _remoteStreams.Create(_slave, session); var stdout = new StreamWriter(new BroadcastingStream(backend, session)); stdout.AutoFlush = true; var stdin = new StreamReader(_master); string commandname = datareader.ReadString(); int argCount = datareader.ReadInt32(); string[] argv = new string[argCount]; for (int i = 0; i < argCount; i++) { argv[i] = datareader.ReadString(); } var trmmgr = backend.GetBackendComponent <TerminalManager>(); Task.Run(() => { try { trmmgr.RunCommand(backend, commandname, argv, session, stdin, stdout); } catch (TerminationRequestException) { Logger.Log("Terminated command."); } finally { stdout.Write((char)0x02); } }); datawriter.Write(slaveRemoteId); return(ServerResponseType.REQ_SUCCESS); }
private void RunDoorDOSEMU(string command, string parameters) { if (Helpers.Debug) { _ClientThread.UpdateStatus("DEBUG: DOSEMU launching " + command + " " + parameters); } PseudoTerminal pty = null; Mono.Unix.UnixStream us = null; int WaitStatus; try { bool DataTransferred = false; int LoopsSinceIO = 0; Exception ReadException = null; // If we're running a batch file, add a CALL to it if (command.ToUpper().Contains(".BAT")) { command = "call " + command; } string[] ExternalBat = new string[] { "@echo off", "lredir g: linux\\fs" + ProcessUtils.StartupPath, "set path=%path%;g:\\dosutils", "fossil.com", "share.com", "ansi.com", "g:", command + " " + parameters, "exitemu" }; FileUtils.FileWriteAllText(StringUtils.PathCombine(ProcessUtils.StartupPath, "node" + _ClientThread.NodeInfo.Node.ToString(), "external.bat"), String.Join("\r\n", ExternalBat), RMEncoding.Ansi); string[] Arguments = new string[] { "HOME=" + ProcessUtils.StartupPath, "HOME=" + ProcessUtils.StartupPath, "QUIET=1", "DOSDRIVE_D=" + StringUtils.PathCombine(ProcessUtils.StartupPath, "node" + _ClientThread.NodeInfo.Node.ToString()), "/usr/bin/nice", "-n19", "/usr/bin/dosemu.bin", "-Ivideo { none }", "-Ikeystroke \\r", "-Iserial { virtual com 1 }", "-t", "-Ed:external.bat", "-o" + StringUtils.PathCombine(ProcessUtils.StartupPath, "node" + _ClientThread.NodeInfo.Node.ToString(), "dosemu.log") };//, "2> /gamesrv/NODE" + _CT.NodeInfo.Node.ToString() + "/DOSEMU_BOOT.LOG" }; // TODO add configuration variable so this path is not hardcoded if (Helpers.Debug) { _ClientThread.UpdateStatus("Executing /usr/bin/env " + string.Join(" ", Arguments)); } lock (Helpers.PrivilegeLock) { try { Helpers.NeedRoot(); pty = PseudoTerminal.Open(null, "/usr/bin/env", Arguments, "/tmp", 80, 25, false, false, false); us = new Mono.Unix.UnixStream(pty.FileDescriptor, false); } finally { Helpers.DropRoot(Config.Instance.UnixUser); } } new Thread(delegate(object p) { // Send data from door to user try { byte[] Buffer = new byte[10240]; int NumRead = 0; while (!_ClientThread.QuitThread()) { NumRead = us.Read(Buffer, 0, Buffer.Length); if (NumRead > 0) { _ClientThread.NodeInfo.Connection.WriteBytes(Buffer, NumRead); DataTransferred = true; } } } catch (Exception ex) { ReadException = ex; } }).Start(); // Check if we need to run cpulimit if (File.Exists(StringUtils.PathCombine(ProcessUtils.StartupPath, "cpulimit.sh"))) { Process.Start(StringUtils.PathCombine(ProcessUtils.StartupPath, "cpulimit.sh"), pty.ChildPid.ToString()); } // Loop until something happens while (!_ClientThread.QuitThread()) // NB: Was simply _Stop before { DataTransferred = false; // Check for exception in read thread if (ReadException != null) { return; } // Check for dropped carrier if (!_ClientThread.NodeInfo.Connection.Connected) { int Sleeps = 0; _ClientThread.UpdateStatus("User hung-up while in external program"); Mono.Unix.Native.Syscall.kill(pty.ChildPid, Mono.Unix.Native.Signum.SIGHUP); while ((Sleeps++ < 5) && (Mono.Unix.Native.Syscall.waitpid(pty.ChildPid, out WaitStatus, Mono.Unix.Native.WaitOptions.WNOHANG) == 0)) { Thread.Sleep(1000); } if (Mono.Unix.Native.Syscall.waitpid(pty.ChildPid, out WaitStatus, Mono.Unix.Native.WaitOptions.WNOHANG) == 0) { _ClientThread.UpdateStatus("Process still active after waiting 5 seconds"); } return; } // Send data from user to door if (_ClientThread.NodeInfo.Connection.CanRead()) { // Write the text to the program byte[] Bytes = _ClientThread.NodeInfo.Connection.ReadBytes(); for (int i = 0; i < Bytes.Length; i++) { us.WriteByte((byte)Bytes[i]); us.Flush(); } DataTransferred = true; } // Checks to perform when there was no I/O if (!DataTransferred) { LoopsSinceIO++; // Only check process termination after 300 milliseconds of no I/O // to allow for last minute reception of output from DOS programs if (LoopsSinceIO >= 3) { // Check if door terminated if (Mono.Unix.Native.Syscall.waitpid(pty.ChildPid, out WaitStatus, Mono.Unix.Native.WaitOptions.WNOHANG) != 0) { break; } } // Let's make sure the socket is up // Sending will trigger a socket d/c detection if (LoopsSinceIO >= 300) { switch (_ClientThread.NodeInfo.ConnectionType) { case ConnectionType.RLogin: _ClientThread.NodeInfo.Connection.Write("\0"); break; case ConnectionType.Telnet: ((TelnetConnection)_ClientThread.NodeInfo.Connection).SendGoAhead(); break; case ConnectionType.WebSocket: _ClientThread.NodeInfo.Connection.Write("\0"); break; } } // Delay for 100ms (unless the user hits a key, in which case break the delay early) _ClientThread.NodeInfo.Connection.CanRead(100); } else { LoopsSinceIO = 0; } } } finally { // Terminate process if it hasn't closed yet if (pty != null) { if (Mono.Unix.Native.Syscall.waitpid(pty.ChildPid, out WaitStatus, Mono.Unix.Native.WaitOptions.WNOHANG) == 0) { _ClientThread.UpdateStatus("Terminating process"); Mono.Unix.Native.Syscall.kill(pty.ChildPid, Mono.Unix.Native.Signum.SIGKILL); } pty.Dispose(); } } }