Resolve 'sync_dir' not read from 'config' file when run in Docker container #306 (#308)

* Fix logic for sync_dir handling on headless systems
* Fix logic where potentially a 'default' ~/OneDrive sync_dir could be set despite 'config' file configured for an alternate
* Add debug handling for sync_dir operations
* Add debug handling for homePath calculation
* Add debug handling for configDirBase calculation
* Reorder main() so that log.vdebug works where required
* Rework configDirName as this had the same issue as syncDir
* Update configDirName & syncDir '~' handling & replacement to be more robust and only replace if '~' is first char in string
* Add additional debug logging if syncDir is created
This commit is contained in:
abraunegg 2018-12-23 11:15:10 +11:00 committed by GitHub
parent 1060d85118
commit 5d388581b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,100 +1,72 @@
import core.stdc.stdlib: EXIT_SUCCESS, EXIT_FAILURE; import core.stdc.stdlib: EXIT_SUCCESS, EXIT_FAILURE;
import core.memory, core.time, core.thread; import core.memory, core.time, core.thread;
import std.getopt, std.file, std.path, std.process, std.stdio, std.conv, std.algorithm.searching; import std.getopt, std.file, std.path, std.process, std.stdio, std.conv, std.algorithm.searching, std.string;
import config, itemdb, monitor, onedrive, selective, sync, util; import config, itemdb, monitor, onedrive, selective, sync, util;
import std.net.curl: CurlException; import std.net.curl: CurlException;
static import log; static import log;
int main(string[] args) int main(string[] args)
{ {
// Determine the users home directory. // Disable buffering on stdout
// Need to avoid using ~ here as expandTilde() below does not interpret correctly when running under init.d or systemd scripts stdout.setvbuf(0, _IONBF);
string homePath = "";
// Check for HOME environment variable
if (environment.get("HOME") != ""){
// Use HOME environment variable
homePath = environment.get("HOME");
} else {
if ((environment.get("SHELL") == "") && (environment.get("USER") == "")){
// No shell is set or username - observed case when running as systemd service under CentOS 7.x
homePath = "/root";
} else {
// A shell & valid user is set, but no HOME is set, use ~ which can be expanded
homePath = "~";
}
}
// Determine the base directory relative to which user specific configuration files should be stored.
string configDirBase = "";
if (environment.get("XDG_CONFIG_HOME") != ""){
configDirBase = environment.get("XDG_CONFIG_HOME");
} else {
// XDG_CONFIG_HOME does not exist on systems where X11 is not present - ie - headless systems / servers
configDirBase = homePath ~ "/.config";
}
// configuration directory // Application Option Variables
string configDirName = configDirBase ~ "/onedrive";
// only download remote changes
bool downloadOnly;
// override the sync directory
string syncDirName;
// enable monitor mode
bool monitor;
// force a full resync
bool resync;
// remove the current user and sync state
bool logout;
// enable verbose logging
bool verbose;
// print the access token
bool printAccessToken;
// print the version and exit
bool printVersion;
// Additional options added to support MyNAS Storage Appliance
// Debug the HTTPS submit operations if required
bool debugHttp;
// Debug the HTTPS response operations if required
bool debugHttpSubmit;
// This allows for selective directory syncing instead of everything under ~/OneDrive/
string singleDirectory;
// Create a single root directory on OneDrive
string createDirectory;
// Remove a single directory on OneDrive
string removeDirectory;
// The source directory if we are using the OneDrive client to rename a directory
string sourceDirectory;
// The destination directory if we are using the OneDrive client to rename a directory
string destinationDirectory;
// Configure a flag to perform a sync
// This is beneficial so that if just running the client itself - without any options, or sync check, the client does not perform a sync
bool synchronize;
// Local sync - Upload local changes first before downloading changes from OneDrive
bool localFirst;
// Upload Only
bool uploadOnly;
// Add a check mounts option to resolve https://github.com/abraunegg/onedrive/issues/8 // Add a check mounts option to resolve https://github.com/abraunegg/onedrive/issues/8
bool checkMount; bool checkMount;
// Add option to skip symlinks // configuration directory
bool skipSymlinks; string configDirName;
// Add option for no remote delete // Create a single root directory on OneDrive
bool noRemoteDelete; string createDirectory;
// Are we able to reach the OneDrive Service // The destination directory if we are using the OneDrive client to rename a directory
bool online = false; string destinationDirectory;
// Do we enable a log file // Debug the HTTPS submit operations if required
bool enableLogFile = false; bool debugHttp;
// Does the user want to disable upload validation - https://github.com/abraunegg/onedrive/issues/205
// SharePoint will associate some metadata from the library the file is uploaded to directly in the file - thus change file size & checksums
bool disableUploadValidation = false;
// SharePoint / Office 365 Shared Library name to query
string o365SharedLibraryName;
// Do not use notifications in monitor mode // Do not use notifications in monitor mode
bool disableNotifications = false; bool disableNotifications = false;
// Display application configuration but do not sync // Display application configuration but do not sync
bool displayConfiguration = false; bool displayConfiguration = false;
// only download remote changes
bool downloadOnly;
// Does the user want to disable upload validation - https://github.com/abraunegg/onedrive/issues/205
// SharePoint will associate some metadata from the library the file is uploaded to directly in the file - thus change file size & checksums
bool disableUploadValidation = false;
// Do we enable a log file
bool enableLogFile = false;
// SharePoint / Office 365 Shared Library name to query
string o365SharedLibraryName;
// Local sync - Upload local changes first before downloading changes from OneDrive
bool localFirst;
// remove the current user and sync state
bool logout;
// enable monitor mode
bool monitor;
// Add option for no remote delete
bool noRemoteDelete;
// print the access token
bool printAccessToken;
// force a full resync
bool resync;
// Remove a single directory on OneDrive
string removeDirectory;
// This allows for selective directory syncing instead of everything under ~/OneDrive/
string singleDirectory;
// Add option to skip symlinks
bool skipSymlinks;
// The source directory if we are using the OneDrive client to rename a directory
string sourceDirectory;
// override the sync directory
string syncDirName;
// Configure a flag to perform a sync
// This is beneficial so that if just running the client itself - without any options, or sync check, the client does not perform a sync
bool synchronize;
// Upload Only
bool uploadOnly;
// enable verbose logging
bool verbose;
// print the version and exit
bool printVersion;
// Application Startup option validation
try { try {
auto opt = getopt( auto opt = getopt(
args, args,
@ -146,17 +118,74 @@ int main(string[] args)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// disable buffering on stdout // Main function variables
stdout.setvbuf(0, _IONBF); string homePath = "";
string configDirBase = "";
// Debug the HTTPS response operations if required
bool debugHttpSubmit;
// Are we able to reach the OneDrive Service
bool online = false;
// Determine the users home directory.
// Need to avoid using ~ here as expandTilde() below does not interpret correctly when running under init.d or systemd scripts
// Check for HOME environment variable
if (environment.get("HOME") != ""){
// Use HOME environment variable
log.vdebug("homePath: HOME environment variable set");
homePath = environment.get("HOME");
} else {
if ((environment.get("SHELL") == "") && (environment.get("USER") == "")){
// No shell is set or username - observed case when running as systemd service under CentOS 7.x
log.vdebug("homePath: WARNING - no HOME environment variable set");
log.vdebug("homePath: WARNING - no SHELL environment variable set");
log.vdebug("homePath: WARNING - no USER environment variable set");
homePath = "/root";
} else {
// A shell & valid user is set, but no HOME is set, use ~ which can be expanded
log.vdebug("homePath: WARNING - no HOME environment variable set");
homePath = "~";
}
}
// Output homePath calculation
log.vdebug("homePath: ", homePath);
// Determine the base directory relative to which user specific configuration files should be stored.
if (environment.get("XDG_CONFIG_HOME") != ""){
log.vdebug("configDirBase: XDG_CONFIG_HOME environment variable set");
configDirBase = environment.get("XDG_CONFIG_HOME");
} else {
// XDG_CONFIG_HOME does not exist on systems where X11 is not present - ie - headless systems / servers
log.vdebug("configDirBase: WARNING - no XDG_CONFIG_HOME environment variable set");
configDirBase = homePath ~ "/.config";
}
// Output configDirBase calculation
log.vdebug("configDirBase: ", configDirBase);
// Determine the correct configuration directory to use
if (configDirName != "") {
// A CLI 'confdir' was passed in
log.vdebug("configDirName: CLI override to set configDirName to: ", configDirName);
if (canFind(configDirName,"~")) {
// A ~ was found
log.vdebug("configDirName: A '~' was found in configDirName, using the calculated 'homePath' to replace '~'");
configDirName = homePath ~ strip(configDirName,"~","~");
}
} else {
// Set the default application configuration directory
log.vdebug("configDirName: Configuring application to use default config path");
// configDirBase contains the correct path so we do not need to check for presence of '~'
configDirName = configDirBase ~ "/onedrive";
}
if (printVersion) { if (printVersion) {
std.stdio.write("onedrive ", import("version")); std.stdio.write("onedrive ", import("version"));
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
// load configuration // load application configuration
log.vlog("Loading config ..."); log.vlog("Loading config ...");
configDirName = configDirName.expandTilde().absolutePath();
log.vlog("Using Config Dir: ", configDirName); log.vlog("Using Config Dir: ", configDirName);
if (!exists(configDirName)) mkdirRecurse(configDirName); if (!exists(configDirName)) mkdirRecurse(configDirName);
auto cfg = new config.Config(configDirName); auto cfg = new config.Config(configDirName);
@ -166,34 +195,51 @@ int main(string[] args)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// Set the local path OneDrive root // command line parameters to override default 'config' & take precedence
if (syncDirName){ // Set the client to skip symbolic links if --skip-symlinks was passed in
// The user passed in an alternate sync_dir if (skipSymlinks) {
// The user passed in an alternate skip_symlinks as to what was either in 'config' file or application default
log.vdebug("CLI override to set skip_symlinks to: true");
cfg.setValue("skip_symlinks", "true");
}
// Set the OneDrive Local Sync Directory if was passed in via --syncdir
if (syncDirName) {
// The user passed in an alternate sync_dir as to what was either in 'config' file or application default
// Do not expandTilde here as we do not know if we reliably can
log.vdebug("CLI override to set sync_dir to: ", syncDirName);
cfg.setValue("sync_dir", syncDirName); cfg.setValue("sync_dir", syncDirName);
} }
// sync_dir environment handling to handle ~ expansion properly
string syncDir; string syncDir;
if ((environment.get("SHELL") == "") && (environment.get("USER") == "")){ if ((environment.get("SHELL") == "") && (environment.get("USER") == "")){
// No shell or user set, so expandTilde() will fail - usually headless system running under init.d / systemd log.vdebug("sync_dir: No SHELL or USER environment variable configuration detected");
// Did the user specify a 'different' sync dir by passing a value in? // No shell or user set, so expandTilde() will fail - usually headless system running under init.d / systemd or potentially Docker
if (syncDirName){ // Does the 'currently configured' sync_dir include a ~
// was there a ~ in the passed in state? it will not work via init.d / systemd if (canFind(cfg.getValue("sync_dir"),"~")) {
if (canFind(cfg.getValue("sync_dir"),"~")) { // A ~ was found
// A ~ was found log.vdebug("sync_dir: A '~' was found in sync_dir, using the calculated 'homePath' to replace '~'");
syncDir = homePath ~ "/OneDrive"; syncDir = homePath ~ strip(cfg.getValue("sync_dir"),"~","~");
} else {
// No ~ found in passed in state, use as is
syncDir = cfg.getValue("sync_dir");
}
} else { } else {
// need to create a default as expanding ~ will not work // No ~ found in sync_dir, use as is
syncDir = homePath ~ "/OneDrive"; log.vdebug("sync_dir: Getting syncDir from config value sync_dir");
syncDir = cfg.getValue("sync_dir");
} }
} else { } else {
// A shell and user is set, expand any ~ as this will be expanded if present // A shell and user is set, expand any ~ as this will be expanded correctly if present
syncDir = expandTilde(cfg.getValue("sync_dir")); log.vdebug("sync_dir: Getting syncDir from config value sync_dir");
if (canFind(cfg.getValue("sync_dir"),"~")) {
log.vdebug("sync_dir: A '~' was found in configured sync_dir, automatically expanding as SHELL and USER environment variable is set");
syncDir = expandTilde(cfg.getValue("sync_dir"));
} else {
syncDir = cfg.getValue("sync_dir");
}
} }
// vdebug syncDir as set and calculated
log.vdebug("syncDir: ", syncDir);
// Configure logging if enabled // Configure logging if enabled
if (enableLogFile){ if (enableLogFile){
// Read in a user defined log directory or use the default // Read in a user defined log directory or use the default
@ -205,10 +251,6 @@ int main(string[] args)
// Configure whether notifications are used // Configure whether notifications are used
log.setNotifications(monitor && !disableNotifications); log.setNotifications(monitor && !disableNotifications);
// command line parameters override the config
if (syncDirName) cfg.setValue("sync_dir", syncDirName.expandTilde().absolutePath());
if (skipSymlinks) cfg.setValue("skip_symlinks", "true");
// upgrades // upgrades
if (exists(configDirName ~ "/items.db")) { if (exists(configDirName ~ "/items.db")) {
remove(configDirName ~ "/items.db"); remove(configDirName ~ "/items.db");
@ -308,7 +350,10 @@ int main(string[] args)
auto itemdb = new ItemDatabase(cfg.databaseFilePath); auto itemdb = new ItemDatabase(cfg.databaseFilePath);
log.vlog("All operations will be performed in: ", syncDir); log.vlog("All operations will be performed in: ", syncDir);
if (!exists(syncDir)) mkdirRecurse(syncDir); if (!exists(syncDir)) {
log.vdebug("syncDir: Configured syncDir is missing. Creating: ", syncDir);
mkdirRecurse(syncDir);
}
chdir(syncDir); chdir(syncDir);
// Configure selective sync by parsing and getting a regex for skip_file config component // Configure selective sync by parsing and getting a regex for skip_file config component