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 }