Commit 08723c36 authored by Yuanle Song's avatar Yuanle Song
Browse files

added rsync options for running in windows env;

added short option support;
use real rsync command in Process.Start();
parent cdc15ad9
Loading
Loading
Loading
Loading
+51 −16
Original line number Diff line number Diff line
@@ -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
@@ -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 "/"

@@ -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()

@@ -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]
@@ -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 ->
@@ -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

@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
# windows specific files
#############################################
Pictures/Saved Pictures/
Documents/

#############################################
# below are file list from linux environment
+34 −2
Original line number Diff line number Diff line
@@ -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
@@ -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.
@@ -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%