Skip to content
Commits on Source (3)
......@@ -27,6 +27,10 @@ type WellsConfig(fn: string) =
match getEnv (configKeyToEnvVar key) with
| null | "" -> keyValuePairs.TryFind key
| envValue -> Some envValue
member this.GetStrDefault key defaultValue =
match this.GetStr key with
| None -> defaultValue
| Some value -> value
member this.GetBool key =
Option.map (fun value -> Regex.IsMatch(value, "^(yes|true|enable|1)$", RegexOptions.IgnoreCase)) (this.GetStr key)
member this.GetFloat key =
......
// Learn more about F# at http://fsharp.org
//
// - mbackup config file
// %programdata%/mbackup/mbackup-config.txt
// - backup file list
// /%appdata%/mbackup/mbackup-default.list
// /%appdata%/mbackup/user-default.list
// /%appdata%/mbackup/local.list (optional)
// %programdata%/mbackup/default-list.txt
// %programdata%/mbackup/user-default-list.txt
// %programdata%/mbackup/local-list.txt (optional)
// - exclude pattern
// /%appdata%/mbackup/mbackup-default.exclude
// /%appdata%/mbackup/local.exclude (optional)
// %programdata%/mbackup/default-exclude.txt
// %programdata%/mbackup/local-exclude.txt (optional)
module Mbackup.Program
......@@ -30,6 +32,20 @@ let ExitUserError = 4
let version = Reflection.Assembly.GetEntryAssembly().GetName().Version
let versionStr = version.ToString()
// base filename for use in mbackup for windows.
// I use .txt and .log extension because user can open/edit them easily.
module MbackupFileName =
let DefaultList = "default-list.txt"
let DefaultExclude = "default-exclude.txt"
let LocalList = "local-list.txt"
let LocalExclude = "local-exclude.txt"
let UserDefaultList = "user-default-list.txt"
let Config = "mbackup-config.txt"
// run time files
let GeneratedList = "mbackup-list.txt"
let Log = "mbackup.log"
[<SuppressMessage("*", "UnionCasesNames")>]
type CLIArguments =
| [<AltCommandLine("-n")>] Dry_Run
......@@ -50,6 +66,11 @@ type CLIArguments =
| Ssh_Key _ -> "ssh private key, used when backup to remote ssh node"
| Version _ -> "show mbackup version and exit"
type MbackupRuntimeConfig =
{ Logger: Logger
Config: WellsConfig
Options: ParseResults<CLIArguments> }
let programFilesDirWin = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) |> ensureWinDir
let programFilesDir = toMingwPath programFilesDirWin
let mbackupProgramDirWin = programFilesDirWin + "mbackup\\"
......@@ -74,7 +95,6 @@ let mbackupInstallDir = mbackupInstallDirWin |> toMingwPath
let userHomeWin =
getEnvDefault "HOME" (Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)) |> ensureWinDir
let userHome = userHomeWin |> toMingwPath
let userConfigDirWin = programDataDirWin + "mbackup\\"
......@@ -82,10 +102,8 @@ let userConfigDir = programDataDir + "mbackup/"
let runtimeDirWin = appDataLocalDirWin + "mbackup\\"
let runtimeDir = appDataLocalDir + "mbackup/"
let mbackupConfigFile = userConfigDirWin + "mbackup.txt"
// return true if target is a local dir. local dir can be unix style or windows style.
let isLocalTarget (target: string) = target.StartsWith "/" || Regex.IsMatch(target, "^[c-z]:", RegexOptions.IgnoreCase)
let isLocalTarget (target: string) = target.StartsWith "/" || Regex.IsMatch(target, "^[a-z]:", RegexOptions.IgnoreCase)
// expand user file to mingw64 rsync supported path.
// abc -> /cygdrive/c/Users/<user>/abc
......@@ -120,19 +138,20 @@ let expandUserFile (fn: string) =
if fn.StartsWith("/") then fn
else userHome + fn
// generate mbackup.list file
let generateMbackupList (logger: Logger) =
// TODO how to only regenerate if source file have changed? should I bundle GNU make with mbackup?
// just compare mbackup.list mtime with its source files?
let mbackupDefaultList = userConfigDirWin + "mbackup-default.list"
let mbackupLocalList = userConfigDirWin + "local.list"
let mbackupUserDefaultList = userConfigDirWin + "user-default.list"
let mbackupList = runtimeDirWin + "mbackup.list"
// local functions
// read mbackup list file
let readMbackupListFile fn =
let dropEmptyLinesAndComments lines =
Seq.filter (fun (line: string) -> not (line.TrimStart().StartsWith("#") || line.TrimEnd().Equals(""))) lines
let readMbackupListFile fn = File.ReadAllLines(fn) |> dropEmptyLinesAndComments
File.ReadAllLines(fn) |> dropEmptyLinesAndComments
// generate MbackupFileName.GeneratedList file
let generateMbackupList (logger: Logger) =
// TODO how to only regenerate if source file have changed? should I bundle GNU make with mbackup?
// just compare MbackupFileName.GeneratedList mtime with its source files?
let mbackupDefaultList = userConfigDirWin + MbackupFileName.DefaultList
let mbackupLocalList = userConfigDirWin + MbackupFileName.LocalList
let mbackupUserDefaultList = userConfigDirWin + MbackupFileName.UserDefaultList
let mbackupList = runtimeDirWin + MbackupFileName.GeneratedList
try
let defaultListLines = readMbackupListFile mbackupDefaultList |> Seq.map toMingwPath
......@@ -144,24 +163,22 @@ let generateMbackupList (logger: Logger) =
with
| :? FileNotFoundException -> (true, Seq.empty)
| ex ->
logger.Error "Read mbackupLocalList failed: %s" ex.Message
logger.Error "Read mbackupLocalList %s failed: %s" mbackupLocalList ex.Message
(false, Seq.empty)
match localListLinesMaybe with
| (false, _) -> failwith "Read mbackup local.list file failed"
| (false, _) -> failwith "Read mbackupLocalList failed"
| (true, localListLines) ->
let userDefaultListLines = readMbackupListFile mbackupUserDefaultList |> Seq.map expandUserFile
let allLines = Seq.append (Seq.append defaultListLines localListLines) userDefaultListLines
// For mbackup-default.list and local.list, exclude empty lines and comment lines.
// skip and give a warning on non-absolute path.
// For user-default.list, auto prefix user's home dir, auto expand Documents, Downloads etc special folder.
// For DefaultList and LocalList, exclude empty lines and comment lines.
// TODO skip and give a warning on non-absolute path.
// For UserDefaultList, auto prefix user's home dir, auto expand Documents, Downloads etc special folder.
Directory.CreateDirectory(runtimeDirWin) |> ignore
File.WriteAllLines(mbackupList, allLines)
logger.Info
"mbackup.list file written: %s"
mbackupList
logger.Info "GeneratedList written: %s" mbackupList
true
with
| :? System.IO.IOException as ex ->
| :? IOException as ex ->
logger.Error "Read/write file failed: %s %s" ex.Source ex.Message
false
| ex ->
......@@ -170,10 +187,12 @@ let generateMbackupList (logger: Logger) =
exception PrivateKeyNotFoundException of string
let addOptionsForRemoteBackup (results: ParseResults<CLIArguments>) (logger: Logger) (rsyncCmd: string list) =
let addOptionsForRemoteBackup (rc: MbackupRuntimeConfig) (rsyncCmd: string list) =
let options = rc.Options
let sshExeFile = mbackupProgramDir + "rsync-w64/usr/bin/ssh.exe"
let sshConfigFile = userHome + ".ssh/config"
let sshPrivateKeyFile = results.GetResult(Ssh_Key, defaultValue = userHome + ".ssh/id_rsa") |> toMingwPath
let sshKnownHostsFile = userHome + ".ssh/known_hosts"
let sshPrivateKeyFile = options.GetResult(Ssh_Key, rc.Config.GetStrDefault "ssh-key" (userHome + ".ssh/id_rsa")) |> toMingwPath
let sshPrivateKeyFileWin = toWinPath sshPrivateKeyFile
if not (File.Exists(sshPrivateKeyFileWin)) then
raise (PrivateKeyNotFoundException("ssh private key doesn't exist: " + sshPrivateKeyFileWin))
......@@ -184,55 +203,69 @@ let addOptionsForRemoteBackup (results: ParseResults<CLIArguments>) (logger: Log
let rsyncCmd =
List.append rsyncCmd
[ sprintf "-e \"'%s'%s -i %s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\"" sshExeFile
sshConfigFileOption sshPrivateKeyFile ]
[ sprintf "-e \"'%s'%s -i %s -o StrictHostKeyChecking=ask -o UserKnownHostsFile=%s\""
sshExeFile sshConfigFileOption sshPrivateKeyFile sshKnownHostsFile]
let nodeName = results.GetResult(Node_Name, defaultValue = Net.Dns.GetHostName())
let nodeName = options.GetResult(Node_Name, (rc.Config.GetStrDefault "node-name" (Net.Dns.GetHostName())))
let remoteLogFile = sprintf "/var/log/mbackup/%s.log" nodeName
let remoteUser = results.GetResult(Remote_User, defaultValue = Environment.UserName)
let remoteUser = options.GetResult(Remote_User, (rc.Config.GetStrDefault "remote-user" 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
[<EntryPoint>]
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
if results.Contains Version then
let logger = Logger()
let options =
let errorHandler = ProcessExiter(colorizer = function ErrorCode.HelpText -> None | _ -> Some ConsoleColor.Red)
let parser = ArgumentParser.Create<CLIArguments>(programName = "mbackup.exe", errorHandler = errorHandler)
parser.Parse argv
let rc = {
MbackupRuntimeConfig.Config =
let mbackupConfigFile = userConfigDirWin + MbackupFileName.Config
WellsConfig(mbackupConfigFile)
Logger = logger
Options = options
}
if options.Contains Version then
printfn "mbackup %s" versionStr
Environment.Exit(ExitSuccess)
let dryRun = results.Contains Dry_Run
let itemizeChanges = results.Contains Itemize_Changes
let logger = Logger()
logger.Info "user config dir: %s" userConfigDirWin
logger.Info "runtime dir: %s" runtimeDirWin
logger.Debug "program dir: %s" mbackupProgramDirWin
let rsyncCmd: string list = []
let rsyncCmd = appendWhen dryRun rsyncCmd "--dry-run"
let rsyncCmd = appendWhen itemizeChanges rsyncCmd "-i"
let rsyncCmd = appendWhen (options.Contains Dry_Run) rsyncCmd "--dry-run"
let rsyncCmd = appendWhen (options.Contains Itemize_Changes) rsyncCmd "-i"
let rsyncCmd =
List.append rsyncCmd
("-h --stats -togr --delete --delete-excluded --ignore-missing-args".Split [| ' ' |] |> Array.toList)
let mbackupFile = runtimeDir + "mbackup.list"
if not (generateMbackupList logger) then failwith "Generate mbackup.list failed"
let rsyncCmd = List.append rsyncCmd [ sprintf "--files-from=%s" mbackupFile ]
if not (generateMbackupList logger) then
failwith (sprintf "Generate %s failed" MbackupFileName.GeneratedList)
let generatedFileList = runtimeDir + MbackupFileName.GeneratedList
let rsyncCmd = List.append rsyncCmd [ sprintf "--files-from=%s" generatedFileList ]
let excludeFile = userConfigDir + "mbackup-default.exclude"
let rsyncCmd = List.append rsyncCmd [ sprintf "--exclude-from=%s" excludeFile ]
let localExcludeFile = userConfigDir + "local.exclude"
let rsyncCmd = appendWhen (IO.File.Exists localExcludeFile) rsyncCmd (sprintf "--exclude-from=%s" localExcludeFile)
let rsyncCmd = List.append rsyncCmd [ sprintf "--exclude-from=%s" (userConfigDir + MbackupFileName.DefaultExclude) ]
let localLogFile = runtimeDir + "mbackup.log"
let rsyncCmd = List.append rsyncCmd [ sprintf "--log-file=%s" localLogFile ]
let runtimeLocalExcludeFile = runtimeDir + MbackupFileName.LocalExclude
let rsyncCmd =
let localExcludeFile = userConfigDir + MbackupFileName.LocalExclude
if File.Exists localExcludeFile then
let convertAbsPathToMingwStyle (line: string) =
if Regex.IsMatch(line, "[a-z]:", RegexOptions.IgnoreCase) then
toMingwPath line
else
line
let lines =
readMbackupListFile localExcludeFile
|> Seq.map convertAbsPathToMingwStyle
File.WriteAllLines(runtimeLocalExcludeFile, lines)
appendWhen (File.Exists localExcludeFile) rsyncCmd (sprintf "--exclude-from=%s" runtimeLocalExcludeFile)
let rsyncCmd = List.append rsyncCmd [ sprintf "--log-file=%s" (runtimeDir + MbackupFileName.Log) ]
// precedence: command line argument > environment variable > config file
let normalizeTarget target =
......@@ -240,10 +273,9 @@ let main argv =
else target
let backupTargetMaybe =
match results.TryGetResult Target with
match options.TryGetResult Target with
| None ->
let mbackupConfig = WellsConfig(mbackupConfigFile)
let backupTargetMaybe = mbackupConfig.GetStr("target")
let backupTargetMaybe = rc.Config.GetStr("target")
Option.map normalizeTarget backupTargetMaybe
| Some backupTarget -> Some(normalizeTarget backupTarget)
......@@ -255,7 +287,7 @@ let main argv =
try
let rsyncCmd =
if not (isLocalTarget backupTarget) then
addOptionsForRemoteBackup results logger rsyncCmd
addOptionsForRemoteBackup rc rsyncCmd
else
rsyncCmd
let rsyncCmd = List.append rsyncCmd [ "/" ]
......@@ -266,7 +298,7 @@ let main argv =
Directory.CreateDirectory(userConfigDirWin) |> ignore
logger.Info
"Note: if you run the following rsync command yourself, make sure the generated file list (%s) is up-to-date.\n%s"
mbackupFile (rsyncExe + " " + rsyncArgs)
generatedFileList (rsyncExe + " " + rsyncArgs)
let processStartInfo =
ProcessStartInfo(
FileName = rsyncExe,
......
# files/dirs to exclude, for syntax, check rsync patterns
# This file is reserved when uninstall/upgrade mbackup.
# lines started with # are comments.
# example:
# *.o
# C:\foo\bar.iso
# local dirs to backup.
# This file is reserved when uninstall/upgrade mbackup.
# lines started with # are comments.
# example:
# C:\mydir
# D:\some dir\some file.doc
# example config file
# example config file, lines start with # are comments.
# target=d:\backup\
# target=myhost.example.com:/data/backup/somedir/
# remote-user=foo
......@@ -6,7 +6,7 @@
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
<RootNamespace>Mbackup</RootNamespace>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>0.2.3.0</Version>
<Version>0.3.0.0</Version>
</PropertyGroup>
<ItemGroup>
......
......@@ -14,58 +14,85 @@
<Directory Id="MBACKUP_DATA" Name="mbackup"/>
</Directory>
<Directory Id="DesktopFolder" Name="Desktop" />
<Directory Id="ProgramMenuFolder" />
<Directory Id="ProgramMenuFolder">
<Directory Id="mbackup_start_menu_dir" Name="mbackup" />
</Directory>
</Directory>
<DirectoryRef Id="MBACKUP_DATA">
<Component Id="default_exclude.txt" Guid="*">
<File Id="default_exclude.txt" Source="mbackup-config\default-exclude.txt" KeyPath="yes"/>
</Component>
<Component Id="default_list.txt" Guid="*">
<File Id="default_list.txt" Source="mbackup-config\default-list.txt" KeyPath="yes"/>
</Component>
<Component Id="local_list.txt" NeverOverwrite="yes" Permanent="yes" Guid="*">
<!-- do not overwrite this component/file if it already exists on target system -->
<File Id="local_list.txt" Source="mbackup-config\local-list.txt" KeyPath="yes"/>
</Component>
<Component Id="local_exclude.txt" NeverOverwrite="yes" Permanent="yes" Guid="*">
<!-- do not overwrite this component/file if it already exists on target system -->
<File Id="local_exclude.txt" Source="mbackup-config\local-exclude.txt" KeyPath="yes"/>
</Component>
<Component Id="mbackup_config.txt" NeverOverwrite="yes" Permanent="yes" Guid="*">
<!-- do not overwrite this component/file if it already exists on target system -->
<File Id="mbackup_config.txt" Source="mbackup-config\mbackup-config.txt" KeyPath="yes"/>
</Component>
<Component Id="user_default_list.txt" Guid="*">
<File Id="user_default_list.txt" Source="mbackup-config\user-default-list.txt" KeyPath="yes"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="DesktopFolder">
<Component Id="ApplicationShortcutDesktop" Guid="*">
<Shortcut Id="ApplicationDesktopShortcut"
<Component Id="mbackup_desktop_shortcuts" Guid="*">
<Shortcut Id="DesktopMbackupExe"
Name="mbackup"
Description="Backup your computer using rsync"
Target="[#filE3FFB5E42FD6DDB7AD945F29409779F0]"
Target="[!filE3FFB5E42FD6DDB7AD945F29409779F0]"
WorkingDirectory="MBACKUP_PROGRAM_FILES"/>
<RemoveFolder Id="DesktopShortcut" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\mbackup" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
<RegistryValue Root="HKCU" Key="Software\mbackup\desktopshortcuts" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="ProgramMenuFolder">
<Component Id="ApplicationShortcutStartMenu" Guid="F7C168BA-BDC9-4C4D-8AE7-722AC399AFD5">
<Shortcut Id="ApplicationStartMenuShortcut"
<DirectoryRef Id="mbackup_start_menu_dir">
<Component Id="mbackup_start_menu" Guid="F7C168BA-BDC9-4C4D-8AE7-722AC399AFD5">
<Shortcut Id="StartMenuMbackupExe"
Name="mbackup"
Description="Backup your computer using rsync"
Target="[#filE3FFB5E42FD6DDB7AD945F29409779F0]"
Target="[!filE3FFB5E42FD6DDB7AD945F29409779F0]"
WorkingDirectory="MBACKUP_PROGRAM_FILES"/>
<Shortcut Id="StartMenuMbackupConfig"
Name="mbackup-config.txt"
Description="mbackup config file"
Target="[!mbackup_config.txt]"
WorkingDirectory="MBACKUP_PROGRAM_FILES"/>
<Shortcut Id="StartMenuLocalList"
Name="local-list.txt"
Description="mbackup local-list.txt file"
Target="[!local_list.txt]"
WorkingDirectory="MBACKUP_PROGRAM_FILES"/>
<RemoveFolder Id="StartMenuShortCut" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\mbackup" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
<Shortcut Id="StartMenuLocalExclude"
Name="local-exclude.txt"
Description="mbackup local-exclude.txt file"
Target="[!local_exclude.txt]"
WorkingDirectory="MBACKUP_PROGRAM_FILES"/>
<RemoveFolder Id="MBACKUP_START_MENU_DIR" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\mbackup\startmenushortcuts" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="MBACKUP_DATA">
<Component Id="mbackup_default.exclude" Guid="*">
<File Id="mbackup_default.exclude" Source="mbackup-config\mbackup-default.exclude" KeyPath="yes"/>
</Component>
<Component Id="mbackup_default.list" Guid="*">
<File Id="mbackup_default.list" Source="mbackup-config\mbackup-default.list" KeyPath="yes"/>
</Component>
<Component Id="user_default.list" Guid="*">
<File Id="user_default.list" Source="mbackup-config\user-default.list" KeyPath="yes"/>
</Component>
<Component Id="mbackup.txt" NeverOverwrite="yes" Permanent="yes" Guid="*">
<!-- do not overwrite this component/file if it already exists on target system -->
<File Id="mbackup.txt" Source="mbackup-config\mbackup.txt" KeyPath="yes"/>
</Component>
</DirectoryRef>
<Feature Id="MainApplication" Title="Main Application" Level="1">
<ComponentGroupRef Id="MbackupHeatGenerated" />
<ComponentGroupRef Id="RsyncHeatGenerated" />
<ComponentRef Id="mbackup_default.exclude" />
<ComponentRef Id="mbackup_default.list" />
<ComponentRef Id="user_default.list" />
<ComponentRef Id="mbackup.txt" />
<ComponentRef Id="ApplicationShortcutStartMenu" />
<ComponentRef Id="ApplicationShortcutDesktop" />
<ComponentRef Id="default_exclude.txt" />
<ComponentRef Id="default_list.txt" />
<ComponentRef Id="local_list.txt" />
<ComponentRef Id="local_exclude.txt" />
<ComponentRef Id="mbackup_config.txt" />
<ComponentRef Id="user_default_list.txt" />
<ComponentRef Id="mbackup_start_menu" />
<ComponentRef Id="mbackup_desktop_shortcuts" />
</Feature>
</Product>
</Wix>
* COMMENT -*- mode: org -*-
#+Date: 2019-11-12
Time-stamp: <2019-11-14>
Time-stamp: <2019-11-16>
#+STARTUP: content
* notes :entry:
** 2019-11-13 how to run it in dev env?
......@@ -95,24 +95,42 @@ dotnet run -- -i
How To: Implement a Major Upgrade In Your Installer
https://wixtoolset.org/documentation/manual/v3/howtos/updates/major_upgrade.html
Shortcut Element
https://wixtoolset.org/documentation/manual/v3/xsd/wix/shortcut.html
xml - Wix - ICE60 and ICE69 warnings - Stack Overflow
https://stackoverflow.com/questions/21320334/wix-ice60-and-ice69-warnings/31192979
From MSI to WiX, Part 10 – Shortcuts – Alex Shevchuk
https://blogs.technet.microsoft.com/alexshev/2008/02/20/from-msi-to-wix-part-10-shortcuts/
ICE errors
https://docs.microsoft.com/en-us/windows/win32/msi/ice90
https://docs.microsoft.com/en-us/windows/win32/msi/ice69
** 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:\Program Files\mbackup\publish\mbackup.exe
C:\Program Files\mbackup\publish\mbackup.dll
C:\ProgramData\mbackup\mbackup-default.exclude
C:\ProgramData\mbackup\mbackup-default.list
C:\ProgramData\mbackup\user-default.list
C:\ProgramData\mbackup\mbackup.txt
C:\ProgramData\mbackup\default-exclude.txt
C:\ProgramData\mbackup\default-list.txt
C:\ProgramData\mbackup\user-default-list.txt
C:\ProgramData\mbackup\mbackup-config.txt
C:\ProgramData\mbackup\local-list.txt
C:\ProgramData\mbackup\local-exclude.txt
- optional user configs
like /etc/mbackup/ in linux
C:\ProgramData\mbackup\local.list
C:\ProgramData\mbackup\local.exclude
C:\ProgramData\mbackup\local-list.txt
C:\ProgramData\mbackup\local-exclude.txt
like ~/.mbackup/ in linux
<My Documents>/mbackup/local.list
<My Documents>/mbackup/local.exclude
- auto create these two files if they do not exist on target node.
never overwrite existing file.
- add shortcut to 3 files in start menu dir.
mbackup/mbackup.exe
mbackup/local-list.txt
mbackup/local-exclude.txt
mbackup/mbackup-config.txt
** 2019-11-14 notes :development:
- Argu optional param support.
......@@ -139,12 +157,47 @@ C:\ProgramData\mbackup\mbackup.txt
it's best to press + to create a regular powershell terminal.
* later :entry:
** TODO 2019-11-15 additionally support <My Documents>/mbackup/local-list.txt file.
This file is easier to open and backup.
no. programdata should be good enough.
but this is per user extra config file.
each user could have their own local file to backup.
one user don't want to manage all other user's local backup list.
yes. I should support this.
Both local.list and local.exclude.
- 2019-11-16 local.list is difficult to open in windows.
use txt file instead for all file lists and ignore lists.
like ~/.mbackup/ in linux
<My Documents>/mbackup/local-list.txt
<My Documents>/mbackup/local-exclude.txt
If <My Documents>/mbackup/local-*.txt exists, it's merged to system level
local-*.txt.
** 2019-11-14 supports expand Downloads dir in user-default.list
** 2019-11-14 vscode f# doesn't support open a module
it can only support open a namespace.
using the vscode Ionide-fsharp extension.
* current :entry:
**
** 2019-11-16 ssh.exe Could not create directory '/home/IEUser/.ssh' error.
when rsync.exe or ssh.exe is run from cmd.exe, it will always show this error.
HOME env var is defined properly.
- create /etc/nsswitch.conf with the following content didn't fix it.
db_home: windows
- openssh get user's home dir from getpwuid call.
Check how ssh get home dir. maybe from /etc/passwd and similar syscall.
check ssh-keygen source code.
https://github.com/openssh/openssh-portable/blob/master/ssh-keygen.c
pw = getpwuid(getuid());
https://manpages.ubuntu.com/manpages/bionic/en/man3/getpwuid.3.html
indeed it read config for passwd entry.
how to bundle passwd file in mingw application?
** 2019-11-15 f# indent is difficult in vscode.
vscode should at least always indent using space for F#.
......@@ -166,17 +219,144 @@ vscode should at least always indent using space for F#.
but there is not format selection in f# buffer.
** TODO 2019-11-15 additionally support <My Documents>/mbackup/local.list file.
This file is easier to open and backup.
* current :entry:
**
** 2019-11-12 make code work in a specific dir. then create an installer.
- bundle dotnet core 3 with installer.
- DONE install binary files to %programfiles%
- DONE install mbackup config files to %programdata%
- install scheduled tasks as admin
no. programdata should be good enough.
but this is per user extra config file.
each user could have their own local file to backup.
one user don't want to manage all other user's local backup list.
SCHTASKS /Create /?
# run mbackup 15m after user logon.
SCHTASKS /Create /NP /SC ONLOGON /TN mbackup-logon /TR "\"<path\to\mbackup.exe>\" \"args\"" /DELAY 15:00
# run mbackup at 10am and 4pm.
SCHTASKS /Create /NP /SC DAILY /TN mbackup-morning /TR "\"<path\to\mbackup.exe>\" \"args\"" /ST 10:00 /ET 13:00 /K
SCHTASKS /Create /NP /SC DAILY /TN mbackup-afternoon /TR "\"<path\to\mbackup.exe>\" \"args\"" /ST 16:00 /ET 19:00 /K
yes. I should support this.
# debug purpose, one time only
SCHTASKS /Create /NP /SC ONCE /TN mbackup-test /TR "\"<path\to\mbackup.exe>\" \"args\"" /ST 19:18
Both local.list and local.exclude.
In admin powershell,
SCHTASKS /Create /NP /SC ONCE /TN mbackup-test /TR $mbackupexe /ST 19:32
/NP option requires admin privilledge.
it works.
to delete it,
schtasks /delete /tn mbackup-test
- config is installed/saved to %appdata% roaming dir.
- problems
- config should be system wide. %appdata% is per-user dir.
where should I save global config file in windows 10?
Windows uses the %APPDATA% directory for user specific application
configuration files. The %PROGRAMDATA% folder is used for system wide
application configuration files, though it appears that Windows Store
apps may not be able to use %PROGRAMDATA%.
C:\ProgramData\mbackup\mbackup-default.list
- on B75I3, rsync is here.
D:\downloads\apps\rsync-w64-3.1.3-2-standalone\usr\bin\rsync.exe
scheduled task command is:
<Actions Context="Author">
<Exec>
<Command>D:\downloads\apps\rsync-w64-3.1.3-2-standalone\usr\bin\rsync.exe</Command>
<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>
<WorkingDirectory>D:\downloads\apps\rsync-w64-3.1.3-2-standalone\usr\bin\</WorkingDirectory>
</Exec>
</Actions>
- I don't want to bundle python with mbackup for windows, so I will use
C# and dotnet core 3 to create mbackup console executable.
mbackup --cron
# run the command as if run in scheduled task.
mbackup
# run backup command interactively.
create new console project.
dotnet new console -lang=F#
Try F# this time. it's said dotnet have very good F# support.
It's a small program, should be easy to handle.
- implementation
- install-package argu
can't find package.
https://www.nuget.org/packages/Argu
try this:
dotnet add package Argu --version 5.5.0
it works.
- how to change output exe file name?
can't find it. won't fix. default name is mbackup-for-windows.dll/exe
- make rsync command work.
- how to backup only documents in "My Documents" and "Downloads"?
I don't want to backup exe/msi/zip/rar/7z/iso/tar.gz files in those dir.
Only backup pdf/docx/xlsx etc.
Maybe just let user add "My Documents" in local.list.
I won't backup anything by default.
This is not good enough. docs should be backed up by default.
can I backup a zip file if I have Documents/**.zip in exclude list?
- TODO F# doesn't have a free logging framework yet?
logary/LICENSE.md at master · logary/logary · GitHub
https://github.com/logary/logary/blob/master/LICENSE.md
this one is not free software.
-
** 2019-11-12 how to test it? test it in a win 10 VM?
- search: lite weight win 10 VM
There are pirated specialized versions of Windows 7 and XP out there called
TinyXP and Tiny7. They are very stripped down and light weight.
Tiny7 is created using AIK.
https://en.wikipedia.org/wiki/Windows_Automated_Installation_Kit
- try get tiny7 iso/disk image.
tiny7 is no longer maintained.
try the old version.
Tiny 7 Windows : eXPreience : Free Download, Borrow, and Streaming : Internet Archive
https://archive.org/details/Tiny7
2017.5
- I have a 4.1G MSEdge win10 vmdk disk on sheni. from 2016.
maybe just use this.
- there is no enough RAM on B75I3. I will stop bogon VM and run win 10 VM.
-
** 2019-11-12 mbackup for windows :featurereq:
- features
- windows installer.
bundles mingw rsync and ssh.
they are NOT installed to PATH. so it will not affect other ssh programs.
- backup file list;
user file list;
default exclude file list;
user exclude file list;
etc
They have different value compared to mbackup for linux.
- script designed to run via windows scheduled task.
no service installed. no auto starts.
taskschd.msc
- implementation.
- there is rsync running on B75I3.
exported tasksched.msc config as xml.
that XML is not portable.
it contains hostname.
-
* done :entry:
** 2019-11-16 local-exclude.txt should convert win path to mingw path.
foo/bar
C:\foo\bar
/cygdrive/c/foo/bar
remove comments and empty lines.
if a line starts with [a-z]:, convert it to mingw path.
** 2019-11-14 learn how to create an msi installer.
- WiX Toolset
......@@ -223,7 +403,7 @@ Both local.list and local.exclude.
maybe show a installer window and let user click Next and Finish.
- TODO how to add some dir to PATH?
I only have one exe. Maybe just create a start menu or desktop shortcut?
create desktop shortcut to mbackup.exe
DONE create desktop shortcut to mbackup.exe
installer - Create shortcut to desktop using WiX - Stack Overflow
https://stackoverflow.com/questions/11868499/create-shortcut-to-desktop-using-wix
it works.
......@@ -321,7 +501,74 @@ Both local.list and local.exclude.
// set HOME dir on system level works on B75I3. maybe when rsync invoke ssh, env variable is lost.
// if system level env variable is required, set HOME when msi is installed if HOME is not set.
*now*
in admin powershell,
setx HOME ^%USERPROFILE^%
// try mbackup to remote node, nope.
setx HOME $env:userprofile
// try mbackup to remote node, nope.
how to make HOME work in the Win 10 GUEST?
TODO Maybe I need to create the rsync zip file using a Dockerfile. LATER.
And include bash or getenv tool for debugging. Does my rsync tool include bash? yes.
on B75I3,
.\bash.exe
echo $HOME
/cygdrive/c/Users/sylecn
on Win10 Guest,
.\bash.exe
bash.exe: warning: could not find /tmp, please create!
echo $HOME
/cygdrive/c/Users/IEUser
// looks HOME var is alright. ssh is not using HOME env variable.
ssh-keygen works fine in bash shell. so bash setup some variable for use with ssh tools.
after reboot, run ssh-keygen directly in cmd.exe also works.
When run ssh.exe from cmd.exe, it still complains about HOME dir.
Could not create directory '/home/IEUser/.ssh'.
I think I know the problem. When start bash.exe, it will convert all path in PATH, HOME to cygdrive style.
When start ssh.exe directly, HOME dir is still in windows style.
Try set HOME to cygdrive style and run ssh.exe.
how to set env var for current shell only in windows?
set HOME=/cygdrive/c/Users/IEUser
ssh.exe ...
Could not create directory '/home/IEUser/.ssh'.
search: mingw HOME
HOWTO Set the HOME variable for cmd exe | MinGW
http://www.mingw.org/wiki/HOWTO_Set_the_HOME_variable_for_cmd_exe
Ubuntu Manpage: ssh — OpenSSH SSH client (remote login program)
https://manpages.ubuntu.com/manpages/bionic/en/man1/ssh.1.html
ssh will set HOME env var, not read it.
Check how ssh get home dir. maybe from /etc/passwd and similar syscall.
check ssh-keygen source code.
https://github.com/openssh/openssh-portable/blob/master/ssh-keygen.c
pw = getpwuid(getuid());
https://manpages.ubuntu.com/manpages/bionic/en/man3/getpwuid.3.html
indeed it read config for passwd entry.
how to bundle passwd file in mingw application?
Configure sshd on MSYS2 and run it as a Windows service · GitHub
https://gist.github.com/samhocevar/00eec26d9e9988d080ac
/etc/nsswitch.conf
db_home: windows
// what does this do? I think it only change HOME, doesn't work for getpwuid call.
http://manpages.ubuntu.com/manpages/precise/man5/nsswitch.conf.5.html
nsswitch can manage how system read passwd db. cool.
after updating /etc/nsswitch.conf, all MSYS applications and terminals must be
closed, as the configuration is only read once at MSYS DLL load time.
try db_home: windows again. reboot and test.
it doesn't work.
why it works on B75I3? what's different?
this is all about UserKnownHostsFile? just specify a path for this option.
after adding UserKnownHostsFile to point to existing file, ssh still wants to create /home/<user>/.ssh dir.
I will leave this problem. It's harmless for my use case.
- System.ArgumentException: Value does not fall within the expected range.
this happens when I run dotnet run on B75I3 dev node.
what is this error?
......@@ -472,135 +719,9 @@ Both local.list and local.exclude.
&"C:\Program Files\mbackup\rsync-w64\usr\bin\rsync.exe" -h --stats -togr --delete --delete-excluded --ignore-missing-args --files-from=/cygdrive/c/Users/sylecn/AppData/Local/mbackup/mbackup.list --exclude-from=/cygdrive/c/ProgramData/mbackup/mbackup-default.exclude --log-file=/cygdrive/c/Users/sylecn/AppData/Local/mbackup/mbackup.log -e "'/cygdrive/c/Program Files/mbackup/rsync-w64/usr/bin/ssh.exe' -F /cygdrive/c/Users/sylecn/.ssh/config -i /cygdrive/c/Users/sylecn/.ssh/id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" --remote-option=--log-file=/var/log/mbackup/B75I3.log --chown=sylecn:sylecn / root@sylecn01.emacsos.com:/data/backup/PC-backup/B75I3/
** 2019-11-12 make code work in a specific dir. then create an installer.
- bundle dotnet core 3 with installer.
- install binary files to %programfiles%
- install mbackup config files to %programdata%
- install scheduled tasks as admin
SCHTASKS /Create /?
# run mbackup 15m after user logon.
SCHTASKS /Create /NP /SC ONLOGON /TN mbackup-logon /TR "\"<path\to\mbackup.exe>\" \"args\"" /DELAY 15:00
# run mbackup at 10am and 4pm.
SCHTASKS /Create /NP /SC DAILY /TN mbackup-morning /TR "\"<path\to\mbackup.exe>\" \"args\"" /ST 10:00 /ET 13:00 /K
SCHTASKS /Create /NP /SC DAILY /TN mbackup-afternoon /TR "\"<path\to\mbackup.exe>\" \"args\"" /ST 16:00 /ET 19:00 /K
# debug purpose, one time only
SCHTASKS /Create /NP /SC ONCE /TN mbackup-test /TR "\"<path\to\mbackup.exe>\" \"args\"" /ST 19:18
In admin powershell,
SCHTASKS /Create /NP /SC ONCE /TN mbackup-test /TR $mbackupexe /ST 19:32
/NP option requires admin privilledge.
it works.
to delete it,
schtasks /delete /tn mbackup-test
- config is installed/saved to %appdata% roaming dir.
- problems
- config should be system wide. %appdata% is per-user dir.
where should I save global config file in windows 10?
Windows uses the %APPDATA% directory for user specific application
configuration files. The %PROGRAMDATA% folder is used for system wide
application configuration files, though it appears that Windows Store
apps may not be able to use %PROGRAMDATA%.
C:\ProgramData\mbackup\mbackup-default.list
- on B75I3, rsync is here.
D:\downloads\apps\rsync-w64-3.1.3-2-standalone\usr\bin\rsync.exe
scheduled task command is:
<Actions Context="Author">
<Exec>
<Command>D:\downloads\apps\rsync-w64-3.1.3-2-standalone\usr\bin\rsync.exe</Command>
<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>
<WorkingDirectory>D:\downloads\apps\rsync-w64-3.1.3-2-standalone\usr\bin\</WorkingDirectory>
</Exec>
</Actions>
- I don't want to bundle python with mbackup for windows, so I will use
C# and dotnet core 3 to create mbackup console executable.
mbackup --cron
# run the command as if run in scheduled task.
mbackup
# run backup command interactively.
create new console project.
dotnet new console -lang=F#
Try F# this time. it's said dotnet have very good F# support.
It's a small program, should be easy to handle.
- implementation
- install-package argu
can't find package.
https://www.nuget.org/packages/Argu
try this:
dotnet add package Argu --version 5.5.0
it works.
- how to change output exe file name?
can't find it. won't fix. default name is mbackup-for-windows.dll/exe
- make rsync command work.
- how to backup only documents in "My Documents" and "Downloads"?
I don't want to backup exe/msi/zip/rar/7z/iso/tar.gz files in those dir.
Only backup pdf/docx/xlsx etc.
Maybe just let user add "My Documents" in local.list.
I won't backup anything by default.
This is not good enough. docs should be backed up by default.
can I backup a zip file if I have Documents/**.zip in exclude list?
- TODO F# doesn't have a free logging framework yet?
logary/LICENSE.md at master · logary/logary · GitHub
https://github.com/logary/logary/blob/master/LICENSE.md
this one is not free software.
-
** 2019-11-12 how to test it? test it in a win 10 VM?
- search: lite weight win 10 VM
There are pirated specialized versions of Windows 7 and XP out there called
TinyXP and Tiny7. They are very stripped down and light weight.
Tiny7 is created using AIK.
https://en.wikipedia.org/wiki/Windows_Automated_Installation_Kit
- try get tiny7 iso/disk image.
tiny7 is no longer maintained.
try the old version.
Tiny 7 Windows : eXPreience : Free Download, Borrow, and Streaming : Internet Archive
https://archive.org/details/Tiny7
2017.5
- I have a 4.1G MSEdge win10 vmdk disk on sheni. from 2016.
maybe just use this.
- there is no enough RAM on B75I3. I will stop bogon VM and run win 10 VM.
-
** 2019-11-12 mbackup for windows :featurereq:
- features
- windows installer.
bundles mingw rsync and ssh.
they are NOT installed to PATH. so it will not affect other ssh programs.
- backup file list;
user file list;
default exclude file list;
user exclude file list;
etc
They have different value compared to mbackup for linux.
- script designed to run via windows scheduled task.
no service installed. no auto starts.
taskschd.msc
- implementation.
- there is rsync running on B75I3.
exported tasksched.msc config as xml.
that XML is not portable.
it contains hostname.
-
* done :entry:
** 2019-11-16 support reading remote-user=xxx option from config file.
cli arg > env var > config file.
same with target option.
** 2019-11-13 next todos; scheduled task command line.
- DONE fix TODOs in F# code
- DONE add rsync command and arguments for running in windows.
......