Skip to content

Commit

Permalink
Report AutoUpdater errors to user
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Jan 27, 2021
1 parent f8f0db2 commit 656548b
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 111 deletions.
1 change: 1 addition & 0 deletions AutoUpdate/CKAN-autoupdate.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\_build\meta\GlobalAssemblyVersionInfo.cs">
Expand Down
147 changes: 111 additions & 36 deletions AutoUpdate/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows.Forms;

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CKAN AUTO-UPDATE TOOL
Expand All @@ -17,73 +18,116 @@

namespace AutoUpdater
{
class Program
public class Program
{
static void Main(string[] args)
public static int Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionEventHandler;

if (args.Length != 4)
{
return;
ReportError("Usage: AutoUpdater.exe pid oldPath newPath [no]launch");
return ExitBADOPT;
}

var pid = int.Parse(args[0]);
var local_path = args[1];
var updated_path = args[2];
// Yes, it's a global variable, but we're an ephemeral singleton so :shrug:
fromGui = (args[3] == "launch");

int pid = int.Parse(args[0]);
string local_path = args[1];
string updated_path = args[2];

if (!File.Exists(updated_path))
{
return;
ReportError($"Downloaded ckan.exe not found at: {updated_path}");
return ExitBADOPT;
}

// wait for CKAN to close
// Wait for CKAN to close
try
{
var process = Process.GetProcessById(pid);

if (!process.HasExited)
if (IsOnWindows())
{
// On Unix you can only wait for CHILD processes to exit
var process = Process.GetProcessById(Math.Abs(pid));
if (!process.HasExited)
{
process.WaitForExit();
}
}
else if (pid < 0)
{
process.WaitForExit();
// v1.29.2 and earlier will send positive, releases after will send negative
// AND redirect stdin so it closes at exit
while (Console.ReadLine() != null)
{
// Do nothing (shouldn't hit this because it doesn't send us anything)
}
}
}
catch (Exception) {}

int retries = 8;
catch (ArgumentException)
{
// Process already exited, we're fine
}
catch (Exception exc)
{
ReportError($"Failed to wait for CKAN to close: {exc.Message}");
return ExitERROR;
}

while (File.Exists(local_path))
for (int retry = 0; retry < maxRetries && File.Exists(local_path); ++retry)
{
try
{
// delete the old ckan.exe
// Delete the old ckan.exe
File.Delete(local_path);
}
catch (Exception)
catch (Exception exc)
{
Thread.Sleep(1000);
}

retries--;
if (retries == 0)
{
return;
if (retry == maxRetries - 1)
{
ReportError($"Failed to delete {local_path}: {exc.Message}");
if (fromGui)
{
// Launch the old EXE that we can't delete
StartCKAN(local_path);
}
return ExitERROR;
}
else
{
// Double sleep every time, starting at 100 ms, ending at 25 sec
Thread.Sleep(100 * (int)Math.Pow(2, retry));
}
}
}

// replace ckan.exe
// Replace ckan.exe
File.Move(updated_path, local_path);

MakeExecutable(local_path);

if (args[3] == "launch")
if (fromGui)
{
//Start CKAN
if (IsOnMono())
{
Process.Start("mono", String.Format("\"{0}\"", local_path));
}
else
{
Process.Start(local_path);
}
StartCKAN(local_path);
}
return ExitOK;
}

/// <summary>
/// Run the CKAN EXE at the given path
/// </summary>
/// <param name="path">Location of our CKAN EXE</param>
private static void StartCKAN(string path)
{
// Start CKAN
if (IsOnMono())
{
Process.Start("mono", String.Format("\"{0}\"", path));
}
else
{
Process.Start(path);
}
}

Expand Down Expand Up @@ -120,5 +164,36 @@ private static bool IsOnWindows()
return platform != PlatformID.MacOSX &&
platform != PlatformID.Unix && platform != PlatformID.Xbox;
}

/// <summary>
/// Display unexpected exceptions to user
/// </summary>
/// <param name="sender">Source of unhandled exception</param>
/// <param name="e">Info about the exception</param>
private static void UnhandledExceptionEventHandler(Object sender, UnhandledExceptionEventArgs e)
{
ReportError($"Unhandled exception:\r\n{e.ExceptionObject}");
}

/// <summary>
/// It's nice to tell the user when something goes wrong!
/// </summary>
/// <param name="err">Description of the problem that happened</param>
private static void ReportError(string err)
{
Console.Error.WriteLine(err);
if (fromGui)
{
// Show a popup in case the console isn't open
MessageBox.Show(err, "Fatal AutoUpdater Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

private const int maxRetries = 8;
private static bool fromGui = false;

private const int ExitOK = 0;
private const int ExitBADOPT = 1;
private const int ExitERROR = 2;
}
}
10 changes: 6 additions & 4 deletions Core/Net/AutoUpdate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,14 @@ public void StartUpdateProcess(bool launchCKANAfterUpdate, IUser user = null)
{
Verb = "runas",
FileName = updaterFilename,
Arguments = String.Format(@"{0} ""{1}"" ""{2}"" {3}", pid, exePath, ckanFilename, launchCKANAfterUpdate ? "launch" : "nolaunch"),
UseShellExecute = false
Arguments = String.Format(@"{0} ""{1}"" ""{2}"" {3}", -pid, exePath, ckanFilename, launchCKANAfterUpdate ? "launch" : "nolaunch"),
UseShellExecute = false,
// Make child's stdin a pipe so it can tell when we exit
RedirectStandardInput = true,
CreateNoWindow = true,
});

// exit this ckan instance
Environment.Exit(0);
// Caller should now exit. Let them do it safely.
}

public static void SetExecutable(string fileName)
Expand Down
3 changes: 3 additions & 0 deletions GUI/CKAN-GUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,9 @@
<Compile Include="Main\Main.Designer.cs">
<DependentUpon>Main.cs</DependentUpon>
</Compile>
<Compile Include="Main\MainAutoUpdate.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Main\MainChangeset.cs">
<SubType>Form</SubType>
</Compile>
Expand Down
7 changes: 4 additions & 3 deletions GUI/Dialogs/NewUpdateDialog.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions GUI/Dialogs/NewUpdateDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ namespace CKAN
{
public partial class NewUpdateDialog : Form
{
/// <summary>
/// Iniitialize the update info form with version and release notes
/// </summary>
/// <param name="version">Version number of new release</param>
/// <param name="releaseNotes">Markdown formatted description of the new release</param>
public NewUpdateDialog(string version, string releaseNotes)
{
InitializeComponent();

VersionLabel.Text = version;
ReleaseNotesTextbox.Text = releaseNotes;
ReleaseNotesTextbox.Text = releaseNotes.Trim();
}
}
}
Loading

0 comments on commit 656548b

Please sign in to comment.