/// <summary> /// Create the data, config and log paths from the variety of inputs(command line args, /// environment variables) or decide on what default to use. For Windows it's %AppPath% /// for everything else the /// <a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html">XDG approach</a> /// is followed. /// </summary> /// <param name="options">The <see cref="StartupOptions" /> for this instance.</param> /// <returns><see cref="ServerApplicationPaths" />.</returns> private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options) { // dataDir // IF --datadir // ELSE IF $JELLYFIN_DATA_DIR // ELSE IF windows, use <%APPDATA%>/jellyfin // ELSE IF $XDG_DATA_HOME then use $XDG_DATA_HOME/jellyfin // ELSE use $HOME/.local/share/jellyfin var dataDir = options.DataDir; if (string.IsNullOrEmpty(dataDir)) { dataDir = Environment.GetEnvironmentVariable("JELLYFIN_DATA_DIR"); if (string.IsNullOrEmpty(dataDir)) { // LocalApplicationData follows the XDG spec on unix machines dataDir = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "jellyfin"); } } // configDir // IF --configdir // ELSE IF $JELLYFIN_CONFIG_DIR // ELSE IF --datadir, use <datadir>/config (assume portable run) // ELSE IF <datadir>/config exists, use that // ELSE IF windows, use <datadir>/config // ELSE IF $XDG_CONFIG_HOME use $XDG_CONFIG_HOME/jellyfin // ELSE $HOME/.config/jellyfin var configDir = options.ConfigDir; if (string.IsNullOrEmpty(configDir)) { configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR"); if (string.IsNullOrEmpty(configDir)) { if (options.DataDir != null || Directory.Exists(Path.Combine(dataDir, "config")) || RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Hang config folder off already set dataDir configDir = Path.Combine(dataDir, "config"); } else { // $XDG_CONFIG_HOME defines the base directory relative to which // user specific configuration files should be stored. configDir = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); // If $XDG_CONFIG_HOME is either not set or empty, // a default equal to $HOME /.config should be used. if (string.IsNullOrEmpty(configDir)) { configDir = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config"); } configDir = Path.Combine(configDir, "jellyfin"); } } } // cacheDir // IF --cachedir // ELSE IF $JELLYFIN_CACHE_DIR // ELSE IF windows, use <datadir>/cache // ELSE IF XDG_CACHE_HOME, use $XDG_CACHE_HOME/jellyfin // ELSE HOME/.cache/jellyfin var cacheDir = options.CacheDir; if (string.IsNullOrEmpty(cacheDir)) { cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR"); if (string.IsNullOrEmpty(cacheDir)) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Hang cache folder off already set dataDir cacheDir = Path.Combine(dataDir, "cache"); } else { // $XDG_CACHE_HOME defines the base directory relative to which // user specific non-essential data files should be stored. cacheDir = Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); // If $XDG_CACHE_HOME is either not set or empty, // a default equal to $HOME/.cache should be used. if (string.IsNullOrEmpty(cacheDir)) { cacheDir = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache"); } cacheDir = Path.Combine(cacheDir, "jellyfin"); } } } // webDir // IF --webdir // ELSE IF $JELLYFIN_WEB_DIR // ELSE use <bindir>/jellyfin-web var webDir = options.WebDir; if (string.IsNullOrEmpty(webDir)) { webDir = Environment.GetEnvironmentVariable("JELLYFIN_WEB_DIR"); if (string.IsNullOrEmpty(webDir)) { // Use default location under ResourcesPath webDir = Path.Combine(AppContext.BaseDirectory, "jellyfin-web"); } } // logDir // IF --logdir // ELSE IF $JELLYFIN_LOG_DIR // ELSE IF --datadir, use <datadir>/log (assume portable run) // ELSE <datadir>/log var logDir = options.LogDir; if (string.IsNullOrEmpty(logDir)) { logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR"); if (string.IsNullOrEmpty(logDir)) { // Hang log folder off already set dataDir logDir = Path.Combine(dataDir, "log"); } } // Ensure the main folders exist before we continue try { Directory.CreateDirectory(dataDir); Directory.CreateDirectory(logDir); Directory.CreateDirectory(configDir); Directory.CreateDirectory(cacheDir); } catch (IOException ex) { Console.Error.WriteLine("Error whilst attempting to create folder"); Console.Error.WriteLine(ex.ToString()); Environment.Exit(1); } return(new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir, webDir)); }
private static async Task StartApp(StartupOptions options) { ServerApplicationPaths appPaths = CreateApplicationPaths(options); // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); IConfiguration appConfig = await CreateConfiguration(appPaths).ConfigureAwait(false); CreateLogger(appConfig, appPaths); _logger = _loggerFactory.CreateLogger("Main"); AppDomain.CurrentDomain.UnhandledException += (sender, e) => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception"); // Intercept Ctrl+C and Ctrl+Break Console.CancelKeyPress += (sender, e) => { if (_tokenSource.IsCancellationRequested) { return; // Already shutting down } e.Cancel = true; _logger.LogInformation("Ctrl+C, shutting down"); Environment.ExitCode = 128 + 2; Shutdown(); }; // Register a SIGTERM handler AppDomain.CurrentDomain.ProcessExit += (sender, e) => { if (_tokenSource.IsCancellationRequested) { return; // Already shutting down } _logger.LogInformation("Received a SIGTERM signal, shutting down"); Environment.ExitCode = 128 + 15; Shutdown(); }; _logger.LogInformation("Jellyfin version: {Version}", Assembly.GetEntryAssembly().GetName().Version); ApplicationHost.LogEnvironmentInfo(_logger, appPaths); // Increase the max http request limit // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); // Disable the "Expect: 100-Continue" header by default // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c ServicePointManager.Expect100Continue = false; // CA5359: Do Not Disable Certificate Validation #pragma warning disable CA5359 // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return(true); }); #pragma warning restore CA5359 Batteries_V2.Init(); if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK) { _logger.LogWarning("Failed to enable shared cache for SQLite"); } var appHost = new CoreAppHost( appPaths, _loggerFactory, options, new ManagedFileSystem(_loggerFactory.CreateLogger <ManagedFileSystem>(), appPaths), new NullImageEncoder(), new NetworkManager(_loggerFactory.CreateLogger <NetworkManager>()), appConfig); try { await appHost.InitAsync(new ServiceCollection()).ConfigureAwait(false); appHost.ImageProcessor.ImageEncoder = GetImageEncoder(appPaths, appHost.LocalizationManager); await appHost.RunStartupTasksAsync().ConfigureAwait(false); // Block main thread until shutdown await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false); } catch (TaskCanceledException) { // Don't throw on cancellation } catch (Exception ex) { _logger.LogCritical(ex, "Error while starting server."); } finally { appHost?.Dispose(); } if (_restartOnShutdown) { StartNewInstance(options); } }
public static async Task Main(string[] args) { StartupOptions options = new StartupOptions(args); Version version = Assembly.GetEntryAssembly().GetName().Version; if (options.ContainsOption("-v") || options.ContainsOption("--version")) { Console.WriteLine(version.ToString()); } ServerApplicationPaths appPaths = CreateApplicationPaths(options); // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); await createLogger(appPaths); _logger = _loggerFactory.CreateLogger("Main"); AppDomain.CurrentDomain.UnhandledException += (sender, e) => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception"); // Intercept Ctrl+C and Ctrl+Break Console.CancelKeyPress += (sender, e) => { if (_tokenSource.IsCancellationRequested) { return; // Already shutting down } e.Cancel = true; _logger.LogInformation("Ctrl+C, shutting down"); Environment.ExitCode = 128 + 2; Shutdown(); }; // Register a SIGTERM handler AppDomain.CurrentDomain.ProcessExit += (sender, e) => { if (_tokenSource.IsCancellationRequested) { return; // Already shutting down } _logger.LogInformation("Received a SIGTERM signal, shutting down"); Environment.ExitCode = 128 + 15; Shutdown(); }; _logger.LogInformation("Jellyfin version: {Version}", version); EnvironmentInfo environmentInfo = new EnvironmentInfo(getOperatingSystem()); ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo); SQLitePCL.Batteries_V2.Init(); // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return(true); }); var fileSystem = new ManagedFileSystem(_loggerFactory, environmentInfo, null, appPaths.TempDirectory, true); using (var appHost = new CoreAppHost( appPaths, _loggerFactory, options, fileSystem, environmentInfo, new NullImageEncoder(), new SystemEvents(), new NetworkManager(_loggerFactory, environmentInfo))) { await appHost.InitAsync(); appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager); _logger.LogInformation("Running startup tasks"); await appHost.RunStartupTasks(); // TODO: read input for a stop command try { // Block main thread until shutdown await Task.Delay(-1, _tokenSource.Token); } catch (TaskCanceledException) { // Don't throw on cancellation } _logger.LogInformation("Disposing app host"); } if (_restartOnShutdown) { StartNewInstance(options); } }
private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options) { string programDataPath = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH"); if (string.IsNullOrEmpty(programDataPath)) { if (options.ContainsOption("-programdata")) { programDataPath = options.GetOption("-programdata"); } else { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); } else { // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. programDataPath = Environment.GetEnvironmentVariable("XDG_DATA_HOME"); // If $XDG_DATA_HOME is either not set or empty, $HOME/.local/share should be used. if (string.IsNullOrEmpty(programDataPath)) { programDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share"); } } programDataPath = Path.Combine(programDataPath, "jellyfin"); } } if (string.IsNullOrEmpty(programDataPath)) { Console.WriteLine("Cannot continue without path to program data folder (try -programdata)"); Environment.Exit(1); } else { Directory.CreateDirectory(programDataPath); } string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR"); if (string.IsNullOrEmpty(configDir)) { if (options.ContainsOption("-configdir")) { configDir = options.GetOption("-configdir"); } else { // Let BaseApplicationPaths set up the default value configDir = null; } } if (configDir != null) { Directory.CreateDirectory(configDir); } string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR"); if (string.IsNullOrEmpty(logDir)) { if (options.ContainsOption("-logdir")) { logDir = options.GetOption("-logdir"); } else { // Let BaseApplicationPaths set up the default value logDir = null; } } if (logDir != null) { Directory.CreateDirectory(logDir); } string appPath = AppContext.BaseDirectory; return(new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir)); }
private static async Task StartApp(StartupOptions options) { var stopWatch = new Stopwatch(); stopWatch.Start();
public static async Task <int> Main(string[] args) { StartupOptions options = new StartupOptions(args); Version version = Assembly.GetEntryAssembly().GetName().Version; if (options.ContainsOption("-v") || options.ContainsOption("--version")) { Console.WriteLine(version.ToString()); return(0); } ServerApplicationPaths appPaths = createApplicationPaths(options); // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); await createLogger(appPaths); _loggerFactory = new SerilogLoggerFactory(); _logger = _loggerFactory.CreateLogger("Main"); AppDomain.CurrentDomain.UnhandledException += (sender, e) => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception"); _logger.LogInformation("Jellyfin version: {Version}", version); EnvironmentInfo environmentInfo = new EnvironmentInfo(getOperatingSystem()); ApplicationHost.LogEnvironmentInfo(_logger, appPaths, environmentInfo); SQLitePCL.Batteries_V2.Init(); // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return(true); }); var fileSystem = new ManagedFileSystem(_loggerFactory.CreateLogger("FileSystem"), environmentInfo, null, appPaths.TempDirectory, true); using (var appHost = new CoreAppHost( appPaths, _loggerFactory, options, fileSystem, environmentInfo, new NullImageEncoder(), new SystemEvents(_loggerFactory.CreateLogger("SystemEvents")), new NetworkManager(_loggerFactory.CreateLogger("NetworkManager"), environmentInfo))) { appHost.Init(); appHost.ImageProcessor.ImageEncoder = getImageEncoder(_logger, fileSystem, options, () => appHost.HttpClient, appPaths, environmentInfo, appHost.LocalizationManager); _logger.LogInformation("Running startup tasks"); await appHost.RunStartupTasks(); // TODO: read input for a stop command // Block main thread until shutdown await ApplicationTaskCompletionSource.Task; _logger.LogInformation("Disposing app host"); } if (_restartOnShutdown) { StartNewInstance(options); } return(0); }
public CoreAppHost(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, MediaBrowser.Common.Net.INetworkManager networkManager) : base(applicationPaths, loggerFactory, options, fileSystem, environmentInfo, imageEncoder, networkManager) { }