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