diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-09-20 21:04:14 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-09-20 21:04:14 -0400 |
| commit | 2e511fba839e86d9393e5eeb10795f1b0aed7ce0 (patch) | |
| tree | d82481b8352ae2f088cbe902cc7fc222b8b0a0ed /MediaBrowser.ServerApplication/MainStartup.cs | |
| parent | b5615cb233923f8424a40af009101a901f30f591 (diff) | |
support run as service
Diffstat (limited to 'MediaBrowser.ServerApplication/MainStartup.cs')
| -rw-r--r-- | MediaBrowser.ServerApplication/MainStartup.cs | 311 |
1 files changed, 273 insertions, 38 deletions
diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index e5d44c0f5..921a4c129 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -1,12 +1,18 @@ using MediaBrowser.Common.Constants; +using MediaBrowser.Common.Implementations.Logging; using MediaBrowser.Common.Implementations.Updates; +using MediaBrowser.Model.Logging; using MediaBrowser.Server.Implementations; +using Microsoft.Win32; using System; +using System.Configuration.Install; using System.Diagnostics; using System.IO; +using System.Linq; +using System.ServiceProcess; using System.Threading; +using System.Threading.Tasks; using System.Windows; -using Microsoft.Win32; namespace MediaBrowser.ServerApplication { @@ -17,7 +23,9 @@ namespace MediaBrowser.ServerApplication /// </summary> private static Mutex _singleInstanceMutex; - private static IApplicationInterface _applicationInterface; + private static ApplicationHost _appHost; + + private static App _app; /// <summary> /// Defines the entry point of the application. @@ -25,6 +33,50 @@ namespace MediaBrowser.ServerApplication [STAThread] public static void Main() { + var startFlag = Environment.GetCommandLineArgs().ElementAtOrDefault(1); + var runService = string.Equals(startFlag, "-service", StringComparison.OrdinalIgnoreCase); + + var appPaths = CreateApplicationPaths(runService); + + var logManager = new NlogManager(appPaths.LogDirectoryPath, "server"); + logManager.ReloadLogger(LogSeverity.Info); + + var logger = logManager.GetLogger("Main"); + + BeginLog(logger); + + // Install directly + if (string.Equals(startFlag, "-installservice", StringComparison.OrdinalIgnoreCase)) + { + logger.Info("Performing service installation"); + InstallService(logger); + return; + } + + // Restart with admin rights, then install + if (string.Equals(startFlag, "-launchinstallservice", StringComparison.OrdinalIgnoreCase)) + { + logger.Info("Performing service installation"); + RunServiceInstallation(); + return; + } + + // Uninstall directly + if (string.Equals(startFlag, "-uninstallservice", StringComparison.OrdinalIgnoreCase)) + { + logger.Info("Performing service uninstallation"); + UninstallService(logger); + return; + } + + // Restart with admin rights, then uninstall + if (string.Equals(startFlag, "-launchuninstallservice", StringComparison.OrdinalIgnoreCase)) + { + logger.Info("Performing service uninstallation"); + RunServiceUninstallation(); + return; + } + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; bool createdNew; @@ -36,78 +88,228 @@ namespace MediaBrowser.ServerApplication if (!createdNew) { _singleInstanceMutex = null; + logger.Info("Shutting down because another instance of Media Browser Server is already running."); return; } - // Look for the existence of an update archive - var appPaths = new ServerApplicationPaths(); - var updateArchive = Path.Combine(appPaths.TempUpdatePath, Constants.MbServerPkgName + ".zip"); - if (File.Exists(updateArchive)) + if (PerformUpdateIfNeeded(appPaths, logger)) { - // Update is there - execute update - try - { - new ApplicationUpdater().UpdateApplication(MBApplication.MBServer, appPaths, updateArchive); + logger.Info("Exiting to perform application update."); + return; + } - // And just let the app exit so it can update - return; - } - catch (Exception e) - { - MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message)); - } + try + { + RunApplication(appPaths, logManager, runService); } + finally + { + logger.Info("Shutting down"); - StartApplication(); + ReleaseMutex(logger); + + _appHost.Dispose(); + } } - private static void StartApplication() + /// <summary> + /// Creates the application paths. + /// </summary> + /// <param name="runAsService">if set to <c>true</c> [run as service].</param> + /// <returns>ServerApplicationPaths.</returns> + private static ServerApplicationPaths CreateApplicationPaths(bool runAsService) + { + if (runAsService) + { +#if (RELEASE) + var systemPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); + + var programDataPath = Path.GetDirectoryName(systemPath); + + return new ServerApplicationPaths(programDataPath); +#endif + } + + return new ServerApplicationPaths(); + } + + /// <summary> + /// Begins the log. + /// </summary> + /// <param name="logger">The logger.</param> + private static void BeginLog(ILogger logger) + { + logger.Info("Media Browser Server started"); + logger.Info("Command line: {0}", string.Join(" ", Environment.GetCommandLineArgs())); + + logger.Info("Server: {0}", Environment.MachineName); + logger.Info("Operating system: {0}", Environment.OSVersion.ToString()); + } + + /// <summary> + /// Runs the application. + /// </summary> + /// <param name="appPaths">The app paths.</param> + /// <param name="logManager">The log manager.</param> + /// <param name="runService">if set to <c>true</c> [run service].</param> + private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService) { SystemEvents.SessionEnding += SystemEvents_SessionEnding; + var commandLineArgs = Environment.GetCommandLineArgs(); - if (commandLineArgs.Length > 1 && commandLineArgs[1].Equals("-service")) + _appHost = new ApplicationHost(appPaths, logManager); + + _app = new App(_appHost, _appHost.LogManager.GetLogger("App"), runService); + + if (runService) { - // Start application as a service - StartBackgroundService(); + _app.AppStarted += (sender, args) => StartService(logManager); } - else + + _app.Run(); + } + + /// <summary> + /// Starts the service. + /// </summary> + private static void StartService(ILogManager logManager) + { + var ctl = ServiceController.GetServices().FirstOrDefault(s => s.ServiceName == BackgroundService.Name); + + if (ctl == null) { - StartWpfApp(); + RunServiceInstallation(); } + + var service = new BackgroundService(logManager.GetLogger("Service")); + + service.Disposed += service_Disposed; + + ServiceBase.Run(service); } - static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e) + /// <summary> + /// Handles the Disposed event of the service control. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> + static async void service_Disposed(object sender, EventArgs e) { - // Try to shutdown gracefully - if (_applicationInterface != null) + await _appHost.Shutdown(); + } + + /// <summary> + /// Installs the service. + /// </summary> + private static void InstallService(ILogger logger) + { + var runningPath = Process.GetCurrentProcess().MainModule.FileName; + + try { - _applicationInterface.ShutdownApplication(); + ManagedInstallerClass.InstallHelper(new[] { runningPath }); + + logger.Info("Service installation succeeded"); + } + catch (Exception ex) + { + logger.ErrorException("Uninstall failed", ex); } } - private static void StartWpfApp() + /// <summary> + /// Uninstalls the service. + /// </summary> + private static void UninstallService(ILogger logger) { - var app = new App(); + var runningPath = Process.GetCurrentProcess().MainModule.FileName; - _applicationInterface = app; + try + { + ManagedInstallerClass.InstallHelper(new[] { "/u", runningPath }); - app.Run(); + logger.Info("Service uninstallation succeeded"); + } + catch (Exception ex) + { + logger.ErrorException("Uninstall failed", ex); + } } - private static void StartBackgroundService() + /// <summary> + /// Runs the service installation. + /// </summary> + private static void RunServiceInstallation() { + var runningPath = Process.GetCurrentProcess().MainModule.FileName; + + var startInfo = new ProcessStartInfo + { + FileName = runningPath, + + Arguments = "-installservice", + + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + Verb = "runas", + ErrorDialog = false + }; + using (var process = Process.Start(startInfo)) + { + process.WaitForExit(); + } } - static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + /// <summary> + /// Runs the service uninstallation. + /// </summary> + private static void RunServiceUninstallation() { - var exception = (Exception)e.ExceptionObject; + var runningPath = Process.GetCurrentProcess().MainModule.FileName; - if (_applicationInterface != null) + var startInfo = new ProcessStartInfo { - _applicationInterface.OnUnhandledException(exception); + FileName = runningPath, + + Arguments = "-uninstallservice", + + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + Verb = "runas", + ErrorDialog = false + }; + + using (var process = Process.Start(startInfo)) + { + process.WaitForExit(); } + } + + /// <summary> + /// Handles the SessionEnding event of the SystemEvents control. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="SessionEndingEventArgs"/> instance containing the event data.</param> + static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e) + { + // Try to shutdown gracefully + var task = _appHost.Shutdown(); + + Task.WaitAll(task); + } + + /// <summary> + /// Handles the UnhandledException event of the CurrentDomain control. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="UnhandledExceptionEventArgs"/> instance containing the event data.</param> + static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + var exception = (Exception)e.ExceptionObject; + + _app.OnUnhandledException(exception); if (!Debugger.IsAttached) { @@ -118,17 +320,50 @@ namespace MediaBrowser.ServerApplication /// <summary> /// Releases the mutex. /// </summary> - internal static void ReleaseMutex() + internal static void ReleaseMutex(ILogger logger) { if (_singleInstanceMutex == null) { return; } + logger.Debug("Releasing mutex"); + _singleInstanceMutex.ReleaseMutex(); _singleInstanceMutex.Close(); _singleInstanceMutex.Dispose(); _singleInstanceMutex = null; } + + /// <summary> + /// Performs the update if needed. + /// </summary> + /// <param name="appPaths">The app paths.</param> + /// <param name="logger">The logger.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + private static bool PerformUpdateIfNeeded(ServerApplicationPaths appPaths, ILogger logger) + { + // Look for the existence of an update archive + var updateArchive = Path.Combine(appPaths.TempUpdatePath, Constants.MbServerPkgName + ".zip"); + if (File.Exists(updateArchive)) + { + logger.Info("An update is available from {0}", updateArchive); + + // Update is there - execute update + try + { + new ApplicationUpdater().UpdateApplication(MBApplication.MBServer, appPaths, updateArchive); + + // And just let the app exit so it can update + return true; + } + catch (Exception e) + { + MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message)); + } + } + + return false; + } } } |
