mirror of
https://github.com/abraunegg/onedrive
synced 2024-06-19 22:25:03 +02:00
* 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:
parent
1060d85118
commit
5d388581b1
253
src/main.d
253
src/main.d
|
@ -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
|
// Application Option Variables
|
||||||
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
|
|
||||||
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,33 +195,50 @@ 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
|
||||||
syncDir = homePath ~ "/OneDrive";
|
log.vdebug("sync_dir: A '~' was found in sync_dir, using the calculated 'homePath' to replace '~'");
|
||||||
|
syncDir = homePath ~ strip(cfg.getValue("sync_dir"),"~","~");
|
||||||
} else {
|
} else {
|
||||||
// No ~ found in passed in state, use as is
|
// No ~ found in sync_dir, use as is
|
||||||
|
log.vdebug("sync_dir: Getting syncDir from config value sync_dir");
|
||||||
syncDir = cfg.getValue("sync_dir");
|
syncDir = cfg.getValue("sync_dir");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// need to create a default as expanding ~ will not work
|
// A shell and user is set, expand any ~ as this will be expanded correctly if present
|
||||||
syncDir = homePath ~ "/OneDrive";
|
log.vdebug("sync_dir: Getting syncDir from config value sync_dir");
|
||||||
}
|
if (canFind(cfg.getValue("sync_dir"),"~")) {
|
||||||
} else {
|
log.vdebug("sync_dir: A '~' was found in configured sync_dir, automatically expanding as SHELL and USER environment variable is set");
|
||||||
// A shell and user is set, expand any ~ as this will be expanded if present
|
|
||||||
syncDir = expandTilde(cfg.getValue("sync_dir"));
|
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){
|
||||||
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue