feature: Bootstrapper secure server discovery
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using Disco.Client.Interop;
|
||||
using Disco.ClientBootstrapper.Interop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@@ -6,46 +8,191 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.ClientBootstrapper
|
||||
{
|
||||
class BootstrapperLoop
|
||||
internal class BootstrapperLoop
|
||||
{
|
||||
|
||||
public Thread LoopThread;
|
||||
public delegate void LoopCompleteCallback();
|
||||
private LoopCompleteCallback mLoopCompleteCallback;
|
||||
private IStatus statusUI;
|
||||
private readonly Func<CancellationToken, Task> completeCallback;
|
||||
private readonly CancellationToken cancellationToken;
|
||||
private readonly IStatus statusUI;
|
||||
private readonly Uri forcedServerUrl;
|
||||
private string tempWorkingDirectory;
|
||||
private StringBuilder errorMessage;
|
||||
private Process clientProcess;
|
||||
|
||||
//#if DEBUG
|
||||
// public const string DiscoServerName = "WS-GSHARP";
|
||||
// public const int DiscoServerPort = 57252;
|
||||
//#else
|
||||
public const string DiscoServerName = "DISCO";
|
||||
public const int DiscoServerPort = 9292;
|
||||
//#endif
|
||||
|
||||
public BootstrapperLoop(IStatus StatusUI, LoopCompleteCallback Callback)
|
||||
public BootstrapperLoop(IStatus statusUI, Uri forcedServerUrl, Func<CancellationToken, Task> callback, CancellationToken cancellationToken)
|
||||
{
|
||||
statusUI = StatusUI;
|
||||
mLoopCompleteCallback = Callback;
|
||||
errorMessage = new StringBuilder();
|
||||
this.statusUI = statusUI;
|
||||
this.forcedServerUrl = forcedServerUrl;
|
||||
completeCallback = callback;
|
||||
this.cancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
LoopThread = new Thread(new ThreadStart(loopHost));
|
||||
LoopThread.Start();
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
await Loop(forcedServerUrl, cancellationToken);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
private void loopHost()
|
||||
private async Task Loop(Uri forcedServerUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
loop();
|
||||
statusUI.UpdateStatus("System Preparation (Bootstrapper)", "Starting", "Please wait...", true, -1);
|
||||
|
||||
tempWorkingDirectory = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), @"Disco\Temp");
|
||||
if (!Directory.Exists(tempWorkingDirectory))
|
||||
Directory.CreateDirectory(tempWorkingDirectory);
|
||||
|
||||
// Check for Network Connectivity
|
||||
statusUI.UpdateStatus(null, "Detecting Network", "Checking network connectivity, Please wait...", true, -1);
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Detecting Network", "No network connectivity detected, Diagnosing...", true, -1);
|
||||
statusUI_WriteAdapterInfo();
|
||||
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
// Check for Wireless
|
||||
var hasWireless = (NetworkInterop.NetworkAdapters.Count(na => na.IsWireless) > 0);
|
||||
if (hasWireless)
|
||||
{
|
||||
// True: Do wireless loop
|
||||
statusUI.UpdateStatus(null, "Configuring Wireless Network", "Wireless adapter detected, Configuring...", true, -1);
|
||||
await NetworkInterop.ConfigureWireless(cancellationToken);
|
||||
statusUI.UpdateStatus(null, "Waiting for Wireless Network", null, true, 0);
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
statusUI_WriteAdapterInfo();
|
||||
statusUI.UpdateStatus(null, null, null, true, i);
|
||||
await Program.SleepThread(2000, false, cancellationToken);
|
||||
if (NetworkInterop.HasNetworkConnectivity())
|
||||
break;
|
||||
}
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Wireless Network Failed", "Unable to connect to the wireless network, please connect the network cable...", false);
|
||||
await Program.SleepThread(3000, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
// Instruct user to connect network cable
|
||||
statusUI.UpdateStatus(null, "Please connect the network cable", null);
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
statusUI_WriteAdapterInfo();
|
||||
statusUI.UpdateStatus(null, null, null, true, i);
|
||||
await Program.SleepThread(2000, false, cancellationToken);
|
||||
if (NetworkInterop.HasNetworkConnectivity())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!NetworkInterop.HasNetworkConnectivity())
|
||||
{
|
||||
// Client Failed
|
||||
if (completeCallback != null)
|
||||
await completeCallback(cancellationToken);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Tuple<Uri, string> serverDiscovery;
|
||||
statusUI.UpdateStatus(null, "Detecting Disco ICT", "Locating Disco ICT Server, Please wait...", true, -1);
|
||||
try
|
||||
{
|
||||
serverDiscovery = EndpointDiscovery.DiscoverServer(forcedServerUrl);
|
||||
statusUI.UpdateStatus(null, null, $"{serverDiscovery.Item1} ({serverDiscovery.Item2})", true, -1);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
statusUI.UpdateStatus(null, null, "Failed to locate Disco ICT Server, exiting...", true, -1);
|
||||
await Program.SleepThread(2000, false, cancellationToken);
|
||||
throw;
|
||||
}
|
||||
|
||||
// Download Client
|
||||
statusUI.UpdateStatus(null, "Downloading", "Retrieving Preparation Client, Please wait...", true, -1);
|
||||
string clientSourceLocation = Path.Combine(tempWorkingDirectory, "PreparationClient.zip");
|
||||
using (var webClient = new WebClient())
|
||||
{
|
||||
// Don't use a proxy when downloading the Client
|
||||
webClient.Proxy = new WebProxy();
|
||||
webClient.Headers.Add("X-DiscoICT-Discovery", serverDiscovery.Item2);
|
||||
try
|
||||
{
|
||||
webClient.DownloadFile(new Uri(serverDiscovery.Item1, "/Services/Client/PreparationClient"), clientSourceLocation);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
if (ex.Response != null &&
|
||||
ex.Response is HttpWebResponse response)
|
||||
{
|
||||
if (response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Download failed: Bad Request", response.StatusDescription, true, -1);
|
||||
await Program.SleepThread(5000, false, cancellationToken);
|
||||
}
|
||||
else if (response.StatusCode == HttpStatusCode.InternalServerError)
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Download failed: Something went wrong on the server", "Review logs for more information (Configuration > Logging)", true, -1);
|
||||
await Program.SleepThread(5000, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Unzip Client
|
||||
statusUI.UpdateStatus(null, "Extracting", "Retrieving Preparation Client, Please wait...", true, -1);
|
||||
string clientLocation = Path.Combine(tempWorkingDirectory, "PreparationClient");
|
||||
if (Directory.Exists(clientLocation))
|
||||
Directory.Delete(clientLocation, true);
|
||||
|
||||
Directory.CreateDirectory(clientLocation);
|
||||
using (var clientSource = Ionic.Zip.ZipFile.Read(clientSourceLocation))
|
||||
{
|
||||
clientSource.ExtractAll(clientLocation, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
|
||||
}
|
||||
|
||||
// Launch Client
|
||||
statusUI.UpdateStatus("System Preparation (Client)", "Running", "Launching Preparation Client, Please wait...", true, -1);
|
||||
ProcessStartInfo clientProcessStart = new ProcessStartInfo(Path.Combine(clientLocation, "Start.bat"), $"2 {Process.GetCurrentProcess().Id} {serverDiscovery.Item1}")
|
||||
{
|
||||
WorkingDirectory = clientLocation,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
};
|
||||
using (clientProcess = Process.Start(clientProcessStart))
|
||||
{
|
||||
// Read StdOutput until End
|
||||
try
|
||||
{
|
||||
clientProcess.OutputDataReceived += new DataReceivedEventHandler(clientProcess_OutputDataReceived);
|
||||
clientProcess.BeginOutputReadLine();
|
||||
clientProcess.WaitForExit();
|
||||
}
|
||||
catch (Exception) { throw; }
|
||||
finally
|
||||
{
|
||||
try { clientProcess.CloseMainWindow(); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
clientProcess = null;
|
||||
|
||||
// Cleanup
|
||||
if (Directory.Exists(tempWorkingDirectory))
|
||||
Directory.Delete(tempWorkingDirectory, true);
|
||||
CertificateInterop.RemoveTempCerts();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -53,167 +200,20 @@ namespace Disco.ClientBootstrapper
|
||||
return;
|
||||
if (ex.GetType() == typeof(ThreadInterruptedException))
|
||||
return;
|
||||
Program.WriteAppError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void loop()
|
||||
{
|
||||
|
||||
#if Debug
|
||||
statusUI.UpdateStatus("Waiting for Debugger", "Please wait...", true, -1);
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
System.Threading.Thread.Sleep(10);
|
||||
} while (!System.Diagnostics.Debugger.IsAttached);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
statusUI.UpdateStatus("Error", ex.Message, true, -1);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
statusUI.UpdateStatus("System Preparation (Bootstrapper)", "Starting", "Please wait...", true, -1);
|
||||
#endif
|
||||
|
||||
tempWorkingDirectory = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco\\Temp");
|
||||
if (!Directory.Exists(tempWorkingDirectory))
|
||||
Directory.CreateDirectory(tempWorkingDirectory);
|
||||
|
||||
// Check for Network Connectivity
|
||||
statusUI.UpdateStatus(null, "Detecting Network", "Checking network connectivity, Please wait...", true, -1);
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Detecting Network", "No network connectivity detected, Diagnosing...", true, -1);
|
||||
statusUI_WriteAdapterInfo();
|
||||
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
// Check for Wireless
|
||||
var hasWireless = (Interop.NetworkInterop.NetworkAdapters.Count(na => na.IsWireless) > 0);
|
||||
if (hasWireless)
|
||||
{
|
||||
// True: Do wireless loop
|
||||
statusUI.UpdateStatus(null, "Configuring Wireless Network", "Wireless adapter detected, Configuring...", true, -1);
|
||||
Interop.NetworkInterop.ConfigureWireless();
|
||||
statusUI.UpdateStatus(null, "Waiting for Wireless Network", null, true, 0);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
statusUI_WriteAdapterInfo();
|
||||
statusUI.UpdateStatus(null, null, null, true, i);
|
||||
Program.SleepThread(500, false);
|
||||
if (Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
break;
|
||||
}
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
statusUI.UpdateStatus(null, "Wireless Network Failed", "Unable to connect to the wireless network, please connect the network cable...", false);
|
||||
Program.SleepThread(3000, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
// Instruct user to connect network cable
|
||||
statusUI.UpdateStatus(null, "Please connect the network cable", null);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
statusUI_WriteAdapterInfo();
|
||||
statusUI.UpdateStatus(null, null, null, true, i);
|
||||
Program.SleepThread(500, false);
|
||||
if (Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Interop.NetworkInterop.PingDiscoIct(DiscoServerName))
|
||||
{
|
||||
// Client Failed
|
||||
if (mLoopCompleteCallback != null)
|
||||
{
|
||||
mLoopCompleteCallback.BeginInvoke(null, null);
|
||||
}
|
||||
if (ex.GetType() == typeof(OperationCanceledException))
|
||||
return;
|
||||
}
|
||||
Program.WriteAppError(ex);
|
||||
}
|
||||
|
||||
// Download Client
|
||||
statusUI.UpdateStatus(null, "Downloading", "Retrieving Preparation Client, Please wait...", true, -1);
|
||||
string clientSourceLocation = Path.Combine(tempWorkingDirectory, "PreparationClient.zip");
|
||||
using (var webClient = new WebClient())
|
||||
{
|
||||
// Don't use a proxy when downloading the Client
|
||||
webClient.Proxy = new WebProxy();
|
||||
|
||||
webClient.DownloadFile($"http://{DiscoServerName}:{DiscoServerPort}/Services/Client/PreparationClient", clientSourceLocation);
|
||||
}
|
||||
|
||||
// Unzip Client
|
||||
statusUI.UpdateStatus(null, "Extracting", "Retrieving Preparation Client, Please wait...", true, -1);
|
||||
string clientLocation = Path.Combine(tempWorkingDirectory, "PreparationClient");
|
||||
if (Directory.Exists(clientLocation))
|
||||
Directory.Delete(clientLocation, true);
|
||||
|
||||
Directory.CreateDirectory(clientLocation);
|
||||
using (var clientSource = Ionic.Zip.ZipFile.Read(clientSourceLocation))
|
||||
{
|
||||
clientSource.ExtractAll(clientLocation, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
|
||||
}
|
||||
|
||||
// Launch Client
|
||||
statusUI.UpdateStatus("System Preparation (Client)", "Running", "Launching Preparation Client, Please wait...", true, -1);
|
||||
ProcessStartInfo clientProcessStart = new ProcessStartInfo(Path.Combine(clientLocation, "Start.bat"))
|
||||
{
|
||||
WorkingDirectory = clientLocation,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
};
|
||||
using (clientProcess = Process.Start(clientProcessStart))
|
||||
{
|
||||
// Read StdOutput until End
|
||||
try
|
||||
{
|
||||
clientProcess.OutputDataReceived += new DataReceivedEventHandler(clientProcess_OutputDataReceived);
|
||||
clientProcess.BeginOutputReadLine();
|
||||
clientProcess.WaitForExit();
|
||||
}
|
||||
catch (Exception) { throw; }
|
||||
finally
|
||||
{
|
||||
try { clientProcess.CloseMainWindow(); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
clientProcess = null;
|
||||
|
||||
// Cleanup
|
||||
if (Directory.Exists(tempWorkingDirectory))
|
||||
Directory.Delete(tempWorkingDirectory, true);
|
||||
Interop.CertificateInterop.RemoveTempCerts();
|
||||
|
||||
// Pause if Error
|
||||
if (errorMessage.Length > 0)
|
||||
{
|
||||
Program.SleepThread(10000, true);
|
||||
}
|
||||
|
||||
// End Of Loop
|
||||
if (mLoopCompleteCallback != null)
|
||||
{
|
||||
mLoopCompleteCallback.BeginInvoke(null, null);
|
||||
}
|
||||
if (completeCallback != null)
|
||||
await completeCallback(cancellationToken);
|
||||
}
|
||||
|
||||
void statusUI_WriteAdapterInfo()
|
||||
private void statusUI_WriteAdapterInfo()
|
||||
{
|
||||
|
||||
var info = new StringBuilder();
|
||||
foreach (var na in Interop.NetworkInterop.NetworkAdapters)
|
||||
foreach (var na in NetworkInterop.NetworkAdapters)
|
||||
{
|
||||
if (na.IsWireless)
|
||||
{
|
||||
@@ -228,11 +228,10 @@ namespace Disco.ClientBootstrapper
|
||||
|
||||
}
|
||||
|
||||
void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
private void clientProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(e.Data))
|
||||
{
|
||||
Debug.WriteLine($"OUTPUT: {e.Data}");
|
||||
var data = e.Data.Substring(1).Split(new char[] { ',' });
|
||||
switch (e.Data[0])
|
||||
{
|
||||
@@ -249,15 +248,5 @@ namespace Disco.ClientBootstrapper
|
||||
}
|
||||
}
|
||||
|
||||
//void clientProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
|
||||
//{
|
||||
// if (!string.IsNullOrEmpty(e.Data))
|
||||
// {
|
||||
// System.Diagnostics.Debug.WriteLine(string.Format("ERROR: {0}", e.Data));
|
||||
// this.errorMessage.AppendLine(e.Data);
|
||||
// statusUI.UpdateStatus(null, "An Error Occurred", this.errorMessage.ToString(), false);
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,9 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Disco.Client\Interop\EndpointDiscovery.cs">
|
||||
<Link>Interop\EndpointDiscovery.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Resources\Libraries\DotNetZip\Source\BZip2\BitWriter.cs">
|
||||
<Link>DotNetZip\BZip2\BitWriter.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -7,22 +7,22 @@ namespace Disco.ClientBootstrapper
|
||||
{
|
||||
|
||||
private delegate void dUpdateStatus(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress);
|
||||
private dUpdateStatus mUpdateStatus;
|
||||
private readonly dUpdateStatus mUpdateStatus;
|
||||
|
||||
public FormStatus()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||
this.labelVersion.Text = $"v{version.ToString(3)}";
|
||||
labelVersion.Text = $"v{version.ToString(3)}";
|
||||
|
||||
this.FormClosed += new FormClosedEventHandler(FormStatus_FormClosed);
|
||||
FormClosed += new FormClosedEventHandler(FormStatus_FormClosed);
|
||||
|
||||
mUpdateStatus = new dUpdateStatus(UpdateStatusDo);
|
||||
Cursor.Hide();
|
||||
}
|
||||
|
||||
void FormStatus_FormClosed(object sender, FormClosedEventArgs e)
|
||||
private void FormStatus_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
Cursor.Show();
|
||||
Program.ExitApplication();
|
||||
@@ -32,43 +32,43 @@ namespace Disco.ClientBootstrapper
|
||||
{
|
||||
try
|
||||
{
|
||||
this.Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress);
|
||||
Invoke(mUpdateStatus, Heading, SubHeading, Message, ShowProgress, Progress);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
private void UpdateStatusDo(string Heading, string SubHeading, string Message, bool? ShowProgress, int? Progress)
|
||||
{
|
||||
if (Heading != null)
|
||||
if (this.labelHeading.Text != Heading)
|
||||
this.labelHeading.Text = Heading;
|
||||
if (labelHeading.Text != Heading)
|
||||
labelHeading.Text = Heading;
|
||||
if (SubHeading != null)
|
||||
if (this.labelSubHeading.Text != SubHeading)
|
||||
this.labelSubHeading.Text = SubHeading;
|
||||
if (labelSubHeading.Text != SubHeading)
|
||||
labelSubHeading.Text = SubHeading;
|
||||
if (Message != null)
|
||||
if (this.labelMessage.Text != Message)
|
||||
this.labelMessage.Text = Message;
|
||||
if (labelMessage.Text != Message)
|
||||
labelMessage.Text = Message;
|
||||
|
||||
if (ShowProgress.HasValue)
|
||||
{
|
||||
if (ShowProgress.Value)
|
||||
{
|
||||
this.progressBar.Visible = true;
|
||||
progressBar.Visible = true;
|
||||
if (Progress.HasValue)
|
||||
{
|
||||
if (Progress.Value >= 0)
|
||||
{
|
||||
this.progressBar.Value = Math.Min(Progress.Value, 100);
|
||||
this.progressBar.Style = ProgressBarStyle.Continuous;
|
||||
progressBar.Value = Math.Min(Progress.Value, 100);
|
||||
progressBar.Style = ProgressBarStyle.Continuous;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.progressBar.Style = ProgressBarStyle.Marquee;
|
||||
progressBar.Style = ProgressBarStyle.Marquee;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.progressBar.Visible = false;
|
||||
progressBar.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,49 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.ClientBootstrapper
|
||||
{
|
||||
class InstallLoop
|
||||
internal class InstallLoop
|
||||
{
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
private readonly string installLocation;
|
||||
private readonly string wimImageId;
|
||||
private readonly string tempPath;
|
||||
private readonly Action completeCallback;
|
||||
private readonly Uri forcedServerUrl;
|
||||
|
||||
public Thread LoopThread;
|
||||
public delegate void CompleteCallback();
|
||||
private CompleteCallback mCompleteCallback;
|
||||
private string InstallLocation;
|
||||
private string WimImageId;
|
||||
private string TempPath;
|
||||
|
||||
public InstallLoop(string InstallLocation, string WimImageId, string TempPath)
|
||||
public InstallLoop(string installLocation, string wimImageId, string tempPath, Action completeCallback, Uri forcedServerUrl)
|
||||
{
|
||||
this.InstallLocation = InstallLocation;
|
||||
this.WimImageId = WimImageId;
|
||||
this.TempPath = TempPath;
|
||||
this.installLocation = installLocation;
|
||||
this.wimImageId = wimImageId;
|
||||
this.tempPath = tempPath;
|
||||
this.completeCallback = completeCallback;
|
||||
this.forcedServerUrl = forcedServerUrl;
|
||||
}
|
||||
|
||||
public void Start(CompleteCallback Callback)
|
||||
public void Start()
|
||||
{
|
||||
mCompleteCallback = Callback;
|
||||
LoopThread = new Thread(new ThreadStart(loopHost));
|
||||
LoopThread.Start();
|
||||
}
|
||||
private void loopHost()
|
||||
{
|
||||
try
|
||||
var cancellationToken = cancellationTokenSource.Token;
|
||||
Task.Run(async () =>
|
||||
{
|
||||
|
||||
//Program.Status.UpdateStatus(null, null, "Testing UI");
|
||||
//Program.SleepThread(5000, false);
|
||||
Interop.InstallInterop.Install(InstallLocation, WimImageId, TempPath);
|
||||
if (mCompleteCallback != null)
|
||||
try
|
||||
{
|
||||
mCompleteCallback.BeginInvoke(null, null);
|
||||
await Interop.InstallInterop.Install(installLocation, wimImageId, tempPath, forcedServerUrl, cancellationToken);
|
||||
completeCallback?.BeginInvoke(null, null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex.GetType() == typeof(ThreadAbortException))
|
||||
return;
|
||||
if (ex.GetType() == typeof(ThreadInterruptedException))
|
||||
return;
|
||||
Program.WriteAppError(ex);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex.GetType() == typeof(ThreadAbortException))
|
||||
return;
|
||||
if (ex.GetType() == typeof(ThreadInterruptedException))
|
||||
return;
|
||||
if (ex.GetType() == typeof(OperationCanceledException))
|
||||
return;
|
||||
Program.WriteAppError(ex);
|
||||
throw;
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
@@ -20,12 +22,12 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
//Remove(StoreName.Root, StoreLocation.LocalMachine, _tempCerts);
|
||||
}
|
||||
}
|
||||
public static void AddTempCerts()
|
||||
public static async Task AddTempCerts(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_tempCerts == null)
|
||||
_tempCerts = new List<string>();
|
||||
|
||||
var inlineCertificateLocation = Program.InlinePath.Value;
|
||||
var inlineCertificateLocation = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||
|
||||
// Root Certificates
|
||||
try
|
||||
@@ -35,6 +37,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
foreach (var certFile in CertFiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
|
||||
var result = Add(StoreName.Root, StoreLocation.LocalMachine, cert);
|
||||
if (result)
|
||||
@@ -42,7 +45,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
||||
_tempCerts.Add(cert.SerialNumber);
|
||||
Program.Status.UpdateStatus(null, null, $"Added Root Certificate: {cert.ShortSubjectName()}");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +63,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
foreach (var certFile in CertFiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password");
|
||||
var result = Add(StoreName.CertificateAuthority, StoreLocation.LocalMachine, cert);
|
||||
if (result)
|
||||
@@ -67,7 +71,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
||||
_tempCerts.Add(cert.SerialNumber);
|
||||
Program.Status.UpdateStatus(null, null, $"Added Intermediate Certificate: {cert.ShortSubjectName()}");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +89,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
foreach (var certFile in CertFiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var cert = new X509Certificate2(File.ReadAllBytes(certFile), "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
|
||||
var result = Add(StoreName.My, StoreLocation.LocalMachine, cert);
|
||||
if (result)
|
||||
@@ -92,7 +97,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
if (Path.GetFileNameWithoutExtension(certFile).ToLower().Contains("temp"))
|
||||
_tempCerts.Add(cert.SerialNumber);
|
||||
Program.Status.UpdateStatus(null, null, $"Added Host Certificate: {cert.ShortSubjectName()}");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,17 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
public static class InstallInterop
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
|
||||
private static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
|
||||
[Flags]
|
||||
enum MoveFileFlags
|
||||
private enum MoveFileFlags
|
||||
{
|
||||
MOVEFILE_REPLACE_EXISTING = 0x00000001,
|
||||
MOVEFILE_COPY_ALLOWED = 0x00000002,
|
||||
@@ -22,19 +24,19 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
|
||||
}
|
||||
|
||||
private static void Install(string RootFilesystemLocation, RegistryKey RootRegistryLocation, string FilesystemInstallLocation, string VirtualRootFilesystemLocation)
|
||||
private static async Task Install(string rootFilesystemLocation, RegistryKey rootRegistryLocation, string filesystemInstallLocation, string virtualRootFilesystemLocation, Uri forcedServerUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
var SourceLocation = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||
var InstallLocation = Path.Combine(RootFilesystemLocation, FilesystemInstallLocation);
|
||||
var BootstrapperCmdLinePath = Path.Combine(VirtualRootFilesystemLocation, FilesystemInstallLocation, "Disco.ClientBootstrapper.exe");
|
||||
var InstallLocation = Path.Combine(rootFilesystemLocation, filesystemInstallLocation);
|
||||
var BootstrapperCmdLinePath = Path.Combine(virtualRootFilesystemLocation, filesystemInstallLocation, "Disco.ClientBootstrapper.exe");
|
||||
|
||||
var GroupPolicyScriptsIniLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\scripts.ini");
|
||||
var GroupPolicyScriptsIniBackupLocation = Path.Combine(RootFilesystemLocation, "Windows\\System32\\GroupPolicy\\Machine\\Scripts\\disco_scripts.ini");
|
||||
var GroupPolicyScriptsIniLocation = Path.Combine(rootFilesystemLocation, @"Windows\System32\GroupPolicy\Machine\Scripts\scripts.ini");
|
||||
var GroupPolicyScriptsIniBackupLocation = Path.Combine(rootFilesystemLocation, @"Windows\System32\GroupPolicy\Machine\Scripts\disco_scripts.ini");
|
||||
|
||||
// Create file system Location
|
||||
#region "Create File System Location"
|
||||
Program.Status.UpdateStatus(null, null, "Creating Installation Location");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
if (Directory.Exists(InstallLocation))
|
||||
{
|
||||
// Try and Delete Directory
|
||||
@@ -52,19 +54,23 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
var installDir = Directory.CreateDirectory(InstallLocation);
|
||||
installDir.Attributes = installDir.Attributes | FileAttributes.Hidden;
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
#endregion
|
||||
|
||||
// Copy files to file system location
|
||||
#region "Copy to File System"
|
||||
Program.Status.UpdateStatus(null, null, "Copying Files");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
|
||||
// Copy Bootstrapper
|
||||
// ie: Executing Assembly
|
||||
File.Copy(System.Reflection.Assembly.GetExecutingAssembly().Location, Path.Combine(InstallLocation, "Disco.ClientBootstrapper.exe"));
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(SourceLocation))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var fileName = Path.GetFileName(file);
|
||||
|
||||
// Only Copy Certain Files
|
||||
@@ -86,7 +92,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
// Backup & Create Group Policy Scripts.ini
|
||||
#region "Group Policy Scripts.ini"
|
||||
Program.Status.UpdateStatus(null, null, "Creating Group Policy Script Entry");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
// Backup
|
||||
if (!File.Exists(GroupPolicyScriptsIniBackupLocation))
|
||||
{
|
||||
@@ -95,6 +101,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
File.Move(GroupPolicyScriptsIniLocation, GroupPolicyScriptsIniBackupLocation);
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Create
|
||||
if (File.Exists(GroupPolicyScriptsIniLocation))
|
||||
@@ -105,56 +112,67 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
using (var scriptsIniStreamWriter = new StreamWriter(scriptsIniStream, Encoding.Unicode))
|
||||
{
|
||||
scriptsIniStreamWriter.Write($"[Startup]{Environment.NewLine}0CmdLine={BootstrapperCmdLinePath}{Environment.NewLine}0Parameters=/AllowUninstall");
|
||||
scriptsIniStreamWriter.Flush();
|
||||
scriptsIniStreamWriter.WriteLine("[Startup]");
|
||||
scriptsIniStreamWriter.WriteLine($"0CmdLine={BootstrapperCmdLinePath}");
|
||||
if (forcedServerUrl == null)
|
||||
scriptsIniStreamWriter.WriteLine("0Parameters=/AllowUninstall");
|
||||
else
|
||||
scriptsIniStreamWriter.WriteLine($"0Parameters=/AllowUninstall {forcedServerUrl}");
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
#endregion
|
||||
|
||||
// Backup & Create Group Policy Registry
|
||||
#region "Group Policy Registry"
|
||||
Program.Status.UpdateStatus(null, null, "Creating Group Policy Registry Entries");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
// Backup Scripts
|
||||
using (var regGroupPolicy = RootRegistryLocation.OpenSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy", true))
|
||||
using (var regGroupPolicy = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy", true))
|
||||
{
|
||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||
{
|
||||
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Create Scripts
|
||||
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Shutdown").Dispose();
|
||||
using (var regScriptsStartup = RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup\\0"))
|
||||
rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown").Dispose();
|
||||
using (var regScriptsStartup = rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0"))
|
||||
{
|
||||
regScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("FileSysPath", Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine"), RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("FileSysPath", Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine"), RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("DisplayName", "Local Group Policy", RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("GPOName", "Local Group Policy", RegistryValueKind.String);
|
||||
regScriptsStartup.SetValue("PSScriptOrder", 1, RegistryValueKind.DWord);
|
||||
using (var regScriptsStartup0 = regScriptsStartup.CreateSubKey("0"))
|
||||
{
|
||||
regScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
|
||||
regScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
|
||||
if (forcedServerUrl == null)
|
||||
regScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
|
||||
else
|
||||
regScriptsStartup0.SetValue("Parameters", $"/AllowUninstall {forcedServerUrl}", RegistryValueKind.String);
|
||||
regScriptsStartup0.SetValue("IsPowershell", 0, RegistryValueKind.DWord);
|
||||
regScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary);
|
||||
}
|
||||
}
|
||||
RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Shutdown").Dispose();
|
||||
rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown").Dispose();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Backup Scripts State
|
||||
using (var regGroupPolicy = RootRegistryLocation.OpenSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine", true))
|
||||
using (var regGroupPolicy = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine", true))
|
||||
{
|
||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Scripts") && !regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||
{
|
||||
RegistryUtilities.RenameSubKey(regGroupPolicy, "Scripts", "Disco_Scripts");
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Create Scripts State
|
||||
using (var regStateScriptsStartup = RootRegistryLocation.CreateSubKey("Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Startup\\0"))
|
||||
using (var regStateScriptsStartup = rootRegistryLocation.CreateSubKey(@"Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup\0"))
|
||||
{
|
||||
regStateScriptsStartup.SetValue("GPO-ID", "LocalGPO", RegistryValueKind.String);
|
||||
regStateScriptsStartup.SetValue("SOM-ID", "Local", RegistryValueKind.String);
|
||||
@@ -165,17 +183,21 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
using (var regStateScriptsStartup0 = regStateScriptsStartup.CreateSubKey("0"))
|
||||
{
|
||||
regStateScriptsStartup0.SetValue("Script", BootstrapperCmdLinePath, RegistryValueKind.String);
|
||||
regStateScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
|
||||
if (forcedServerUrl == null)
|
||||
regStateScriptsStartup0.SetValue("Parameters", "/AllowUninstall", RegistryValueKind.String);
|
||||
else
|
||||
regStateScriptsStartup0.SetValue("Parameters", $"/AllowUninstall {forcedServerUrl}", RegistryValueKind.String);
|
||||
regStateScriptsStartup0.SetValue("ExecTime", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, RegistryValueKind.Binary);
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
#endregion
|
||||
|
||||
// Set Registry Startup Environment Policies
|
||||
#region "Registry Startup Policies"
|
||||
Program.Status.UpdateStatus(null, null, "Creating Startup Policy Registry Entries");
|
||||
Program.SleepThread(500, false);
|
||||
using (var regWinlogon = RootRegistryLocation.OpenSubKey("Microsoft\\Windows NT\\CurrentVersion\\Winlogon", true))
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
using (var regWinlogon = rootRegistryLocation.OpenSubKey(@"Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||
{
|
||||
regWinlogon.SetValue("HideStartupScripts", 0, RegistryValueKind.DWord);
|
||||
regWinlogon.SetValue("RunStartupScriptSync", 1, RegistryValueKind.DWord);
|
||||
@@ -183,94 +205,110 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static void Install(string InstallLocation, string WimImageId, string TempPath)
|
||||
public static async Task Install(string installLocation, string wimImageId, string tempPath, Uri forcedServerUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper", "Starting", "Please wait...", false);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(InstallLocation))
|
||||
InstallLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco");
|
||||
if (string.IsNullOrWhiteSpace(installLocation))
|
||||
installLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco");
|
||||
|
||||
if (InstallLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase))
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (installLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Offline File System (WIM)
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", $"Install Location: {InstallLocation}");
|
||||
Program.SleepThread(1000, false);
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", $"Install Location: {installLocation}");
|
||||
await Program.SleepThread(1000, false, cancellationToken);
|
||||
|
||||
// Mount WIM
|
||||
int wimImageIndex = 0;
|
||||
using (var wim = new WIMInterop.WindowsImageContainer(InstallLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write))
|
||||
using (var wim = new WIMInterop.WindowsImageContainer(installLocation, WIMInterop.WindowsImageContainer.CreateFileMode.OpenExisting, WIMInterop.WindowsImageContainer.CreateFileAccess.Write))
|
||||
{
|
||||
if (WimImageId == null)
|
||||
WimImageId = "1";
|
||||
if (!int.TryParse(WimImageId, out wimImageIndex))
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (wimImageId == null)
|
||||
wimImageId = "1";
|
||||
if (!int.TryParse(wimImageId, out wimImageIndex))
|
||||
{
|
||||
Program.Status.UpdateStatus(null, "Analysing WIM", $"Looking for Image Name: {WimImageId}");
|
||||
Program.SleepThread(500, false);
|
||||
Program.Status.UpdateStatus(null, "Analysing WIM", $"Looking for Image Name: {wimImageId}");
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
for (int i = 0; i < wim.ImageCount; i++)
|
||||
{
|
||||
var wimImageInfo = new System.Xml.XmlDocument();
|
||||
using (var wimImage = wim[i])
|
||||
wimImageInfo.LoadXml(wimImage.ImageInformation);
|
||||
var wimImageInfoName = wimImageInfo.SelectSingleNode("//IMAGE/NAME");
|
||||
if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(WimImageId, StringComparison.OrdinalIgnoreCase))
|
||||
if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(wimImageId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
wimImageIndex = i + 1;
|
||||
Program.Status.UpdateStatus(null, "Analysing WIM", $"Found Image Id '{WimImageId}' at Index {wimImageIndex}");
|
||||
Program.SleepThread(500, false);
|
||||
Program.Status.UpdateStatus(null, "Analysing WIM", $"Found Image Id '{wimImageId}' at Index {wimImageIndex}");
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (wimImageIndex == 0)
|
||||
{
|
||||
Program.Status.UpdateStatus(null, "Error", $"Unable to load WIM Image Id: {WimImageId}");
|
||||
Program.SleepThread(5000, false);
|
||||
Program.Status.UpdateStatus(null, "Error", $"Unable to load WIM Image Id: {wimImageId}");
|
||||
await Program.SleepThread(5000, false, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Temp Path
|
||||
var wimMountPath = Path.Combine(TempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimMount");
|
||||
var wimMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimMount");
|
||||
if (Directory.Exists(wimMountPath))
|
||||
Directory.Delete(wimMountPath, true);
|
||||
Directory.CreateDirectory(wimMountPath);
|
||||
|
||||
var wimTempMountPath = Path.Combine(TempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount");
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var wimTempMountPath = Path.Combine(tempPath ?? Path.GetTempPath(), "DiscoClientBootstrapperWimTempMount");
|
||||
if (Directory.Exists(wimTempMountPath))
|
||||
Directory.Delete(wimTempMountPath, true);
|
||||
Directory.CreateDirectory(wimTempMountPath);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
bool wimCommitChanges = true;
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback m_MessageCallback = null;
|
||||
try
|
||||
{
|
||||
// Mount WIM
|
||||
Program.Status.UpdateStatus(null, "Mounting WIM", $"Mounting WIM Image to '{wimMountPath}'");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
m_MessageCallback = new WIMInterop.WindowsImageContainer.NativeMethods.MessageCallback(WimImageEventMessagePump);
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.RegisterCallback(m_MessageCallback);
|
||||
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, InstallLocation, wimImageIndex, wimTempMountPath);
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.MountImage(wimMountPath, installLocation, wimImageIndex, wimTempMountPath);
|
||||
|
||||
// Load Local Machine Registry
|
||||
var wimHivePath = Path.Combine(wimMountPath, "Windows\\System32\\config\\SOFTWARE");
|
||||
Program.Status.UpdateStatus(null, "Mounting Offline Registry Hive", $"Mounting Offline Registry Hive at '{wimHivePath}'");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
using (var wimReg = new RegistryInterop(RegistryInterop.RegistryHives.HKEY_LOCAL_MACHINE, "DiscoClientBootstrapperWimHive", wimHivePath))
|
||||
{
|
||||
using (RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("DiscoClientBootstrapperWimHive", true))
|
||||
try
|
||||
{
|
||||
string rootFileSystemLocation = wimMountPath;
|
||||
string fileSystemInstallLocation = "Disco";
|
||||
string virtualRootFileSystemLocation = "C:\\";
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("DiscoClientBootstrapperWimHive", true))
|
||||
{
|
||||
string rootFileSystemLocation = wimMountPath;
|
||||
string fileSystemInstallLocation = "Disco";
|
||||
string virtualRootFileSystemLocation = "C:\\";
|
||||
|
||||
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, virtualRootFileSystemLocation, forcedServerUrl, cancellationToken);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Unload Local Machine Registry
|
||||
Program.Status.UpdateStatus(null, "Unmounting Offline Registry Hive", $"Unmounting Offline Registry Hive at '{wimHivePath}'");
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
wimReg.Unload();
|
||||
}
|
||||
|
||||
// Unload Local Machine Registry
|
||||
Program.Status.UpdateStatus(null, "Unmounting Offline Registry Hive", $"Unmounting Offline Registry Hive at '{wimHivePath}'");
|
||||
Program.SleepThread(500, false);
|
||||
wimReg.Unload();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
@@ -282,8 +320,8 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
// Unmount WIM
|
||||
Program.Status.UpdateStatus(null, "Unmounting WIM", $"Unmounting WIM Image at '{wimMountPath}'");
|
||||
Program.SleepThread(500, false);
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, InstallLocation, wimImageIndex, wimCommitChanges);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
WIMInterop.WindowsImageContainer.NativeMethods.DismountImage(wimMountPath, installLocation, wimImageIndex, wimCommitChanges);
|
||||
|
||||
if (m_MessageCallback != null)
|
||||
{
|
||||
@@ -295,23 +333,25 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
Directory.Delete(wimMountPath, true);
|
||||
if (Directory.Exists(wimTempMountPath))
|
||||
Directory.Delete(wimTempMountPath, true);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Online File System
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", $"Install Location: {InstallLocation}", true, -1);
|
||||
Program.SleepThread(1000, false);
|
||||
string rootFileSystemLocation = Path.GetPathRoot(InstallLocation);
|
||||
Program.Status.UpdateStatus("Installing Bootstrapper (Online)", "Installing", $"Install Location: {installLocation}", true, -1);
|
||||
await Program.SleepThread(1000, false, cancellationToken);
|
||||
string rootFileSystemLocation = Path.GetPathRoot(installLocation);
|
||||
RegistryKey rootRegistryLocation = Registry.LocalMachine.OpenSubKey("SOFTWARE", true);
|
||||
string fileSystemInstallLocation = InstallLocation.Substring(rootFileSystemLocation.Length);
|
||||
string fileSystemInstallLocation = installLocation.Substring(rootFileSystemLocation.Length);
|
||||
|
||||
Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation);
|
||||
await Install(rootFileSystemLocation, rootRegistryLocation, fileSystemInstallLocation, rootFileSystemLocation, forcedServerUrl, cancellationToken);
|
||||
Program.Status.UpdateStatus(null, "Online File System Installation Complete", string.Empty, true, -1);
|
||||
Program.SleepThread(1000, false);
|
||||
await Program.SleepThread(1000, false, cancellationToken);
|
||||
}
|
||||
Program.Status.UpdateStatus(null, "Complete", "Finished Installing Bootstrapper");
|
||||
Program.SleepThread(1500, false);
|
||||
await Program.SleepThread(1500, false, cancellationToken);
|
||||
}
|
||||
|
||||
private static uint WimImageEventMessagePump(
|
||||
@@ -349,41 +389,28 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
return status;
|
||||
}
|
||||
|
||||
public static void Uninstall()
|
||||
public static async Task Uninstall(CancellationToken cancellationToken)
|
||||
{
|
||||
// Application Directory
|
||||
var appDirectory = Program.InlinePath.Value;
|
||||
if (Program.AllowUninstall && !appDirectory.StartsWith("\\\\"))
|
||||
var appDirectory = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||
if (Program.AllowUninstall && !appDirectory.StartsWith(@"\\"))
|
||||
{
|
||||
Program.Status.UpdateStatus("System Preparation (Bootstrapper)", "Uninstalling Bootstrapper...", string.Empty, false, 0);
|
||||
Program.SleepThread(1000, true);
|
||||
//var uninstallScriptLocation = System.IO.Path.Combine(appDirectory, "UninstallBootstrapper.vbs");
|
||||
//if (System.IO.File.Exists(uninstallScriptLocation))
|
||||
//{
|
||||
// var bootstrapperPID = System.Diagnostics.Process.GetCurrentProcess().Id;
|
||||
// var cscriptPath = System.IO.Path.Combine(Environment.SystemDirectory, "cscript.exe");
|
||||
// var cscriptArgs = string.Format("\"{0}\" /WaitForProcessID:{1}", uninstallScriptLocation, bootstrapperPID);
|
||||
|
||||
// var startProc = new ProcessStartInfo(cscriptPath, cscriptArgs);
|
||||
// startProc.WorkingDirectory = Environment.SystemDirectory;
|
||||
// startProc.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
|
||||
// Process.Start(startProc);
|
||||
//}
|
||||
await Program.SleepThread(1000, true, cancellationToken);
|
||||
|
||||
// Remove Registry Entries
|
||||
using (var regWinlogon = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", true))
|
||||
using (var regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||
{
|
||||
regWinlogon.DeleteValue("HideStartupScripts", false);
|
||||
regWinlogon.DeleteValue("RunStartupScriptSync", false);
|
||||
}
|
||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Shutdown", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\Scripts\\Startup", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Shutdown", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine\\Scripts\\Startup", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Shutdown", false);
|
||||
Registry.LocalMachine.DeleteSubKeyTree(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Scripts\Startup", false);
|
||||
|
||||
// Restore Registry Backups
|
||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy", true))
|
||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy", true))
|
||||
{
|
||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||
{
|
||||
@@ -391,7 +418,7 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
RegistryUtilities.RenameSubKey(regGroupPolicy, "Disco_Scripts", "Scripts");
|
||||
}
|
||||
}
|
||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\State\\Machine", true))
|
||||
using (var regGroupPolicy = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine", true))
|
||||
{
|
||||
if (regGroupPolicy != null && regGroupPolicy.GetSubKeyNames().Contains("Disco_Scripts"))
|
||||
{
|
||||
@@ -401,10 +428,10 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
}
|
||||
|
||||
// Delete Group Policy Script File
|
||||
var groupPolicyScriptsPath = Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine\\Scripts\\scripts.ini");
|
||||
var groupPolicyScriptsPath = Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine\Scripts\scripts.ini");
|
||||
if (File.Exists(groupPolicyScriptsPath))
|
||||
File.Delete(groupPolicyScriptsPath);
|
||||
var groupPolicyScriptsBackupPath = Path.Combine(Environment.SystemDirectory, "GroupPolicy\\Machine\\Scripts\\disco_scripts.ini");
|
||||
var groupPolicyScriptsBackupPath = Path.Combine(Environment.SystemDirectory, @"GroupPolicy\Machine\Scripts\disco_scripts.ini");
|
||||
if (File.Exists(groupPolicyScriptsBackupPath))
|
||||
File.Move(groupPolicyScriptsBackupPath, groupPolicyScriptsPath);
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
static class NetworkInterop
|
||||
internal static class NetworkInterop
|
||||
{
|
||||
|
||||
#region PInvoke
|
||||
@@ -164,30 +167,35 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
}
|
||||
}
|
||||
|
||||
public static bool PingDiscoIct(string ServerName)
|
||||
public static bool HasNetworkConnectivity()
|
||||
{
|
||||
using (Ping p = new Ping())
|
||||
var nics = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||
.ToList();
|
||||
|
||||
foreach (var nic in nics)
|
||||
{
|
||||
try
|
||||
if (nic.Supports(NetworkInterfaceComponent.IPv4))
|
||||
{
|
||||
PingReply pr = p.Send(ServerName, 2000);
|
||||
if (pr.Status == IPStatus.Success)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
var ipProps = nic.GetIPProperties();
|
||||
var ipv4Props = ipProps.GetIPv4Properties();
|
||||
if (ipv4Props.IsAutomaticPrivateAddressingActive)
|
||||
continue;
|
||||
|
||||
return ipProps.UnicastAddresses
|
||||
.Where(ua => ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||
.Any();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void ConfigureWireless()
|
||||
public static async Task ConfigureWireless(CancellationToken cancellationToken)
|
||||
{
|
||||
// Add Certificates
|
||||
Program.Status.UpdateStatus(null, null, "Configuring Wireless Certificates");
|
||||
CertificateInterop.AddTempCerts();
|
||||
await CertificateInterop.AddTempCerts(cancellationToken);
|
||||
|
||||
// Add Wireless Profiles
|
||||
Program.Status.UpdateStatus(null, null, "Configuring Wireless Profiles");
|
||||
@@ -208,15 +216,16 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
{
|
||||
foreach (var inlineWirelessProfile in wirelessInlineProfiles)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (inlineWirelessProfile.AddProfile(wlanHandle, na.Guid))
|
||||
{
|
||||
Program.Status.UpdateStatus(null, null, $"Added Wireless Profile: {inlineWirelessProfile.ProfileName}");
|
||||
Program.SleepThread(500, false);
|
||||
await Program.SleepThread(500, false, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Status.UpdateStatus(null, null, $"Unable to add Wireless Profile: {inlineWirelessProfile.ProfileName}");
|
||||
Program.SleepThread(5000, false);
|
||||
await Program.SleepThread(5000, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,14 +255,15 @@ namespace Disco.ClientBootstrapper.Interop
|
||||
|
||||
private static List<WirelessProfile> GetInlineWirelessProfiles()
|
||||
{
|
||||
var inlineProfileFiles = System.IO.Directory.EnumerateFiles(Program.InlinePath.Value, "WLAN_Profile_*.xml").ToList();
|
||||
var directoryPath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
|
||||
var inlineProfileFiles = Directory.EnumerateFiles(directoryPath, "WLAN_Profile_*.xml").ToList();
|
||||
var inlineProfiles = new List<WirelessProfile>(inlineProfileFiles.Count);
|
||||
foreach (var filename in inlineProfileFiles)
|
||||
{
|
||||
var profile = new WirelessProfile()
|
||||
{
|
||||
Filename = filename,
|
||||
ProfileXml = System.IO.File.ReadAllText(filename)
|
||||
ProfileXml = File.ReadAllText(filename)
|
||||
};
|
||||
var profileXml = new XmlDocument();
|
||||
profileXml.LoadXml(profile.ProfileXml);
|
||||
|
||||
@@ -1,36 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Disco.ClientBootstrapper
|
||||
{
|
||||
static class Program
|
||||
internal static class Program
|
||||
{
|
||||
public static IStatus Status { get; set; }
|
||||
public static BootstrapperLoop BootstrapperLoop { get; set; }
|
||||
public static InstallLoop InstallLoop { get; set; }
|
||||
private static readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
public static IStatus Status { get; private set; }
|
||||
|
||||
public static List<string> PostBootstrapperActions { get; set; }
|
||||
public static bool AllowUninstall { get; set; }
|
||||
public static bool ApplicationExiting { get; set; }
|
||||
public static Lazy<string> InlinePath = new Lazy<string>(() =>
|
||||
{
|
||||
return System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
|
||||
});
|
||||
public static bool AllowUninstall { get; private set; }
|
||||
public static Uri ForcedServerUrl { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main(string[] args)
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
||||
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
#if DEBUG
|
||||
if (args.Any(a => a.Equals("debug", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
do
|
||||
{
|
||||
Console.WriteLine("Waiting for Debugger to Attach");
|
||||
Thread.Sleep(1000);
|
||||
} while (!System.Diagnostics.Debugger.IsAttached);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (args.Any(a => a.StartsWith("http://", StringComparison.OrdinalIgnoreCase)))
|
||||
throw new ArgumentException("Only HTTPS URLs are supported for a forced server URL.");
|
||||
var forcedServerArg = args.FirstOrDefault(a => a.StartsWith("https://", StringComparison.OrdinalIgnoreCase));
|
||||
if (forcedServerArg != null)
|
||||
{
|
||||
if (Uri.TryCreate(forcedServerArg, UriKind.Absolute, out var forcedUri))
|
||||
ForcedServerUrl = forcedUri;
|
||||
else
|
||||
throw new ArgumentException("The provided forced server URL is not valid.");
|
||||
}
|
||||
|
||||
switch (args[0].ToLower())
|
||||
{
|
||||
case "/install":
|
||||
@@ -46,14 +68,17 @@ namespace Disco.ClientBootstrapper
|
||||
wimImage = args[2];
|
||||
if (args.Length > 3)
|
||||
tempPath = args[3];
|
||||
InstallLoop = new InstallLoop(installLocation, wimImage, tempPath);
|
||||
InstallLoop.Start(new InstallLoop.CompleteCallback(InstallComplete));
|
||||
var installLoop = new InstallLoop(installLocation, wimImage, tempPath, InstallComplete, ForcedServerUrl);
|
||||
installLoop.Start();
|
||||
Application.Run();
|
||||
return;
|
||||
case "/uninstall":
|
||||
AllowUninstall = true;
|
||||
Status = new NullStatus();
|
||||
Interop.InstallInterop.Uninstall();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Interop.InstallInterop.Uninstall(cancellationTokenSource.Token);
|
||||
}).Wait(cancellationTokenSource.Token);
|
||||
return;
|
||||
case "/allowuninstall":
|
||||
AllowUninstall = true;
|
||||
@@ -71,13 +96,13 @@ namespace Disco.ClientBootstrapper
|
||||
statusForm.Show();
|
||||
}
|
||||
|
||||
BootstrapperLoop = new BootstrapperLoop(Status, new BootstrapperLoop.LoopCompleteCallback(LoopComplete));
|
||||
BootstrapperLoop.Start();
|
||||
var bootstrapperLoop = new BootstrapperLoop(Status, ForcedServerUrl, LoopComplete, cancellationTokenSource.Token);
|
||||
bootstrapperLoop.Start();
|
||||
|
||||
Application.Run();
|
||||
}
|
||||
|
||||
static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
|
||||
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
|
||||
{
|
||||
WriteAppError(e.Exception);
|
||||
}
|
||||
@@ -100,7 +125,7 @@ namespace Disco.ClientBootstrapper
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
public static void LoopComplete()
|
||||
public static async Task LoopComplete(CancellationToken cancellationToken)
|
||||
{
|
||||
// Run Post Actions
|
||||
if (PostBootstrapperActions != null)
|
||||
@@ -108,32 +133,32 @@ namespace Disco.ClientBootstrapper
|
||||
// Check Uninstall
|
||||
if (AllowUninstall && PostBootstrapperActions.Contains("UninstallBootstrapper"))
|
||||
{
|
||||
Interop.InstallInterop.Uninstall();
|
||||
await Interop.InstallInterop.Uninstall(cancellationToken);
|
||||
}
|
||||
|
||||
// Check ShutdownActions
|
||||
if (PostBootstrapperActions.Contains("Shutdown"))
|
||||
{
|
||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Shutting Down; Finished...", string.Empty, false, 0);
|
||||
SleepThread(4000, true);
|
||||
await SleepThread(4000, true, cancellationToken);
|
||||
Interop.ShutdownInterop.Shutdown();
|
||||
}
|
||||
else if (PostBootstrapperActions.Contains("Reboot"))
|
||||
{
|
||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Rebooting; Finished...", string.Empty, false, 0);
|
||||
SleepThread(4000, true);
|
||||
await SleepThread(4000, true, cancellationToken);
|
||||
Interop.ShutdownInterop.Reboot();
|
||||
}
|
||||
else
|
||||
{
|
||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
|
||||
SleepThread(2000, true);
|
||||
await SleepThread(2000, true, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Status.UpdateStatus("System Preparation (Bootstrapper)", "Starting System; Finished...", string.Empty, false, 0);
|
||||
SleepThread(2000, true);
|
||||
await SleepThread(2000, true, cancellationToken);
|
||||
}
|
||||
|
||||
ExitApplication();
|
||||
@@ -146,33 +171,12 @@ namespace Disco.ClientBootstrapper
|
||||
|
||||
public static void ExitApplication()
|
||||
{
|
||||
if (!ApplicationExiting)
|
||||
{
|
||||
ApplicationExiting = true;
|
||||
if (BootstrapperLoop != null)
|
||||
{
|
||||
if (BootstrapperLoop.LoopThread != null)
|
||||
{
|
||||
if (BootstrapperLoop.LoopThread.ThreadState == ThreadState.WaitSleepJoin)
|
||||
{
|
||||
BootstrapperLoop.LoopThread.Interrupt();
|
||||
}
|
||||
if (BootstrapperLoop.LoopThread.ThreadState == ThreadState.Running)
|
||||
{
|
||||
BootstrapperLoop.LoopThread.Abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
Application.Exit();
|
||||
}
|
||||
if (!cancellationTokenSource.IsCancellationRequested)
|
||||
cancellationTokenSource.Cancel();
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
public static void Trace(string Format, params string[] args)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(Format, args);
|
||||
}
|
||||
|
||||
public static void SleepThread(int millisecondsTimeout, bool updateUI)
|
||||
public static async Task SleepThread(int millisecondsTimeout, bool updateUI, CancellationToken cancellationToken)
|
||||
{
|
||||
if (updateUI)
|
||||
{
|
||||
@@ -180,12 +184,12 @@ namespace Disco.ClientBootstrapper
|
||||
{
|
||||
int progress = Convert.ToInt32(((Convert.ToDouble(i) / Convert.ToDouble(millisecondsTimeout)) * 100));
|
||||
Status.UpdateStatus(null, null, null, true, progress);
|
||||
Thread.Sleep(500);
|
||||
await Task.Delay(500, cancellationToken);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(millisecondsTimeout);
|
||||
await Task.Delay(millisecondsTimeout, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user