//Do we expose Unix.Stream or just the generic IO.Stream? Decisions... public /*Mono.Unix.Unix*/ System.IO.Stream OpenAsStream(bool transferOwnership) { var stream = new Mono.Unix.UnixStream(FD, transferOwnership); Owns = !transferOwnership; return(stream); }
//Do we expose Unix.Stream or just the generic IO.Stream? Decisions... public /*Mono.Unix.Unix*/System.IO.Stream OpenAsStream(bool transferOwnership) { var stream = new Mono.Unix.UnixStream (FD, transferOwnership); Owns = !transferOwnership; return stream; }
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(); } } }