@ -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 ;
}
}
}