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);
        }
Esempio n. 2
0
        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);
                }
            }
        }