#region Disclaimer / License // Copyright (C) 2010, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Windows.Forms; using System.Data.LightDatamodel; using System.Drawing; using Duplicati.Datamodel; namespace Duplicati.GUI { static class Program { /// /// This is the only access to the database /// public static IDataFetcherWithRelations DataConnection; /// /// This is the lock to be used before manipulating the shared resources /// public static object MainLock = new object(); /// /// This is the scheduling thread /// public static Scheduler Scheduler; /// /// This is the working thread /// public static WorkerThread WorkThread; /// /// The path to the file that contains the current database /// public static string DatabasePath; /// /// The actual runner, do not call directly. Only used for events. /// public static DuplicatiRunner Runner; /// /// The controller interface for pause/resume and throttle options /// public static LiveControls LiveControl; /// /// The main form that contains the tray icon /// public static MainForm DisplayHelper; /// /// The single instance keeper /// public static SingleInstance SingleInstance; /// /// The main entry point for the application. /// [STAThread] static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); try { try { #if DEBUG //debug mode uses a lock file located in the app folder SingleInstance = new SingleInstance(Application.ProductName, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)); #else //release mode uses the systems "Application Data" folder SingleInstance = new SingleInstance(Application.ProductName); #endif } 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; } #if DEBUG DatabasePath = System.IO.Path.Combine(Application.StartupPath, "Duplicati.sqlite"); #else DatabasePath = System.IO.Path.Combine(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Application.ProductName), "Duplicati.sqlite"); #endif 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; } System.Data.IDbConnection con = (System.Data.IDbConnection)Activator.CreateInstance(SQLiteLoader.SQLiteConnectionType); try { if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(DatabasePath))) System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(DatabasePath)); //This also opens the db for us :) DatabaseUpgrader.UpgradeDatebase(con, DatabasePath); } catch (Exception ex) { MessageBox.Show(string.Format(Strings.Program.DatabaseOpenError, ex.Message), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } DataConnection = new DataFetcherWithRelations(new SQLiteDataProvider(con)); if (!string.IsNullOrEmpty(new Datamodel.ApplicationSettings(DataConnection).DisplayLanguage)) try { System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(new Datamodel.ApplicationSettings(DataConnection).DisplayLanguage); System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(new Datamodel.ApplicationSettings(DataConnection).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 } 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(new WorkerThread.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; Application.Run(DisplayHelper); } catch (Exception ex) { MessageBox.Show(string.Format(Strings.Program.SeriousError, ex.ToString()), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } if (Runner != null && WorkThread != null && WorkThread.Active) { Runner.Pause(); if (!Runner.IsStopRequested) Runner.Stop(); //Wait 10 seconds to see if the stop works for (int i = 0; i < 10; i++) { System.Threading.Thread.Sleep(1000); if (!WorkThread.Active) break; } while (WorkThread.Active) { if (MessageBox.Show(Strings.Program.TerminateForExitQuestion, Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { Runner.Terminate(); System.Threading.Thread.Sleep(500); break; } //Wait 18 * 10 seconds = 3 minutes before asking again for (int i = 0; i < 18; i++) { System.Threading.Thread.Sleep(1000 * 10); if (!WorkThread.Active) break; } } } if (Scheduler != null) Scheduler.Terminate(true); if (WorkThread != null) WorkThread.Terminate(true); if (SingleInstance != null) SingleInstance.Dispose(); } /// /// Handles a change in the LiveControl and updates the Runner /// /// /// private static void LiveControl_ThreadPriorityChanged(object sender, EventArgs e) { if (LiveControl.ThreadPriority == null) Runner.UnsetThreadPriority(); else Runner.SetThreadPriority(LiveControl.ThreadPriority.Value); } /// /// Handles a change in the LiveControl and updates the Runner /// /// /// private static void LiveControl_ThrottleSpeedChanged(object sender, EventArgs e) { if (LiveControl.DownloadLimit == null) Runner.SetDownloadLimit(null); else Runner.SetDownloadLimit(LiveControl.DownloadLimit.Value.ToString() + "b"); if (LiveControl.UploadLimit == null) Runner.SetUploadLimit(null); else Runner.SetUploadLimit(LiveControl.UploadLimit.Value.ToString() + "b"); } /// /// This event handler updates the trayicon menu with the current state of the runner. /// static void LiveControl_StateChanged(object sender, EventArgs e) { switch (LiveControl.State) { case LiveControls.LiveControlState.Paused: WorkThread.Pause(); Runner.Pause(); break; case LiveControls.LiveControlState.Running: WorkThread.Resume(); Runner.Resume(); break; } } private static void DataConnection_AfterDataConnection(object sender, DataActions action) { if (action == DataActions.Insert || action == DataActions.Update) Scheduler.Reschedule(); } /// /// Returns a localized name for a task type /// /// /// public static string LocalizeTaskType(DuplicityTaskType type) { switch (type) { case DuplicityTaskType.FullBackup: return Strings.TaskType.FullBackup; case DuplicityTaskType.IncrementalBackup: return Strings.TaskType.IncrementalBackup; case DuplicityTaskType.ListActualFiles: return Strings.TaskType.ListActualFiles; case DuplicityTaskType.ListBackupEntries: return Strings.TaskType.ListBackupEntries; case DuplicityTaskType.ListBackups: return Strings.TaskType.ListBackups; case DuplicityTaskType.ListFiles: return Strings.TaskType.ListFiles; case DuplicityTaskType.RemoveAllButNFull: return Strings.TaskType.RemoveAllButNFull; case DuplicityTaskType.RemoveOlderThan: return Strings.TaskType.RemoveOlderThan; case DuplicityTaskType.Restore: return Strings.TaskType.Restore; case DuplicityTaskType.RestoreSetup: return Strings.TaskType.RestoreSetup; default: return type.ToString(); } } } }