/// <summary> /// Prepare updates synchronously /// </summary> public void PrepareUpdates() { if (IsWorking) { throw new InvalidOperationException("Another update process is already in progress"); } using (WorkScope.New(isWorking => IsWorking = isWorking)) { lock (UpdatesToApply) { if (State != UpdateProcessState.Checked) { throw new InvalidOperationException("Invalid state when calling PrepareUpdates(): " + State); } if (UpdatesToApply.Count == 0) { throw new InvalidOperationException("No updates to prepare"); } if (!Directory.Exists(Config.TempFolder)) { Logger.Log("Creating Temp directory {0}", Config.TempFolder); Directory.CreateDirectory(Config.TempFolder); } else { Logger.Log("Using existing Temp directory {0}", Config.TempFolder); } foreach (var task in UpdatesToApply) { if (ShouldStop) { throw new UserAbortException(); } var t = task; task.ProgressDelegate += status => TaskProgressCallback(status, t); try { task.Prepare(UpdateSource); } catch (Exception ex) { task.ExecutionStatus = TaskExecutionStatus.FailedToPrepare; Logger.Log(ex); throw new UpdateProcessFailedException("Failed to prepare task: " + task.Description, ex); } task.ExecutionStatus = TaskExecutionStatus.Prepared; } State = UpdateProcessState.Prepared; } } }
/// <summary> /// Check for updates synchronouly /// </summary> /// <param name="source">Updates source to use</param> public void CheckForUpdates(IUpdateSource source) { if (IsWorking) { throw new InvalidOperationException("Another update process is already in progress"); } using (WorkScope.New(isWorking => IsWorking = isWorking)) { if (UpdateFeedReader == null) { throw new ArgumentException("An update feed reader is required; please set one before checking for updates"); } if (source == null) { throw new ArgumentException("An update source was not specified"); } if (State != UpdateProcessState.NotChecked) { throw new InvalidOperationException("Already checked for updates; to reset the current state call CleanUp()"); } lock (UpdatesToApply) { UpdatesToApply.Clear(); using (Stream s = source.GetUpdatesFeed()) { var tasks = UpdateFeedReader.Read(s); foreach (var t in tasks) { if (ShouldStop) { throw new UserAbortException(); } if (t.UpdateConditions == null || t.UpdateConditions.IsMet(t)) // Only execute if all conditions are met { UpdatesToApply.Add(t); } } } } State = UpdateProcessState.Checked; } }
/// <summary> /// Check for updates synchronously /// </summary> public void CheckForUpdates() { if (IsWorking) { throw new InvalidOperationException("Another update process is already in progress"); } else if (UpdateFeedReader == null) { throw new InvalidOperationException("UpdateFeedReader must be set before calling CheckForUpdates()"); } else if (UpdateSource == null) { throw new InvalidOperationException("UpdateSource must be set before calling CheckForUpdates()"); } using (WorkScope.New(isWorking => IsWorking = isWorking)) { if (State != UpdateProcessState.NotChecked) { throw new InvalidOperationException("Already checked for updates; to reset the current state call CleanUp()"); } lock (UpdatesToApply) { UpdatesToApply.Clear(); var tasks = UpdateFeedReader.Read(UpdateSource.GetUpdatesFeed()); foreach (var t in tasks) { if (ShouldStop) { throw new UserAbortException(); } if (t.UpdateConditions == null || t.UpdateConditions.IsMet(t)) // Only execute if all conditions are met { UpdatesToApply.Add(t); } } } State = UpdateProcessState.Checked; } }
/// <summary> /// Starts the updater executable and sends update data to it /// </summary> /// <param name="relaunchApplication">true if relaunching the caller application is required; false otherwise</param> /// <param name="updaterDoLogging">true if the updater writes to a log file; false otherwise</param> /// <param name="updaterShowConsole">true if the updater shows the console window; false otherwise</param> /// <returns>True if successful (unless a restart was required</returns> public void ApplyUpdates(bool relaunchApplication, bool updaterDoLogging, bool updaterShowConsole) { if (IsWorking) { throw new InvalidOperationException("Another update process is already in progress"); } lock (UpdatesToApply) { using (WorkScope.New(isWorking => IsWorking = isWorking)) { bool revertToDefaultBackupPath = true; // Set current directory the the application directory // this prevents the updater from writing to e.g. c:\windows\system32 // if the process is started by autorun on windows logon. // ReSharper disable AssignNullToNotNullAttribute Environment.CurrentDirectory = Path.GetDirectoryName(ApplicationPath); // ReSharper restore AssignNullToNotNullAttribute // Make sure the current backup folder is accessible for writing from this process string backupParentPath = Path.GetDirectoryName(Config.BackupFolder) ?? string.Empty; if (Directory.Exists(backupParentPath) && PermissionsCheck.HaveWritePermissionsForFolder(backupParentPath)) { // Remove old backup folder, in case this same folder was used previously, // and it wasn't removed for some reason try { if (Directory.Exists(Config.BackupFolder)) { FileSystem.DeleteDirectory(Config.BackupFolder); } revertToDefaultBackupPath = false; } catch (UnauthorizedAccessException) { } // Attempt to (re-)create the backup folder try { Directory.CreateDirectory(Config.BackupFolder); if (!PermissionsCheck.HaveWritePermissionsForFolder(Config.BackupFolder)) { revertToDefaultBackupPath = true; } } catch (UnauthorizedAccessException) { // We're having permissions issues with this folder, so we'll attempt // using a backup in a default location revertToDefaultBackupPath = true; } } if (revertToDefaultBackupPath) { Config._backupFolder = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Config.UpdateProcessName + "UpdateBackups" + DateTime.UtcNow.Ticks); try { Directory.CreateDirectory(Config.BackupFolder); } catch (UnauthorizedAccessException ex) { // We can't backup, so we abort throw new UpdateProcessFailedException("Could not create backup folder " + Config.BackupFolder, ex); } } bool runPrivileged = false, hasColdUpdates = false; State = UpdateProcessState.RollbackRequired; foreach (var task in UpdatesToApply) { IUpdateTask t = task; task.ProgressDelegate += status => TaskProgressCallback(status, t); try { // Execute the task task.ExecutionStatus = task.Execute(false); } catch (Exception ex) { task.ExecutionStatus = TaskExecutionStatus.Failed; // mark the failing task before rethrowing throw new UpdateProcessFailedException("Update task execution failed: " + task.Description, ex); } if (task.ExecutionStatus == TaskExecutionStatus.RequiresAppRestart || task.ExecutionStatus == TaskExecutionStatus.RequiresPrivilegedAppRestart) { // Record that we have cold updates to run, and if required to run any of them privileged runPrivileged = runPrivileged || task.ExecutionStatus == TaskExecutionStatus.RequiresPrivilegedAppRestart; hasColdUpdates = true; continue; } // We are being quite explicit here - only Successful return values are considered // to be Ok (cold updates are already handled above) if (task.ExecutionStatus != TaskExecutionStatus.Successful) { throw new UpdateProcessFailedException("Update task execution failed: " + task.Description); } } // If an application restart is required if (hasColdUpdates) { var dto = new NauIpc.NauDto { Configs = Instance.Config, Tasks = Instance.UpdatesToApply, AppPath = ApplicationPath, WorkingDirectory = Environment.CurrentDirectory, RelaunchApplication = relaunchApplication, LogItems = Logger.LogItems, }; NauIpc.ExtractUpdaterFromResource(Config.TempFolder, Instance.Config.UpdateExecutableName); var info = new ProcessStartInfo { UseShellExecute = true, WorkingDirectory = Environment.CurrentDirectory, FileName = Path.Combine(Config.TempFolder, Instance.Config.UpdateExecutableName), Arguments = string.Format(@"""{0}"" {1} {2}", Config.UpdateProcessName, updaterShowConsole ? "-showConsole" : string.Empty, updaterDoLogging ? "-log" : string.Empty), }; if (!updaterShowConsole) { info.WindowStyle = ProcessWindowStyle.Hidden; info.CreateNoWindow = true; } // If we can't write to the destination folder, then lets try elevating priviledges. if (runPrivileged || !PermissionsCheck.HaveWritePermissionsForFolder(Environment.CurrentDirectory)) { info.Verb = "runas"; } bool createdNew; _shutdownMutex = new Mutex(true, Config.UpdateProcessName + "Mutex", out createdNew); try { NauIpc.LaunchProcessAndSendDto(dto, info, Config.UpdateProcessName); } catch (Exception ex) { throw new UpdateProcessFailedException("Could not launch cold update process", ex); } Environment.Exit(0); } State = UpdateProcessState.AppliedSuccessfully; UpdatesToApply.Clear(); } } }
/// <summary> /// Check for updates synchronously /// </summary> public void CheckForUpdates() { if (IsWorking) { throw new InvalidOperationException("Another update process is already in progress"); } else if (UpdateFeedReader == null) { throw new InvalidOperationException("UpdateFeedReader must be set before calling CheckForUpdates()"); } else if (UpdateSource == null) { throw new InvalidOperationException("UpdateSource must be set before calling CheckForUpdates()"); } using (WorkScope.New(isWorking => IsWorking = isWorking)) { if (State != UpdateProcessState.NotChecked) { throw new InvalidOperationException("Already checked for updates; to reset the current state call CleanUp()"); } lock (UpdatesToApply) { UpdatesToApply.Clear(); IList <IUpdateTask> tasks = null; int currentRetry = 0; while (currentRetry < MaximumRetries) { ++currentRetry; try { tasks = UpdateFeedReader.Read(UpdateSource.GetUpdatesFeed()); break; } catch (Exception ex) { Logger.Log(ex); if (ex is WebException) { var e = ex as WebException; if (e.Status == WebExceptionStatus.Timeout || e.Status == WebExceptionStatus.ConnectFailure || e.Status == WebExceptionStatus.NameResolutionFailure) { throw new UpdateProcessFailedException("Failed to retrieve feed: " + ex.ToString()); } } if (currentRetry == MaximumRetries) { throw new UpdateProcessFailedException("Failed to retrieve feed: " + ex.ToString()); } Thread.Sleep(RetriesTimeout); } } foreach (var t in tasks) { if (ShouldStop) { throw new UserAbortException(); } if (t.UpdateConditions == null || t.UpdateConditions.IsMet(t)) // Only execute if all conditions are met { UpdatesToApply.Add(t); } } } State = UpdateProcessState.Checked; } }