/// <summary> /// Constructs a new instance of the LiveControl /// </summary> /// <param name="initialTimeout">The duration that the backups should be initially suspended</param> public LiveControls(Datamodel.ApplicationSettings settings) { m_state = LiveControlState.Running; m_waitTimer = new System.Windows.Forms.Timer(); m_waitTimer.Tick += new EventHandler(m_waitTimer_Tick); if (!string.IsNullOrEmpty(settings.StartupDelayDuration) && settings.StartupDelayDuration != "0") { TimeSpan ts = Duplicati.Library.Utility.Timeparser.ParseTimeSpan(settings.StartupDelayDuration); m_waitTimer.Interval = (int)ts.TotalMilliseconds; m_waitTimer.Enabled = true; m_waitTimeExpiration = DateTime.Now + ts; m_state = LiveControlState.Paused; } m_priority = settings.ThreadPriorityOverride; if (!string.IsNullOrEmpty(settings.DownloadSpeedLimit)) { m_downloadLimit = Library.Utility.Sizeparser.ParseSize(settings.DownloadSpeedLimit, "kb"); } if (!string.IsNullOrEmpty(settings.UploadSpeedLimit)) { m_uploadLimit = Library.Utility.Sizeparser.ParseSize(settings.UploadSpeedLimit, "kb"); } try { if (!Library.Utility.Utility.IsClientLinux) { RegisterHibernateMonitor(); } } catch { } }
private void NormalizeApplicationSettings() { //Make sure we have a startup delay, so a restart won't accidently wipe something bool settingsModified = false; Datamodel.ApplicationSettings appset = new Datamodel.ApplicationSettings(Program.DataConnection); if (string.IsNullOrEmpty(appset.StartupDelayDuration) || Duplicati.Library.Utility.Timeparser.ParseTimeSpan(appset.StartupDelayDuration) < TimeSpan.FromMinutes(5)) { appset.StartupDelayDuration = "5m"; settingsModified = true; } if (!System.IO.Directory.Exists(appset.TempPath)) { appset.TempPath = ""; settingsModified = true; } if (!System.IO.Directory.Exists(appset.SignatureCachePath)) { appset.SignatureCachePath = ""; settingsModified = true; } if (settingsModified) { Program.DataConnection.CommitRecursive(Program.DataConnection.GetObjects <Datamodel.ApplicationSetting>()); } }
/// <summary> /// A monitor for detecting when the system hibernates or resumes /// </summary> /// <param name="sender">Unused sender parameter</param> /// <param name="_e">The event information</param> private void SystemEvents_PowerModeChanged(object sender, object _e) { Microsoft.Win32.PowerModeChangedEventArgs e = _e as Microsoft.Win32.PowerModeChangedEventArgs; if (e == null) { return; } if (e.Mode == Microsoft.Win32.PowerModes.Suspend) { //If we are running, register as being paused due to suspending if (this.m_state == LiveControlState.Running) { this.Pause(); m_pausedForSuspend = true; m_suspendMinimumPause = new DateTime(0); } else { if (m_waitTimeExpiration.Ticks != 0) { m_pausedForSuspend = true; m_suspendMinimumPause = m_waitTimeExpiration; m_waitTimeExpiration = new DateTime(0); m_waitTimer.Enabled = false; } } } else if (e.Mode == Microsoft.Win32.PowerModes.Resume) { //If we have been been paused due to suspending, we un-pause now if (m_pausedForSuspend) { long delayTicks = (m_suspendMinimumPause - DateTime.Now).Ticks; Datamodel.ApplicationSettings appset = new Datamodel.ApplicationSettings(Program.DataConnection); if (!string.IsNullOrEmpty(appset.StartupDelayDuration) && appset.StartupDelayDuration != "0") { delayTicks = Math.Max(delayTicks, Library.Utility.Timeparser.ParseTimeSpan(appset.StartupDelayDuration).Ticks); } if (delayTicks > 0) { this.Pause(TimeSpan.FromTicks(delayTicks)); } else { this.Resume(); } } m_pausedForSuspend = false; m_suspendMinimumPause = new DateTime(0); } }
void DataConnection_AfterDataChange(object sender, string propertyname, object oldvalue, object newvalue) { if (sender as Datamodel.ApplicationSetting == null) { return; } if (this.InvokeRequired) { this.BeginInvoke(new System.Data.LightDatamodel.DataChangeEventHandler(DataConnection_AfterDataChange), sender, propertyname, oldvalue, newvalue); return; } m_settings = new Duplicati.Datamodel.ApplicationSettings(Program.DataConnection); }
void Runner_DuplicatiProgress(Duplicati.Library.Main.DuplicatiOperation operation, DuplicatiRunner.RunnerState state, string message, string submessage, int progress, int subprogress) { if (this.InvokeRequired) { this.BeginInvoke(new DuplicatiRunner.ProgressEventDelegate(Runner_DuplicatiProgress), operation, state, message, submessage, progress, subprogress); return; } string name = ""; //Dirty read of the instance variable try { name = Program.WorkThread.CurrentTask.Schedule.Name; } catch { } Datamodel.ApplicationSettings.NotificationLevel level; try { level = m_settings.BallonNotificationLevel; } catch { m_settings = new Datamodel.ApplicationSettings(Program.DataConnection); try { level = m_settings.BallonNotificationLevel; } catch { //TODO: Should find the cause for this, but at least we do not crash the process return; } } if (state == DuplicatiRunner.RunnerState.Started && (level == Duplicati.Datamodel.ApplicationSettings.NotificationLevel.StartAndStop || level == Duplicati.Datamodel.ApplicationSettings.NotificationLevel.Start || level == Duplicati.Datamodel.ApplicationSettings.NotificationLevel.Continous)) { //Show start balloon m_trayIcon.ShowBalloonTip(BALLOON_SHOW_TIME, Application.ProductName, String.Format(Strings.MainForm.BalloonTip_Started, name), ToolTipIcon.Info); } else if (state == DuplicatiRunner.RunnerState.Stopped && (level == Duplicati.Datamodel.ApplicationSettings.NotificationLevel.StartAndStop || level == Duplicati.Datamodel.ApplicationSettings.NotificationLevel.Continous)) { //Show stop balloon m_trayIcon.ShowBalloonTip(BALLOON_SHOW_TIME, Application.ProductName, String.Format(Strings.MainForm.BalloonTip_Stopped, name), ToolTipIcon.Info); } else if (state == DuplicatiRunner.RunnerState.Running && level == Duplicati.Datamodel.ApplicationSettings.NotificationLevel.Continous) { //Show update balloon m_trayIcon.ShowBalloonTip(BALLOON_SHOW_TIME, Application.ProductName, String.Format(Strings.MainForm.BalloonTip_Running, message), ToolTipIcon.Info); } }
public MainForm() { InitializeComponent(); m_trayIcon = new TrayIconProxy(TrayIcon); m_currentIcon = Properties.Resources.TrayNormal; m_currentTooltip = Strings.MainForm.TrayStatusReady; Program.LiveControl.StateChanged += new EventHandler(LiveControl_StateChanged); Program.WorkThread.StartingWork += new EventHandler(WorkThread_StartingWork); Program.WorkThread.CompletedWork += new EventHandler(WorkThread_CompletedWork); Program.SingleInstance.SecondInstanceDetected += new SingleInstance.SecondInstanceDelegate(SingleInstance_SecondInstanceDetected); m_settings = new Duplicati.Datamodel.ApplicationSettings(Program.DataConnection); Program.DataConnection.AfterDataChange += new System.Data.LightDatamodel.DataChangeEventHandler(DataConnection_AfterDataChange); Program.Runner.ProgressEvent += new DuplicatiRunner.ProgressEventDelegate(Runner_DuplicatiProgress); Program.Runner.ResultEvent += new DuplicatiRunner.ResultEventDelegate(Runner_ResultEvent); #if DEBUG this.Text += " (DEBUG)"; #endif }
static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); //If we are on Windows, append the bundled "win-tools" programs to the search path //We add it last, to allow the user to override with other versions if (!Library.Utility.Utility.IsClientLinux) { string wintools = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "win-tools"); Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH") + System.IO.Path.PathSeparator.ToString() + wintools + System.IO.Path.PathSeparator.ToString() + System.IO.Path.Combine(wintools, "gpg") //GPG needs to be in a subfolder for wrapping reasons ); } Library.Utility.UrlUtillity.ErrorHandler = new XervBackup.Library.Utility.UrlUtillity.ErrorHandlerDelegate(DisplayURLOpenError); //If we are on windows we encrypt the database by default //We do not encrypt on Linux as most distros use a SQLite library without encryption support, //Linux users can use an encrypted home folder, or install a SQLite library with encryption support if (!Library.Utility.Utility.IsClientLinux && string.IsNullOrEmpty(Environment.GetEnvironmentVariable(DB_KEY_ENV_NAME))) { //Note that the password here is a default password and public knowledge // //The purpose of this is to prevent casual read of the database, as well // as protect from harddisk string scans, not to protect from determined // attacks. // //If you desire better security, start XervBackup once with the commandline option // --unencrypted-database to decrypt the database. //Then set the environment variable XERVBACKUP_DB_KEY to the desired key, // and run XervBackup again without the --unencrypted-database option // to re-encrypt it with the new key // //If you change the key, please note that you need to supply the same // key when restoring the setup, as the setup being backed up will // be encrypted as well. Environment.SetEnvironmentVariable(DB_KEY_ENV_NAME, "XervBackup_Key_42"); } //Find commandline options here for handling special startup cases Dictionary<string, string> commandlineOptions = CommandLine.CommandLineParser.ExtractOptions(new List<string>(args)); foreach (string s in args) if ( s.Equals("help", StringComparison.InvariantCultureIgnoreCase) || s.Equals("/help", StringComparison.InvariantCultureIgnoreCase) || s.Equals("usage", StringComparison.InvariantCultureIgnoreCase) || s.Equals("/usage", StringComparison.InvariantCultureIgnoreCase)) commandlineOptions["help"] = ""; //If the commandline issues --help, just stop here if (commandlineOptions.ContainsKey("help")) { List<string> lines = new List<string>(); foreach (Library.Interface.ICommandLineArgument arg in SupportedCommands) lines.Add(string.Format(Strings.Program.HelpDisplayFormat, arg.Name, arg.LongDescription)); MessageBox.Show(string.Format(Strings.Program.HelpDisplayDialog, string.Join(Environment.NewLine, lines.ToArray())), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (commandlineOptions.ContainsKey("trayless")) Program.TraylessMode = Library.Utility.Utility.ParseBoolOption(commandlineOptions, "trayless"); #if DEBUG //Log various information in the logfile if (!commandlineOptions.ContainsKey("log-file")) { commandlineOptions["log-file"] = System.IO.Path.Combine(Application.StartupPath, "XervBackup.debug.log"); commandlineOptions["log-level"] = XervBackup.Library.Logging.LogMessageType.Profiling.ToString(); } #endif if (commandlineOptions.ContainsKey("log-level")) foreach (string s in Enum.GetNames(typeof(XervBackup.Library.Logging.LogMessageType))) if (s.Equals(commandlineOptions["log-level"].Trim(), StringComparison.InvariantCultureIgnoreCase)) XervBackup.Library.Logging.Log.LogLevel = (XervBackup.Library.Logging.LogMessageType)Enum.Parse(typeof(XervBackup.Library.Logging.LogMessageType), s); if (commandlineOptions.ContainsKey("log-file")) { if (System.IO.File.Exists(commandlineOptions["log-file"])) System.IO.File.Delete(commandlineOptions["log-file"]); XervBackup.Library.Logging.Log.CurrentLog = new XervBackup.Library.Logging.StreamLog(commandlineOptions["log-file"]); } //Set the %XERVBACKUP_HOME% env variable, if it is not already set if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(DATAFOLDER_ENV_NAME))) { #if DEBUG //debug mode uses a lock file located in the app folder Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)); #else bool portableMode = Library.Utility.Utility.ParseBoolOption(commandlineOptions, "portable-mode"); if (portableMode) { //Portable mode uses a data folder in the application home dir Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "data")); } else { //Normal release mode uses the systems "Application Data" folder Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Application.ProductName)); } #endif } try { try { //This will also create Program.DATAFOLDER if it does not exist SingleInstance = new SingleInstance(Application.ProductName, Program.DATAFOLDER); } catch (Exception ex) { MessageBox.Show(string.Format(Strings.Program.StartupFailure, ex.ToString()), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!SingleInstance.IsFirstInstance) { //Linux shows this output Console.WriteLine(Strings.Program.AnotherInstanceDetected); return; } Version sqliteVersion = new Version((string)SQLiteLoader.SQLiteConnectionType.GetProperty("SQLiteVersion").GetValue(null, null)); if (sqliteVersion < new Version(3, 6, 3)) { //The official Mono SQLite provider is also broken with less than 3.6.3 MessageBox.Show(string.Format(Strings.Program.WrongSQLiteVersion, sqliteVersion, "3.6.3"), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } //Create the connection instance System.Data.IDbConnection con = (System.Data.IDbConnection)Activator.CreateInstance(SQLiteLoader.SQLiteConnectionType); try { #if DEBUG //Default is to not use encryption for debugging Program.UseDatabaseEncryption = commandlineOptions.ContainsKey("unencrypted-database") ? !Library.Utility.Utility.ParseBoolOption(commandlineOptions, "unencrypted-database") : false; #else //Default is to use encryption for release Program.UseDatabaseEncryption = !Library.Utility.Utility.ParseBoolOption(commandlineOptions, "unencrypted-database"); #endif OpenSettingsDatabase(con, Program.DATAFOLDER); } catch (Exception ex) { //Unwrap the reflection exceptions if (ex is System.Reflection.TargetInvocationException && ex.InnerException != null) ex = ex.InnerException; MessageBox.Show(string.Format(Strings.Program.DatabaseOpenError, ex.Message), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } DataConnection = new DataFetcherWithRelations(new SQLiteDataProvider(con)); string displayLanguage = new Datamodel.ApplicationSettings(DataConnection).DisplayLanguage; if (!string.IsNullOrEmpty(displayLanguage) && displayLanguage != Library.Utility.Utility.DefaultCulture.Name) { try { System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(displayLanguage); System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(displayLanguage); } catch (Exception ex) { MessageBox.Show(string.Format(Strings.Program.LanguageSelectionError, ex.Message), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); //This is non-fatal, just keep running with system default language } } try { DataFetcherNested logcon = new DataFetcherNested(DataConnection); Log[] items = logcon.GetObjects<Log>("Subaction LIKE ?", "InProgress"); if (items != null && items.Length > 0) { foreach (Log l in items) l.SubAction = "Primary"; logcon.CommitAllRecursive(); } } catch { //Non-fatal but any interrupted backup will not show } LiveControl = new LiveControls(new ApplicationSettings(DataConnection)); LiveControl.StateChanged += new EventHandler(LiveControl_StateChanged); LiveControl.ThreadPriorityChanged += new EventHandler(LiveControl_ThreadPriorityChanged); LiveControl.ThrottleSpeedChanged += new EventHandler(LiveControl_ThrottleSpeedChanged); Runner = new XervBackupRunner(); WorkThread = new WorkerThread<IDuplicityTask>(new WorkerThread<IDuplicityTask>.ProcessItemDelegate(Runner.ExecuteTask), LiveControl.State == LiveControls.LiveControlState.Paused); Scheduler = new Scheduler(DataConnection, WorkThread, MainLock); DataConnection.AfterDataConnection += new DataConnectionEventHandler(DataConnection_AfterDataConnection); DisplayHelper = new MainForm(); DisplayHelper.InitialArguments = args; Program.IsRunningMainLoop = true; Application.Run(DisplayHelper); Program.IsRunningMainLoop = false; } catch (Exception ex) { //If the helper thread aborts the main thread, it also sets IsRunningMainLoop to false // and in that case we accept the abort call if (ex is System.Threading.ThreadAbortException && !Program.IsRunningMainLoop) System.Threading.Thread.ResetAbort(); else MessageBox.Show(string.Format(Strings.Program.SeriousError, ex.ToString()), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } try { //Find logs that are no longer displayed, and delete them DataFetcherNested con = new DataFetcherNested(DataConnection); foreach (Log x in con.GetObjects<Log>("EndTime < ?", Library.Utility.Timeparser.ParseTimeInterval(new ApplicationSettings(con).RecentBackupDuration, DateTime.Now, true))) { if (x.Blob != null) //Load the blob part if required con.DeleteObject(x.Blob); con.DeleteObject(x); } con.CommitAllRecursive(); } catch { } try { //Compact the database using (System.Data.IDbCommand vaccum_cmd = DataConnection.Provider.Connection.CreateCommand()) { vaccum_cmd.CommandText = "VACUUM;"; vaccum_cmd.ExecuteNonQuery(); } } catch { } if (Scheduler != null) Scheduler.Terminate(true); if (WorkThread != null) WorkThread.Terminate(true); if (SingleInstance != null) SingleInstance.Dispose(); #if DEBUG using(XervBackup.Library.Logging.Log.CurrentLog as XervBackup.Library.Logging.StreamLog) XervBackup.Library.Logging.Log.CurrentLog = null; #endif }
void DataConnection_AfterDataChange(object sender, string propertyname, object oldvalue, object newvalue) { if (sender as Datamodel.ApplicationSetting == null) return; if (this.InvokeRequired) { this.BeginInvoke(new System.Data.LightDatamodel.DataChangeEventHandler(DataConnection_AfterDataChange), sender, propertyname, oldvalue, newvalue); return; } m_settings = new Duplicati.Datamodel.ApplicationSettings(Program.DataConnection); }
private void NormalizeApplicationSettings() { //Make sure we have a startup delay, so a restart won't accidently wipe something bool settingsModified = false; Datamodel.ApplicationSettings appset = new Datamodel.ApplicationSettings(Program.DataConnection); if (string.IsNullOrEmpty(appset.StartupDelayDuration) || XervBackup.Library.Utility.Timeparser.ParseTimeSpan(appset.StartupDelayDuration) < TimeSpan.FromMinutes(5)) { appset.StartupDelayDuration = "5m"; settingsModified = true; } if (!System.IO.Directory.Exists(appset.TempPath)) { appset.TempPath = ""; settingsModified = true; } if (!System.IO.Directory.Exists(appset.SignatureCachePath)) { appset.SignatureCachePath = ""; settingsModified = true; } if (settingsModified) { Program.DataConnection.CommitRecursive(Program.DataConnection.GetObjects<Datamodel.ApplicationSetting>()); } }
/// <summary> /// A monitor for detecting when the system hibernates or resumes /// </summary> /// <param name="sender">Unused sender parameter</param> /// <param name="_e">The event information</param> private void SystemEvents_PowerModeChanged(object sender, object _e) { Microsoft.Win32.PowerModeChangedEventArgs e = _e as Microsoft.Win32.PowerModeChangedEventArgs; if (e == null) return; if (e.Mode == Microsoft.Win32.PowerModes.Suspend) { //If we are running, register as being paused due to suspending if (this.m_state == LiveControlState.Running) { this.Pause(); m_pausedForSuspend = true; m_suspendMinimumPause = new DateTime(0); } else { if (m_waitTimeExpiration.Ticks != 0) { m_pausedForSuspend = true; m_suspendMinimumPause = m_waitTimeExpiration; m_waitTimeExpiration = new DateTime(0); m_waitTimer.Enabled = false; } } } else if (e.Mode == Microsoft.Win32.PowerModes.Resume) { //If we have been been paused due to suspending, we un-pause now if (m_pausedForSuspend) { long delayTicks = (m_suspendMinimumPause - DateTime.Now).Ticks; Datamodel.ApplicationSettings appset = new Datamodel.ApplicationSettings(Program.DataConnection); if (!string.IsNullOrEmpty(appset.StartupDelayDuration) && appset.StartupDelayDuration != "0") delayTicks = Math.Max(delayTicks, Library.Utility.Timeparser.ParseTimeSpan(appset.StartupDelayDuration).Ticks); if (delayTicks > 0) { this.Pause(TimeSpan.FromTicks(delayTicks)); } else { this.Resume(); } } m_pausedForSuspend = false; m_suspendMinimumPause = new DateTime(0); } }
static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); //If we are on Windows, append the bundled "win-tools" programs to the search path //We add it last, to allow the user to override with other versions if (!Library.Utility.Utility.IsClientLinux) { string wintools = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "win-tools"); Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH") + System.IO.Path.PathSeparator.ToString() + wintools + System.IO.Path.PathSeparator.ToString() + System.IO.Path.Combine(wintools, "gpg") //GPG needs to be in a subfolder for wrapping reasons ); } Library.Utility.UrlUtillity.ErrorHandler = new Duplicati.Library.Utility.UrlUtillity.ErrorHandlerDelegate(DisplayURLOpenError); //If we are on windows we encrypt the database by default //We do not encrypt on Linux as most distros use a SQLite library without encryption support, //Linux users can use an encrypted home folder, or install a SQLite library with encryption support if (!Library.Utility.Utility.IsClientLinux && string.IsNullOrEmpty(Environment.GetEnvironmentVariable(DB_KEY_ENV_NAME))) { //Note that the password here is a default password and public knowledge // //The purpose of this is to prevent casual read of the database, as well // as protect from harddisk string scans, not to protect from determined // attacks. // //If you desire better security, start Duplicati once with the commandline option // --unencrypted-database to decrypt the database. //Then set the environment variable DUPLICATI_DB_KEY to the desired key, // and run Duplicati again without the --unencrypted-database option // to re-encrypt it with the new key // //If you change the key, please note that you need to supply the same // key when restoring the setup, as the setup being backed up will // be encrypted as well. Environment.SetEnvironmentVariable(DB_KEY_ENV_NAME, "Duplicati_Key_42"); } //Find commandline options here for handling special startup cases Dictionary <string, string> commandlineOptions = CommandLine.CommandLineParser.ExtractOptions(new List <string>(args)); foreach (string s in args) { if ( s.Equals("help", StringComparison.InvariantCultureIgnoreCase) || s.Equals("/help", StringComparison.InvariantCultureIgnoreCase) || s.Equals("usage", StringComparison.InvariantCultureIgnoreCase) || s.Equals("/usage", StringComparison.InvariantCultureIgnoreCase)) { commandlineOptions["help"] = ""; } } //If the commandline issues --help, just stop here if (commandlineOptions.ContainsKey("help")) { List <string> lines = new List <string>(); foreach (Library.Interface.ICommandLineArgument arg in SupportedCommands) { lines.Add(string.Format(Strings.Program.HelpDisplayFormat, arg.Name, arg.LongDescription)); } MessageBox.Show(string.Format(Strings.Program.HelpDisplayDialog, string.Join(Environment.NewLine, lines.ToArray())), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (commandlineOptions.ContainsKey("trayless")) { Program.TraylessMode = Library.Utility.Utility.ParseBoolOption(commandlineOptions, "trayless"); } #if DEBUG //Log various information in the logfile if (!commandlineOptions.ContainsKey("log-file")) { commandlineOptions["log-file"] = System.IO.Path.Combine(Application.StartupPath, "Duplicati.debug.log"); commandlineOptions["log-level"] = Duplicati.Library.Logging.LogMessageType.Profiling.ToString(); } #endif if (commandlineOptions.ContainsKey("log-level")) { foreach (string s in Enum.GetNames(typeof(Duplicati.Library.Logging.LogMessageType))) { if (s.Equals(commandlineOptions["log-level"].Trim(), StringComparison.InvariantCultureIgnoreCase)) { Duplicati.Library.Logging.Log.LogLevel = (Duplicati.Library.Logging.LogMessageType)Enum.Parse(typeof(Duplicati.Library.Logging.LogMessageType), s); } } } if (commandlineOptions.ContainsKey("log-file")) { if (System.IO.File.Exists(commandlineOptions["log-file"])) { System.IO.File.Delete(commandlineOptions["log-file"]); } Duplicati.Library.Logging.Log.CurrentLog = new Duplicati.Library.Logging.StreamLog(commandlineOptions["log-file"]); } //Set the %DUPLICATI_HOME% env variable, if it is not already set if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(DATAFOLDER_ENV_NAME))) { #if DEBUG //debug mode uses a lock file located in the app folder Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)); #else bool portableMode = Library.Utility.Utility.ParseBoolOption(commandlineOptions, "portable-mode"); if (portableMode) { //Portable mode uses a data folder in the application home dir Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "data")); } else { //Normal release mode uses the systems "Application Data" folder Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Application.ProductName)); } #endif } try { try { //This will also create Program.DATAFOLDER if it does not exist SingleInstance = new SingleInstance(Application.ProductName, Program.DATAFOLDER); } catch (Exception ex) { MessageBox.Show(string.Format(Strings.Program.StartupFailure, ex.ToString()), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!SingleInstance.IsFirstInstance) { //Linux shows this output Console.WriteLine(Strings.Program.AnotherInstanceDetected); return; } Version sqliteVersion = new Version((string)SQLiteLoader.SQLiteConnectionType.GetProperty("SQLiteVersion").GetValue(null, null)); if (sqliteVersion < new Version(3, 6, 3)) { //The official Mono SQLite provider is also broken with less than 3.6.3 MessageBox.Show(string.Format(Strings.Program.WrongSQLiteVersion, sqliteVersion, "3.6.3"), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } //Create the connection instance System.Data.IDbConnection con = (System.Data.IDbConnection)Activator.CreateInstance(SQLiteLoader.SQLiteConnectionType); try { #if DEBUG //Default is to not use encryption for debugging Program.UseDatabaseEncryption = commandlineOptions.ContainsKey("unencrypted-database") ? !Library.Utility.Utility.ParseBoolOption(commandlineOptions, "unencrypted-database") : false; #else //Default is to use encryption for release Program.UseDatabaseEncryption = !Library.Utility.Utility.ParseBoolOption(commandlineOptions, "unencrypted-database"); #endif OpenSettingsDatabase(con, Program.DATAFOLDER); } catch (Exception ex) { //Unwrap the reflection exceptions if (ex is System.Reflection.TargetInvocationException && ex.InnerException != null) { ex = ex.InnerException; } MessageBox.Show(string.Format(Strings.Program.DatabaseOpenError, ex.Message), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } DataConnection = new DataFetcherWithRelations(new SQLiteDataProvider(con)); string displayLanguage = new Datamodel.ApplicationSettings(DataConnection).DisplayLanguage; if (!string.IsNullOrEmpty(displayLanguage) && displayLanguage != Library.Utility.Utility.DefaultCulture.Name) { try { System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(displayLanguage); System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(displayLanguage); } catch (Exception ex) { MessageBox.Show(string.Format(Strings.Program.LanguageSelectionError, ex.Message), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); //This is non-fatal, just keep running with system default language } } try { DataFetcherNested logcon = new DataFetcherNested(DataConnection); Log[] items = logcon.GetObjects <Log>("Subaction LIKE ?", "InProgress"); if (items != null && items.Length > 0) { foreach (Log l in items) { l.SubAction = "Primary"; } logcon.CommitAllRecursive(); } } catch { //Non-fatal but any interrupted backup will not show } LiveControl = new LiveControls(new ApplicationSettings(DataConnection)); LiveControl.StateChanged += new EventHandler(LiveControl_StateChanged); LiveControl.ThreadPriorityChanged += new EventHandler(LiveControl_ThreadPriorityChanged); LiveControl.ThrottleSpeedChanged += new EventHandler(LiveControl_ThrottleSpeedChanged); Runner = new DuplicatiRunner(); WorkThread = new WorkerThread <IDuplicityTask>(new WorkerThread <IDuplicityTask> .ProcessItemDelegate(Runner.ExecuteTask), LiveControl.State == LiveControls.LiveControlState.Paused); Scheduler = new Scheduler(DataConnection, WorkThread, MainLock); DataConnection.AfterDataConnection += new DataConnectionEventHandler(DataConnection_AfterDataConnection); DisplayHelper = new MainForm(); DisplayHelper.InitialArguments = args; Program.IsRunningMainLoop = true; Application.Run(DisplayHelper); Program.IsRunningMainLoop = false; } catch (Exception ex) { //If the helper thread aborts the main thread, it also sets IsRunningMainLoop to false // and in that case we accept the abort call if (ex is System.Threading.ThreadAbortException && !Program.IsRunningMainLoop) { System.Threading.Thread.ResetAbort(); } else { MessageBox.Show(string.Format(Strings.Program.SeriousError, ex.ToString()), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } try { //Find logs that are no longer displayed, and delete them DataFetcherNested con = new DataFetcherNested(DataConnection); foreach (Log x in con.GetObjects <Log>("EndTime < ?", Library.Utility.Timeparser.ParseTimeInterval(new ApplicationSettings(con).RecentBackupDuration, DateTime.Now, true))) { if (x.Blob != null) //Load the blob part if required { con.DeleteObject(x.Blob); } con.DeleteObject(x); } con.CommitAllRecursive(); } catch { } try { //Compact the database using (System.Data.IDbCommand vaccum_cmd = DataConnection.Provider.Connection.CreateCommand()) { vaccum_cmd.CommandText = "VACUUM;"; vaccum_cmd.ExecuteNonQuery(); } } catch { } if (Scheduler != null) { Scheduler.Terminate(true); } if (WorkThread != null) { WorkThread.Terminate(true); } if (SingleInstance != null) { SingleInstance.Dispose(); } #if DEBUG using (Duplicati.Library.Logging.Log.CurrentLog as Duplicati.Library.Logging.StreamLog) Duplicati.Library.Logging.Log.CurrentLog = null; #endif }