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