using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Constants; using MediaBrowser.Common.Implementations.Logging; using MediaBrowser.Common.Implementations.Updates; using MediaBrowser.Model.Logging; using MediaBrowser.Server.Implementations; using MediaBrowser.ServerApplication; using MediaBrowser.ServerApplication.Native; using MediaBrowser.ServerApplication.IO; using Microsoft.Win32; using System; using System.Diagnostics; using System.IO; using System.Threading; using System.Windows; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using System.Reflection; using System.Linq; // MONOMKBUNDLE: For the embedded version, mkbundle tool #if MONOMKBUNDLE using Mono.Unix; using Mono.Unix.Native; using System.Text; #endif namespace MediaBrowser.Server.Mono { public class MainClass { private static ApplicationHost _appHost; private static ILogger _logger; public static void Main (string[] args) { //GetEntryAssembly is empty when running from a mkbundle package #if MONOMKBUNDLE var applicationPath = GetExecutablePath(); #else var applicationPath = Assembly.GetEntryAssembly ().Location; #endif var options = new StartupOptions(); // Allow this to be specified on the command line. var customProgramDataPath = options.GetOption("-programdata"); var appPaths = CreateApplicationPaths(applicationPath, customProgramDataPath); var logManager = new NlogManager(appPaths.LogDirectoryPath, "server"); logManager.ReloadLogger(LogSeverity.Info); logManager.AddConsoleOutput(); var logger = _logger = logManager.GetLogger("Main"); BeginLog(logger, appPaths); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; if (PerformUpdateIfNeeded(appPaths, logger)) { logger.Info("Exiting to perform application update."); return; } try { RunApplication(appPaths, logManager, options); } finally { logger.Info("Shutting down"); _appHost.Dispose(); } } private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, string programDataPath) { if (string.IsNullOrEmpty(programDataPath)) { return new ServerApplicationPaths(applicationPath); } return new ServerApplicationPaths(programDataPath, applicationPath); } /// /// Determines whether this instance [can self restart]. /// /// true if this instance [can self restart]; otherwise, false. public static bool CanSelfRestart { get { return false; } } /// /// 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 RemoteCertificateValidationCallback _ignoreCertificates = new RemoteCertificateValidationCallback(delegate { return true; }); private static TaskCompletionSource _applicationTaskCompletionSource = new TaskCompletionSource(); private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, StartupOptions options) { SystemEvents.SessionEnding += SystemEvents_SessionEnding; // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = _ignoreCertificates; _appHost = new ApplicationHost(appPaths, logManager, false, false, options); Console.WriteLine ("appHost.Init"); var initProgress = new Progress(); var task = _appHost.Init(initProgress); Task.WaitAll (task); Console.WriteLine ("Running startup tasks"); task = _appHost.RunStartupTasks(); Task.WaitAll (task); task = _applicationTaskCompletionSource.Task; Task.WaitAll (task); } /// /// Handles the SessionEnding event of the SystemEvents control. /// /// The source of the event. /// The instance containing the event data. static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e) { if (e.Reason == SessionEndReasons.SystemShutdown) { Shutdown(); } } /// /// Begins the log. /// /// The logger. private static void BeginLog(ILogger logger, IApplicationPaths appPaths) { logger.Info("Media Browser Server started"); ApplicationHost.LogEnvironmentInfo(logger, appPaths); } /// /// 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; LogUnhandledException(exception); if (!Debugger.IsAttached) { Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(exception)); } } private static void LogUnhandledException(Exception ex) { _logger.ErrorException("UnhandledException", ex); _appHost.LogManager.Flush (); var path = Path.Combine(_appHost.ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, "crash_" + Guid.NewGuid() + ".txt"); var builder = LogHelper.GetLogMessage(ex); Console.WriteLine ("UnhandledException"); Console.WriteLine (builder.ToString()); File.WriteAllText(path, builder.ToString()); } /// /// 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; } public static void Shutdown() { _applicationTaskCompletionSource.SetResult (true); } public static void Restart() { // Second instance will start first, so dispose so that the http ports will be available to the new instance _appHost.Dispose(); // Right now this method will just shutdown, but not restart Shutdown (); } // Return the running process path #if MONOMKBUNDLE public static string GetExecutablePath() { var builder = new StringBuilder (8192); if (Syscall.readlink("/proc/self/exe", builder) >= 0) return builder.ToString (); else return null; } #endif } class NoCheckCertificatePolicy : ICertificatePolicy { public bool CheckValidationResult (ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } }