internal static string FindWritableDirectory(params CommonDirectories[] preferenceOrdering) { foreach (var candidate in preferenceOrdering) { string candidatePath; switch (candidate) { case CommonDirectories.CurrentExecutingDirectory: candidatePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); break; case CommonDirectories.LocalAppData: candidatePath = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.LocalApplicationData), "DesktopBootstrap"); break; case CommonDirectories.Temp: candidatePath = Path.GetTempPath(); break; default: // I'd ordinarily throw an exception here, but I'm weary to because of // this function's use in the updater. Just skip unrecognized // CommonDirectories. continue; } if (LibraryIO.CanWriteToDirectory(candidatePath)) { return(candidatePath); } } return(null); }
internal static void InitNLog() { var config = new LoggingConfiguration(); try { ConfigurationItemFactory.Default = new ConfigurationItemFactory(); foreach (var type in typeof(Logger).Assembly.GetTypes()) { ConfigurationItemFactory.Default.RegisterType(type, string.Empty); } } catch (ReflectionTypeLoadException rtle) { // NLog has a bug that manifests itself on .NET framework 2.0 with no service pack // wherein when it does its own type registering, it can't handle types that depend // on a type in an assembly that hasn't been loaded yet. // See: http://nlog-project.org/forum#nabble-td5542525 // Also: http://msdn.microsoft.com/en-us/library/system.reflection.assembly.gettypes.aspx // Start over with a fresh ConfigurationItemFactory ConfigurationItemFactory.Default = new ConfigurationItemFactory(); foreach (var type in rtle.Types) { if (type != null) { ConfigurationItemFactory.Default.RegisterType(type, string.Empty); } } } ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("dateutc", typeof(DateUtcLayoutRenderer)); ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("messagewithexceptioninfo", typeof(MessageWithExceptionInfoLayoutRenderer)); var versionString = Assembly.GetExecutingAssembly().GetName().Version.ToString(); var basicLayout = "[${dateutc}] DesktopBootstrap-" + versionString + ": ${level}: ${messagewithexceptioninfo}"; var rootLogDir = LibraryIO.FindWritableDirectory(CommonDirectories.LocalAppData, CommonDirectories.CurrentExecutingDirectory, CommonDirectories.Temp); // Create targets and rules var outputDebugStringTarget = new OutputDebugStringTarget(); outputDebugStringTarget.Layout = basicLayout; config.AddTarget("outputdebugstring", outputDebugStringTarget); if (Library.IsDebugMode()) { config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, outputDebugStringTarget)); } var consoleTarget = new ColoredConsoleTarget(); consoleTarget.Layout = basicLayout; config.AddTarget("console", consoleTarget); if (Debugger.IsAttached) { config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, consoleTarget)); } if (rootLogDir != null) { var basicLogFileTarget = new FileTarget(); var logDirectory = Path.Combine(rootLogDir, "Logs"); basicLogFileTarget.FileName = Path.Combine(logDirectory, "DesktopBootstrap.log"); basicLogFileTarget.ArchiveFileName = Path.Combine(logDirectory, "DesktopBootstrap-{#}.log"); basicLogFileTarget.ArchiveAboveSize = 1024 * 1024; // 1 MB basicLogFileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling; basicLogFileTarget.MaxArchiveFiles = 14; basicLogFileTarget.Encoding = UTF8Encoding.UTF8; basicLogFileTarget.ConcurrentWrites = false; basicLogFileTarget.KeepFileOpen = false; basicLogFileTarget.Layout = basicLayout; config.AddTarget("file", basicLogFileTarget); config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, basicLogFileTarget)); var errorLogFileTarget = new FileTarget(); var errorLogDirectory = Path.Combine(rootLogDir, "ErrorLogs"); errorLogFileTarget.FileName = Path.Combine(logDirectory, "DesktopBootstrapError.log"); errorLogFileTarget.ArchiveFileName = Path.Combine(logDirectory, "DesktopBootstrapError-{#}.log"); errorLogFileTarget.ArchiveAboveSize = 1024 * 1024; // 1 MB errorLogFileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling; errorLogFileTarget.MaxArchiveFiles = 14; errorLogFileTarget.Encoding = UTF8Encoding.UTF8; errorLogFileTarget.ConcurrentWrites = true; errorLogFileTarget.KeepFileOpen = false; errorLogFileTarget.Layout = basicLayout; config.AddTarget("errorfile", errorLogFileTarget); config.LoggingRules.Add(new LoggingRule("*", LogLevel.Error, errorLogFileTarget)); } // Activate the configuration LogManager.ThrowExceptions = false; // swallow logging exceptions LogManager.Configuration = config; }
// Note: could block for a while, but will execute on a ThreadPool thread. // // Note 2: don't let exceptions bubble past the scope of this function, or else bad things // will happen since it's a ThreadPool thread on .NET 2.0. // // Note 3: this function in theory should be reentrant, but is not since it's unlikely any // single execution will last for longer than 24 hours (the time for subsequent // invocations). // // Note 4: it could also be reentrant if the server commands ForceCheckForUpdates(), but that // is also unlikely. private static void CheckForAndRunUpdates(object requestDataObject) { UpdateCheckRequest requestData = null; try { requestData = (requestDataObject as UpdateCheckRequest ?? new UpdateCheckRequest()); // try to download update info var updateInfoXml = AskServerForUpdate(requestData); if (string.IsNullOrEmpty(updateInfoXml)) { return; } // decode and verify update info var verifiedUpdateInfo = VerifiedUpdateInfo.FromUpdateXml(updateInfoXml, (requestData == null ? false : requestData.AcceptTestCertificate)); // try to download update executable var downloadFileDir = LibraryIO.FindWritableDirectory(CommonDirectories.CurrentExecutingDirectory, CommonDirectories.LocalAppData, CommonDirectories.Temp); if (downloadFileDir == null) { throw new Exception("Cannot find writable directory for update download."); } var downloadFilePath = Path.Combine(downloadFileDir, "DesktopBootstrapUpdater.exe"); if (File.Exists(downloadFilePath)) { try { File.Delete(downloadFilePath); } catch (IOException ioe) { // this is probably an old update that didn't terminate for some reason. try to kill it. SafeLogMessage(requestData, "An updater seems to be running already. Trying to kill it..."); int nKilled = 0; foreach (var runningUpdater in Process.GetProcessesByName("DesktopBootstrapUpdater")) { runningUpdater.Kill(); nKilled++; } SafeLogMessage(requestData, String.Format("Killed {0} already-running updaters", nKilled)); } } using (var webClient = new WebClient()) { webClient.DownloadFile(verifiedUpdateInfo.DownloadUrl, downloadFilePath); } // verify downloaded file hash string downloadedFileHashBase64; using (var fileReader = new FileStream(downloadFilePath, FileMode.Open, FileAccess.Read)) { downloadedFileHashBase64 = Convert.ToBase64String(new SHA1CryptoServiceProvider().ComputeHash(fileReader)); } if (downloadedFileHashBase64 != verifiedUpdateInfo.DownloadHash) { throw new Exception("Updater executable hash is mismatched."); } // we're about the run the updater. if it sees fit it will TerminateProcess() us, which can // leave the tray icon laying behind well after we're gone, sometimes resulting in multiple // DesktopBootstrap icons in the system tray. so instead we're going to hide our tray icon for // a little bit, in case the updater wants to kill us (ha!). // // note, if this update code is executing on DesktopBootstrap.exe, the icon will just be hidden. // if it's being ran from the service, then DesktopBootstrap.exe will be asked to close gracefully, // which should remove the tray icon BUT if the update doesn't restart DesktopBootstrap.exe then it // won't be restarted until the next machine reboot. try { if (requestData.BeforeExecutingUpdateFile != null) { requestData.BeforeExecutingUpdateFile(); } } catch (Exception eBeforeExecuting) { if (requestData != null && requestData.LogWarningHandler != null) { ExecuteDelegateSwallowExceptions(requestData.LogWarningHandler, "Exception calling BeforeExecutingUpdateFile delegate", eBeforeExecuting); } } SafeLogMessage(requestData, String.Format("Launching update executable {0}", downloadFilePath)); Process.Start(downloadFilePath); } catch (Exception e) { if (requestData != null && requestData.LogErrorHandler != null) { ExecuteDelegateSwallowExceptions(requestData.LogErrorHandler, "Exception while checking for and running updates", e); } } }