Rewrite rules for determining app paths and use XDG_CONFIG_HOME for configDir (#781)

Re-write rules for determining dataDir, configDir and logDir.  Generally, arguments from command line take precedence, then JELLYFIN env vars, before using XDG names.

Co-Authored-By: ploughpuff <33969763+ploughpuff@users.noreply.github.com>
pull/889/head
ploughpuff 5 years ago committed by Bond-009
parent eb4b705167
commit a2dd2ddd55

@ -1,3 +1,4 @@
using System;
using System.IO;
using MediaBrowser.Common.Configuration;
@ -14,50 +15,44 @@ namespace Emby.Server.Implementations.AppBase
/// </summary>
protected BaseApplicationPaths(
string programDataPath,
string appFolderPath,
string logDirectoryPath = null,
string configurationDirectoryPath = null,
string cacheDirectoryPath = null)
string logDirectoryPath,
string configurationDirectoryPath,
string cacheDirectoryPath)
{
ProgramDataPath = programDataPath;
ProgramSystemPath = appFolderPath;
LogDirectoryPath = logDirectoryPath;
ConfigurationDirectoryPath = configurationDirectoryPath;
CachePath = cacheDirectoryPath;
DataPath = Path.Combine(ProgramDataPath, "data");
}
/// <summary>
/// Gets the path to the program data folder
/// </summary>
/// <value>The program data path.</value>
public string ProgramDataPath { get; private set; }
/// <summary>
/// Gets the path to the system folder
/// </summary>
public string ProgramSystemPath { get; private set; }
public string ProgramSystemPath { get; } = AppContext.BaseDirectory;
/// <summary>
/// The _data directory
/// </summary>
private string _dataDirectory;
/// <summary>
/// Gets the folder path to the data directory
/// </summary>
/// <value>The data directory.</value>
private string _dataPath;
public string DataPath
{
get
{
if (_dataDirectory == null)
{
_dataDirectory = Path.Combine(ProgramDataPath, "data");
Directory.CreateDirectory(_dataDirectory);
}
return _dataDirectory;
}
get => _dataPath;
private set => _dataPath = Directory.CreateDirectory(value).FullName;
}
private const string _virtualDataPath = "%AppDataPath%";
public string VirtualDataPath => _virtualDataPath;
/// <summary>
/// Gets the magic strings used for virtual path manipulation.
/// </summary>
public string VirtualDataPath { get; } = "%AppDataPath%";
/// <summary>
/// Gets the image cache path.
@ -83,55 +78,17 @@ namespace Emby.Server.Implementations.AppBase
/// <value>The plugin configurations path.</value>
public string TempUpdatePath => Path.Combine(ProgramDataPath, "updates");
/// <summary>
/// The _log directory
/// </summary>
private string _logDirectoryPath;
/// <summary>
/// Gets the path to the log directory
/// </summary>
/// <value>The log directory path.</value>
public string LogDirectoryPath
{
get
{
if (string.IsNullOrEmpty(_logDirectoryPath))
{
_logDirectoryPath = Path.Combine(ProgramDataPath, "logs");
Directory.CreateDirectory(_logDirectoryPath);
}
return _logDirectoryPath;
}
set => _logDirectoryPath = value;
}
/// <summary>
/// The _config directory
/// </summary>
private string _configurationDirectoryPath;
public string LogDirectoryPath { get; private set; }
/// <summary>
/// Gets the path to the application configuration root directory
/// </summary>
/// <value>The configuration directory path.</value>
public string ConfigurationDirectoryPath
{
get
{
if (string.IsNullOrEmpty(_configurationDirectoryPath))
{
_configurationDirectoryPath = Path.Combine(ProgramDataPath, "config");
Directory.CreateDirectory(_configurationDirectoryPath);
}
return _configurationDirectoryPath;
}
set => _configurationDirectoryPath = value;
}
public string ConfigurationDirectoryPath { get; private set; }
/// <summary>
/// Gets the path to the system configuration file
@ -139,29 +96,11 @@ namespace Emby.Server.Implementations.AppBase
/// <value>The system configuration file path.</value>
public string SystemConfigurationFilePath => Path.Combine(ConfigurationDirectoryPath, "system.xml");
/// <summary>
/// The _cache directory
/// </summary>
private string _cachePath;
/// <summary>
/// Gets the folder path to the cache directory
/// </summary>
/// <value>The cache directory.</value>
public string CachePath
{
get
{
if (string.IsNullOrEmpty(_cachePath))
{
_cachePath = Path.Combine(ProgramDataPath, "cache");
Directory.CreateDirectory(_cachePath);
}
return _cachePath;
}
set => _cachePath = value;
}
public string CachePath { get; set; }
/// <summary>
/// Gets the folder path to the temp directory within the cache folder

@ -15,21 +15,17 @@ namespace Emby.Server.Implementations
/// </summary>
public ServerApplicationPaths(
string programDataPath,
string appFolderPath,
string applicationResourcesPath,
string logDirectoryPath = null,
string configurationDirectoryPath = null,
string cacheDirectoryPath = null)
string logDirectoryPath,
string configurationDirectoryPath,
string cacheDirectoryPath)
: base(programDataPath,
appFolderPath,
logDirectoryPath,
configurationDirectoryPath,
cacheDirectoryPath)
{
ApplicationResourcesPath = applicationResourcesPath;
}
public string ApplicationResourcesPath { get; private set; }
public string ApplicationResourcesPath { get; } = AppContext.BaseDirectory;
/// <summary>
/// Gets the path to the base root media directory
@ -148,7 +144,6 @@ namespace Emby.Server.Implementations
set => _internalMetadataPath = value;
}
private const string _virtualInternalMetadataPath = "%MetadataPath%";
public string VirtualInternalMetadataPath => _virtualInternalMetadataPath;
public string VirtualInternalMetadataPath { get; } = "%MetadataPath%";
}
}

@ -139,112 +139,156 @@ namespace Jellyfin.Server
}
}
/// <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 XDG approach is followed:
/// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
/// </summary>
/// <param name="options"></param>
/// <returns>ServerApplicationPaths</returns>
private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options)
{
string programDataPath = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH");
if (string.IsNullOrEmpty(programDataPath))
// dataDir
// IF --datadir
// ELSE IF $JELLYFIN_DATA_PATH
// 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))
{
if (options.DataDir != null)
{
programDataPath = options.DataDir;
}
else
dataDir = Environment.GetEnvironmentVariable("JELLYFIN_DATA_PATH");
if (string.IsNullOrEmpty(dataDir))
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
programDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
dataDir = 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))
dataDir = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
if (string.IsNullOrEmpty(dataDir))
{
programDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share");
dataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share");
}
}
programDataPath = Path.Combine(programDataPath, "jellyfin");
dataDir = Path.Combine(dataDir, "jellyfin");
}
}
if (string.IsNullOrEmpty(programDataPath))
{
Console.WriteLine("Cannot continue without path to program data folder (try -programdata)");
Environment.Exit(1);
}
else
{
Directory.CreateDirectory(programDataPath);
}
// 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;
string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");
if (string.IsNullOrEmpty(configDir))
{
if (options.ConfigDir != null)
{
configDir = options.ConfigDir;
}
else
configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");
if (string.IsNullOrEmpty(configDir))
{
// Let BaseApplicationPaths set up the default value
configDir = null;
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");
}
}
}
if (configDir != null)
{
Directory.CreateDirectory(configDir);
}
// 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;
string cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR");
if (string.IsNullOrEmpty(cacheDir))
{
if (options.CacheDir != null)
{
cacheDir = options.CacheDir;
}
else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
cacheDir = Environment.GetEnvironmentVariable("JELLYFIN_CACHE_DIR");
if (string.IsNullOrEmpty(cacheDir))
{
// $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, $HOME/.cache should be used.
if (string.IsNullOrEmpty(cacheDir))
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Hang cache folder off already set dataDir
cacheDir = Path.Combine(dataDir, "cache");
}
else
{
cacheDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache");
// $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");
}
cacheDir = Path.Combine(cacheDir, "jellyfin");
}
}
if (cacheDir != null)
{
Directory.CreateDirectory(cacheDir);
}
// logDir
// IF --logdir
// ELSE IF $JELLYFIN_LOG_DIR
// ELSE IF --datadir, use <datadir>/log (assume portable run)
// ELSE <datadir>/log
var logDir = options.LogDir;
string logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");
if (string.IsNullOrEmpty(logDir))
{
if (options.LogDir != null)
{
logDir = options.LogDir;
}
else
logDir = Environment.GetEnvironmentVariable("JELLYFIN_LOG_DIR");
if (string.IsNullOrEmpty(logDir))
{
// Let BaseApplicationPaths set up the default value
logDir = null;
// Hang log folder off already set dataDir
logDir = Path.Combine(dataDir, "log");
}
}
if (logDir != null)
// 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);
}
string appPath = AppContext.BaseDirectory;
return new ServerApplicationPaths(programDataPath, appPath, appPath, logDir, configDir, cacheDir);
return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir);
}
private static async Task CreateLogger(IApplicationPaths appPaths)

Loading…
Cancel
Save