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.Implementations.Browser; using Emby.Server.Implementations.IO; using MediaBrowser.Common.Net; using Emby.Server.IO; using Emby.Server.Implementations; namespace Emby.Server { public class Program { private static ApplicationHost _appHost; private static ILogger _logger; 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(Environment.GetCommandLineArgs()); var environmentInfo = new EnvironmentInfo(); var baseDirectory = System.AppContext.BaseDirectory; string archPath = baseDirectory; if (environmentInfo.SystemArchitecture == MediaBrowser.Model.System.Architecture.X64) { archPath = Path.Combine(archPath, "x64"); } else if (environmentInfo.SystemArchitecture == MediaBrowser.Model.System.Architecture.X86) { archPath = Path.Combine(archPath, "x86"); } else { archPath = Path.Combine(archPath, "arm"); } //Wand.SetMagickCoderModulePath(architecturePath); if (environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) { SetDllDirectory(archPath); } var appPaths = CreateApplicationPaths(baseDirectory); SetSqliteProvider(); 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; } RunApplication(appPaths, logManager, options, environmentInfo); } private static void SetSqliteProvider() { SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3()); } /// /// 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; } } return false; } /// /// Creates the application paths. /// private static ServerApplicationPaths CreateApplicationPaths(string appDirectory) { var resourcesPath = appDirectory; return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(appDirectory), appDirectory, 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 { 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 { return false; } } private static readonly TaskCompletionSource ApplicationTaskCompletionSource = new TaskCompletionSource(); /// /// Runs the application. /// /// The app paths. /// The log manager. /// The options. private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, StartupOptions options, EnvironmentInfo environmentInfo) { var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, false, appPaths.TempDirectory); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); var imageEncoder = new NullImageEncoder(); _appHost = new CoreAppHost(appPaths, logManager, options, fileSystem, new PowerManagement(), "emby.windows.zip", environmentInfo, imageEncoder, new CoreSystemEvents(), new MemoryStreamFactory(), new NetworkManager(logManager.GetLogger("NetworkManager")), GenerateCertificate, () => "EmbyUser"); var initProgress = new Progress(); if (environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) { // 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); Task.WaitAll(task); task = ApplicationTaskCompletionSource.Task; Task.WaitAll(task); } private static void GenerateCertificate(string certPath, string certHost) { //CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger); } /// /// 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); 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) { return false; } private static void ShowMessageBox(string msg) { } public static void Shutdown() { DisposeAppHost(); //_logger.Info("Calling Application.Exit"); //Application.Exit(); _logger.Info("Calling Environment.Exit"); Environment.Exit(0); _logger.Info("Calling ApplicationTaskCompletionSource.SetResult"); ApplicationTaskCompletionSource.SetResult(true); } public static void Restart() { DisposeAppHost(); // todo: start new instance Shutdown(); } private static void DisposeAppHost() { if (!_appHostDisposed) { _logger.Info("Disposing app host"); _appHostDisposed = true; _appHost.Dispose(); } } /// /// 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 } } }