using MediaBrowser.Model.Logging; using MediaBrowser.Server.Implementations; using Microsoft.Win32; using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using Emby.Common.Implementations.EnvironmentInfo; using Emby.Common.Implementations.IO; using Emby.Common.Implementations.Logging; using Emby.Common.Implementations.Networking; using Emby.Drawing; using Emby.Server.Core; using Emby.Server.Core.Browser; using Emby.Server.Implementations.IO; using MediaBrowser.Common.Net; using Emby.Server.IO; namespace Emby.Server { public class Program { private static ApplicationHost _appHost; private static ILogger _logger; private static bool _isRunningAsService = false; private static bool _canRestartService = false; private static bool _appHostDisposed; [DllImport("kernel32.dll", SetLastError = true)] static extern bool SetDllDirectory(string lpPathName); /// /// Defines the entry point of the application. /// public static void Main(string[] args) { var options = new StartupOptions(); _isRunningAsService = options.ContainsOption("-service"); if (_isRunningAsService) { //_canRestartService = CanRestartWindowsService(); } var currentProcess = Process.GetCurrentProcess(); var applicationPath = currentProcess.MainModule.FileName; //var architecturePath = Path.Combine(Path.GetDirectoryName(applicationPath), Environment.Is64BitProcess ? "x64" : "x86"); //Wand.SetMagickCoderModulePath(architecturePath); //var success = SetDllDirectory(architecturePath); var appPaths = CreateApplicationPaths(applicationPath, _isRunningAsService); var logManager = new NlogManager(appPaths.LogDirectoryPath, "server"); logManager.ReloadLogger(LogSeverity.Debug); logManager.AddConsoleOutput(); var logger = _logger = logManager.GetLogger("Main"); ApplicationHost.LogEnvironmentInfo(logger, appPaths, true); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; if (IsAlreadyRunning(applicationPath, currentProcess)) { logger.Info("Shutting down because another instance of Emby Server is already running."); return; } if (PerformUpdateIfNeeded(appPaths, logger)) { logger.Info("Exiting to perform application update."); return; } try { RunApplication(appPaths, logManager, _isRunningAsService, options); } finally { OnServiceShutdown(); } } /// /// Determines whether [is already running] [the specified current process]. /// /// The application path. /// The current process. /// true if [is already running] [the specified current process]; otherwise, false. private static bool IsAlreadyRunning(string applicationPath, Process currentProcess) { var duplicate = Process.GetProcesses().FirstOrDefault(i => { try { if (currentProcess.Id == i.Id) { return false; } } catch (Exception) { return false; } try { //_logger.Info("Module: {0}", i.MainModule.FileName); if (string.Equals(applicationPath, i.MainModule.FileName, StringComparison.OrdinalIgnoreCase)) { return true; } return false; } catch (Exception) { return false; } }); if (duplicate != null) { _logger.Info("Found a duplicate process. Giving it time to exit."); if (!duplicate.WaitForExit(30000)) { _logger.Info("The duplicate process did not exit."); return true; } } if (!_isRunningAsService) { return false; } return false; } /// /// Creates the application paths. /// /// The application path. /// if set to true [run as service]. /// ServerApplicationPaths. private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, bool runAsService) { var resourcesPath = Path.GetDirectoryName(applicationPath); if (runAsService) { var systemPath = Path.GetDirectoryName(applicationPath); var programDataPath = Path.GetDirectoryName(systemPath); return new ServerApplicationPaths(programDataPath, applicationPath, resourcesPath); } return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(applicationPath), applicationPath, resourcesPath); } /// /// Gets a value indicating whether this instance can self restart. /// /// true if this instance can self restart; otherwise, false. public static bool CanSelfRestart { get { if (_isRunningAsService) { return _canRestartService; } else { return true; } } } /// /// Gets a value indicating whether this instance can self update. /// /// true if this instance can self update; otherwise, false. public static bool CanSelfUpdate { get { if (_isRunningAsService) { return _canRestartService; } else { return true; } } } private static readonly TaskCompletionSource ApplicationTaskCompletionSource = new TaskCompletionSource(); /// /// Runs the application. /// /// The app paths. /// The log manager. /// if set to true [run service]. /// The options. private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService, StartupOptions options) { var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); var imageEncoder = new NullImageEncoder(); _appHost = new CoreAppHost(appPaths, logManager, options, fileSystem, new PowerManagement(), "emby.windows.zip", new EnvironmentInfo(), imageEncoder, new CoreSystemEvents(), new MemoryStreamFactory(), new NetworkManager(logManager.GetLogger("NetworkManager")), GenerateCertificate, () => "EmbyUser"); var initProgress = new Progress(); if (!runService) { // Not crazy about this but it's the only way to suppress ffmpeg crash dialog boxes SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS | ErrorModes.SEM_NOALIGNMENTFAULTEXCEPT | ErrorModes.SEM_NOGPFAULTERRORBOX | ErrorModes.SEM_NOOPENFILEERRORBOX); } var task = _appHost.Init(initProgress); Task.WaitAll(task); task = task.ContinueWith(new Action(a => _appHost.RunStartupTasks()), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent); if (runService) { StartService(logManager); } else { Task.WaitAll(task); task = ApplicationTaskCompletionSource.Task; Task.WaitAll(task); } } private static void GenerateCertificate(string certPath, string certHost) { //CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger); } /// /// Starts the service. /// private static void StartService(ILogManager logManager) { } /// /// Handles the Disposed event of the service control. /// /// The source of the event. /// The instance containing the event data. static void service_Disposed(object sender, EventArgs e) { ApplicationTaskCompletionSource.SetResult(true); OnServiceShutdown(); } private static void OnServiceShutdown() { _logger.Info("Shutting down"); DisposeAppHost(); } /// /// Handles the UnhandledException event of the CurrentDomain control. /// /// The source of the event. /// The instance containing the event data. static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { var exception = (Exception)e.ExceptionObject; new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager).Log(exception); if (!_isRunningAsService) { ShowMessageBox("Unhandled exception: " + exception.Message); } if (!Debugger.IsAttached) { Environment.Exit(Marshal.GetHRForException(exception)); } } /// /// Performs the update if needed. /// /// The app paths. /// The logger. /// true if XXXX, false otherwise private static bool PerformUpdateIfNeeded(ServerApplicationPaths appPaths, ILogger logger) { // Look for the existence of an update archive var updateArchive = Path.Combine(appPaths.TempUpdatePath, "MBServer" + ".zip"); if (File.Exists(updateArchive)) { logger.Info("An update is available from {0}", updateArchive); // Update is there - execute update try { //var serviceName = _isRunningAsService ? BackgroundService.GetExistingServiceName() : string.Empty; //new ApplicationUpdater().UpdateApplication(appPaths, updateArchive, logger, serviceName); // And just let the app exit so it can update return true; } catch (Exception e) { logger.ErrorException("Error starting updater.", e); ShowMessageBox(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message)); } } return false; } private static void ShowMessageBox(string msg) { } public static void Shutdown() { if (_isRunningAsService) { ShutdownWindowsService(); } else { DisposeAppHost(); ShutdownWindowsApplication(); } } public static void Restart() { DisposeAppHost(); if (_isRunningAsService) { RestartWindowsService(); } else { //_logger.Info("Hiding server notify icon"); //_serverNotifyIcon.Visible = false; _logger.Info("Starting new instance"); //Application.Restart(); Process.Start(_appHost.ServerConfigurationManager.ApplicationPaths.ApplicationPath); ShutdownWindowsApplication(); } } private static void DisposeAppHost() { if (!_appHostDisposed) { _logger.Info("Disposing app host"); _appHostDisposed = true; _appHost.Dispose(); } } private static void ShutdownWindowsApplication() { //_logger.Info("Calling Application.Exit"); //Application.Exit(); _logger.Info("Calling Environment.Exit"); Environment.Exit(0); _logger.Info("Calling ApplicationTaskCompletionSource.SetResult"); ApplicationTaskCompletionSource.SetResult(true); } private static void ShutdownWindowsService() { } private static void RestartWindowsService() { } private static bool CanRestartWindowsService() { return false; } /// /// Sets the error mode. /// /// The u mode. /// ErrorModes. [DllImport("kernel32.dll")] static extern ErrorModes SetErrorMode(ErrorModes uMode); /// /// Enum ErrorModes /// [Flags] public enum ErrorModes : uint { /// /// The SYSTE m_ DEFAULT /// SYSTEM_DEFAULT = 0x0, /// /// The SE m_ FAILCRITICALERRORS /// SEM_FAILCRITICALERRORS = 0x0001, /// /// The SE m_ NOALIGNMENTFAULTEXCEPT /// SEM_NOALIGNMENTFAULTEXCEPT = 0x0004, /// /// The SE m_ NOGPFAULTERRORBOX /// SEM_NOGPFAULTERRORBOX = 0x0002, /// /// The SE m_ NOOPENFILEERRORBOX /// SEM_NOOPENFILEERRORBOX = 0x8000 } } }