public static void Run(ClientThread clientThread) { if (clientThread == null) { throw new ArgumentNullException("clientThread"); } // Loop through the options, and run the ones we allow here bool ExitFor = false; string[] Processes = LogOnProcess.GetProcesses(); for (int i = 0; i < Processes.Length; i++) { try { LogOnProcess LP = new LogOnProcess(Processes[i]); if ((LP.Loaded) && (!clientThread.QuitThread())) { switch (LP.Action) { case Action.Disconnect: case Action.DisplayFile: case Action.DisplayFileMore: case Action.DisplayFilePause: case Action.MainMenu: case Action.Pause: case Action.RunDoor: MenuOption MO = new MenuOption("", '\0') { Action = LP.Action, Name = LP.Name, Parameters = LP.Parameters, RequiredAccess = LP.RequiredAccess, }; ExitFor = clientThread.HandleMenuOption(MO); break; } if (ExitFor) { break; } } } catch (Exception ex) { // If there's something wrong with the ini entry (Action is invalid for example), this will throw a System.ArgumentException error, so we just ignore that menu item RMLog.Exception(ex, "Error during logon process '" + Processes[i] + "'"); } } }
public unsafe void Run(string command, string parameters, int forceQuitDelay) { if (Helpers.Debug) { _ClientThread.UpdateStatus("DEBUG: SBBSEXECNT launching " + command + " " + parameters); } _Command = command; _Parameters = parameters; _ForceQuitDelay = forceQuitDelay; try { // Initialize variables if (Initialize()) { // Start the door if (StartProcess()) { // Loop until something happens bool DataTransferred = false; while (!_ClientThread.QuitThread()) // NB: Was simply _Stop before { // Check for dropped carrier if (UserHungUp()) { return; } // Send data from user to door DataTransferred = TransmitFromUserToDoor(out bool UserToDoorError); if (UserToDoorError) { return; } // Send data from door to user DataTransferred |= TransmitFromDoorToUser(out bool DoorToUserError); if (DoorToUserError) { return; } // Checks to perform when there was no I/O if (!DataTransferred) { // Numer of loop iterations with no I/O 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 is requesting a hangup (dropped DTR) if (DoorDroppedDTR()) { return; } // Check if door terminated if (P.HasExited) { _ClientThread.UpdateStatus("External terminated with exit code: " + P.ExitCode); return; } // Check if door is trying to launch a native door CheckForW32DoorRun(); } // Let's make sure the socket is up // Sending will trigger a socket d/c detection if (LoopsSinceIO % 300 == 0) { SendPing(); } // 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 { // Stop the process, if it's still running StopProcess(); // Free unmanaged resources CleanUp(); } }
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(); } } }