1 /** 2 Copyright: Copyright (c) 2019, Joakim Brännström. All rights reserved. 3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 */ 6 module dsnapshot.types; 7 8 public import sumtype; 9 10 @safe: 11 12 /// Snapshots that are in the progress of being transfered have this suffix. 13 immutable snapshotInProgressSuffix = "-in-progress"; 14 /// The actual rsync'ed data is in this directory. 15 immutable snapshotData = "data"; 16 /// name of the fakeroot environment. 17 immutable snapshotFakerootEnv = "fakeroot.env"; 18 /// User id that is replaced by the actual path to the file to save the env in 19 immutable snapshotFakerootSaveEnvId = "$$SAVE_ENV_FILE$$"; 20 21 /// Tag a string as a path and make it absolute+normalized. 22 struct Path { 23 import std.path : absolutePath, buildNormalizedPath, buildPath; 24 25 private string value_; 26 27 this(Path p) @safe pure nothrow @nogc { 28 value_ = p.value_; 29 } 30 31 this(string p) @safe { 32 value_ = p.absolutePath.buildNormalizedPath; 33 } 34 35 Path dirName() @safe const { 36 import std.path : dirName; 37 38 return Path(value_.dirName); 39 } 40 41 string baseName() @safe const { 42 import std.path : baseName; 43 44 return value_.baseName; 45 } 46 47 void opAssign(string rhs) @safe pure { 48 value_ = rhs.absolutePath.buildNormalizedPath; 49 } 50 51 void opAssign(typeof(this) rhs) @safe pure nothrow { 52 value_ = rhs.value_; 53 } 54 55 Path opBinary(string op)(string rhs) @safe const { 56 static if (op == "~") { 57 return Path(buildPath(value_, rhs)); 58 } else 59 static assert(false, typeof(this).stringof ~ " does not have operator " ~ op); 60 } 61 62 void opOpAssign(string op)(string rhs) @safe nothrow { 63 static if (op == "~=") { 64 value_ = buildNormalizedPath(value_, rhs); 65 } else 66 static assert(false, typeof(this).stringof ~ " does not have operator " ~ op); 67 } 68 69 T opCast(T : string)() { 70 return value_; 71 } 72 73 string toString() @safe pure nothrow const @nogc { 74 return value_; 75 } 76 77 import std.range : isOutputRange; 78 79 void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) { 80 import std.range : put; 81 82 put(w, value_); 83 } 84 } 85 86 // TODO: maybe rename to SnapshotName? 87 /// Name of an existing snapshot. 88 struct Name { 89 string value; 90 } 91 92 struct SnapshotConfig { 93 import dsnapshot.layout : Layout; 94 95 /// Name of this snapshot 96 string name; 97 98 SyncCmd syncCmd; 99 100 // TODO: change to a generic "execute commmand". 101 /// How to interact with the destination for e.g. list snapshots. 102 RemoteCmd remoteCmd; 103 104 /// The snapshot layout to use. 105 Layout layout; 106 107 /// Hooks to run when taking a new snapshot. 108 Hooks hooks; 109 110 /// Crypt config. 111 CryptConfig crypt; 112 } 113 114 struct Hooks { 115 string[] preExec; 116 string[] postExec; 117 } 118 119 alias CryptConfig = SumType!(None, EncFsConfig); 120 121 struct EncFsConfig { 122 /// The xml config for encfs 123 string configFile; 124 /// Where the encrypted encfs is. The SyncCmd, via Flow, determines where it is mounted. 125 string encryptedPath; 126 string passwd; 127 string[] mountCmd = ["encfs", "-i", "1"]; 128 string[] mountFuseOpts; 129 string[] unmountCmd = ["encfs", "-u"]; 130 string[] unmountFuseOpts; 131 } 132 133 alias RemoteCmd = SumType!(SshRemoteCmd); 134 135 enum RemoteSubCmd { 136 none, 137 lsDirs, 138 mkdirRecurse, 139 rmdirRecurse, 140 /// Change the status of a snapshot from "in progress" to available. 141 publishSnapshot, 142 /// Transfer the remote fakeroot.env to a local representation. 143 fakerootStats, 144 } 145 146 /// Info of how to execute dsnapshot on the remote host. 147 struct SshRemoteCmd { 148 /// Path/lookup to use to find dsnapshot on the remote host. 149 string dsnapshot = "dsnapshot"; 150 151 /// dsnapshot is executed via ssh or equivalent command. 152 string[] rsh = ["ssh"]; 153 154 /// Returns: a cmd to execute with `std.process`. 155 string[] toCmd(RemoteSubCmd subCmd, string addr, string path) @safe pure const { 156 import std.conv : to; 157 158 return rsh ~ [ 159 addr, dsnapshot, "remotecmd", "--cmd", subCmd.to!string, "--path", 160 path 161 ]; 162 } 163 } 164 165 alias SyncCmd = SumType!(None, RsyncConfig); 166 167 struct None { 168 } 169 170 struct LocalAddr { 171 string value; 172 173 this(string v) { 174 import std.path : expandTilde; 175 176 value = v.expandTilde; 177 } 178 } 179 180 struct RemoteHost { 181 string addr; 182 string path; 183 } 184 185 string fixRemteHostForRsync(const string a) { 186 if (a.length != 0 && a[$ - 1] != '/') 187 return a ~ "/"; 188 return a; 189 } 190 191 string makeRsyncAddr(string addr, string path) { 192 import std.format : format; 193 194 return format("%s:%s", addr, path); 195 } 196 197 /// Local flow of data. 198 struct FlowLocal { 199 LocalAddr src; 200 LocalAddr dst; 201 } 202 203 /// Flow of data using a remote rsync address to a local destination. 204 struct FlowRsyncToLocal { 205 RemoteHost src; 206 LocalAddr dst; 207 } 208 209 struct FlowLocalToRsync { 210 LocalAddr src; 211 RemoteHost dst; 212 } 213 214 alias Flow = SumType!(None, FlowLocal, FlowRsyncToLocal, FlowLocalToRsync); 215 216 struct RsyncConfig { 217 Flow flow; 218 219 /// If --link-dest should be used with rsync 220 bool useLinkDest = true; 221 222 /// One filesystem, don't cross partitions within a backup point. 223 bool oneFs = true; 224 225 /// If fakeroot should be used for this snapshot 226 bool useFakeRoot = false; 227 228 /// Arguments to use with fakeroot 229 string[] rsyncFakerootArgs = ["--rsync-path"]; 230 string[] fakerootArgs = [ 231 "fakeroot", "-u", "-i", snapshotFakerootSaveEnvId, "-s", 232 snapshotFakerootSaveEnvId 233 ]; 234 235 /// Low process and io priority 236 bool lowPrio = true; 237 238 /// Patterns to exclude from rsync. 239 string[] exclude; 240 241 /// Rsync command to use. 242 string cmdRsync = "rsync"; 243 244 /// disk usage command to use. 245 string[] cmdDiskUsage = ["du", "-hcs"]; 246 247 /// rsh argument for rsync, --rsh=<rsh>. 248 string rsh; 249 250 /// Configure how to print the progress bar when in interactive shell, if any. 251 string[] progress = ["--info=stats1", "--info=progress2"]; 252 253 // -a archive mode; equals -rlptgoD (no -H,-A,-X) 254 // -r recursive 255 // -l copy symlinks as symlinks 256 // -p preserve permissions 257 // -t preserve modification times 258 // -g preserve groups permissions 259 // -o preserve owner permission 260 // -D preserve devices 261 // --delete delete files from dest if they are removed in src 262 // --partial keep partially transferred files 263 // --delete-excluded also delete excluded files from dest dirs 264 // --chmod change permission on transfered files 265 // --numeric-ids don't map uid/gid values by user/group name 266 // --modify-window set the accuracy for mod-time comparisons 267 string[] backupArgs = [ 268 "-ahv", "--numeric-ids", "--modify-window", "1", "--delete", 269 "--delete-excluded", "--partial" 270 ]; 271 272 string[] restoreArgs = ["-ahv", "--numeric-ids", "--modify-window", "1"]; 273 }