예제 #1
0
        public void UpdaterDeploymentWorks()
        {
            var path = Path.Combine(Path.GetTempPath(), "NAppUpdate-Tests");

            NauIpc.ExtractUpdaterFromResource(path, "Foo.exe");

            Assert.IsTrue(Directory.Exists(path));
            Assert.IsTrue(File.Exists(Path.Combine(path, "Foo.exe")));
            Assert.IsTrue(File.Exists(Path.Combine(path, "NAppUpdate.Framework.dll")));

            // Cleanup test
            FileSystem.DeleteDirectory(path);
        }
예제 #2
0
 public void ReinstateIfRestarted()
 {
     lock (UpdatesToApply)
     {
         var dto = NauIpc.ReadDto(Config.UpdateProcessName) as NauIpc.NauDto;
         if (dto != null)
         {
             Config         = dto.Configs;
             UpdatesToApply = dto.Tasks;
             Logger         = new Logger(dto.LogItems);
             State          = UpdateProcessState.AfterRestart;
         }
     }
 }
예제 #3
0
        public void UpdaterDeploymentAndIPCWorks()
        {
            var dto = new NauIpc.NauDto
            {
                Configs = UpdateManager.Instance.Config,
                Tasks   = new List <IUpdateTask>
                {
                    new FileUpdateTask {
                        Description = "Task #1", ExecutionStatus = TaskExecutionStatus.RequiresAppRestart
                    }
                },
                AppPath             = Process.GetCurrentProcess().MainModule.FileName,
                WorkingDirectory    = Environment.CurrentDirectory,
                RelaunchApplication = false
            };

            var path = dto.Configs.TempFolder;

            if (Directory.Exists(path))
            {
                FileSystem.DeleteDirectory(path);
            }

            NauIpc.ExtractUpdaterFromResource(path, dto.Configs.UpdateExecutableName);
            var info = new ProcessStartInfo
            {
                UseShellExecute  = true,
                WorkingDirectory = Environment.CurrentDirectory,
                FileName         = Path.Combine(path, dto.Configs.UpdateExecutableName),
                Arguments        = string.Format(@"""{0}"" -showConsole", dto.Configs.UpdateProcessName)
            };

            var p = NauIpc.LaunchProcessAndSendDto(dto, info, dto.Configs.UpdateProcessName);

            Assert.IsNotNull(p);
            p.WaitForExit();

            Assert.IsTrue(Directory.Exists(path));
            Assert.IsTrue(File.Exists(Path.Combine(path, "Foo.exe")));
            Assert.IsTrue(File.Exists(Path.Combine(path, "NAppUpdate.Framework.dll")));

            // Cleanup test
            FileSystem.DeleteDirectory(path);
        }
예제 #4
0
        public void ReinstateIfRestarted()
        {
            if (!IsAfterRestart())
            {
                return;
            }

            lock (UpdatesToApply)
            {
                NauIpc.NauDto dto = NauIpc.ReadDto(Config.UpdateProcessName);

                if (dto == null)
                {
                    return;
                }

                Config         = dto.Configs;
                UpdatesToApply = dto.Tasks;
                Logger         = new Logger(dto.LogItems);
                State          = UpdateProcessState.AfterRestart;
            }
        }
예제 #5
0
        private static void PerformUpdates()
        {
            string syncProcessName = _args.ProcessName;

            if (string.IsNullOrEmpty(syncProcessName))
            {
                throw new ArgumentException("Required command line argument is missing", "ProcessName");
            }

            Log("Update process name: '{0}'", syncProcessName);

            // Load extra assemblies to the app domain, if present
            var availableAssemblies = FileSystem.GetFiles(_workingDir, "*.exe|*.dll", SearchOption.TopDirectoryOnly);

            foreach (var assemblyPath in availableAssemblies)
            {
                Log("Loading {0}", assemblyPath);

                if (assemblyPath.Equals(Assembly.GetEntryAssembly().Location, StringComparison.InvariantCultureIgnoreCase) || assemblyPath.EndsWith("NAppUpdate.Framework.dll"))
                {
                    Log("\tSkipping (part of current execution)");
                    continue;
                }

                try
                {
                    // ReSharper disable UnusedVariable
                    var assembly = Assembly.LoadFile(assemblyPath);
                    // ReSharper restore UnusedVariable
                }
                catch (BadImageFormatException ex)
                {
                    Log("\tSkipping due to an error: {0}", ex.Message);
                }
            }

            // Connect to the named pipe and retrieve the updates list
            _dto = NauIpc.ReadDto(syncProcessName);

            // Make sure we start updating only once the application has completely terminated
            Thread.Sleep(1000); // Let's even wait a bit
            bool createdNew;

            using (var mutex = new Mutex(false, syncProcessName + "Mutex", out createdNew))
            {
                try
                {
                    if (!createdNew)
                    {
                        mutex.WaitOne();
                    }
                }
                catch (AbandonedMutexException)
                {
                    // An abandoned mutex is exactly what we are expecting...
                }
                finally
                {
                    Log("The application has terminated (as expected)");
                    _appRunning = false;
                }
            }

            _logger.LogItems.InsertRange(0, _dto.LogItems);
            _dto.LogItems = _logger.LogItems;

            // Get some required environment variables
            string appPath         = _dto.AppPath;
            string appDir          = _dto.WorkingDirectory ?? Path.GetDirectoryName(appPath) ?? string.Empty;
            string relaunchAppArgs = _dto.Configs.RelaunchAppArgs;

            if (!string.IsNullOrEmpty(_dto.AppPath))
            {
                _logFilePath = Path.Combine(Path.GetDirectoryName(_dto.AppPath), @"NauUpdate.log"); // now we can log to a more accessible location
            }

            if (_dto.Tasks == null)
            {
                throw new Exception("The Task list received in the dto is null");
            }
            else if (_dto.Tasks.Count == 0)
            {
                throw new Exception("The Task list received in the dto is empty");
            }

            Log("Got {0} task objects", _dto.Tasks.Count);

            // Perform the actual off-line update process
            foreach (var t in _dto.Tasks)
            {
                Log("Task \"{0}\": {1}", t.Description, t.ExecutionStatus);

                if (t.ExecutionStatus != TaskExecutionStatus.RequiresAppRestart && t.ExecutionStatus != TaskExecutionStatus.RequiresPrivilegedAppRestart)
                {
                    Log("\tSkipping");
                    continue;
                }

                Exception exception = null;

                try
                {
                    Log("\tExecuting...");
                    t.ExecutionStatus = t.Execute(true);
                }
                catch (Exception ex)
                {
                    t.ExecutionStatus = TaskExecutionStatus.Failed;
                    exception         = ex;
                }

                if (t.ExecutionStatus != TaskExecutionStatus.Successful)
                {
                    string taskFailedMessage = string.Format("Update failed, task execution failed, description: {0}, execution status: {1}", t.Description, t.ExecutionStatus);
                    throw new Exception(taskFailedMessage, exception);
                }
            }

            Log("Finished successfully");
            Log("Removing backup folder");

            if (Directory.Exists(_dto.Configs.BackupFolder))
            {
                FileSystem.DeleteDirectory(_dto.Configs.BackupFolder);
            }

            // Start the application only if requested to do so
            if (_dto.RelaunchApplication)
            {
                Log("Re-launching process {0} with working dir {1}", appPath, appDir);

                bool useShellExecute = !_args.ShowConsole;

                ProcessStartInfo info = new ProcessStartInfo
                {
                    UseShellExecute  = useShellExecute,
                    WorkingDirectory = appDir,
                    FileName         = appPath,
                    Arguments        = "-nappupdate-afterrestart" + " " + relaunchAppArgs
                };

                try
                {
                    NauIpc.LaunchProcessAndSendDto(_dto, info, syncProcessName);
                    _appRunning = true;
                }
                catch (Exception ex)
                {
                    throw new UpdateProcessFailedException("Unable to relaunch application and/or send DTO", ex);
                }
            }
        }
예제 #6
0
        private static void Main()
        {
            string tempFolder = string.Empty;
            string logFile    = string.Empty;

            _args = ArgumentsParser.Get();

            _logger = UpdateManager.Instance.Logger;
            _args.ParseCommandLineArgs();
            if (_args.ShowConsole)
            {
                //_console = new ConsoleForm();
                //_console.Show();
            }

            Log("Starting to process cold updates...");
            Log("Arguments parsed: {0}{1}.", Environment.NewLine, _args.DumpArgs());

            var workingDir = Directory.GetCurrentDirectory();

            if (_args.Log)
            {
                // Setup a temporary location for the log file, until we can get the DTO
                logFile = Path.Combine(workingDir, @"NauUpdate.log");
            }

            try {
                // Get the update process name, to be used to create a named pipe and to wait on the application
                // to quit
                string syncProcessName = _args.ProcessName;
                if (string.IsNullOrEmpty(syncProcessName))                 //Application.Exit();
                {
                    throw new ArgumentException("The command line needs to specify the mutex of the program to update.", "ar" + "gs");
                }

                Log("Update process name: '{0}'", syncProcessName);
//TODO: I suppose this was done to load custom tasks, however there were some problems when loading an assembly which later was updated (msg: can't update because file is in use).
//                // Load extra assemblies to the app domain, if present
//                Log("Getting files in : '{0}'", workingDir);
//                var availableAssemblies = FileSystem.GetFiles(workingDir, "*.exe|*.dll", SearchOption.TopDirectoryOnly);
//                foreach (var assemblyPath in availableAssemblies) {
//                    Log("Loading {0}", assemblyPath);

//                    if (assemblyPath.Equals(Assembly.GetEntryAssembly().Location, StringComparison.InvariantCultureIgnoreCase) || assemblyPath.EndsWith("NAppUpdate.Framework.dll")) {
//                        Log("\tSkipping (part of current execution)");
//                        continue;
//                    }

//                    try {
//// ReSharper disable UnusedVariable
//                        var assembly = Assembly.LoadFile(assemblyPath);
//// ReSharper restore UnusedVariable
//                    } catch (BadImageFormatException ex) {
//                        Log("\tSkipping due to an error: {0}", ex.Message);
//                    }
//                }

                // Connect to the named pipe and retrieve the updates list
                var dto = NauIpc.ReadDto(syncProcessName) as NauIpc.NauDto;

                // Make sure we start updating only once the application has completely terminated
                Thread.Sleep(100);                 // hell, let's even wait a bit
                bool createdNew;
                using (var mutex = new Mutex(false, syncProcessName + "Mutex", out createdNew)) {
                    try {
                        if (!createdNew)
                        {
                            mutex.WaitOne();
                        }
                    } catch (AbandonedMutexException) {
                        // An abandoned mutex is exactly what we are expecting...
                    } finally {
                        Log("The application has terminated (as expected)");
                    }
                }

                bool updateSuccessful = true;

                if (dto == null || dto.Configs == null)
                {
                    throw new Exception("Invalid DTO received");
                }

                if (dto.LogItems != null)                 // shouldn't really happen
                {
                    _logger.LogItems.InsertRange(0, dto.LogItems);
                }
                dto.LogItems = _logger.LogItems;

                // Get some required environment variables
                string appPath = dto.AppPath;
                string appDir  = dto.WorkingDirectory ?? Path.GetDirectoryName(appPath) ?? string.Empty;
                tempFolder = dto.Configs.TempFolder;
                string backupFolder = dto.Configs.BackupFolder;
                bool   relaunchApp  = dto.RelaunchApplication;

                if (!string.IsNullOrEmpty(dto.AppPath))
                {
                    logFile = Path.Combine(Path.GetDirectoryName(dto.AppPath), @"NauUpdate.log");                                                     // now we can log to a more accessible location
                }
                if (dto.Tasks == null || dto.Tasks.Count == 0)
                {
                    throw new Exception("Could not find the updates list (or it was empty).");
                }

                Log("Got {0} task objects", dto.Tasks.Count);

//This can be handy if you're trying to debug the updater.exe!
//#if (DEBUG)
//{
//                if (_args.ShowConsole) {
//                    _console.WriteLine();
//                    _console.WriteLine("Pausing to attach debugger.  Press any key to continue.");
//                    _console.ReadKey();
//                }

//}
//#endif

                // Perform the actual off-line update process
                foreach (var t in dto.Tasks)
                {
                    Log("Task \"{0}\": {1}", t.Description, t.ExecutionStatus);

                    if (t.ExecutionStatus != TaskExecutionStatus.RequiresAppRestart && t.ExecutionStatus != TaskExecutionStatus.RequiresPrivilegedAppRestart)
                    {
                        Log("\tSkipping");
                        continue;
                    }

                    Log("\tExecuting...");

                    // TODO: Better handling on failure: logging, rollbacks
                    try {
                        t.ExecutionStatus = t.Execute(true);
                    } catch (Exception ex) {
                        Log(ex);
                        updateSuccessful  = false;
                        t.ExecutionStatus = TaskExecutionStatus.Failed;
                    }

                    if (t.ExecutionStatus == TaskExecutionStatus.Successful)
                    {
                        continue;
                    }
                    Log("\tTask execution failed");
                    updateSuccessful = false;
                    break;
                }

                if (updateSuccessful)
                {
                    Log("Finished successfully");
                    Log("Removing backup folder");
                    if (Directory.Exists(backupFolder))
                    {
                        FileSystem.DeleteDirectory(backupFolder);
                    }
                }
                else
                {
                    Console.WriteLine("Update Failed");
                    //MessageBox.Show();
                    Log(Logger.SeverityLevel.Error, "Update failed");
                }

                // Start the application only if requested to do so
                if (relaunchApp)
                {
                    Log("Re-launching process {0} with working dir {1}", appPath, appDir);

                    var info = new ProcessStartInfo {
                        UseShellExecute  = false,
                        WorkingDirectory = appDir,
                        FileName         = appPath,
                    };

                    var p = NauIpc.LaunchProcessAndSendDto(dto, info, syncProcessName);
                    if (p == null)
                    {
                        throw new UpdateProcessFailedException("Unable to relaunch application and/or send DTO");
                    }
                }

                Log("All done");
                //Application.Exit();
            } catch (Exception ex) {
                // supressing catch because if at any point we get an error the update has failed
                Log(ex);
            } finally {
                if (_args.Log)
                {
                    // at this stage we can't make any assumptions on correctness of the path
                    FileSystem.CreateDirectoryStructure(logFile, true);
                    _logger.Dump(logFile);
                }

                if (_args.ShowConsole)
                {
                    if (_args.Log)
                    {
                        Console.WriteLine();
                        Console.WriteLine("Log file was saved to {0}", logFile);
                        Console.WriteLine();
                    }
                    Console.WriteLine();
                    Console.WriteLine("Exiting.");                     //Must not read console!
                    //see: http://msdn.microsoft.com/en-us/library/system.console.aspx
                    //Especially:
                    //Console class members that work normally when the underlying stream is directed to a console might
                    //throw an exception if the stream is redirected, for example, to a file. Program your application to
                    //catch System.IO.IOException exceptions if you redirect a standard stream. You can also use
                    //the IsOutputRedirected, IsInputRedirected, and IsErrorRedirected properties to determine whether
                    //a standard stream is redirected before performing an operation that throws an System.IO.IOException exception.
                }
                if (!string.IsNullOrEmpty(tempFolder))
                {
                    SelfCleanUp(tempFolder);
                }
                //return;
                //Application.Exit();
            }
        }
예제 #7
0
        /// <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();
                }
            }
        }
예제 #8
0
        private static void Main()
        {
            //Debugger.Launch();
            string tempFolder = string.Empty;
            string logFile    = string.Empty;

            _args = ArgumentsParser.Get();

            _logger = UpdateManager.Instance.Logger;
            _args.ParseCommandLineArgs();
            if (_args.ShowConsole)
            {
                _console = new ConsoleForm();
                _console.Show();
            }

            Log("Starting to process cold updates...");

            var workingDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);

            if (_args.Log)
            {
                // Setup a temporary location for the log file, until we can get the DTO
                logFile = Path.Combine(workingDir, @"NauUpdate.log");
            }

            try
            {
                // Get the update process name, to be used to create a named pipe and to wait on the application
                // to quit
                string syncProcessName = _args.ProcessName;
                if (string.IsNullOrEmpty(syncProcessName))
                {
                    //Application.Exit();
                    throw new ArgumentException("The command line needs to specify the mutex of the program to update.", "args");
                }

                Log("Update process name: '{0}'", syncProcessName);

                // Load extra assemblies to the app domain, if present
                var availableAssemblies = FileSystem.GetFiles(workingDir, "*.exe|*.dll", SearchOption.TopDirectoryOnly);
                foreach (var assemblyPath in availableAssemblies)
                {
                    Log("Loading {0}", assemblyPath);

                    if (assemblyPath.Equals(System.Reflection.Assembly.GetEntryAssembly().Location, StringComparison.InvariantCultureIgnoreCase) ||
                        assemblyPath.EndsWith("NAppUpdate.Framework.dll"))
                    {
                        Log("\tSkipping (part of current execution)");
                        continue;
                    }

                    try
                    {
                        var assembly = System.Reflection.Assembly.LoadFile(assemblyPath);
                    }
                    catch (System.BadImageFormatException ex)
                    {
                        Log("\tSkipping due to an error: {0}", ex.Message);
                    }
                }

                // Connect to the named pipe and retrieve the updates list
                var dto = NauIpc.ReadDto(syncProcessName) as NauIpc.NauDto;

                // Make sure we start updating only once the application has completely terminated
                Thread.Sleep(100);                 // hell, let's even wait a bit
                bool createdNew;
                using (var mutex = new Mutex(false, syncProcessName + "Mutex", out createdNew))
                {
                    try
                    {
                        if (!createdNew)
                        {
                            mutex.WaitOne();
                        }
                    }
                    catch (AbandonedMutexException)
                    {
                        // An abandoned mutex is exactly what we are expecting...
                        Log("The application has terminated (as expected)");
                    }
                }

                bool updateSuccessful = true;

                if (dto == null || dto.Configs == null)
                {
                    throw new Exception("Invalid DTO received");
                }

                if (dto.LogItems != null)                 // shouldn't really happen
                {
                    _logger.LogItems.InsertRange(0, dto.LogItems);
                }
                dto.LogItems = _logger.LogItems;

                // Get some required environment variables
                string appPath = dto.AppPath;
                string appDir  = dto.WorkingDirectory ?? Path.GetDirectoryName(appPath) ?? string.Empty;
                tempFolder = dto.Configs.TempFolder;
                string backupFolder = dto.Configs.BackupFolder;
                bool   relaunchApp  = dto.RelaunchApplication;

                if (!string.IsNullOrEmpty(dto.AppPath))
                {
                    logFile = Path.Combine(Path.GetDirectoryName(dto.AppPath), @"NauUpdate.log");                     // now we can log to a more accessible location
                }
                if (dto.Tasks == null || dto.Tasks.Count == 0)
                {
                    throw new Exception("Could not find the updates list (or it was empty).");
                }

                Log("Got {0} task objects", dto.Tasks.Count);

                // Perform the actual off-line update process
                foreach (var t in dto.Tasks)
                {
                    Log("Task \"{0}\": {1}", t.Description, t.ExecutionStatus);

                    if (t.ExecutionStatus != TaskExecutionStatus.RequiresAppRestart &&
                        t.ExecutionStatus != TaskExecutionStatus.RequiresPrivilegedAppRestart)
                    {
                        Log("\tSkipping");
                        continue;
                    }

                    Log("\tExecuting...");

                    // TODO: Better handling on failure: logging, rollbacks
                    try
                    {
                        t.ExecutionStatus = t.Execute(true);
                    }
                    catch (Exception ex)
                    {
                        Log(ex);
                        updateSuccessful  = false;
                        t.ExecutionStatus = TaskExecutionStatus.Failed;
                    }

                    if (t.ExecutionStatus != TaskExecutionStatus.Successful)
                    {
                        Log("\tTask execution failed");
                        updateSuccessful = false;
                        break;
                    }
                }

                if (updateSuccessful)
                {
                    Log("Finished successfully");
                    Log("Removing backup folder");
                    if (Directory.Exists(backupFolder))
                    {
                        FileSystem.DeleteDirectory(backupFolder);
                    }
                }
                else
                {
                    MessageBox.Show("Update Failed");
                    Log(Logger.SeverityLevel.Error, "Update failed");
                }

                // Start the application only if requested to do so
                if (relaunchApp)
                {
                    Log("Re-launching process {0} with working dir {1}", appPath, appDir);

                    var info = new ProcessStartInfo
                    {
                        UseShellExecute  = true,
                        WorkingDirectory = appDir,
                        FileName         = appPath,
                    };

                    var p = NauIpc.LaunchProcessAndSendDto(dto, info, syncProcessName);
                    if (p == null)
                    {
                        throw new UpdateProcessFailedException("Unable to relaunch application");
                    }
                }

                Log("All done");
                //Application.Exit();
            }
            catch (Exception ex)
            {
                // supressing catch because if at any point we get an error the update has failed
                Log(ex);
            }
            finally
            {
                if (_args.Log)
                {
                    // at this stage we can't make any assumptions on correctness of the path
                    FileSystem.CreateDirectoryStructure(logFile, true);
                    _logger.Dump(logFile);
                }

                if (_args.ShowConsole)
                {
                    if (_args.Log)
                    {
                        _console.WriteLine();
                        _console.WriteLine("Log file was saved to {0}", logFile);
                        _console.WriteLine();
                    }
                    _console.WriteLine();
                    _console.WriteLine("Press any key or close this window to exit.");
                    _console.ReadKey();
                }
                if (!string.IsNullOrEmpty(tempFolder))
                {
                    SelfCleanUp(tempFolder);
                }
                Application.Exit();
            }
        }