diff options
author | Kenneth Skovhede <kenneth@hexad.dk> | 2017-10-11 13:49:14 +0300 |
---|---|---|
committer | Kenneth Skovhede <kenneth@hexad.dk> | 2017-10-11 13:49:14 +0300 |
commit | 6ad0fa649e837382fa7376ff2a223ee7ba4c0037 (patch) | |
tree | 7d197500b812dcb3aee0607f6ccf91a02be72fde /Duplicati | |
parent | c7e0071d70f86b371e891f3f7f7584284bf1045c (diff) |
Re-wrote the auto-updater to use process spawning instead of AppDomain loading to execute updates.
Diffstat (limited to 'Duplicati')
-rw-r--r-- | Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs | 2 | ||||
-rw-r--r-- | Duplicati/Library/AutoUpdater/UpdaterManager.cs | 113 | ||||
-rw-r--r-- | Duplicati/Library/Localization/MoLocalizationService.cs | 9 | ||||
-rw-r--r-- | Duplicati/Server/Program.cs | 22 | ||||
-rw-r--r-- | Duplicati/Server/UpdatePollThread.cs | 5 |
5 files changed, 142 insertions, 9 deletions
diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs index d9a247204..79324e237 100644 --- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs @@ -67,7 +67,7 @@ namespace Duplicati.GUI.TrayIcon List<string> args = new List<string>(_args);
Dictionary<string, string> options = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(args);
- if (Duplicati.Library.Utility.Utility.IsClientWindows && !Duplicati.Library.Utility.Utility.ParseBoolOption(options, DETACHED_PROCESS))
+ if (Duplicati.Library.Utility.Utility.IsClientWindows && (Duplicati.Library.AutoUpdater.UpdaterManager.IsRunningInUpdateEnvironment || !Duplicati.Library.Utility.Utility.ParseBoolOption(options, DETACHED_PROCESS)))
Duplicati.Library.Utility.Win32.AttachConsole(Duplicati.Library.Utility.Win32.ATTACH_PARENT_PROCESS);
foreach (string s in args)
diff --git a/Duplicati/Library/AutoUpdater/UpdaterManager.cs b/Duplicati/Library/AutoUpdater/UpdaterManager.cs index 6328d461e..6f0857cf8 100644 --- a/Duplicati/Library/AutoUpdater/UpdaterManager.cs +++ b/Duplicati/Library/AutoUpdater/UpdaterManager.cs @@ -18,6 +18,8 @@ using System;
using System.Linq;
using System.Collections.Generic;
+using System.IO; +using System.Threading.Tasks;
namespace Duplicati.Library.AutoUpdater
{
@@ -34,6 +36,11 @@ namespace Duplicati.Library.AutoUpdater public static class UpdaterManager
{
+ /// <summary>
+ /// The magic exit code that signals an update has been installed and that the app should restart
+ /// </summary>
+ public const int MAGIC_EXIT_CODE = 126;
+
private static readonly System.Security.Cryptography.RSACryptoServiceProvider SIGN_KEY = AutoUpdateSettings.SignKey;
private static readonly string[] MANIFEST_URLS = AutoUpdateSettings.URLs;
private static readonly string APPNAME = AutoUpdateSettings.AppName;
@@ -1002,8 +1009,114 @@ namespace Duplicati.Library.AutoUpdater }
}
+ private static KeyValuePair<string, UpdateInfo> GetBestUpdateVersion(bool forcecheck = false)
+ {
+ if (forcecheck)
+ m_hasUpdateInstalled = null;
+
+ // Check if there are updates installed, otherwise use current
+ KeyValuePair<string, UpdateInfo> best = new KeyValuePair<string, UpdateInfo>(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, SelfVersion);
+ if (HasUpdateInstalled)
+ best = m_hasUpdateInstalled.Value;
+
+ if (INSTALLDIR != null && System.IO.File.Exists(System.IO.Path.Combine(INSTALLDIR, CURRENT_FILE)))
+ {
+ try
+ {
+ var current = System.IO.File.ReadAllText(System.IO.Path.Combine(INSTALLDIR, CURRENT_FILE)).Trim();
+ if (!string.IsNullOrWhiteSpace(current))
+ {
+ var targetfolder = System.IO.Path.Combine(INSTALLDIR, current);
+ var currentmanifest = ReadInstalledManifest(targetfolder);
+ if (currentmanifest != null && TryParseVersion(currentmanifest.Version) > TryParseVersion(best.Value.Version) && VerifyUnpackedFolder(targetfolder, currentmanifest))
+ best = new KeyValuePair<string, UpdateInfo>(targetfolder, currentmanifest);
+ }
+ }
+ catch (Exception ex)
+ {
+ if (OnError != null)
+ OnError(ex);
+ }
+ }
+
+ return best;
+ }
+
+ public static bool IsRunningInUpdateEnvironment => !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(string.Format(BASEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME)));
+
public static int RunFromMostRecent(System.Reflection.MethodInfo method, string[] cmdargs, AutoUpdateStrategy defaultstrategy = AutoUpdateStrategy.CheckDuring)
{
+ return RunFromMostRecentSpawn(method, cmdargs, defaultstrategy);
+ }
+
+ public static int RunFromMostRecentSpawn(System.Reflection.MethodInfo method, string[] cmdargs, AutoUpdateStrategy defaultstrategy = AutoUpdateStrategy.CheckDuring)
+ {
+ // If the update is disabled, go straight in
+ //if (DISABLE_UPDATE_DOMAIN)
+ // return RunMethod(method, cmdargs);
+
+ // If we are not the primary entry, just execute
+ if (IsRunningInUpdateEnvironment)
+ {
+ int r = 0;
+ WrapWithUpdater(defaultstrategy, () => {
+ r = RunMethod(method, cmdargs);
+ });
+
+ return r;
+ }
+
+ var args = Environment.CommandLine;
+ var app = Environment.GetCommandLineArgs().First();
+ args = args.Substring(app.Length);
+
+ if (!Path.IsPathRooted(app))
+ app = Path.Combine(InstalledBaseDir, app);
+
+ var executable = Path.GetFileName(app);
+
+ while (true)
+ {
+ var best = GetBestUpdateVersion(true);
+ var folder = best.Key;
+
+ var pi = new System.Diagnostics.ProcessStartInfo(Path.Combine(folder, executable), args)
+ {
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true,
+ ErrorDialog = false,
+ };
+ pi.EnvironmentVariables.Clear();
+
+ var cur = Environment.GetEnvironmentVariables();
+ foreach (var e in cur.Keys)
+ if (e is string)
+ pi.EnvironmentVariables[(string)e] = cur[(string)e] as string;
+
+ pi.EnvironmentVariables[string.Format(BASEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME)] = InstalledBaseDir;
+ pi.EnvironmentVariables["LOCALIZATION_FOLDER"] = InstalledBaseDir;
+
+ var proc = System.Diagnostics.Process.Start(pi);
+ var tasks = Task.WhenAll(
+ Console.OpenStandardInput().CopyToAsync(proc.StandardInput.BaseStream),
+ proc.StandardOutput.BaseStream.CopyToAsync(Console.OpenStandardOutput()),
+ proc.StandardError.BaseStream.CopyToAsync(Console.OpenStandardError())
+ );
+
+ proc.WaitForExit();
+ tasks.Wait(1000);
+
+ if (proc.ExitCode != MAGIC_EXIT_CODE)
+ return proc.ExitCode;
+ }
+
+ }
+
+ public static int RunFromMostRecentAppDomain(System.Reflection.MethodInfo method, string[] cmdargs, AutoUpdateStrategy defaultstrategy = AutoUpdateStrategy.CheckDuring)
+ {
// If the update is disabled, go straight in
if (DISABLE_UPDATE_DOMAIN)
return RunMethod(method, cmdargs);
diff --git a/Duplicati/Library/Localization/MoLocalizationService.cs b/Duplicati/Library/Localization/MoLocalizationService.cs index 2b94d6f1c..38667a471 100644 --- a/Duplicati/Library/Localization/MoLocalizationService.cs +++ b/Duplicati/Library/Localization/MoLocalizationService.cs @@ -40,13 +40,18 @@ namespace Duplicati.Library.Localization /// </summary> public const string LOCALIZATIONDIR_ENVNAME = "LOCALIZATION_FOLDER"; + private static string LOCALIZATIONDIR_VALUE = + string.IsNullOrWhiteSpace(AppDomain.CurrentDomain.GetData(LOCALIZATIONDIR_ENVNAME) as string) + ? Environment.GetEnvironmentVariable(LOCALIZATIONDIR_ENVNAME) + : AppDomain.CurrentDomain.GetData(LOCALIZATIONDIR_ENVNAME) as string; + /// <summary> /// Path to search for extra .mo files in /// </summary> public static string[] SearchPaths = - string.IsNullOrWhiteSpace(AppDomain.CurrentDomain.GetData(LOCALIZATIONDIR_ENVNAME) as string) + string.IsNullOrWhiteSpace(LOCALIZATIONDIR_VALUE) ? new string[] { Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) } - : (AppDomain.CurrentDomain.GetData(LOCALIZATIONDIR_ENVNAME) as string).Split(new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); + : (LOCALIZATIONDIR_VALUE).Split(new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); /// <summary> diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index ddc688ebe..28d116882 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -153,7 +153,7 @@ namespace Duplicati.Server return Duplicati.Library.AutoUpdater.UpdaterManager.RunFromMostRecent(typeof(Program).GetMethod("RealMain"), args, Duplicati.Library.AutoUpdater.AutoUpdateStrategy.Never);
}
- public static void RealMain(string[] args)
+ public static int RealMain(string[] args)
{
//If we are on Windows, append the bundled "win-tools" programs to the search path
//We add it last, to allow the user to override with other versions
@@ -192,7 +192,7 @@ namespace Duplicati.Server foreach(Library.Interface.ICommandLineArgument arg in SupportedCommands)
Console.WriteLine(Strings.Program.HelpDisplayFormat(arg.Name, arg.LongDescription));
- return;
+ return 0;
}
else
{
@@ -246,7 +246,7 @@ namespace Duplicati.Server if (writeConsole)
{
Console.WriteLine(Strings.Program.StartupFailure(ex));
- return;
+ return 200;
}
throw new Exception(Strings.Program.StartupFailure(ex));
@@ -257,7 +257,7 @@ namespace Duplicati.Server if (writeConsole)
{
Console.WriteLine(Strings.Program.AnotherInstanceDetected);
- return;
+ return 200;
}
throw new SingleInstance.MultipleInstanceException(Strings.Program.AnotherInstanceDetected);
@@ -323,7 +323,7 @@ namespace Duplicati.Server try
{
PurgeTempFilesTimer = new System.Threading.Timer(purgeTempFilesCallback, null, TimeSpan.FromHours(1), TimeSpan.FromDays(1));
- }
+ }
catch (ArgumentOutOfRangeException)
{
//Bugfix for older Mono, slightly more resources used to avoid large values in the period field
@@ -399,7 +399,10 @@ namespace Duplicati.Server {
System.Diagnostics.Trace.WriteLine(Strings.Program.SeriousError(mex.ToString()));
if (writeConsole)
+ {
Console.WriteLine(Strings.Program.SeriousError(mex.ToString()));
+ return 100;
+ }
else
throw mex;
}
@@ -407,7 +410,10 @@ namespace Duplicati.Server {
System.Diagnostics.Trace.WriteLine(Strings.Program.SeriousError(ex.ToString()));
if (writeConsole)
+ {
Console.WriteLine(Strings.Program.SeriousError(ex.ToString()));
+ return 100;
+ }
else
throw new Exception(Strings.Program.SeriousError(ex.ToString()), ex);
}
@@ -434,8 +440,12 @@ namespace Duplicati.Server if (LogHandler != null)
LogHandler.Dispose();
-
}
+
+ if (UpdatePoller != null && UpdatePoller.IsUpdateRequested)
+ return Library.AutoUpdater.UpdaterManager.MAGIC_EXIT_CODE;
+
+ return 0;
}
public static Database.Connection GetDatabaseConnection(Dictionary<string, string> commandlineOptions)
diff --git a/Duplicati/Server/UpdatePollThread.cs b/Duplicati/Server/UpdatePollThread.cs index 4a66b284d..0c741aff5 100644 --- a/Duplicati/Server/UpdatePollThread.cs +++ b/Duplicati/Server/UpdatePollThread.cs @@ -35,6 +35,8 @@ namespace Duplicati.Server private AutoResetEvent m_waitSignal;
private double m_downloadProgress;
+ public bool IsUpdateRequested { get; private set; } = false;
+
public UpdatePollerStates ThreadState { get; private set; }
public double DownloadProgess
{
@@ -81,7 +83,10 @@ namespace Duplicati.Server public void ActivateUpdate()
{
if (Duplicati.Library.AutoUpdater.UpdaterManager.SetRunUpdate())
+ {
+ IsUpdateRequested = true;
Program.ApplicationExitEvent.Set();
+ }
}
public void Terminate()
|