From 492d7c25094452e1183be86ccc75d39de233b7ef Mon Sep 17 00:00:00 2001 From: Jason Rogena Date: Sat, 5 Oct 2024 22:42:58 +0100 Subject: [PATCH 1/3] Rewrite the inotify code to support adding a new notification system Signed-off-by: Jason Rogena --- Cargo.lock | 387 +++++++++++++++------------- Cargo.toml | 6 +- src/fs_notify/inotify.rs | 96 +++++++ src/fs_notify/mod.rs | 81 +++--- src/fs_notify/tests_supported_os.rs | 4 +- src/main.rs | 18 +- 6 files changed, 376 insertions(+), 216 deletions(-) create mode 100644 src/fs_notify/inotify.rs diff --git a/Cargo.lock b/Cargo.lock index 4f12e8d..f911347 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" version = "0.7.18" @@ -83,12 +98,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "block-buffer" version = "0.10.3" @@ -113,6 +149,12 @@ version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + [[package]] name = "cc" version = "1.0.77" @@ -140,7 +182,7 @@ dependencies = [ "iana-time-zone", "num-integer", "num-traits", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -211,7 +253,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -247,13 +289,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" -dependencies = [ - "cfg-if 1.0.0", - "lazy_static", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -333,14 +371,14 @@ checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" [[package]] name = "filetime" -version = "0.2.16" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.13", - "winapi 0.3.9", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -359,6 +397,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" name = "fs-librarian" version = "0.3.3" dependencies = [ + "bitflags 2.6.0", "clap", "exitcode", "notify", @@ -367,45 +406,23 @@ dependencies = [ "serde_derive", "tera", "thiserror", + "tokio-util", "toml", "tree_magic", "ttl_cache", ] [[package]] -name = "fsevent" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" -dependencies = [ - "bitflags", - "fsevent-sys", -] - -[[package]] -name = "fsevent-sys" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" -dependencies = [ - "libc", -] - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" +name = "futures-sink" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "generic-array" @@ -425,9 +442,15 @@ checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "globset" version = "0.4.8" @@ -447,7 +470,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "ignore", "walkdir", ] @@ -484,7 +507,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -527,11 +550,11 @@ dependencies = [ [[package]] name = "inotify" -version = "0.7.1" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" dependencies = [ - "bitflags", + "bitflags 1.3.2", "inotify-sys", "libc", ] @@ -545,15 +568,6 @@ dependencies = [ "libc", ] -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - [[package]] name = "itoa" version = "1.0.2" @@ -570,32 +584,36 @@ dependencies = [ ] [[package]] -name = "kernel32-sys" -version = "0.2.2" +name = "kqueue" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "kqueue-sys", + "libc", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "kqueue-sys" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] [[package]] -name = "lazycell" -version = "1.3.0" +name = "lazy_static" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libm" @@ -603,6 +621,17 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.5.7", +] + [[package]] name = "link-cplusplus" version = "1.0.8" @@ -652,57 +681,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow", - "net2", - "slab", - "winapi 0.2.8", -] - -[[package]] -name = "mio-extras" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" -dependencies = [ - "lazycell", - "log", - "mio", - "slab", -] - -[[package]] -name = "miow" -version = "0.2.2" +name = "miniz_oxide" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", + "adler2", ] [[package]] -name = "net2" -version = "0.2.37" +name = "mio" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ - "cfg-if 0.1.10", "libc", - "winapi 0.3.9", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", ] [[package]] @@ -716,20 +712,19 @@ dependencies = [ [[package]] name = "notify" -version = "4.0.17" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags", + "bitflags 2.6.0", "filetime", - "fsevent", - "fsevent-sys", "inotify", + "kqueue", "libc", + "log", "mio", - "mio-extras", "walkdir", - "winapi 0.3.9", + "windows-sys 0.48.0", ] [[package]] @@ -751,6 +746,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr 2.6.3", +] + [[package]] name = "once_cell" version = "1.12.0" @@ -778,7 +782,7 @@ dependencies = [ "libc", "redox_syscall 0.1.57", "smallvec", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -888,6 +892,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -950,11 +960,11 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -986,6 +996,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "ryu" version = "1.0.10" @@ -1061,12 +1077,6 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" -[[package]] -name = "slab" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" - [[package]] name = "slug" version = "0.1.4" @@ -1170,6 +1180,29 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "pin-project-lite", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.5.11" @@ -1294,7 +1327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", - "winapi 0.3.9", + "winapi", "winapi-util", ] @@ -1304,6 +1337,12 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.82" @@ -1358,12 +1397,6 @@ version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -1374,12 +1407,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -1392,7 +1419,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1416,7 +1443,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -1436,17 +1472,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1457,9 +1494,9 @@ checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1469,9 +1506,9 @@ checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1481,9 +1518,15 @@ checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1493,9 +1536,9 @@ checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1505,9 +1548,9 @@ checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -1517,9 +1560,9 @@ checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1529,16 +1572,6 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index aae96b8..ed0e835 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,8 @@ tree_magic = "0.2.3" tera = "1" clap = { version = "4.5", features = ["derive"] } exitcode = "1.1.2" -notify = "4.0.16" ttl_cache = "0.5.1" -thiserror = "1.0.63" \ No newline at end of file +thiserror = "1.0.63" +bitflags = "2.6.0" +tokio-util = "0.7.10" +notify = { version = "6.1.1", default-features = false, features = ["macos_kqueue"] } diff --git a/src/fs_notify/inotify.rs b/src/fs_notify/inotify.rs new file mode 100644 index 0000000..0fda65b --- /dev/null +++ b/src/fs_notify/inotify.rs @@ -0,0 +1,96 @@ +use super::{FsOp, Notification, Notifier}; +use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; +use std::{ + collections::HashSet, + env::consts::OS, + path::PathBuf, + sync::mpsc::{channel, Sender}, + thread, +}; +use tokio_util::sync::CancellationToken; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("The feature '{0}' is unsupported")] + UnsupportedFeature(String), + #[error("An error was thrown by the filesystem notification system")] + Notify(#[from] notify::Error), + #[error("An error was thrown while trying to interract with the notification system")] + Send(#[from] std::sync::mpsc::SendError), +} + +pub(crate) struct Inotify { + stop_cancellation_token: CancellationToken, +} + +impl Inotify { + pub(crate) fn init() -> Self { + Inotify { + stop_cancellation_token: CancellationToken::new(), + } + } + fn convert_op(event_kind: EventKind) -> FsOp { + let mut fs_op = FsOp::all(); + if !event_kind.is_remove() { + fs_op.remove(FsOp::REMOVE); + } + + fs_op + } +} + +impl Notifier for Inotify { + fn start_watching( + &mut self, + paths: &HashSet, + notification_sender: Sender, + ) -> Result<(), super::Error> { + if OS == "windows" { + return Err(Error::UnsupportedFeature("directory watching".to_string()).into()); + } + + let (watcher_sender, watcher_receiver) = channel(); + let mut watcher: RecommendedWatcher = + RecommendedWatcher::new(watcher_sender, Config::default())?; + + for cur_path in paths { + println!("Watching path {:?}", cur_path); + watcher.watch(&cur_path, RecursiveMode::Recursive)?; + } + + let stop_cancellation_token = self.stop_cancellation_token.clone(); + thread::spawn(move || loop { + match watcher_receiver.recv() { + Ok(Ok(Event { + kind, + paths, + attrs: _, + })) => { + for path in paths { + let notification = Notification { + path, + op: Self::convert_op(kind), + }; + if let Err(e) = notification_sender.send(notification) { + eprint!("Unable to notify upwards a filesystem event: {:?}", e); + } + } + } + Ok(e) => eprintln!("FS watcher returned a broken event: {:?}", e), + Err(e) => { + panic!("Inotify watcher returned an error: {}", e); + } + } + + if stop_cancellation_token.is_cancelled() { + break; + } + }); + + Ok(()) + } + + fn stop_watching(&mut self) { + self.stop_cancellation_token.cancel(); + } +} diff --git a/src/fs_notify/mod.rs b/src/fs_notify/mod.rs index d60efdb..d1e5115 100644 --- a/src/fs_notify/mod.rs +++ b/src/fs_notify/mod.rs @@ -1,11 +1,12 @@ use crate::config::FsWatch; -use notify::{Op, RawEvent, RecommendedWatcher, RecursiveMode, Watcher}; use std::collections::HashSet; -use std::env::consts::OS; +use std::path::PathBuf; use std::sync::mpsc::{channel, Receiver, Sender}; use std::time::Duration; use ttl_cache::TtlCache; +mod inotify; + #[cfg(test)] #[cfg(target_family = "unix")] mod tests_supported_os; @@ -15,51 +16,67 @@ mod tests_unsupported_os; #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("The feature '{0}' is unsupported")] - UnsupportedFeature(String), #[error("An error was thrown by the filesystem notification system")] Notify(#[from] notify::Error), #[error("An error was thrown while trying to interract with the notification system")] Send(#[from] std::sync::mpsc::SendError), + #[error("An error was returned by the Inotify notification system")] + Inotify(#[from] inotify::Error), } pub struct Notify<'a> { - _watcher: RecommendedWatcher, on_event_sender: Sender, - watcher_receiver: Receiver, unwatch_receiver: Receiver, notify_ttl: TtlCache, config: &'a Option, + paths: HashSet, +} + +bitflags! { +/// Holds a set of bit flags representing the actions for the event. +/// For a list of possible values, have a look at the [notify::op](index.html) documentation. +/// Multiple actions may be delivered in a single event. + + #[derive(Debug)] + pub struct FsOp: u32 { +/// Removed + const REMOVE = 0b000_0001; +/// Catch-all for any other + const OTHER = 0b000_0010; + } +} + +#[derive(Debug)] +struct Notification { + path: PathBuf, + op: FsOp, +} + +trait Notifier { + fn start_watching( + &mut self, + paths: &HashSet, + notification_sender: Sender, + ) -> Result<(), Error>; + fn stop_watching(&mut self); } impl<'a> Notify<'a> { #[allow(dead_code)] pub fn new( config: &'a Option, - paths: &HashSet, + paths: HashSet, on_event_sender: Sender, ) -> Result<(Notify<'a>, Sender), Error> { - if OS == "windows" { - return Err(Error::UnsupportedFeature("directory watching".to_string())); - } - - let (watcher_sender, watcher_receiver) = channel(); - let mut watcher: RecommendedWatcher = Watcher::new_raw(watcher_sender)?; - - for cur_path in paths { - watcher.watch(cur_path, RecursiveMode::Recursive)?; - } - let (unwatch_sender, unwatch_receiver) = channel(); let notify_ttl: TtlCache = TtlCache::new(100000); Ok(( Notify { - _watcher: watcher, on_event_sender, - watcher_receiver, unwatch_receiver, notify_ttl, config, + paths, }, unwatch_sender, )) @@ -102,15 +119,15 @@ impl<'a> Notify<'a> { } #[allow(dead_code)] - pub fn watch(&mut self) { + pub fn watch(&mut self) -> Result<(), Error> { + let (notification_sender, notification_receiver) = channel(); + let mut i = inotify::Inotify::init(); + i.start_watching(&self.paths, notification_sender)?; + loop { - match self.watcher_receiver.recv() { - Ok(RawEvent { - path: Some(path), - op: Ok(op), - cookie: _, - }) => { - if !path.is_dir() && !op.contains(Op::REMOVE) { + match notification_receiver.recv() { + Ok(Notification { path, op }) => { + if !path.is_dir() && !op.contains(FsOp::REMOVE) { if let Some(path_str) = path.as_os_str().to_str() { if !self.should_notify(path_str) { println!("Ignoring {:?} event for '{}' since it occurred within the TTL of last event", op, path_str) @@ -124,16 +141,20 @@ impl<'a> Notify<'a> { } } } - Ok(e) => eprintln!("FS watcher returned a broken event: {:?}", e), - Err(e) => eprintln!("FS watcher returned an error: {}", e), + Err(e) => { + panic!("FS watcher returned an error: {}", e); + } } if let Ok(k) = self.unwatch_receiver.try_recv() { if k { + i.stop_watching(); break; } } } + + Ok(()) } #[allow(dead_code)] diff --git a/src/fs_notify/tests_supported_os.rs b/src/fs_notify/tests_supported_os.rs index 2e97194..572ff38 100644 --- a/src/fs_notify/tests_supported_os.rs +++ b/src/fs_notify/tests_supported_os.rs @@ -25,7 +25,7 @@ fn test_watch() { let (mut notify_obj, unwatch_sender) = Notify::new(&None, &paths, on_event_sender).unwrap(); thread::spawn(move || { - notify_obj.watch(); + notify_obj.watch().unwrap(); }); let test_str_clone = test_str.to_string(); let (run_tests_sender, run_tests_receiver) = channel(); @@ -81,7 +81,7 @@ fn test_notify_ttl() { .unwrap(); thread::spawn(move || { - notify_obj.watch(); + notify_obj.watch().unwrap(); }); let test_str_clone = test_str.to_string(); let (run_tests_sender, run_tests_receiver) = channel(); diff --git a/src/main.rs b/src/main.rs index 90feb24..0dc1238 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,12 +5,15 @@ mod mime_type; mod template; use clap::{Args, Parser, Subcommand}; use std::collections::HashSet; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::channel; use std::thread; use std::time::Duration; +#[macro_use] +extern crate bitflags; + static GLOBAL_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0); static GLOBAL_FAILED_TREADS: AtomicUsize = AtomicUsize::new(0); @@ -101,17 +104,17 @@ fn get_config(config_path: &String) -> config::Config { fn watch(config_path: &String, dry_run: bool) { let conf = get_config(config_path); - let mut paths: HashSet = HashSet::new(); + let mut paths: HashSet = HashSet::new(); // let mut libraries = Vec::new(); for cur_lib_config in conf.libraries { for cur_dir in cur_lib_config.1.filter.directories { - paths.insert(cur_dir); + paths.insert(PathBuf::from(cur_dir)); } } let (on_event_sender, on_event_receiver) = channel(); let (mut notify_obj, _) = - fs_notify::Notify::new(&conf.fs_watch, &paths, on_event_sender).unwrap(); + fs_notify::Notify::new(&conf.fs_watch, paths, on_event_sender).unwrap(); let config_path_clone = config_path.clone(); thread::spawn(move || loop { let path = match on_event_receiver.recv() { @@ -147,7 +150,12 @@ fn watch(config_path: &String, dry_run: bool) { }); single_shot(config_path, dry_run); - notify_obj.watch(); + if let Err(e) = notify_obj.watch() { + eprint!( + "An error was thrown while attempting to watch a library: {:?}", + e + ); + } } fn single_shot(config_path: &String, dry_run: bool) { From e68655c52c23914bb428534e846be63e3046b57b Mon Sep 17 00:00:00 2001 From: Jason Rogena Date: Sun, 6 Oct 2024 16:49:54 +0100 Subject: [PATCH 2/3] Add support for fanotify on Linux Signed-off-by: Jason Rogena --- Cargo.lock | 60 ++++++++++++ Cargo.toml | 3 + src/fs_notify/fanotify_notifier.rs | 126 ++++++++++++++++++++++++++ src/fs_notify/inotify.rs | 96 -------------------- src/fs_notify/mod.rs | 31 +++++-- src/fs_notify/notify_notifier.rs | 106 ++++++++++++++++++++++ src/fs_notify/tests_unsupported_os.rs | 6 +- 7 files changed, 322 insertions(+), 106 deletions(-) create mode 100644 src/fs_notify/fanotify_notifier.rs delete mode 100644 src/fs_notify/inotify.rs create mode 100644 src/fs_notify/notify_notifier.rs diff --git a/Cargo.lock b/Cargo.lock index f911347..4c8c9dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -363,12 +363,42 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "enum-iterator" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd242f399be1da0a5354aa462d57b4ab2b4ee0683cc552f7c007d2d12d36e94" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.46", +] + [[package]] name = "exitcode" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" +[[package]] +name = "fanotify-rs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9525f3fd6f3ae518fdc1642663a93a7c8155e863699d203303b22c45ebbbfe8" +dependencies = [ + "enum-iterator", + "libc", +] + [[package]] name = "filetime" version = "0.2.25" @@ -400,6 +430,8 @@ dependencies = [ "bitflags 2.6.0", "clap", "exitcode", + "fanotify-rs", + "nix", "notify", "regex", "serde", @@ -680,6 +712,15 @@ version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -701,6 +742,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset", + "pin-utils", +] + [[package]] name = "nom" version = "3.2.1" @@ -898,6 +952,12 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "ppv-lite86" version = "0.2.16" diff --git a/Cargo.toml b/Cargo.toml index ed0e835..e13f1c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,6 @@ thiserror = "1.0.63" bitflags = "2.6.0" tokio-util = "0.7.10" notify = { version = "6.1.1", default-features = false, features = ["macos_kqueue"] } +[target.'cfg(target_os = "linux")'.dependencies] +fanotify-rs = "0.3.1" +nix = "0.26.4" diff --git a/src/fs_notify/fanotify_notifier.rs b/src/fs_notify/fanotify_notifier.rs new file mode 100644 index 0000000..71ee19e --- /dev/null +++ b/src/fs_notify/fanotify_notifier.rs @@ -0,0 +1,126 @@ +use super::{Notification, Notifier}; +#[cfg(target_os = "linux")] +use { + fanotify::{high_level::{Fanotify, FanotifyMode,FanEvent}, low_level::{FAN_CLOSE_WRITE, FAN_CREATE, FAN_MODIFY, FAN_MOVE_SELF}}, + nix::poll::{poll, PollFd, PollFlags}, + std::thread, + super::FsOp, + std::os::fd::AsFd, + std::os::fd::AsRawFd, +}; +use std::{ + collections::HashSet, + env::consts::OS, + path::PathBuf, + sync::mpsc::Sender, +}; +use tokio_util::sync::CancellationToken; + +#[allow(dead_code)] +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("The feature '{0}' is unsupported")] + UnsupportedFeature(String), + //#[error("An error was thrown by the filesystem notification system")] + //Faotify(#[from] fanotify::Error), + #[error("An error was thrown while trying to interract with the notification system")] + Send(#[from] std::sync::mpsc::SendError), +} + +#[allow(dead_code)] +pub(crate) struct FanotifyNotifier { + stop_cancellation_token: CancellationToken, +} + +impl FanotifyNotifier { + pub(crate) fn new() -> Self { + FanotifyNotifier { + stop_cancellation_token: CancellationToken::new(), + } + } + + #[cfg(target_os = "linux")] + fn convert_op(events: Vec) -> FsOp { + let mut fs_op = FsOp::OTHER; + for cur_event in events { + if cur_event == FanEvent::Delete || cur_event == FanEvent::DeleteSelf { + fs_op.insert(FsOp::REMOVE); + } + } + + fs_op + } + + #[cfg(target_os = "linux")] + fn start_watching_linux( + &mut self, + paths: &HashSet, + notification_sender: Sender, + ) -> Result<(), Error> { + let stop_cancellation_token = self.stop_cancellation_token.clone(); + let local_paths = paths.clone(); + thread::spawn(move || { + let fd = match Fanotify::new_nonblocking(FanotifyMode::NOTIF) { + Ok(f) => f, + Err(e) => panic!("An error occurred while trying to initialise the fanotify watcher: {}", e), + }; + for cur_path in local_paths { + fd.add_mountpoint( + FAN_CREATE | FAN_CLOSE_WRITE | FAN_MOVE_SELF | FAN_MODIFY, + (&cur_path).into(), + ).unwrap(); + } + let fd_handle = fd.as_fd(); + let mut fds = [PollFd::new(fd_handle.as_raw_fd(), PollFlags::POLLIN)]; + loop { + let poll_num = poll(&mut fds, -1).unwrap(); + if poll_num > 0 { + for event in fd.read_event() { + let notification = Notification { + path: PathBuf::from(event.path), + op: Self::convert_op(event.events), + }; + if let Err(e) = notification_sender.send(notification) { + eprint!("Unable to notify upwards a filesystem event: {:?}", e); + } + } + } else { + eprintln!("poll_num <= 0!"); + break; + } + + if stop_cancellation_token.is_cancelled() { + println!("Cancelling watching using FanotifyNotifier"); + break; + } + } + }); + + Ok(()) + } +} + +impl Notifier for FanotifyNotifier { + fn start_watching( + &mut self, + _paths: &HashSet, + _notification_sender: Sender, + ) -> Result<(), super::Error> { + #[cfg(target_os = "linux")] + self.start_watching_linux(_paths, _notification_sender)?; + + Ok(()) + } + + fn stop_watching(&mut self) { + self.stop_cancellation_token.cancel(); + } + + fn is_supported(&self) -> bool { + if OS != "linux" { + return false; + } + + true + } +} diff --git a/src/fs_notify/inotify.rs b/src/fs_notify/inotify.rs deleted file mode 100644 index 0fda65b..0000000 --- a/src/fs_notify/inotify.rs +++ /dev/null @@ -1,96 +0,0 @@ -use super::{FsOp, Notification, Notifier}; -use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; -use std::{ - collections::HashSet, - env::consts::OS, - path::PathBuf, - sync::mpsc::{channel, Sender}, - thread, -}; -use tokio_util::sync::CancellationToken; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("The feature '{0}' is unsupported")] - UnsupportedFeature(String), - #[error("An error was thrown by the filesystem notification system")] - Notify(#[from] notify::Error), - #[error("An error was thrown while trying to interract with the notification system")] - Send(#[from] std::sync::mpsc::SendError), -} - -pub(crate) struct Inotify { - stop_cancellation_token: CancellationToken, -} - -impl Inotify { - pub(crate) fn init() -> Self { - Inotify { - stop_cancellation_token: CancellationToken::new(), - } - } - fn convert_op(event_kind: EventKind) -> FsOp { - let mut fs_op = FsOp::all(); - if !event_kind.is_remove() { - fs_op.remove(FsOp::REMOVE); - } - - fs_op - } -} - -impl Notifier for Inotify { - fn start_watching( - &mut self, - paths: &HashSet, - notification_sender: Sender, - ) -> Result<(), super::Error> { - if OS == "windows" { - return Err(Error::UnsupportedFeature("directory watching".to_string()).into()); - } - - let (watcher_sender, watcher_receiver) = channel(); - let mut watcher: RecommendedWatcher = - RecommendedWatcher::new(watcher_sender, Config::default())?; - - for cur_path in paths { - println!("Watching path {:?}", cur_path); - watcher.watch(&cur_path, RecursiveMode::Recursive)?; - } - - let stop_cancellation_token = self.stop_cancellation_token.clone(); - thread::spawn(move || loop { - match watcher_receiver.recv() { - Ok(Ok(Event { - kind, - paths, - attrs: _, - })) => { - for path in paths { - let notification = Notification { - path, - op: Self::convert_op(kind), - }; - if let Err(e) = notification_sender.send(notification) { - eprint!("Unable to notify upwards a filesystem event: {:?}", e); - } - } - } - Ok(e) => eprintln!("FS watcher returned a broken event: {:?}", e), - Err(e) => { - panic!("Inotify watcher returned an error: {}", e); - } - } - - if stop_cancellation_token.is_cancelled() { - break; - } - }); - - Ok(()) - } - - fn stop_watching(&mut self) { - self.stop_cancellation_token.cancel(); - } -} diff --git a/src/fs_notify/mod.rs b/src/fs_notify/mod.rs index d1e5115..4098058 100644 --- a/src/fs_notify/mod.rs +++ b/src/fs_notify/mod.rs @@ -5,7 +5,8 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::time::Duration; use ttl_cache::TtlCache; -mod inotify; +mod notify_notifier; +mod fanotify_notifier; #[cfg(test)] #[cfg(target_family = "unix")] @@ -16,12 +17,14 @@ mod tests_unsupported_os; #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("An error was thrown by the filesystem notification system")] - Notify(#[from] notify::Error), + #[error("An error was thrown by NotifyNotifier")] + NotifyNotifier(#[from] notify_notifier::Error), + #[error("An error was thrown by NotifyNotifier")] + FanotifyNotifier(#[from] fanotify_notifier::Error), #[error("An error was thrown while trying to interract with the notification system")] Send(#[from] std::sync::mpsc::SendError), - #[error("An error was returned by the Inotify notification system")] - Inotify(#[from] inotify::Error), + #[error("The watching feature is unsupported")] + FeatureNotSupported(), } pub struct Notify<'a> { @@ -59,6 +62,7 @@ trait Notifier { notification_sender: Sender, ) -> Result<(), Error>; fn stop_watching(&mut self); + fn is_supported(&self) -> bool; } impl<'a> Notify<'a> { @@ -121,8 +125,21 @@ impl<'a> Notify<'a> { #[allow(dead_code)] pub fn watch(&mut self) -> Result<(), Error> { let (notification_sender, notification_receiver) = channel(); - let mut i = inotify::Inotify::init(); - i.start_watching(&self.paths, notification_sender)?; + let mut watching = false; + let mut i = notify_notifier::NotifyNotifier::new(); + if i.is_supported() { + i.start_watching(&self.paths, notification_sender.clone())?; + watching = true; + } + let mut fa = fanotify_notifier::FanotifyNotifier::new(); + if fa.is_supported() { + fa.start_watching(&self.paths, notification_sender)?; + watching = true; + } + + if !watching { + return Err(Error::FeatureNotSupported()); + } loop { match notification_receiver.recv() { diff --git a/src/fs_notify/notify_notifier.rs b/src/fs_notify/notify_notifier.rs new file mode 100644 index 0000000..cfd1d0e --- /dev/null +++ b/src/fs_notify/notify_notifier.rs @@ -0,0 +1,106 @@ +use super::{FsOp, Notification, Notifier}; +use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; +use std::{ + collections::HashSet, + env::consts::OS, + path::PathBuf, + sync::mpsc::{channel, Sender}, + thread, +}; +use tokio_util::sync::CancellationToken; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("An error was thrown by the filesystem notification system")] + Notify(#[from] notify::Error), + #[error("An error was thrown while trying to interract with the notification system")] + Send(#[from] std::sync::mpsc::SendError), +} + +pub(crate) struct NotifyNotifier { + stop_cancellation_token: CancellationToken, +} + +impl NotifyNotifier { + pub(crate) fn new() -> Self { + NotifyNotifier { + stop_cancellation_token: CancellationToken::new(), + } + } + fn convert_op(event_kind: EventKind) -> FsOp { + let mut fs_op = FsOp::all(); + if !event_kind.is_remove() { + fs_op.remove(FsOp::REMOVE); + } + + fs_op + } +} + +impl Notifier for NotifyNotifier { + fn start_watching( + &mut self, + paths: &HashSet, + notification_sender: Sender, + ) -> Result<(), super::Error> { + let stop_cancellation_token = self.stop_cancellation_token.clone(); + let local_paths = paths.clone(); + thread::spawn(move || { + let (watcher_sender, watcher_receiver) = channel(); + let mut watcher: RecommendedWatcher = + match RecommendedWatcher::new(watcher_sender, Config::default()) { + Ok(w) => w, + Err(e) => panic!("NotifyNotifier returned an error while initializing: {}", e), + }; + + for cur_path in local_paths { + println!("Watching path {:?}", cur_path); + if let Err(e) = watcher.watch(&cur_path, RecursiveMode::Recursive) { + panic!("NotifyNotifier returned an error while attempting to watch a directory: {}", e); + } + } + loop { + match watcher_receiver.recv() { + Ok(Ok(Event { + kind, + paths, + attrs: _, + })) => { + for path in paths { + let notification = Notification { + path, + op: Self::convert_op(kind), + }; + if let Err(e) = notification_sender.send(notification) { + eprint!("Unable to notify upwards a filesystem event: {:?}", e); + } + } + } + Ok(e) => eprintln!("NotifyNotifier returned a broken event: {:?}", e), + Err(e) => { + panic!("NotifyNotifier returned an error: {}", e); + } + } + + if stop_cancellation_token.is_cancelled() { + println!("Cancelling watching using NotifyNotifier"); + break; + } + } + }); + + Ok(()) + } + + fn stop_watching(&mut self) { + self.stop_cancellation_token.cancel(); + } + + fn is_supported(&self) -> bool { + if OS == "windows" { + return false; + } + + true + } +} diff --git a/src/fs_notify/tests_unsupported_os.rs b/src/fs_notify/tests_unsupported_os.rs index bbd0a17..cf7d687 100644 --- a/src/fs_notify/tests_unsupported_os.rs +++ b/src/fs_notify/tests_unsupported_os.rs @@ -4,12 +4,12 @@ use std::sync::mpsc::channel; #[test] fn test_unsupported_os() { - let paths: HashSet = HashSet::new(); + let paths: HashSet = HashSet::new(); let (on_event_sender, _) = channel(); - match Notify::new(&None, &paths, on_event_sender) { + match Notify::new(&None, paths, on_event_sender) { Ok(_) => panic!(), Err(e) => assert_eq!( - e.get_message(), + e.watch(), "Directory watching is currently not supported in this OS" ), } From cf69f1d95480bba9a291a92b8e851c4a820fed6d Mon Sep 17 00:00:00 2001 From: Jason Rogena Date: Sat, 26 Oct 2024 14:24:03 -0500 Subject: [PATCH 3/3] Fix issue with the MacOS build Signed-off-by: Jason Rogena --- .github/workflows/release.yml | 4 ++++ src/fs_notify/tests_supported_os.rs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e001e17..f517d38 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,6 +92,10 @@ jobs: run: | sudo apt-get update sudo apt-get install gcc-9-multilib lib32gcc-9-dev + - uses: gerlero/brew-install@v1 + if: matrix.os == 'macos-latest' + with: + packages: yamllint - name: Install rust uses: actions-rs/toolchain@v1 with: diff --git a/src/fs_notify/tests_supported_os.rs b/src/fs_notify/tests_supported_os.rs index 572ff38..3031bf1 100644 --- a/src/fs_notify/tests_supported_os.rs +++ b/src/fs_notify/tests_supported_os.rs @@ -18,11 +18,11 @@ fn test_watch() { test_sub_dir.push("sub_dir"); fs::create_dir_all(test_sub_dir.clone()).unwrap(); let test_str = format!("{:?}", time::SystemTime::now()); - let mut paths: HashSet = HashSet::new(); - paths.insert(test_dir.as_os_str().to_str().unwrap().to_string()); - paths.insert(test_sub_dir.as_os_str().to_str().unwrap().to_string()); + let mut paths: HashSet = HashSet::new(); + paths.insert(test_dir.clone()); + paths.insert(test_sub_dir.clone()); let (on_event_sender, on_event_receiver) = channel(); - let (mut notify_obj, unwatch_sender) = Notify::new(&None, &paths, on_event_sender).unwrap(); + let (mut notify_obj, unwatch_sender) = Notify::new(&None, paths, on_event_sender).unwrap(); thread::spawn(move || { notify_obj.watch().unwrap(); @@ -67,15 +67,15 @@ fn test_notify_ttl() { test_sub_dir.push("sub_dir"); fs::create_dir_all(test_sub_dir.clone()).unwrap(); let test_str = format!("{:?}", time::SystemTime::now()); - let mut paths: HashSet = HashSet::new(); - paths.insert(test_dir.as_os_str().to_str().unwrap().to_string()); - paths.insert(test_sub_dir.as_os_str().to_str().unwrap().to_string()); + let mut paths: HashSet = HashSet::new(); + paths.insert(test_dir.clone()); + paths.insert(test_sub_dir.clone()); let (on_event_sender, on_event_receiver) = channel(); let (mut notify_obj, unwatch_sender) = Notify::new( &Some(FsWatch { min_command_exec_freq: Some(60), }), - &paths, + paths, on_event_sender, ) .unwrap();