Loading Program.fs +51 −16 Original line number Diff line number Diff line Loading @@ -18,16 +18,22 @@ open Argu let ExitBadParam = 1 let ExitTimeout = 2 let ExitIOError = 3 type CLIArguments = | DryRun | [<AltCommandLine("-n")>] Dry_Run | Target of backupTarget: string | Remote_User of remoteUser: string | [<AltCommandLine("-i")>] Itemize_Changes | Node_Name of nodeName: string with interface IArgParserTemplate with member s.Usage = match s with | DryRun _ -> "only show what will be done, do not transfer any file" | Dry_Run _ -> "only show what will be done, do not transfer any file" | Target _ -> "rsync target, could be local dir or remote ssh dir" | Remote_User _ -> "remote linux user to own the backup files" | Itemize_Changes _ -> "add -i option to rsync" type Logger() = let mutable level = Logger.DEBUG Loading Loading @@ -95,9 +101,12 @@ let appDataRoamingDir = Environment.GetFolderPath(Environment.SpecialFolder.Appl let programDataDir = GetEnv "PROGRAMDATA" |> ToMingwPath |> EnsureDir let appDataLocalDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) |> ToMingwPath |> EnsureDir let mbackupInstallDirWin = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) |> EnsureDir |> fun s -> s + "mbackup" let mbackupInstallDir = mbackupInstallDirWin |> ToMingwPath //let userConfigDir = appDataRoamingDir + "mbackup/" let userConfigDir = programDataDir + "mbackup/" let runtimeDir = appDataLocalDir let runtimeDir = appDataLocalDir + "mbackup/" let isLocalTarget (target: string) = target.StartsWith "/" Loading @@ -114,7 +123,8 @@ let main argv = let errorHandler = ProcessExiter(colorizer = function ErrorCode.HelpText -> None | _ -> Some ConsoleColor.Red) let parser = ArgumentParser.Create<CLIArguments>(programName = "mbackup.exe", errorHandler = errorHandler) let results = parser.Parse argv let dryRun = results.Contains DryRun let dryRun = results.Contains Dry_Run let itemizeChanges = results.Contains Itemize_Changes let logger = Logger() Loading @@ -123,7 +133,9 @@ let main argv = let rsyncCmd: string list = [] let rsyncCmd = appendWhen dryRun rsyncCmd "--dry-run" let rsyncCmd = List.append rsyncCmd ("-h --stats -air --delete --delete-excluded --ignore-missing-args".Split [|' '|] |> Array.toList) let rsyncCmd = appendWhen itemizeChanges rsyncCmd "-i" let rsyncCmd = List.append rsyncCmd ("-h --stats -togr --delete --delete-excluded --ignore-missing-args".Split [|' '|] |> Array.toList) let mbackupFile = runtimeDir + "mbackup.list" generateMbackupList |> ignore let rsyncCmd = List.append rsyncCmd [sprintf "--files-from=%s" mbackupFile] Loading @@ -136,6 +148,14 @@ let main argv = let localLogFile = runtimeDir + "mbackup.log" let rsyncCmd = List.append rsyncCmd [sprintf "--log-file=%s" localLogFile] // TODO remove usage of test dir. let mbackupInstallDirWinTest = "D:\\downloads\\apps\\mbackupTest\\" let mbackupInstallDirTest = mbackupInstallDirWinTest |> ToMingwPath |> EnsureDir let sshExeFile = mbackupInstallDirTest + "rsync-w64/usr/bin/ssh.exe" let sshConfigFile = userConfigDir + "ssh_config" let sshPrivateKeyFile = userConfigDir + "ssh_id_rsa" let rsyncCmd = List.append rsyncCmd [sprintf "-e \"%s -F %s -i %s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\"" sshExeFile sshConfigFile sshPrivateKeyFile] let backupTarget = results.GetResult (Target, defaultValue = Environment.GetEnvironmentVariable "TARGET") match backupTarget with | null -> Loading @@ -146,10 +166,18 @@ let main argv = let rsyncCmd = if not (isLocalTarget backupTarget) then let hostname: string = Net.Dns.GetHostName() let nodeName: string = GetEnvDefault "NODE_NAME" hostname let nodeName = match results.GetResult Node_Name with | null -> match GetEnv "NODE_NAME" with | null -> Net.Dns.GetHostName() | X -> X | X -> X let remoteLogFile = sprintf "/var/log/mbackup/%s.log" nodeName List.append rsyncCmd [sprintf "--remote-option=--log-file=%s" remoteLogFile] let remoteUser = results.GetResult (Remote_User, defaultValue = Environment.UserName) let rsyncCmd = List.append rsyncCmd [sprintf "--remote-option=--log-file=%s" remoteLogFile] let rsyncCmd = List.append rsyncCmd [sprintf "--chown=%s:%s" remoteUser remoteUser] rsyncCmd else rsyncCmd Loading @@ -158,12 +186,19 @@ let main argv = let rsyncArgs = rsyncCmd |> String.concat " " // TODO print rsyncArgs in the same Info call when Info supports multiple params. logger.Info "Note: if you run the following rsync command yourself, make sure the generated file list (%s) is up-to-date." mbackupFile let rsyncExe = "D:\\downloads\\apps\\rsync-w64-3.1.3-2-standalone\\usr\\bin\\rsync.exe" let rsyncExe = mbackupInstallDirWinTest + "rsync-w64\\usr\\bin\\rsync.exe" let echoExe = "C:\\Program Files\\Git\\usr\\bin\\echo.exe" let proc = Process.Start(echoExe, rsyncArgs) try IO.Directory.CreateDirectory(runtimeDir) |> ignore IO.Directory.CreateDirectory(userConfigDir) |> ignore let proc = Process.Start(rsyncExe, rsyncArgs) logger.Info "%s" (rsyncExe + " " + rsyncArgs) if proc.WaitForExit Int32.MaxValue then proc.ExitCode else logger.Error "mbackup timed out while waiting for rsync to complete" ExitTimeout with | _ -> logger.Error "Create runtime dir failed" ExitIOError mbackup-config/user-default.list +1 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ # windows specific files ############################################# Pictures/Saved Pictures/ Documents/ ############################################# # below are file list from linux environment Loading operational +34 −2 Original line number Diff line number Diff line Loading @@ -3,6 +3,10 @@ Time-stamp: <2019-11-12> #+STARTUP: content * notes :entry: ** 2019-11-13 how to run it in dev env? dotnet run -- --dry-run --itemize-changes --target d:\backup dotnet run -- -n -i --target d:\backup ** 2019-11-12 docs - rsync https://www.samba.org/ftp/rsync/rsync.html Loading Loading @@ -45,11 +49,37 @@ Time-stamp: <2019-11-12> * later :entry: * current :entry: ** ** 2019-11-13 extra user default list. Documents is replaced by real path. Downloads Pictures User should always use forward slash in backup list and exclude list files. These path are supported: C:\Foo\Bar C:/Foo/Bar Pictures/Saved Pictures Documents/ ** 2019-11-13 install dir layout. C:\Program Files\mbackup\rsync-w64\usr\bin\rsync.exe C:\Program Files\mbackup\rsync-w64\usr\bin\ssh.exe C:\ProgramData\mbackup\mbackup-default.exclude C:\ProgramData\mbackup\mbackup-default.list C:\ProgramData\mbackup\user-default.list ** 2019-11-13 next todos - fix TODOs in F# code - DONE fix TODOs in F# code - add rsync command and arguments for running in windows. i.e. -e etc. - test param parsing is working. <Arguments>--stats -togr --chown=sylecn:sylecn --exclude-from=/cygdrive/d/sylecn_docs/texts/configs/rsync-exclude --files-from=/cygdrive/d/sylecn_docs/texts/configs/rsync-file-list --log-file=/cygdrive/d/sylecn_docs/rsync-b75i3.log -e ".\ssh.exe -F c:/users/sylecn/.ssh/config -i c:/Users/sylecn/.ssh/id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" / sheni:/data/backup/server-backup/b75i3/</Arguments> - DONE test param parsing is working. command line param > env var > mbackup default value. support --node-name param. - test run in console and scheduled task. Loading @@ -69,6 +99,8 @@ Time-stamp: <2019-11-12> dotnet add reference ../mbackup-for-windows.fsproj dotnet build # this will build both ref project and test project. dotnet test - how to support short option names? --dry-run -n - ** 2019-11-12 make code work in a specific dir. then create an installer. - install to %programfiles% Loading Loading
Program.fs +51 −16 Original line number Diff line number Diff line Loading @@ -18,16 +18,22 @@ open Argu let ExitBadParam = 1 let ExitTimeout = 2 let ExitIOError = 3 type CLIArguments = | DryRun | [<AltCommandLine("-n")>] Dry_Run | Target of backupTarget: string | Remote_User of remoteUser: string | [<AltCommandLine("-i")>] Itemize_Changes | Node_Name of nodeName: string with interface IArgParserTemplate with member s.Usage = match s with | DryRun _ -> "only show what will be done, do not transfer any file" | Dry_Run _ -> "only show what will be done, do not transfer any file" | Target _ -> "rsync target, could be local dir or remote ssh dir" | Remote_User _ -> "remote linux user to own the backup files" | Itemize_Changes _ -> "add -i option to rsync" type Logger() = let mutable level = Logger.DEBUG Loading Loading @@ -95,9 +101,12 @@ let appDataRoamingDir = Environment.GetFolderPath(Environment.SpecialFolder.Appl let programDataDir = GetEnv "PROGRAMDATA" |> ToMingwPath |> EnsureDir let appDataLocalDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) |> ToMingwPath |> EnsureDir let mbackupInstallDirWin = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) |> EnsureDir |> fun s -> s + "mbackup" let mbackupInstallDir = mbackupInstallDirWin |> ToMingwPath //let userConfigDir = appDataRoamingDir + "mbackup/" let userConfigDir = programDataDir + "mbackup/" let runtimeDir = appDataLocalDir let runtimeDir = appDataLocalDir + "mbackup/" let isLocalTarget (target: string) = target.StartsWith "/" Loading @@ -114,7 +123,8 @@ let main argv = let errorHandler = ProcessExiter(colorizer = function ErrorCode.HelpText -> None | _ -> Some ConsoleColor.Red) let parser = ArgumentParser.Create<CLIArguments>(programName = "mbackup.exe", errorHandler = errorHandler) let results = parser.Parse argv let dryRun = results.Contains DryRun let dryRun = results.Contains Dry_Run let itemizeChanges = results.Contains Itemize_Changes let logger = Logger() Loading @@ -123,7 +133,9 @@ let main argv = let rsyncCmd: string list = [] let rsyncCmd = appendWhen dryRun rsyncCmd "--dry-run" let rsyncCmd = List.append rsyncCmd ("-h --stats -air --delete --delete-excluded --ignore-missing-args".Split [|' '|] |> Array.toList) let rsyncCmd = appendWhen itemizeChanges rsyncCmd "-i" let rsyncCmd = List.append rsyncCmd ("-h --stats -togr --delete --delete-excluded --ignore-missing-args".Split [|' '|] |> Array.toList) let mbackupFile = runtimeDir + "mbackup.list" generateMbackupList |> ignore let rsyncCmd = List.append rsyncCmd [sprintf "--files-from=%s" mbackupFile] Loading @@ -136,6 +148,14 @@ let main argv = let localLogFile = runtimeDir + "mbackup.log" let rsyncCmd = List.append rsyncCmd [sprintf "--log-file=%s" localLogFile] // TODO remove usage of test dir. let mbackupInstallDirWinTest = "D:\\downloads\\apps\\mbackupTest\\" let mbackupInstallDirTest = mbackupInstallDirWinTest |> ToMingwPath |> EnsureDir let sshExeFile = mbackupInstallDirTest + "rsync-w64/usr/bin/ssh.exe" let sshConfigFile = userConfigDir + "ssh_config" let sshPrivateKeyFile = userConfigDir + "ssh_id_rsa" let rsyncCmd = List.append rsyncCmd [sprintf "-e \"%s -F %s -i %s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\"" sshExeFile sshConfigFile sshPrivateKeyFile] let backupTarget = results.GetResult (Target, defaultValue = Environment.GetEnvironmentVariable "TARGET") match backupTarget with | null -> Loading @@ -146,10 +166,18 @@ let main argv = let rsyncCmd = if not (isLocalTarget backupTarget) then let hostname: string = Net.Dns.GetHostName() let nodeName: string = GetEnvDefault "NODE_NAME" hostname let nodeName = match results.GetResult Node_Name with | null -> match GetEnv "NODE_NAME" with | null -> Net.Dns.GetHostName() | X -> X | X -> X let remoteLogFile = sprintf "/var/log/mbackup/%s.log" nodeName List.append rsyncCmd [sprintf "--remote-option=--log-file=%s" remoteLogFile] let remoteUser = results.GetResult (Remote_User, defaultValue = Environment.UserName) let rsyncCmd = List.append rsyncCmd [sprintf "--remote-option=--log-file=%s" remoteLogFile] let rsyncCmd = List.append rsyncCmd [sprintf "--chown=%s:%s" remoteUser remoteUser] rsyncCmd else rsyncCmd Loading @@ -158,12 +186,19 @@ let main argv = let rsyncArgs = rsyncCmd |> String.concat " " // TODO print rsyncArgs in the same Info call when Info supports multiple params. logger.Info "Note: if you run the following rsync command yourself, make sure the generated file list (%s) is up-to-date." mbackupFile let rsyncExe = "D:\\downloads\\apps\\rsync-w64-3.1.3-2-standalone\\usr\\bin\\rsync.exe" let rsyncExe = mbackupInstallDirWinTest + "rsync-w64\\usr\\bin\\rsync.exe" let echoExe = "C:\\Program Files\\Git\\usr\\bin\\echo.exe" let proc = Process.Start(echoExe, rsyncArgs) try IO.Directory.CreateDirectory(runtimeDir) |> ignore IO.Directory.CreateDirectory(userConfigDir) |> ignore let proc = Process.Start(rsyncExe, rsyncArgs) logger.Info "%s" (rsyncExe + " " + rsyncArgs) if proc.WaitForExit Int32.MaxValue then proc.ExitCode else logger.Error "mbackup timed out while waiting for rsync to complete" ExitTimeout with | _ -> logger.Error "Create runtime dir failed" ExitIOError
mbackup-config/user-default.list +1 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ # windows specific files ############################################# Pictures/Saved Pictures/ Documents/ ############################################# # below are file list from linux environment Loading
operational +34 −2 Original line number Diff line number Diff line Loading @@ -3,6 +3,10 @@ Time-stamp: <2019-11-12> #+STARTUP: content * notes :entry: ** 2019-11-13 how to run it in dev env? dotnet run -- --dry-run --itemize-changes --target d:\backup dotnet run -- -n -i --target d:\backup ** 2019-11-12 docs - rsync https://www.samba.org/ftp/rsync/rsync.html Loading Loading @@ -45,11 +49,37 @@ Time-stamp: <2019-11-12> * later :entry: * current :entry: ** ** 2019-11-13 extra user default list. Documents is replaced by real path. Downloads Pictures User should always use forward slash in backup list and exclude list files. These path are supported: C:\Foo\Bar C:/Foo/Bar Pictures/Saved Pictures Documents/ ** 2019-11-13 install dir layout. C:\Program Files\mbackup\rsync-w64\usr\bin\rsync.exe C:\Program Files\mbackup\rsync-w64\usr\bin\ssh.exe C:\ProgramData\mbackup\mbackup-default.exclude C:\ProgramData\mbackup\mbackup-default.list C:\ProgramData\mbackup\user-default.list ** 2019-11-13 next todos - fix TODOs in F# code - DONE fix TODOs in F# code - add rsync command and arguments for running in windows. i.e. -e etc. - test param parsing is working. <Arguments>--stats -togr --chown=sylecn:sylecn --exclude-from=/cygdrive/d/sylecn_docs/texts/configs/rsync-exclude --files-from=/cygdrive/d/sylecn_docs/texts/configs/rsync-file-list --log-file=/cygdrive/d/sylecn_docs/rsync-b75i3.log -e ".\ssh.exe -F c:/users/sylecn/.ssh/config -i c:/Users/sylecn/.ssh/id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" / sheni:/data/backup/server-backup/b75i3/</Arguments> - DONE test param parsing is working. command line param > env var > mbackup default value. support --node-name param. - test run in console and scheduled task. Loading @@ -69,6 +99,8 @@ Time-stamp: <2019-11-12> dotnet add reference ../mbackup-for-windows.fsproj dotnet build # this will build both ref project and test project. dotnet test - how to support short option names? --dry-run -n - ** 2019-11-12 make code work in a specific dir. then create an installer. - install to %programfiles% Loading