aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2024-01-01 18:13:30 +0000
committerdiogo464 <[email protected]>2024-01-01 18:13:30 +0000
commit48d563959b3d6cbe6edc7921cfa1fd22935177d2 (patch)
tree59a0518e1309f073172870652bbd00a935065e3c
init
-rw-r--r--.gitea/workflows/build.yaml11
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock922
-rw-r--r--Cargo.toml21
-rw-r--r--README.md30
-rw-r--r--src/cfg.rs90
-rw-r--r--src/main.rs135
-rw-r--r--src/zfs.rs87
8 files changed, 1297 insertions, 0 deletions
diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml
new file mode 100644
index 0000000..af317fd
--- /dev/null
+++ b/.gitea/workflows/build.yaml
@@ -0,0 +1,11 @@
1name: build
2on: [push]
3jobs:
4 build:
5 runs-on: [ubuntu-latest]
6 steps:
7 - name: Checkout repository
8 uses: actions/checkout@v4
9 - uses: dtolnay/rust-toolchain@nightly
10 - name: Build
11 run: cargo build --release
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..50a974f
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,922 @@
1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing.
3version = 3
4
5[[package]]
6name = "addr2line"
7version = "0.21.0"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
10dependencies = [
11 "gimli",
12]
13
14[[package]]
15name = "adler"
16version = "1.0.2"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
19
20[[package]]
21name = "aho-corasick"
22version = "1.1.2"
23source = "registry+https://github.com/rust-lang/crates.io-index"
24checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
25dependencies = [
26 "memchr",
27]
28
29[[package]]
30name = "android-tzdata"
31version = "0.1.1"
32source = "registry+https://github.com/rust-lang/crates.io-index"
33checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
34
35[[package]]
36name = "android_system_properties"
37version = "0.1.5"
38source = "registry+https://github.com/rust-lang/crates.io-index"
39checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
40dependencies = [
41 "libc",
42]
43
44[[package]]
45name = "anstream"
46version = "0.6.5"
47source = "registry+https://github.com/rust-lang/crates.io-index"
48checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
49dependencies = [
50 "anstyle",
51 "anstyle-parse",
52 "anstyle-query",
53 "anstyle-wincon",
54 "colorchoice",
55 "utf8parse",
56]
57
58[[package]]
59name = "anstyle"
60version = "1.0.4"
61source = "registry+https://github.com/rust-lang/crates.io-index"
62checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
63
64[[package]]
65name = "anstyle-parse"
66version = "0.2.3"
67source = "registry+https://github.com/rust-lang/crates.io-index"
68checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
69dependencies = [
70 "utf8parse",
71]
72
73[[package]]
74name = "anstyle-query"
75version = "1.0.2"
76source = "registry+https://github.com/rust-lang/crates.io-index"
77checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
78dependencies = [
79 "windows-sys",
80]
81
82[[package]]
83name = "anstyle-wincon"
84version = "3.0.2"
85source = "registry+https://github.com/rust-lang/crates.io-index"
86checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
87dependencies = [
88 "anstyle",
89 "windows-sys",
90]
91
92[[package]]
93name = "autocfg"
94version = "1.1.0"
95source = "registry+https://github.com/rust-lang/crates.io-index"
96checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
97
98[[package]]
99name = "backtrace"
100version = "0.3.69"
101source = "registry+https://github.com/rust-lang/crates.io-index"
102checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
103dependencies = [
104 "addr2line",
105 "cc",
106 "cfg-if",
107 "libc",
108 "miniz_oxide",
109 "object",
110 "rustc-demangle",
111]
112
113[[package]]
114name = "bumpalo"
115version = "3.14.0"
116source = "registry+https://github.com/rust-lang/crates.io-index"
117checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
118
119[[package]]
120name = "cc"
121version = "1.0.83"
122source = "registry+https://github.com/rust-lang/crates.io-index"
123checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
124dependencies = [
125 "libc",
126]
127
128[[package]]
129name = "cfg-if"
130version = "1.0.0"
131source = "registry+https://github.com/rust-lang/crates.io-index"
132checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
133
134[[package]]
135name = "chrono"
136version = "0.4.31"
137source = "registry+https://github.com/rust-lang/crates.io-index"
138checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
139dependencies = [
140 "android-tzdata",
141 "iana-time-zone",
142 "js-sys",
143 "num-traits",
144 "wasm-bindgen",
145 "windows-targets 0.48.5",
146]
147
148[[package]]
149name = "clap"
150version = "4.4.12"
151source = "registry+https://github.com/rust-lang/crates.io-index"
152checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d"
153dependencies = [
154 "clap_builder",
155 "clap_derive",
156]
157
158[[package]]
159name = "clap_builder"
160version = "4.4.12"
161source = "registry+https://github.com/rust-lang/crates.io-index"
162checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
163dependencies = [
164 "anstream",
165 "anstyle",
166 "clap_lex",
167 "strsim",
168]
169
170[[package]]
171name = "clap_derive"
172version = "4.4.7"
173source = "registry+https://github.com/rust-lang/crates.io-index"
174checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
175dependencies = [
176 "heck",
177 "proc-macro2",
178 "quote",
179 "syn",
180]
181
182[[package]]
183name = "clap_lex"
184version = "0.6.0"
185source = "registry+https://github.com/rust-lang/crates.io-index"
186checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
187
188[[package]]
189name = "color-eyre"
190version = "0.6.2"
191source = "registry+https://github.com/rust-lang/crates.io-index"
192checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
193dependencies = [
194 "backtrace",
195 "color-spantrace",
196 "eyre",
197 "indenter",
198 "once_cell",
199 "owo-colors",
200 "tracing-error",
201]
202
203[[package]]
204name = "color-spantrace"
205version = "0.2.1"
206source = "registry+https://github.com/rust-lang/crates.io-index"
207checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
208dependencies = [
209 "once_cell",
210 "owo-colors",
211 "tracing-core",
212 "tracing-error",
213]
214
215[[package]]
216name = "colorchoice"
217version = "1.0.0"
218source = "registry+https://github.com/rust-lang/crates.io-index"
219checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
220
221[[package]]
222name = "core-foundation-sys"
223version = "0.8.6"
224source = "registry+https://github.com/rust-lang/crates.io-index"
225checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
226
227[[package]]
228name = "cron"
229version = "0.12.0"
230source = "registry+https://github.com/rust-lang/crates.io-index"
231checksum = "1ff76b51e4c068c52bfd2866e1567bee7c567ae8f24ada09fd4307019e25eab7"
232dependencies = [
233 "chrono",
234 "nom",
235 "once_cell",
236]
237
238[[package]]
239name = "equivalent"
240version = "1.0.1"
241source = "registry+https://github.com/rust-lang/crates.io-index"
242checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
243
244[[package]]
245name = "eyre"
246version = "0.6.11"
247source = "registry+https://github.com/rust-lang/crates.io-index"
248checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799"
249dependencies = [
250 "indenter",
251 "once_cell",
252]
253
254[[package]]
255name = "gimli"
256version = "0.28.1"
257source = "registry+https://github.com/rust-lang/crates.io-index"
258checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
259
260[[package]]
261name = "hashbrown"
262version = "0.14.3"
263source = "registry+https://github.com/rust-lang/crates.io-index"
264checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
265
266[[package]]
267name = "heck"
268version = "0.4.1"
269source = "registry+https://github.com/rust-lang/crates.io-index"
270checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
271
272[[package]]
273name = "iana-time-zone"
274version = "0.1.59"
275source = "registry+https://github.com/rust-lang/crates.io-index"
276checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
277dependencies = [
278 "android_system_properties",
279 "core-foundation-sys",
280 "iana-time-zone-haiku",
281 "js-sys",
282 "wasm-bindgen",
283 "windows-core",
284]
285
286[[package]]
287name = "iana-time-zone-haiku"
288version = "0.1.2"
289source = "registry+https://github.com/rust-lang/crates.io-index"
290checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
291dependencies = [
292 "cc",
293]
294
295[[package]]
296name = "indenter"
297version = "0.3.3"
298source = "registry+https://github.com/rust-lang/crates.io-index"
299checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
300
301[[package]]
302name = "indexmap"
303version = "2.1.0"
304source = "registry+https://github.com/rust-lang/crates.io-index"
305checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
306dependencies = [
307 "equivalent",
308 "hashbrown",
309]
310
311[[package]]
312name = "js-sys"
313version = "0.3.66"
314source = "registry+https://github.com/rust-lang/crates.io-index"
315checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
316dependencies = [
317 "wasm-bindgen",
318]
319
320[[package]]
321name = "lazy_static"
322version = "1.4.0"
323source = "registry+https://github.com/rust-lang/crates.io-index"
324checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
325
326[[package]]
327name = "libc"
328version = "0.2.151"
329source = "registry+https://github.com/rust-lang/crates.io-index"
330checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
331
332[[package]]
333name = "log"
334version = "0.4.20"
335source = "registry+https://github.com/rust-lang/crates.io-index"
336checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
337
338[[package]]
339name = "matchers"
340version = "0.1.0"
341source = "registry+https://github.com/rust-lang/crates.io-index"
342checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
343dependencies = [
344 "regex-automata 0.1.10",
345]
346
347[[package]]
348name = "memchr"
349version = "2.7.1"
350source = "registry+https://github.com/rust-lang/crates.io-index"
351checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
352
353[[package]]
354name = "minimal-lexical"
355version = "0.2.1"
356source = "registry+https://github.com/rust-lang/crates.io-index"
357checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
358
359[[package]]
360name = "miniz_oxide"
361version = "0.7.1"
362source = "registry+https://github.com/rust-lang/crates.io-index"
363checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
364dependencies = [
365 "adler",
366]
367
368[[package]]
369name = "nom"
370version = "7.1.3"
371source = "registry+https://github.com/rust-lang/crates.io-index"
372checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
373dependencies = [
374 "memchr",
375 "minimal-lexical",
376]
377
378[[package]]
379name = "nu-ansi-term"
380version = "0.46.0"
381source = "registry+https://github.com/rust-lang/crates.io-index"
382checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
383dependencies = [
384 "overload",
385 "winapi",
386]
387
388[[package]]
389name = "num-traits"
390version = "0.2.17"
391source = "registry+https://github.com/rust-lang/crates.io-index"
392checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
393dependencies = [
394 "autocfg",
395]
396
397[[package]]
398name = "object"
399version = "0.32.2"
400source = "registry+https://github.com/rust-lang/crates.io-index"
401checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
402dependencies = [
403 "memchr",
404]
405
406[[package]]
407name = "once_cell"
408version = "1.19.0"
409source = "registry+https://github.com/rust-lang/crates.io-index"
410checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
411
412[[package]]
413name = "overload"
414version = "0.1.1"
415source = "registry+https://github.com/rust-lang/crates.io-index"
416checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
417
418[[package]]
419name = "owo-colors"
420version = "3.5.0"
421source = "registry+https://github.com/rust-lang/crates.io-index"
422checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
423
424[[package]]
425name = "pin-project-lite"
426version = "0.2.13"
427source = "registry+https://github.com/rust-lang/crates.io-index"
428checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
429
430[[package]]
431name = "proc-macro2"
432version = "1.0.71"
433source = "registry+https://github.com/rust-lang/crates.io-index"
434checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
435dependencies = [
436 "unicode-ident",
437]
438
439[[package]]
440name = "quote"
441version = "1.0.33"
442source = "registry+https://github.com/rust-lang/crates.io-index"
443checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
444dependencies = [
445 "proc-macro2",
446]
447
448[[package]]
449name = "regex"
450version = "1.10.2"
451source = "registry+https://github.com/rust-lang/crates.io-index"
452checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
453dependencies = [
454 "aho-corasick",
455 "memchr",
456 "regex-automata 0.4.3",
457 "regex-syntax 0.8.2",
458]
459
460[[package]]
461name = "regex-automata"
462version = "0.1.10"
463source = "registry+https://github.com/rust-lang/crates.io-index"
464checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
465dependencies = [
466 "regex-syntax 0.6.29",
467]
468
469[[package]]
470name = "regex-automata"
471version = "0.4.3"
472source = "registry+https://github.com/rust-lang/crates.io-index"
473checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
474dependencies = [
475 "aho-corasick",
476 "memchr",
477 "regex-syntax 0.8.2",
478]
479
480[[package]]
481name = "regex-syntax"
482version = "0.6.29"
483source = "registry+https://github.com/rust-lang/crates.io-index"
484checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
485
486[[package]]
487name = "regex-syntax"
488version = "0.8.2"
489source = "registry+https://github.com/rust-lang/crates.io-index"
490checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
491
492[[package]]
493name = "rustc-demangle"
494version = "0.1.23"
495source = "registry+https://github.com/rust-lang/crates.io-index"
496checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
497
498[[package]]
499name = "serde"
500version = "1.0.193"
501source = "registry+https://github.com/rust-lang/crates.io-index"
502checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
503dependencies = [
504 "serde_derive",
505]
506
507[[package]]
508name = "serde_derive"
509version = "1.0.193"
510source = "registry+https://github.com/rust-lang/crates.io-index"
511checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
512dependencies = [
513 "proc-macro2",
514 "quote",
515 "syn",
516]
517
518[[package]]
519name = "serde_spanned"
520version = "0.6.5"
521source = "registry+https://github.com/rust-lang/crates.io-index"
522checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
523dependencies = [
524 "serde",
525]
526
527[[package]]
528name = "sharded-slab"
529version = "0.1.7"
530source = "registry+https://github.com/rust-lang/crates.io-index"
531checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
532dependencies = [
533 "lazy_static",
534]
535
536[[package]]
537name = "smallvec"
538version = "1.11.2"
539source = "registry+https://github.com/rust-lang/crates.io-index"
540checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
541
542[[package]]
543name = "strsim"
544version = "0.10.0"
545source = "registry+https://github.com/rust-lang/crates.io-index"
546checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
547
548[[package]]
549name = "syn"
550version = "2.0.43"
551source = "registry+https://github.com/rust-lang/crates.io-index"
552checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53"
553dependencies = [
554 "proc-macro2",
555 "quote",
556 "unicode-ident",
557]
558
559[[package]]
560name = "thread_local"
561version = "1.1.7"
562source = "registry+https://github.com/rust-lang/crates.io-index"
563checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
564dependencies = [
565 "cfg-if",
566 "once_cell",
567]
568
569[[package]]
570name = "toml"
571version = "0.8.8"
572source = "registry+https://github.com/rust-lang/crates.io-index"
573checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
574dependencies = [
575 "serde",
576 "serde_spanned",
577 "toml_datetime",
578 "toml_edit",
579]
580
581[[package]]
582name = "toml_datetime"
583version = "0.6.5"
584source = "registry+https://github.com/rust-lang/crates.io-index"
585checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
586dependencies = [
587 "serde",
588]
589
590[[package]]
591name = "toml_edit"
592version = "0.21.0"
593source = "registry+https://github.com/rust-lang/crates.io-index"
594checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
595dependencies = [
596 "indexmap",
597 "serde",
598 "serde_spanned",
599 "toml_datetime",
600 "winnow",
601]
602
603[[package]]
604name = "tracing"
605version = "0.1.40"
606source = "registry+https://github.com/rust-lang/crates.io-index"
607checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
608dependencies = [
609 "pin-project-lite",
610 "tracing-attributes",
611 "tracing-core",
612]
613
614[[package]]
615name = "tracing-attributes"
616version = "0.1.27"
617source = "registry+https://github.com/rust-lang/crates.io-index"
618checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
619dependencies = [
620 "proc-macro2",
621 "quote",
622 "syn",
623]
624
625[[package]]
626name = "tracing-core"
627version = "0.1.32"
628source = "registry+https://github.com/rust-lang/crates.io-index"
629checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
630dependencies = [
631 "once_cell",
632 "valuable",
633]
634
635[[package]]
636name = "tracing-error"
637version = "0.2.0"
638source = "registry+https://github.com/rust-lang/crates.io-index"
639checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
640dependencies = [
641 "tracing",
642 "tracing-subscriber",
643]
644
645[[package]]
646name = "tracing-log"
647version = "0.2.0"
648source = "registry+https://github.com/rust-lang/crates.io-index"
649checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
650dependencies = [
651 "log",
652 "once_cell",
653 "tracing-core",
654]
655
656[[package]]
657name = "tracing-subscriber"
658version = "0.3.18"
659source = "registry+https://github.com/rust-lang/crates.io-index"
660checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
661dependencies = [
662 "matchers",
663 "nu-ansi-term",
664 "once_cell",
665 "regex",
666 "sharded-slab",
667 "smallvec",
668 "thread_local",
669 "tracing",
670 "tracing-core",
671 "tracing-log",
672]
673
674[[package]]
675name = "unicode-ident"
676version = "1.0.12"
677source = "registry+https://github.com/rust-lang/crates.io-index"
678checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
679
680[[package]]
681name = "utf8parse"
682version = "0.2.1"
683source = "registry+https://github.com/rust-lang/crates.io-index"
684checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
685
686[[package]]
687name = "valuable"
688version = "0.1.0"
689source = "registry+https://github.com/rust-lang/crates.io-index"
690checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
691
692[[package]]
693name = "wasm-bindgen"
694version = "0.2.89"
695source = "registry+https://github.com/rust-lang/crates.io-index"
696checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
697dependencies = [
698 "cfg-if",
699 "wasm-bindgen-macro",
700]
701
702[[package]]
703name = "wasm-bindgen-backend"
704version = "0.2.89"
705source = "registry+https://github.com/rust-lang/crates.io-index"
706checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
707dependencies = [
708 "bumpalo",
709 "log",
710 "once_cell",
711 "proc-macro2",
712 "quote",
713 "syn",
714 "wasm-bindgen-shared",
715]
716
717[[package]]
718name = "wasm-bindgen-macro"
719version = "0.2.89"
720source = "registry+https://github.com/rust-lang/crates.io-index"
721checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
722dependencies = [
723 "quote",
724 "wasm-bindgen-macro-support",
725]
726
727[[package]]
728name = "wasm-bindgen-macro-support"
729version = "0.2.89"
730source = "registry+https://github.com/rust-lang/crates.io-index"
731checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
732dependencies = [
733 "proc-macro2",
734 "quote",
735 "syn",
736 "wasm-bindgen-backend",
737 "wasm-bindgen-shared",
738]
739
740[[package]]
741name = "wasm-bindgen-shared"
742version = "0.2.89"
743source = "registry+https://github.com/rust-lang/crates.io-index"
744checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
745
746[[package]]
747name = "winapi"
748version = "0.3.9"
749source = "registry+https://github.com/rust-lang/crates.io-index"
750checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
751dependencies = [
752 "winapi-i686-pc-windows-gnu",
753 "winapi-x86_64-pc-windows-gnu",
754]
755
756[[package]]
757name = "winapi-i686-pc-windows-gnu"
758version = "0.4.0"
759source = "registry+https://github.com/rust-lang/crates.io-index"
760checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
761
762[[package]]
763name = "winapi-x86_64-pc-windows-gnu"
764version = "0.4.0"
765source = "registry+https://github.com/rust-lang/crates.io-index"
766checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
767
768[[package]]
769name = "windows-core"
770version = "0.52.0"
771source = "registry+https://github.com/rust-lang/crates.io-index"
772checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
773dependencies = [
774 "windows-targets 0.52.0",
775]
776
777[[package]]
778name = "windows-sys"
779version = "0.52.0"
780source = "registry+https://github.com/rust-lang/crates.io-index"
781checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
782dependencies = [
783 "windows-targets 0.52.0",
784]
785
786[[package]]
787name = "windows-targets"
788version = "0.48.5"
789source = "registry+https://github.com/rust-lang/crates.io-index"
790checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
791dependencies = [
792 "windows_aarch64_gnullvm 0.48.5",
793 "windows_aarch64_msvc 0.48.5",
794 "windows_i686_gnu 0.48.5",
795 "windows_i686_msvc 0.48.5",
796 "windows_x86_64_gnu 0.48.5",
797 "windows_x86_64_gnullvm 0.48.5",
798 "windows_x86_64_msvc 0.48.5",
799]
800
801[[package]]
802name = "windows-targets"
803version = "0.52.0"
804source = "registry+https://github.com/rust-lang/crates.io-index"
805checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
806dependencies = [
807 "windows_aarch64_gnullvm 0.52.0",
808 "windows_aarch64_msvc 0.52.0",
809 "windows_i686_gnu 0.52.0",
810 "windows_i686_msvc 0.52.0",
811 "windows_x86_64_gnu 0.52.0",
812 "windows_x86_64_gnullvm 0.52.0",
813 "windows_x86_64_msvc 0.52.0",
814]
815
816[[package]]
817name = "windows_aarch64_gnullvm"
818version = "0.48.5"
819source = "registry+https://github.com/rust-lang/crates.io-index"
820checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
821
822[[package]]
823name = "windows_aarch64_gnullvm"
824version = "0.52.0"
825source = "registry+https://github.com/rust-lang/crates.io-index"
826checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
827
828[[package]]
829name = "windows_aarch64_msvc"
830version = "0.48.5"
831source = "registry+https://github.com/rust-lang/crates.io-index"
832checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
833
834[[package]]
835name = "windows_aarch64_msvc"
836version = "0.52.0"
837source = "registry+https://github.com/rust-lang/crates.io-index"
838checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
839
840[[package]]
841name = "windows_i686_gnu"
842version = "0.48.5"
843source = "registry+https://github.com/rust-lang/crates.io-index"
844checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
845
846[[package]]
847name = "windows_i686_gnu"
848version = "0.52.0"
849source = "registry+https://github.com/rust-lang/crates.io-index"
850checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
851
852[[package]]
853name = "windows_i686_msvc"
854version = "0.48.5"
855source = "registry+https://github.com/rust-lang/crates.io-index"
856checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
857
858[[package]]
859name = "windows_i686_msvc"
860version = "0.52.0"
861source = "registry+https://github.com/rust-lang/crates.io-index"
862checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
863
864[[package]]
865name = "windows_x86_64_gnu"
866version = "0.48.5"
867source = "registry+https://github.com/rust-lang/crates.io-index"
868checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
869
870[[package]]
871name = "windows_x86_64_gnu"
872version = "0.52.0"
873source = "registry+https://github.com/rust-lang/crates.io-index"
874checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
875
876[[package]]
877name = "windows_x86_64_gnullvm"
878version = "0.48.5"
879source = "registry+https://github.com/rust-lang/crates.io-index"
880checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
881
882[[package]]
883name = "windows_x86_64_gnullvm"
884version = "0.52.0"
885source = "registry+https://github.com/rust-lang/crates.io-index"
886checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
887
888[[package]]
889name = "windows_x86_64_msvc"
890version = "0.48.5"
891source = "registry+https://github.com/rust-lang/crates.io-index"
892checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
893
894[[package]]
895name = "windows_x86_64_msvc"
896version = "0.52.0"
897source = "registry+https://github.com/rust-lang/crates.io-index"
898checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
899
900[[package]]
901name = "winnow"
902version = "0.5.31"
903source = "registry+https://github.com/rust-lang/crates.io-index"
904checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c"
905dependencies = [
906 "memchr",
907]
908
909[[package]]
910name = "zsnap"
911version = "0.1.0"
912dependencies = [
913 "chrono",
914 "clap",
915 "color-eyre",
916 "cron",
917 "eyre",
918 "serde",
919 "toml",
920 "tracing",
921 "tracing-subscriber",
922]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..221fcd3
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,21 @@
1[package]
2name = "zsnap"
3version = "0.1.0"
4edition = "2021"
5
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
8[dependencies]
9chrono = "0.4.31"
10clap = { version = "4.4.12", features = ["env", "derive"] }
11color-eyre = "0.6.2"
12cron = "0.12.0"
13eyre = "0.6.11"
14serde = { version = "1.0.193", features = ["derive"] }
15toml = "0.8.8"
16tracing = "0.1.40"
17tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
18
19[profile.release]
20strip = true
21opt-level = "z"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..864dfdf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
1# zsnap
2
3zsnap [--config zsnap.toml] run
4zsnap [--config zsnap.toml] snapshot --tag my-tag dataset
5zsnap [--config zsnap.toml] prune [--tag my-tag] [--keep n] dataset
6
7```zsnap.toml
8[dataset."blackmesa/k8s"]
9schedules = ["hourly", "daily", "weekly", "monthly"]
10recursive = true
11
12[dataset."blackmesa/gitea"]
13schedules = ["hourly", "daily", "weekly", "monthly"]
14
15[schedule.hourly]
16cron = "@hourly"
17keep = 24
18
19[schedule.daily]
20cron = "@daily"
21keep = 7
22
23[schedule.weekly]
24cron = "@weekly"
25keep = 4
26
27[schedule.monthly]
28cron = "@monthly"
29keep = 6
30```
diff --git a/src/cfg.rs b/src/cfg.rs
new file mode 100644
index 0000000..6c3740c
--- /dev/null
+++ b/src/cfg.rs
@@ -0,0 +1,90 @@
1use std::{collections::HashMap, path::Path, str::FromStr};
2
3use eyre::{Context, Result};
4use serde::Deserialize;
5
6#[derive(Debug, Default)]
7pub struct Config {
8 pub datasets: Vec<Dataset>,
9 pub schedules: Vec<Schedule>,
10}
11
12impl Config {
13 pub fn dataset(&self, name: &str) -> Option<&Dataset> {
14 self.datasets.iter().find(|d| d.name == name)
15 }
16
17 pub fn tag(&self, tag: &str) -> Option<&Schedule> {
18 self.schedules.iter().find(|s| s.tag == tag)
19 }
20}
21
22#[derive(Debug, Clone)]
23pub struct Dataset {
24 pub name: String,
25 pub schedules: Vec<String>,
26 pub recursive: bool,
27}
28
29impl Dataset {
30 pub fn has_tag(&self, tag: &str) -> bool {
31 self.schedules.iter().any(|s| s == tag)
32 }
33}
34
35#[derive(Debug, Clone)]
36pub struct Schedule {
37 pub tag: String,
38 pub cron: cron::Schedule,
39 pub keep: usize,
40}
41
42#[derive(Debug, Deserialize)]
43struct TomlConfig {
44 dataset: HashMap<String, TomlDataset>,
45 schedule: HashMap<String, TomlSchedule>,
46}
47
48#[derive(Debug, Deserialize)]
49struct TomlDataset {
50 recursive: Option<bool>,
51 schedules: Vec<String>,
52}
53
54#[derive(Debug, Deserialize)]
55struct TomlSchedule {
56 cron: String,
57 keep: usize,
58}
59
60pub fn read(path: &Path) -> Result<Config> {
61 let content = std::fs::read_to_string(path)?;
62 let config = toml::from_str::<TomlConfig>(&content)?;
63
64 let mut datasets = Vec::new();
65 let mut schedules = Vec::new();
66
67 for (name, dataset) in config.dataset {
68 let schedules = dataset.schedules;
69 let recursive = dataset.recursive;
70
71 datasets.push(Dataset {
72 name,
73 schedules,
74 recursive: recursive.unwrap_or_default(),
75 });
76 }
77
78 for (name, schedule) in config.schedule {
79 let cron = cron::Schedule::from_str(&schedule.cron)
80 .with_context(|| format!("parsing cron schedule: '{}'", &schedule.cron))?;
81 let keep = schedule.keep;
82
83 schedules.push(Schedule { tag: name, cron, keep });
84 }
85
86 Ok(Config {
87 datasets,
88 schedules,
89 })
90}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..e31764d
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,135 @@
1#![feature(try_blocks)]
2use std::path::PathBuf;
3
4use clap::Parser;
5use eyre::{Context, Result};
6
7mod cfg;
8mod zfs;
9
10#[derive(Debug, Parser)]
11struct Args {
12 /// Path to the configuration file
13 #[clap(long, env = "ZSNAP_CONFIG")]
14 config: Option<PathBuf>,
15
16 #[clap(subcommand)]
17 command: Subcommand,
18}
19
20#[derive(Debug, Parser)]
21enum Subcommand {
22 Run(RunArgs),
23 Snapshot(SnapshotArgs),
24 Prune(PruneArgs),
25}
26
27#[derive(Debug, Parser)]
28struct RunArgs {}
29
30#[derive(Debug, Parser)]
31struct SnapshotArgs {
32 /// the tag to snapshot with
33 #[clap(long)]
34 tag: String,
35
36 #[clap(long)]
37 recursive: bool,
38
39 /// the datasets to snapshot
40 dataset: Vec<String>,
41}
42
43#[derive(Debug, Parser)]
44struct PruneArgs {
45 /// the tag to prune
46 #[clap(long)]
47 tag: String,
48
49 /// the number of snapshots to keep
50 #[clap(long)]
51 keep: usize,
52
53 /// whether to prune recursively
54 #[clap(long)]
55 recursive: bool,
56
57 /// the datasets to prune
58 dataset: Vec<String>,
59}
60
61fn main() -> Result<()> {
62 color_eyre::install()?;
63 tracing_subscriber::fmt::init();
64
65 let args = Args::parse();
66 let config = match args.config {
67 Some(config_path) => cfg::read(&config_path).context("reading config")?,
68 None => Default::default(),
69 };
70
71 match args.command {
72 Subcommand::Run(_) => cmd_run(config),
73 Subcommand::Snapshot(args) => cmd_snapshot(args),
74 Subcommand::Prune(args) => cmd_prune(args),
75 }
76}
77
78fn cmd_run(config: cfg::Config) -> Result<()> {
79 for schedule in config.schedules {
80 let datasets = config.datasets.clone();
81 std::thread::spawn(move || snapshot_mainloop(schedule, datasets));
82 }
83 loop {
84 std::thread::park();
85 }
86}
87
88fn cmd_snapshot(args: SnapshotArgs) -> Result<()> {
89 for dataset in args.dataset {
90 zfs::snapshot_create(&args.tag, &dataset, args.recursive)?;
91 }
92 Ok(())
93}
94
95fn cmd_prune(args: PruneArgs) -> Result<()> {
96 for dataset in args.dataset {
97 zfs::snapshot_prune(&args.tag, &dataset, args.recursive, args.keep)?;
98 }
99 Ok(())
100}
101
102fn snapshot_mainloop(schedule: cfg::Schedule, datasets: Vec<cfg::Dataset>) {
103 for next_trigger in schedule.cron.upcoming(chrono::Utc) {
104 let now = chrono::Utc::now().timestamp();
105 let next = next_trigger.timestamp();
106 let sleep = next.saturating_sub(now) as u64;
107 std::thread::sleep(std::time::Duration::from_secs(sleep));
108
109 for dataset in datasets.iter().filter(|d| d.has_tag(&schedule.tag)) {
110 if let Err(err) = zfs::snapshot_create(&schedule.tag, &dataset.name, dataset.recursive)
111 {
112 tracing::error!(
113 tag = schedule.tag,
114 dataset = dataset.name,
115 "failed to create snapshot: {:#}",
116 err
117 );
118 }
119
120 if let Err(err) = zfs::snapshot_prune(
121 &schedule.tag,
122 &dataset.name,
123 dataset.recursive,
124 schedule.keep,
125 ) {
126 tracing::error!(
127 tag = schedule.tag,
128 dataset = dataset.name,
129 "failed to prune snapshots: {:#}",
130 err
131 );
132 }
133 }
134 }
135}
diff --git a/src/zfs.rs b/src/zfs.rs
new file mode 100644
index 0000000..380efa6
--- /dev/null
+++ b/src/zfs.rs
@@ -0,0 +1,87 @@
1use std::process::Command;
2
3use eyre::{Context, Result};
4
5const PROPERTY_TAG: &str = "zsnap:tag";
6
7#[derive(Debug)]
8struct ZfsSnapshot {
9 name: String,
10 dataset: String,
11 time: chrono::DateTime<chrono::Utc>,
12 tag: String,
13}
14
15pub fn snapshot_create(tag: &str, dataset: &str, recursive: bool) -> Result<()> {
16 let time = chrono::Utc::now();
17 let name = format!("{}@{}", dataset, time.format("zsnap_%Y-%m-%d_%H-%M-%S"));
18 let rec = if recursive { "-r" } else { "" };
19 let cmd = format!("zfs snapshot -o {PROPERTY_TAG}={tag} {rec} {name}");
20 let _output = run_shell_command(&cmd)?;
21 Ok(())
22}
23
24pub fn snapshot_prune(tag: &str, dataset: &str, recursive: bool, keep: usize) -> Result<()> {
25 let mut snapshots = zfs_snapshot_list()?
26 .into_iter()
27 .filter(|s| s.dataset == dataset && s.tag == tag)
28 .collect::<Vec<_>>();
29 snapshots.sort_by_key(|s| s.time);
30 snapshots.reverse();
31 tracing::trace!("snapshots for dataset '{dataset}': {snapshots:?}");
32 tracing::trace!("skipping {keep} snapshots");
33
34 for snapshot in snapshots.into_iter().skip(keep) {
35 tracing::debug!(name = snapshot.name.as_str(), "destroying snapshot");
36 let rec = if recursive { "-r" } else { "" };
37 let cmd = format!("zfs destroy {} {}", rec, snapshot.name);
38 let _output = run_shell_command(&cmd)?;
39 }
40
41 Ok(())
42}
43
44fn zfs_snapshot_list() -> Result<Vec<ZfsSnapshot>> {
45 fn parse_line(line: &str) -> Option<ZfsSnapshot> {
46 let mut parts = line.split('\t');
47 let name = parts.next()?;
48 let tag = parts.next()?;
49
50 let mut parts = name.split('@');
51 let dataset = parts.next()?;
52 let time = parts.next()?;
53 let time = chrono::NaiveDateTime::parse_from_str(time, "zsnap_%Y-%m-%d_%H-%M-%S").ok()?;
54
55 if tag == "-" {
56 return None;
57 }
58
59 Some(ZfsSnapshot {
60 name: name.to_string(),
61 dataset: dataset.to_string(),
62 time: time.and_utc(),
63 tag: tag.to_string(),
64 })
65 }
66
67 let output = run_shell_command(&format!("zfs list -H -t snapshot -o name,{PROPERTY_TAG}"))?;
68 Ok(output.lines().filter_map(parse_line).collect())
69}
70
71fn run_shell_command(cmd: &str) -> Result<String> {
72 tracing::debug!("running shell command '{cmd}'");
73 let output = Command::new("/bin/sh")
74 .arg("-c")
75 .arg(cmd)
76 .output()
77 .with_context(|| format!("running shell command '{cmd}'"))?;
78 if !output.status.success() {
79 let stderr = String::from_utf8(output.stderr).context("converting shell output to utf8")?;
80 let stderr = stderr.trim();
81 tracing::error!("shell command failed: '{stderr}'");
82 return Err(eyre::eyre!("shell command failed: '{stderr}'"));
83 }
84 let output = String::from_utf8(output.stdout).context("converting shell output to utf8")?;
85 tracing::trace!("shell command output: '{output}'");
86 Ok(output)
87}