|
|
- using System;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows.Documents;
- using TrustedUninstaller.Shared.Tasks;
- using YamlDotNet.Serialization;
-
- namespace TrustedUninstaller.Shared.Actions
- {
- public enum Privilege
- {
- TrustedInstaller,
- System,
- CurrentUserElevated,
- CurrentUser,
- }
-
- public class RunAction : TaskAction, ITaskAction
- {
- public void RunTaskOnMainThread()
- {
- if (RawPath != null) RawPath = Environment.ExpandEnvironmentVariables(RawPath);
- InProgress = true;
-
- var privilegeText = RunAs == Privilege.CurrentUser ? " as the current user" : RunAs == Privilege.CurrentUserElevated ? " as the current user elevated" : RunAs == Privilege.System ?
- " as the system account" : "";
-
- if (Arguments == null) Console.WriteLine($"Running '{Exe + privilegeText}'...");
- else Console.WriteLine($"Running '{Exe}' with arguments '{Arguments + privilegeText}'...");
-
- WinUtil.CheckKph();
-
- var currentDir = Directory.GetCurrentDirectory();
-
- if (ExeDir) RawPath = AmeliorationUtil.Playbook.Path + "\\Executables";
- if (BaseDir) RawPath = currentDir;
-
- string file = null;
- if (RawPath != null && File.Exists(Path.Combine(Environment.ExpandEnvironmentVariables(RawPath), Exe)))
- file = Path.Combine(Environment.ExpandEnvironmentVariables(RawPath), Exe);
- else if (ExistsInPath(Exe) || File.Exists(Environment.ExpandEnvironmentVariables(Exe)))
- file = Environment.ExpandEnvironmentVariables(Exe);
-
- if (file == null)
- throw new FileNotFoundException($"Executable not found.");
-
- if (RunAs == Privilege.TrustedInstaller)
- RunAsProcess(file);
- else
- RunAsPrivilegedProcess(file);
-
- InProgress = false;
- return;
- }
- [YamlMember(typeof(Privilege), Alias = "runas")]
- public Privilege RunAs { get; set; } = Privilege.TrustedInstaller;
-
- [YamlMember(typeof(string), Alias = "path")]
- public string RawPath { get; set; } = null;
-
- [YamlMember(typeof(string), Alias = "exe")]
- public string Exe { get; set; }
-
- [YamlMember(typeof(string), Alias = "args")]
- public string? Arguments { get; set; }
-
- [YamlMember(typeof(bool), Alias = "baseDir")]
- public bool BaseDir { get; set; } = false;
-
- [YamlMember(typeof(bool), Alias = "exeDir")]
- public bool ExeDir { get; set; } = false;
-
- [YamlMember(typeof(bool), Alias = "createWindow")]
- public bool CreateWindow { get; set; } = false;
-
- [YamlMember(typeof(bool), Alias = "showOutput")]
- public bool ShowOutput { get; set; } = true;
-
- [YamlMember(typeof(bool), Alias = "showError")]
- public bool ShowError { get; set; } = true;
-
- [YamlMember(typeof(int), Alias = "timeout")]
- public int? Timeout { get; set; }
-
- [YamlMember(typeof(string), Alias = "wait")]
- public bool Wait { get; set; } = true;
-
- [YamlMember(typeof(string), Alias = "weight")]
- public int ProgressWeight { get; set; } = 5;
- public int GetProgressWeight() => ProgressWeight;
-
- private bool InProgress { get; set; } = false;
- public void ResetProgress() => InProgress = false;
- private bool HasExited { get; set; } = false;
- //public int ExitCode { get; set; }
- public string? Output { get; private set; }
- private string? StandardError { get; set; }
-
- public string ErrorString() => String.IsNullOrEmpty(Arguments) ? $"RunAction failed to execute '{Exe}'." : $"RunAction failed to execute '{Exe}' with arguments '{Arguments}'.";
-
- public static bool ExistsInPath(string fileName)
- {
- if (File.Exists(fileName))
- return true;
-
- var values = Environment.GetEnvironmentVariable("PATH");
- foreach (var path in values.Split(Path.PathSeparator))
- {
- var fullPath = Path.Combine(path, fileName);
- if (File.Exists(fullPath))
- return true;
- if (!fileName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) && File.Exists(fullPath + ".exe"))
- return true;
- }
- return false;
- }
-
- public UninstallTaskStatus GetStatus()
- {
- if (InProgress)
- {
- return UninstallTaskStatus.InProgress;
- }
-
- return HasExited || !Wait ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo;
- }
-
- public Task<bool> RunTask()
- {
- return null;
- }
-
- private void RunAsProcess(string file)
- {
- var startInfo = new ProcessStartInfo
- {
- CreateNoWindow = !this.CreateWindow,
- UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Normal,
- RedirectStandardError = true,
- RedirectStandardOutput = true,
- FileName = file,
- };
- if (Arguments != null) startInfo.Arguments = Environment.ExpandEnvironmentVariables(Arguments);
-
- if (ExeDir) startInfo.WorkingDirectory = AmeliorationUtil.Playbook.Path + "\\Executables";
- if (!Wait)
- {
- startInfo.RedirectStandardError = false;
- startInfo.RedirectStandardOutput = false;
- startInfo.WindowStyle = ProcessWindowStyle.Hidden;
- startInfo.UseShellExecute = true;
- }
-
- if (!ShowOutput)
- startInfo.RedirectStandardOutput = false;
- if (!ShowError)
- startInfo.RedirectStandardError = false;
-
- var exeProcess = new Process
- {
- StartInfo = startInfo,
- EnableRaisingEvents = true
- };
-
- exeProcess.Start();
-
- if (!Wait)
- {
- exeProcess.Dispose();
- return;
- }
-
- if (ShowOutput)
- exeProcess.OutputDataReceived += ProcOutputHandler;
- if (ShowError)
- exeProcess.ErrorDataReceived += ProcOutputHandler;
-
- if (ShowOutput)
- exeProcess.BeginOutputReadLine();
- if (ShowError)
- exeProcess.BeginErrorReadLine();
-
- if (Timeout.HasValue)
- {
- var exited = exeProcess.WaitForExit(Timeout.Value);
- if (!exited)
- {
- exeProcess.Kill();
- throw new TimeoutException($"Executable run timeout exceeded.");
- }
- }
- else
- {
- bool exited = exeProcess.WaitForExit(30000);
-
- // WaitForExit alone seems to not be entirely reliable
- while (!exited && ExeRunning(exeProcess.ProcessName, exeProcess.Id))
- {
- exited = exeProcess.WaitForExit(30000);
- }
- }
-
- int exitCode = 0;
- try
- {
- exitCode = exeProcess.ExitCode;
- }
- catch (Exception ex)
- {
- ErrorLogger.WriteToErrorLog("Error fetching process exit code. (1)", null, "RunAction Error", Exe + " " + Arguments);
- }
- if (exitCode != 0)
- {
- ErrorLogger.WriteToErrorLog("Process exited with a non-zero exit code: " + exitCode, null, "RunAction Error", Exe + " " + Arguments);
- }
-
- HasExited = true;
-
- if (ShowOutput)
- exeProcess.CancelOutputRead();
- if (ShowError)
- exeProcess.CancelErrorRead();
-
- exeProcess.Dispose();
- }
- private void RunAsPrivilegedProcess(string file)
- {
- var startInfo = new AugmentedProcess.ProcessStartInfo
- {
- CreateNoWindow = !this.CreateWindow,
- UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Normal,
- RedirectStandardError = true,
- RedirectStandardOutput = true,
- FileName = file,
- };
- if (Arguments != null) startInfo.Arguments = Arguments;
-
- if (ExeDir) startInfo.WorkingDirectory = AmeliorationUtil.Playbook.Path + "\\Executables";
- if (!Wait)
- {
- startInfo.RedirectStandardError = false;
- startInfo.RedirectStandardOutput = false;
- startInfo.WindowStyle = ProcessWindowStyle.Hidden;
- startInfo.UseShellExecute = true;
- }
-
- if (!ShowOutput)
- startInfo.RedirectStandardOutput = false;
- if (!ShowError)
- startInfo.RedirectStandardError = false;
-
- var exeProcess = new AugmentedProcess.Process
- {
- StartInfo = startInfo,
- EnableRaisingEvents = true
- };
-
- ProcessPrivilege.StartPrivilegedTask(exeProcess, RunAs);
-
- if (!Wait)
- {
- exeProcess.Dispose();
- return;
- }
-
- if (ShowOutput)
- exeProcess.OutputDataReceived += PrivilegedProcOutputHandler;
- if (ShowError)
- exeProcess.ErrorDataReceived += PrivilegedProcOutputHandler;
-
- if (ShowOutput)
- exeProcess.BeginOutputReadLine();
- if (ShowError)
- exeProcess.BeginErrorReadLine();
-
- if (Timeout.HasValue)
- {
- var exited = exeProcess.WaitForExit(Timeout.Value);
- if (!exited)
- {
- exeProcess.Kill();
- throw new TimeoutException($"Executable run timeout exceeded.");
- }
- }
- else
- {
- bool exited = exeProcess.WaitForExit(30000);
-
- // WaitForExit alone seems to not be entirely reliable
- while (!exited && ExeRunning(exeProcess.ProcessName, exeProcess.Id))
- {
- exited = exeProcess.WaitForExit(30000);
- }
- }
-
- try
- {
- if (exeProcess.ExitCode != 0)
- {
- ErrorLogger.WriteToErrorLog("Process exited with a non-zero exit code: " + exeProcess.ExitCode, null, "RunAction Error", Exe + " " + Arguments);
- }
- }
- catch (Exception ex)
- {
- ErrorLogger.WriteToErrorLog("Error fetching process exit code. (1)", null, "RunAction Error", Exe + " " + Arguments);
-
- Thread.Sleep(500);
- try
- {
- if (exeProcess.ExitCode != 0)
- {
- ErrorLogger.WriteToErrorLog("Process exited with a non-zero exit code: " + exeProcess.ExitCode, null, "RunAction Error", Exe + " " + Arguments);
- }
- }
- catch (Exception e)
- {
- ErrorLogger.WriteToErrorLog("Error fetching process exit code. (2)", null, "RunAction Error", Exe + " " + Arguments);
- }
- }
-
- HasExited = true;
-
- if (ShowOutput)
- exeProcess.CancelOutputRead();
- if (ShowError)
- exeProcess.CancelErrorRead();
-
- exeProcess.Dispose();
- }
-
- private static bool ExeRunning(string name, int id)
- {
- try
- {
- return Process.GetProcessesByName(name).Any(x => x.Id == id);
- }
- catch (Exception)
- {
- return false;
- }
- }
-
- private void PrivilegedProcOutputHandler(object sendingProcess, AugmentedProcess.DataReceivedEventArgs outLine)
- {
- try
- {
- // Collect the sort command output.
- if (!String.IsNullOrEmpty(outLine.Data))
- {
- var outputString = outLine.Data;
-
- if (outputString.Contains("\\AME"))
- {
- outputString = outputString.Substring(outputString.IndexOf('>') + 1);
- }
- Console.WriteLine(outputString);
- Output += outputString + Environment.NewLine;
- }
- else
- {
- Console.WriteLine();
- }
- }
- catch (Exception e)
- {
- ErrorLogger.WriteToErrorLog("Error processing process output", e.StackTrace, "RunAction Error", Exe);
- }
- }
- private void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
- {
- try
- {
- // Collect the sort command output.
- if (!String.IsNullOrEmpty(outLine.Data))
- {
- var outputString = outLine.Data;
-
- if (outputString.Contains("\\AME"))
- {
- outputString = outputString.Substring(outputString.IndexOf('>') + 1);
- }
- Console.WriteLine(outputString);
- Output += outputString + Environment.NewLine;
- }
- else
- {
- Console.WriteLine();
- }
- }
- catch (Exception e)
- {
- ErrorLogger.WriteToErrorLog("Error processing process output", e.StackTrace, "RunAction Error", Exe);
- }
- }
- }
- }
|