621 lines
29 KiB
C#
621 lines
29 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Duplicati.Library.Localization.Short;
|
|
|
|
namespace Duplicati.Server
|
|
{
|
|
public class Program
|
|
{
|
|
/// <summary>
|
|
/// The path to the directory that contains the main executable
|
|
/// </summary>
|
|
public static readonly string StartupPath = Duplicati.Library.AutoUpdater.UpdaterManager.InstalledBaseDir;
|
|
|
|
/// <summary>
|
|
/// The name of the environment variable that holds the path to the data folder used by Duplicati
|
|
/// </summary>
|
|
public static readonly string DATAFOLDER_ENV_NAME = Duplicati.Library.AutoUpdater.AutoUpdateSettings.AppName.ToUpper() + "_HOME";
|
|
|
|
/// <summary>
|
|
/// The environment variable that holdes the database key used to encrypt the SQLite database
|
|
/// </summary>
|
|
public static readonly string DB_KEY_ENV_NAME = Duplicati.Library.AutoUpdater.AutoUpdateSettings.AppName.ToUpper() + "_DB_KEY";
|
|
|
|
/// <summary>
|
|
/// Gets the folder where Duplicati data is stored
|
|
/// </summary>
|
|
public static string DATAFOLDER { get { return Library.Utility.Utility.AppendDirSeparator(Environment.ExpandEnvironmentVariables("%" + DATAFOLDER_ENV_NAME + "%").TrimStart('"').TrimEnd('"')); } }
|
|
|
|
/// <summary>
|
|
/// The single instance
|
|
/// </summary>
|
|
public static SingleInstance Instance = null;
|
|
|
|
/// <summary>
|
|
/// A flag indicating if database encryption is in use
|
|
/// </summary>
|
|
public static bool UseDatabaseEncryption;
|
|
|
|
/// <summary>
|
|
/// This is the only access to the database
|
|
/// </summary>
|
|
public static Database.Connection DataConnection;
|
|
|
|
/// <summary>
|
|
/// This is the lock to be used before manipulating the shared resources
|
|
/// </summary>
|
|
public static object MainLock = new object();
|
|
|
|
/// <summary>
|
|
/// This is the scheduling thread
|
|
/// </summary>
|
|
public static Scheduler Scheduler;
|
|
|
|
/// <summary>
|
|
/// This is the working thread
|
|
/// </summary>
|
|
public static Duplicati.Library.Utility.WorkerThread<Runner.IRunnerData> WorkThread;
|
|
|
|
/// <summary>
|
|
/// The thread running the ping-pong handler
|
|
/// </summary>
|
|
public static System.Threading.Thread PingPongThread;
|
|
|
|
/// <summary>
|
|
/// The path to the file that contains the current database
|
|
/// </summary>
|
|
public static string DatabasePath;
|
|
|
|
/// <summary>
|
|
/// The controller interface for pause/resume and throttle options
|
|
/// </summary>
|
|
public static LiveControls LiveControl;
|
|
|
|
/// <summary>
|
|
/// The application exit event
|
|
/// </summary>
|
|
public static System.Threading.ManualResetEvent ApplicationExitEvent;
|
|
|
|
/// <summary>
|
|
/// The webserver instance
|
|
/// </summary>
|
|
public static WebServer.Server WebServer;
|
|
|
|
/// <summary>
|
|
/// The update poll thread.
|
|
/// </summary>
|
|
public static UpdatePollThread UpdatePoller;
|
|
|
|
/// <summary>
|
|
/// An event that is set once the server is ready to respond to requests
|
|
/// </summary>
|
|
public static System.Threading.ManualResetEvent ServerStartedEvent = new System.Threading.ManualResetEvent(false);
|
|
|
|
/// <summary>
|
|
/// The status event signaler, used to controll long polling of status updates
|
|
/// </summary>
|
|
public static EventPollNotify StatusEventNotifyer = new EventPollNotify();
|
|
|
|
/// <summary>
|
|
/// A delegate method for creating a copy of the current progress state
|
|
/// </summary>
|
|
public static Func<Duplicati.Server.Serialization.Interface.IProgressEventData> GenerateProgressState;
|
|
|
|
/// <summary>
|
|
/// An event ID that increases whenever the database is updated
|
|
/// </summary>
|
|
public static long LastDataUpdateID = 0;
|
|
|
|
/// <summary>
|
|
/// The log redirect handler
|
|
/// </summary>
|
|
public static LogWriteHandler LogHandler = new LogWriteHandler();
|
|
|
|
public static int ServerPort
|
|
{
|
|
get
|
|
{
|
|
return WebServer.Port;
|
|
}
|
|
}
|
|
|
|
public static bool IsFirstRun
|
|
{
|
|
get { return DataConnection.ApplicationSettings.IsFirstRun; }
|
|
set { DataConnection.ApplicationSettings.IsFirstRun = value; }
|
|
}
|
|
|
|
public static bool ServerPortChanged
|
|
{
|
|
get { return DataConnection.ApplicationSettings.ServerPortChanged; }
|
|
set { DataConnection.ApplicationSettings.ServerPortChanged = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The main entry point for the application.
|
|
/// </summary>
|
|
[STAThread]
|
|
public static int Main(string[] args)
|
|
{
|
|
return Duplicati.Library.AutoUpdater.UpdaterManager.RunFromMostRecent(typeof(Program).GetMethod("RealMain"), args, Duplicati.Library.AutoUpdater.AutoUpdateStrategy.Never);
|
|
}
|
|
|
|
public static void RealMain(string[] args)
|
|
{
|
|
//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)
|
|
{
|
|
Environment.SetEnvironmentVariable("PATH",
|
|
Environment.GetEnvironmentVariable("PATH") +
|
|
System.IO.Path.PathSeparator.ToString() +
|
|
System.IO.Path.Combine(
|
|
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
|
|
"win-tools")
|
|
);
|
|
}
|
|
|
|
//If this executable is invoked directly, write to console, otherwise throw exceptions
|
|
bool writeConsole = System.Reflection.Assembly.GetEntryAssembly() == System.Reflection.Assembly.GetExecutingAssembly();
|
|
|
|
//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, Library.AutoUpdater.AutoUpdateSettings.AppName + "_Key_42");
|
|
}
|
|
|
|
|
|
//Find commandline options here for handling special startup cases
|
|
Dictionary<string, string> commandlineOptions = Duplicati.Library.Utility.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"))
|
|
{
|
|
if (writeConsole)
|
|
{
|
|
Console.WriteLine(Strings.Program.HelpDisplayDialog);
|
|
|
|
foreach(Library.Interface.ICommandLineArgument arg in SupportedCommands)
|
|
Console.WriteLine(Strings.Program.HelpDisplayFormat, arg.Name, arg.LongDescription);
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("Server invoked with --help");
|
|
}
|
|
|
|
}
|
|
|
|
#if DEBUG
|
|
//Log various information in the logfile
|
|
if (!commandlineOptions.ContainsKey("log-file"))
|
|
{
|
|
commandlineOptions["log-file"] = System.IO.Path.Combine(StartupPath, "Duplicati.debug.log");
|
|
commandlineOptions["log-level"] = Duplicati.Library.Logging.LogMessageType.Profiling.ToString();
|
|
}
|
|
#endif
|
|
|
|
//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, StartupPath);
|
|
#else
|
|
bool portableMode = commandlineOptions.ContainsKey("portable-mode") ? Library.Utility.Utility.ParseBool(commandlineOptions["portable-mode"], true) : false;
|
|
|
|
if (portableMode)
|
|
{
|
|
//Portable mode uses a data folder in the application home dir
|
|
Environment.SetEnvironmentVariable(DATAFOLDER_ENV_NAME, System.IO.Path.Combine(StartupPath, "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), Library.AutoUpdater.AutoUpdateSettings.AppName));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
try
|
|
{
|
|
try
|
|
{
|
|
//This will also create Program.DATAFOLDER if it does not exist
|
|
Instance = new SingleInstance(Duplicati.Library.AutoUpdater.AutoUpdateSettings.AppName, Program.DATAFOLDER);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (writeConsole)
|
|
{
|
|
Console.WriteLine(Strings.Program.StartupFailure, ex.ToString());
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception(Strings.Program.StartupFailure, ex);
|
|
}
|
|
}
|
|
|
|
if (!Instance.IsFirstInstance)
|
|
{
|
|
if (writeConsole)
|
|
{
|
|
Console.WriteLine(Strings.Program.AnotherInstanceDetected);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
throw new SingleInstance.MultipleInstanceException(Strings.Program.AnotherInstanceDetected);
|
|
}
|
|
}
|
|
|
|
// Setup the log redirect
|
|
Duplicati.Library.Logging.Log.CurrentLog = Program.LogHandler;
|
|
|
|
if (commandlineOptions.ContainsKey("log-file"))
|
|
{
|
|
if (System.IO.File.Exists(commandlineOptions["log-file"]))
|
|
System.IO.File.Delete(commandlineOptions["log-file"]);
|
|
|
|
var loglevel = Duplicati.Library.Logging.LogMessageType.Error;
|
|
|
|
if (commandlineOptions.ContainsKey("log-level"))
|
|
Enum.TryParse<Duplicati.Library.Logging.LogMessageType>(commandlineOptions["log-level"], true, out loglevel);
|
|
|
|
Program.LogHandler.SetServerFile(commandlineOptions["log-file"], loglevel);
|
|
}
|
|
|
|
Version sqliteVersion = new Version((string)Duplicati.Library.SQLiteHelper.SQLiteLoader.SQLiteConnectionType.GetProperty("SQLiteVersion").GetValue(null, null));
|
|
if (sqliteVersion < new Version(3, 6, 3))
|
|
{
|
|
if (writeConsole)
|
|
{
|
|
//The official Mono SQLite provider is also broken with less than 3.6.3
|
|
Console.WriteLine(Strings.Program.WrongSQLiteVersion, sqliteVersion, "3.6.3");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception(string.Format(Strings.Program.WrongSQLiteVersion, sqliteVersion, "3.6.3"));
|
|
}
|
|
}
|
|
|
|
//Create the connection instance
|
|
System.Data.IDbConnection con = (System.Data.IDbConnection)Activator.CreateInstance(Duplicati.Library.SQLiteHelper.SQLiteLoader.SQLiteConnectionType);
|
|
|
|
try
|
|
{
|
|
DatabasePath = System.IO.Path.Combine(Program.DATAFOLDER, "Duplicati-server.sqlite");
|
|
if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(DatabasePath)))
|
|
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(DatabasePath));
|
|
|
|
#if DEBUG
|
|
//Default is to not use encryption for debugging
|
|
Program.UseDatabaseEncryption = commandlineOptions.ContainsKey("unencrypted-database") ? !Library.Utility.Utility.ParseBool(commandlineOptions["unencrypted-database"], true) : false;
|
|
#else
|
|
Program.UseDatabaseEncryption = commandlineOptions.ContainsKey("unencrypted-database") ? !Library.Utility.Utility.ParseBool(commandlineOptions["unencrypted-database"], true) : true;
|
|
#endif
|
|
con.ConnectionString = "Data Source=" + DatabasePath;
|
|
|
|
//Attempt to open the database, handling any encryption present
|
|
OpenDatabase(con);
|
|
|
|
Duplicati.Library.SQLiteHelper.DatabaseUpgrader.UpgradeDatabase(con, DatabasePath, typeof(Duplicati.Server.Database.Connection));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//Unwrap the reflection exceptions
|
|
if (ex is System.Reflection.TargetInvocationException && ex.InnerException != null)
|
|
ex = ex.InnerException;
|
|
|
|
if (writeConsole)
|
|
{
|
|
Console.WriteLine(Strings.Program.DatabaseOpenError, ex.Message);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception(string.Format(Strings.Program.DatabaseOpenError, ex.Message), ex);
|
|
}
|
|
}
|
|
|
|
DataConnection = new Duplicati.Server.Database.Connection(con);
|
|
|
|
if (commandlineOptions.ContainsKey("webservice-password"))
|
|
Program.DataConnection.ApplicationSettings.SetWebserverPassword(commandlineOptions["webservice-password"]);
|
|
|
|
ApplicationExitEvent = new System.Threading.ManualResetEvent(false);
|
|
|
|
Duplicati.Library.AutoUpdater.UpdaterManager.OnError += (Exception obj) =>
|
|
{
|
|
Program.DataConnection.LogError(null, "Error in updater", obj);
|
|
};
|
|
|
|
UpdatePoller = new UpdatePollThread();
|
|
|
|
LiveControl = new LiveControls(DataConnection.ApplicationSettings);
|
|
LiveControl.StateChanged += new EventHandler(LiveControl_StateChanged);
|
|
LiveControl.ThreadPriorityChanged += new EventHandler(LiveControl_ThreadPriorityChanged);
|
|
LiveControl.ThrottleSpeedChanged += new EventHandler(LiveControl_ThrottleSpeedChanged);
|
|
|
|
Program.WorkThread = new Duplicati.Library.Utility.WorkerThread<Runner.IRunnerData>((x) =>
|
|
{
|
|
Runner.Run(x, true);
|
|
}, LiveControl.State == LiveControls.LiveControlState.Paused);
|
|
Program.Scheduler = new Scheduler(WorkThread);
|
|
|
|
Program.WorkThread.StartingWork += new EventHandler(SignalNewEvent);
|
|
Program.WorkThread.CompletedWork += new EventHandler(SignalNewEvent);
|
|
Program.WorkThread.WorkQueueChanged += new EventHandler(SignalNewEvent);
|
|
Program.Scheduler.NewSchedule += new EventHandler(SignalNewEvent);
|
|
|
|
Program.WebServer = new WebServer.Server(commandlineOptions);
|
|
|
|
if (Program.WebServer.Port != DataConnection.ApplicationSettings.LastWebserverPort)
|
|
ServerPortChanged = true;
|
|
DataConnection.ApplicationSettings.LastWebserverPort = Program.WebServer.Port;
|
|
|
|
if (Library.Utility.Utility.ParseBoolOption(commandlineOptions, "ping-pong-keepalive"))
|
|
{
|
|
Program.PingPongThread = new System.Threading.Thread(PingPongMethod);
|
|
Program.PingPongThread.IsBackground = true;
|
|
Program.PingPongThread.Start();
|
|
}
|
|
|
|
ServerStartedEvent.Set();
|
|
ApplicationExitEvent.WaitOne();
|
|
}
|
|
catch (SingleInstance.MultipleInstanceException mex)
|
|
{
|
|
System.Diagnostics.Trace.WriteLine(string.Format(Strings.Program.SeriousError, mex.ToString()));
|
|
if (writeConsole)
|
|
Console.WriteLine(Strings.Program.SeriousError, mex.ToString());
|
|
else
|
|
throw mex;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Trace.WriteLine(string.Format(Strings.Program.SeriousError, ex.ToString()));
|
|
if (writeConsole)
|
|
Console.WriteLine(Strings.Program.SeriousError, ex.ToString());
|
|
else
|
|
throw new Exception(string.Format(Strings.Program.SeriousError, ex.ToString()), ex);
|
|
}
|
|
finally
|
|
{
|
|
StatusEventNotifyer.SignalNewEvent();
|
|
|
|
if (UpdatePoller != null)
|
|
UpdatePoller.Terminate();
|
|
if (Scheduler != null)
|
|
Scheduler.Terminate(true);
|
|
if (WorkThread != null)
|
|
WorkThread.Terminate(true);
|
|
if (Instance != null)
|
|
Instance.Dispose();
|
|
|
|
if (PingPongThread != null)
|
|
try { PingPongThread.Abort(); }
|
|
catch { }
|
|
|
|
if (LogHandler != null)
|
|
LogHandler.Dispose();
|
|
|
|
}
|
|
}
|
|
|
|
private static void SignalNewEvent(object sender, EventArgs e)
|
|
{
|
|
StatusEventNotifyer.SignalNewEvent();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Handles a change in the LiveControl and updates the Runner
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private static void LiveControl_ThreadPriorityChanged(object sender, EventArgs e)
|
|
{
|
|
StatusEventNotifyer.SignalNewEvent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles a change in the LiveControl and updates the Runner
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private static void LiveControl_ThrottleSpeedChanged(object sender, EventArgs e)
|
|
{
|
|
StatusEventNotifyer.SignalNewEvent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This event handler updates the trayicon menu with the current state of the runner.
|
|
/// </summary>
|
|
static void LiveControl_StateChanged(object sender, EventArgs e)
|
|
{
|
|
switch (LiveControl.State)
|
|
{
|
|
case LiveControls.LiveControlState.Paused:
|
|
{
|
|
WorkThread.Pause();
|
|
var t = WorkThread.CurrentTask;
|
|
if (t != null)
|
|
t.Pause();
|
|
break;
|
|
}
|
|
case LiveControls.LiveControlState.Running:
|
|
{
|
|
WorkThread.Resume();
|
|
var t = WorkThread.CurrentTask;
|
|
if (t != null)
|
|
t.Resume();
|
|
break;
|
|
}
|
|
}
|
|
|
|
StatusEventNotifyer.SignalNewEvent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a localized name for a task type
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <returns></returns>
|
|
public static string LocalizeTaskType(Duplicati.Server.Serialization.DuplicatiOperation type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case Duplicati.Server.Serialization.DuplicatiOperation.Backup:
|
|
return Strings.TaskType.FullBackup;
|
|
case Duplicati.Server.Serialization.DuplicatiOperation.List:
|
|
return Strings.TaskType.IncrementalBackup;
|
|
case Duplicati.Server.Serialization.DuplicatiOperation.Remove:
|
|
return Strings.TaskType.ListActualFiles;
|
|
case Duplicati.Server.Serialization.DuplicatiOperation.Verify:
|
|
return Strings.TaskType.ListBackupEntries;
|
|
case Duplicati.Server.Serialization.DuplicatiOperation.Restore:
|
|
return Strings.TaskType.ListBackups;
|
|
default:
|
|
return type.ToString();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method with logic to handle opening a database in possibly encrypted format
|
|
/// </summary>
|
|
/// <param name="con">The SQLite connection object</param>
|
|
internal static void OpenDatabase(System.Data.IDbConnection con)
|
|
{
|
|
bool noEncryption = !Program.UseDatabaseEncryption;
|
|
string password = Environment.GetEnvironmentVariable(DB_KEY_ENV_NAME);
|
|
|
|
System.Reflection.MethodInfo setPwdMethod = con.GetType().GetMethod("SetPassword", new Type[] { typeof(string) });
|
|
string attemptedPassword;
|
|
|
|
if (noEncryption || string.IsNullOrEmpty(password))
|
|
attemptedPassword = null; //No encryption specified, attempt to open without
|
|
else
|
|
attemptedPassword = password; //Encryption specified, attempt to open with
|
|
|
|
if (setPwdMethod != null)
|
|
setPwdMethod.Invoke(con, new object[] { attemptedPassword });
|
|
|
|
try
|
|
{
|
|
//Attempt to open in preferred state
|
|
con.Open();
|
|
|
|
// Do a dummy query to make sure we have a working db
|
|
using (var cmd = con.CreateCommand())
|
|
{
|
|
cmd.CommandText = "SELECT COUNT(*) FROM SQLITE_MASTER";
|
|
cmd.ExecuteScalar();
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
try
|
|
{
|
|
//We can't try anything else without a password
|
|
if (string.IsNullOrEmpty(password))
|
|
throw;
|
|
|
|
//Open failed, now try the reverse
|
|
if (attemptedPassword == null)
|
|
attemptedPassword = password;
|
|
else
|
|
attemptedPassword = null;
|
|
|
|
con.Close();
|
|
setPwdMethod.Invoke(con, new object[] { attemptedPassword });
|
|
con.Open();
|
|
|
|
// Do a dummy query to make sure we have a working db
|
|
using (var cmd = con.CreateCommand())
|
|
{
|
|
cmd.CommandText = "SELECT COUNT(*) FROM SQLITE_MASTER";
|
|
cmd.ExecuteScalar();
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
try { con.Close(); }
|
|
catch { }
|
|
}
|
|
|
|
//If the db is not open now, it won't open
|
|
if (con.State != System.Data.ConnectionState.Open)
|
|
throw; //Report original error
|
|
|
|
//The open method succeeded with the non-default method, now change the password
|
|
System.Reflection.MethodInfo changePwdMethod = con.GetType().GetMethod("ChangePassword", new Type[] { typeof(string) });
|
|
changePwdMethod.Invoke(con, new object[] { noEncryption ? null : password });
|
|
}
|
|
}
|
|
|
|
private static void PingPongMethod()
|
|
{
|
|
var rd = new System.IO.StreamReader(Console.OpenStandardInput());
|
|
var wr = new System.IO.StreamWriter(Console.OpenStandardOutput());
|
|
while (rd.ReadLine() != null)
|
|
{
|
|
wr.WriteLine("pong");
|
|
wr.Flush();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of all supported commandline options
|
|
/// </summary>
|
|
public static Library.Interface.ICommandLineArgument[] SupportedCommands
|
|
{
|
|
get
|
|
{
|
|
return new Duplicati.Library.Interface.ICommandLineArgument[] {
|
|
new Duplicati.Library.Interface.CommandLineArgument("help", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.HelpCommandDescription, Strings.Program.HelpCommandDescription),
|
|
new Duplicati.Library.Interface.CommandLineArgument("unencrypted-database", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.UnencrypteddatabaseCommandDescription, Strings.Program.UnencrypteddatabaseCommandDescription),
|
|
new Duplicati.Library.Interface.CommandLineArgument("portable-mode", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.PortablemodeCommandDescription, Strings.Program.PortablemodeCommandDescription),
|
|
new Duplicati.Library.Interface.CommandLineArgument("log-file", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Path, Strings.Program.LogfileCommandDescription, Strings.Program.LogfileCommandDescription),
|
|
new Duplicati.Library.Interface.CommandLineArgument("log-level", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Enumeration, Strings.Program.LoglevelCommandDescription, Strings.Program.LoglevelCommandDescription, "Warning", null, Enum.GetNames(typeof(Duplicati.Library.Logging.LogMessageType))),
|
|
new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_WEBROOT, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Path, Strings.Program.WebserverWebrootDescription, Strings.Program.WebserverWebrootDescription, Duplicati.Server.WebServer.Server.DEFAULT_OPTION_WEBROOT),
|
|
new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_PORT, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.Program.WebserverPortDescription, Strings.Program.WebserverPortDescription, Duplicati.Server.WebServer.Server.DEFAULT_OPTION_PORT.ToString()),
|
|
new Duplicati.Library.Interface.CommandLineArgument(Duplicati.Server.WebServer.Server.OPTION_INTERFACE, Duplicati.Library.Interface.CommandLineArgument.ArgumentType.String, Strings.Program.WebserverInterfaceDescription, Strings.Program.WebserverInterfaceDescription, Duplicati.Server.WebServer.Server.DEFAULT_OPTION_INTERFACE),
|
|
new Duplicati.Library.Interface.CommandLineArgument("webservice-password", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Password, Strings.Program.WebserverPasswordDescription, Strings.Program.WebserverPasswordDescription),
|
|
new Duplicati.Library.Interface.CommandLineArgument("ping-pong-keepalive", Duplicati.Library.Interface.CommandLineArgument.ArgumentType.Boolean, LC.L("Enables the ping-pong responder"), LC.L("When running as a server, the service daemon must verify that the process is responding. If this option is enabled, the server reads stdin and writes a reply to each line read")),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|