From 15312b25a105dc85371401cb9758571ca3f25c92 Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Tue, 21 Apr 2026 13:26:46 +0200 Subject: [PATCH 01/13] Updated project directory structure Signed-off-by: Sathishkumar Duraisamy --- .gitignore | 2 + Cargo.lock | 415 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 + build.rs | 3 + src/lib.rs | 3 + src/main.rs | 8 + src/safeaction.rs | 0 src/safeappdb.rs | 2 + src/safeappdb/appsdb.rs | 0 src/safeappdb/global.rs | 23 +++ src/safeinstrument.rs | 0 src/safemonitor.rs | 0 12 files changed, 464 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/safeaction.rs create mode 100644 src/safeappdb.rs create mode 100644 src/safeappdb/appsdb.rs create mode 100644 src/safeappdb/global.rs create mode 100644 src/safeinstrument.rs create mode 100644 src/safemonitor.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef862 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target/ +.idea/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..eeb5f26 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,415 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "annotate-snippets" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92570a3f9c98e7e84df84b71d0965ac99b1871fcd75a3773a3bd1bad13f64cf7" +dependencies = [ + "anstyle", + "memchr", + "unicode-width", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "encoding_rs_io" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83" +dependencies = [ + "encoding_rs", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "js-sys" +version = "0.3.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "safetyguard" +version = "0.1.0" +dependencies = [ + "serde-saphyr", +] + +[[package]] +name = "saphyr-parser-bw" +version = "0.0.611" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dec0c833db75dc98957956b303fe447ffc5eb13f2325ef4c2350f7f3aa69e3" +dependencies = [ + "arraydeque", + "smallvec", + "thiserror", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-saphyr" +version = "0.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fbdfe7a27a1b1633dfc0c4c8e65940b8d819c5ddb9cca48ebc3223b00c8b14" +dependencies = [ + "ahash", + "annotate-snippets", + "base64", + "encoding_rs_io", + "getrandom", + "nohash-hasher", + "num-traits", + "regex", + "saphyr-parser-bw", + "serde", + "smallvec", + "zmij", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e26c1df --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "safetyguard" +version = "0.1.0" +edition = "2024" + +[dependencies] +serde-saphyr = "0.0.23" + diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..cf74f49 --- /dev/null +++ b/build.rs @@ -0,0 +1,3 @@ +fn main() { + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..56a27a4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +#![forbid(unsafe_code)] + +pub mod safeappdb; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..aac9ce3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,8 @@ +mod safemonitor; +mod safeaction; +mod safeinstrument; +mod safeappdb; + +fn main() { + println!("Hello, world!"); +} \ No newline at end of file diff --git a/src/safeaction.rs b/src/safeaction.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/safeappdb.rs b/src/safeappdb.rs new file mode 100644 index 0000000..059665b --- /dev/null +++ b/src/safeappdb.rs @@ -0,0 +1,2 @@ +mod appsdb; +pub mod global; \ No newline at end of file diff --git a/src/safeappdb/appsdb.rs b/src/safeappdb/appsdb.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/safeappdb/global.rs b/src/safeappdb/global.rs new file mode 100644 index 0000000..87a0d83 --- /dev/null +++ b/src/safeappdb/global.rs @@ -0,0 +1,23 @@ + + +pub enum LogLevel { + debug, + info, + warn, + error, +} + +pub enum Mode { + debug, + release, +} +pub struct Global { + pub loglevel: LogLevel, + pub log_file: String, + pub state_file: String, + pub config_file: String, + pub config_file_gen: String, + pub mode: Mode, + pub endpoint: String, +} + diff --git a/src/safeinstrument.rs b/src/safeinstrument.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/safemonitor.rs b/src/safemonitor.rs new file mode 100644 index 0000000..e69de29 From 4324c254811f3ab79f54194818f906986a3e3ef1 Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Tue, 21 Apr 2026 13:27:50 +0200 Subject: [PATCH 02/13] Updated UML digram for Application heartbeat update Signed-off-by: Sathishkumar Duraisamy --- doc/config.yaml | 94 +++++ doc/designdecision.md | 0 doc/papyrus_uml/.project | 11 + doc/papyrus_uml/SafeGuard.aird | 534 +++++++++++++++++++++++++++++ doc/papyrus_uml/SafeGuard.di | 2 + doc/papyrus_uml/SafeGuard.notation | 364 ++++++++++++++++++++ doc/papyrus_uml/SafeGuard.uml | 64 ++++ 7 files changed, 1069 insertions(+) create mode 100644 doc/config.yaml create mode 100644 doc/designdecision.md create mode 100644 doc/papyrus_uml/.project create mode 100644 doc/papyrus_uml/SafeGuard.aird create mode 100644 doc/papyrus_uml/SafeGuard.di create mode 100644 doc/papyrus_uml/SafeGuard.notation create mode 100644 doc/papyrus_uml/SafeGuard.uml diff --git a/doc/config.yaml b/doc/config.yaml new file mode 100644 index 0000000..1738998 --- /dev/null +++ b/doc/config.yaml @@ -0,0 +1,94 @@ +# ========================= +# SafetyGuard Configuration +# +# All config parameter are lower case seperated by "_" if required. +# ========================= + +config_version: 1 + +global: + log_level: INFO + check_interval_ms: 500 # cyclic time to check states of apps + state_file: /var/lib/safetyguard/state.yaml + log_file: /var/log/safetyguard.log + init_file: /etc/safetygurad/config.yaml + init_file_gen: /var/lib/safetyguard/gen_config.yaml + mode: production # Can be development. In case of development, the app configuration + # will be generated which can act as guidance, for example heartbeat_interval_ms as average heartbeat + endpoint: /tmp/safetyguard.sock + +watchdog: + device: /dev/watchdog + timeout_sec: 10 + pretimeout_sec: 5 + kick_interval_ms: 3000 + +recovery: + system_reset_on_failure: true + max_global_failures: 5 + +system_apps: + - name: systemd + timeout_ms: 5000 + safemonitor: + - heartbeat: + heartbeat_update_interval_ms: 500 # Interval it is expected update the heartbeat to safetyguard. It should be less then heartbeat Intervals of threads. Ideally half of the minimum heartbeat interval threads. + threads: + - main: + heartbeat_interval_ms: 1000 + safeaction: + - coresystem: + restart: true + wait_time_ms: 1000 + wait_for_reboot_accept: true # for example, safeapp sends this info or user accepts reboot + - safestate: + - app_name: safeapp + manager: systemd + systemd: + - service_name: safeapp # Runs this app, till system is safe. + +apps: + - name: app_a + timeout_ms: 3000 + max_retries: 3 + critical: true + manager: systemd + systemd: + - service_name: app_a # unit file name. + safeaction: + - systemd: + restart: true + - coresystem: # If not present, system will not rebooted. + restart: true #Upon all retries failure. + - safestate: + - app_name: safeapp + manager: systemd + systemd: + - service_name: safeapp # Runs this app, till system is safe. + safemonitor: + - heartbeat: + threads: + heartbeat_update_interval_ms: 300 + - main: + heartbeat_interval_ms: 1000 + - thread_b: + heartbeat_interval_ms: 500 + - memory: + max_mem: 10kb + + - name: app_b + timeout_ms: 2000 + max_retries: 5 + critical: false + manager: docker # Can be another runtime + docker: + - container_name: app_b + safeaction: + - container: + restart: true + safemonitor: + - heartbeat: + threads: + - main: + heartbeat_interval_ms: 500 + diff --git a/doc/designdecision.md b/doc/designdecision.md new file mode 100644 index 0000000..e69de29 diff --git a/doc/papyrus_uml/.project b/doc/papyrus_uml/.project new file mode 100644 index 0000000..e452601 --- /dev/null +++ b/doc/papyrus_uml/.project @@ -0,0 +1,11 @@ + + + SafeGuard + + + + + + + + diff --git a/doc/papyrus_uml/SafeGuard.aird b/doc/papyrus_uml/SafeGuard.aird new file mode 100644 index 0000000..880d4a8 --- /dev/null +++ b/doc/papyrus_uml/SafeGuard.aird @@ -0,0 +1,534 @@ + + + + SafeGuard.uml + pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml + pathmap://UML_PROFILES/Ecore.profile.uml + pathmap://UML_LIBRARIES/EcorePrimitiveTypes.library.uml + pathmap://UML_PROFILES/Standard.profile.uml + pathmap://UML_METAMODELS/UML.metamodel.uml + pathmap://UML_METAMODELS/Ecore.metamodel.uml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/papyrus_uml/SafeGuard.di b/doc/papyrus_uml/SafeGuard.di new file mode 100644 index 0000000..8c549ee --- /dev/null +++ b/doc/papyrus_uml/SafeGuard.di @@ -0,0 +1,2 @@ + + diff --git a/doc/papyrus_uml/SafeGuard.notation b/doc/papyrus_uml/SafeGuard.notation new file mode 100644 index 0000000..d5ffa5d --- /dev/null +++ b/doc/papyrus_uml/SafeGuard.notation @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/papyrus_uml/SafeGuard.uml b/doc/papyrus_uml/SafeGuard.uml new file mode 100644 index 0000000..3974966 --- /dev/null +++ b/doc/papyrus_uml/SafeGuard.uml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enable security measures + + + + + + + + + + + + + + + + + + + + + + + Enable security aspects as required. + + + + + + + + + + + + From 93c18638c7e82efee85fbbf9af3417f9793c9306 Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Wed, 22 Apr 2026 08:32:21 +0200 Subject: [PATCH 03/13] Updated safetyguard activity diagram Signed-off-by: Sathishkumar Duraisamy --- doc/designdecision.md | 7 + doc/papyrus_uml/SafeGuard.notation | 370 ++++++++++++++++++++++++++++- doc/papyrus_uml/SafeGuard.uml | 42 +++- 3 files changed, 417 insertions(+), 2 deletions(-) diff --git a/doc/designdecision.md b/doc/designdecision.md index e69de29..cbc8c89 100644 --- a/doc/designdecision.md +++ b/doc/designdecision.md @@ -0,0 +1,7 @@ +Control loop: +* No memory allocation is allowed. The various IPC in linux involves memory allocation, where as shared memory doesn't. So shared memory is good for the use-case. Also, it is better to be lock-free. So Single producer and signle consumer queue with shared memory will be use as communication mechanism between apps and safetygurad. +* The application will update the shared memory and then send event via eventfd to indicate new data available in the shared memory. The safety guard will update the app specific details in the safetyguard. +* Also, there is timerfd in the control loop of safety guard. With this the state of all apps will be evaluvated. Such as heartbeats, memory-consuption details, or safeinstruments of the entire system. The timerfd is also responsible for update the watchdog of the system based on the monitored data. +* + + diff --git a/doc/papyrus_uml/SafeGuard.notation b/doc/papyrus_uml/SafeGuard.notation index d5ffa5d..51045df 100644 --- a/doc/papyrus_uml/SafeGuard.notation +++ b/doc/papyrus_uml/SafeGuard.notation @@ -142,7 +142,7 @@ - + @@ -204,6 +204,16 @@ + + + + + + + + + + @@ -361,4 +371,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/papyrus_uml/SafeGuard.uml b/doc/papyrus_uml/SafeGuard.uml index 3974966..3ff29cb 100644 --- a/doc/papyrus_uml/SafeGuard.uml +++ b/doc/papyrus_uml/SafeGuard.uml @@ -22,12 +22,22 @@ - + + + Enable security measures + + Threads update the datestore via API which is part of client library + + + + Heartbeat update are based on heartbeat_update_interval. +Either use threaded model or can call API as part of client library + @@ -61,4 +71,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 78c1d9edc631d3a54bbb1aa96fa13e58c349b28e Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Tue, 28 Apr 2026 15:49:22 +0200 Subject: [PATCH 04/13] Updated global yaml configuration file change update Signed-off-by: Sathishkumar Duraisamy --- Cargo.lock | 82 ++++++++++++++++++------------------ Cargo.toml | 4 +- doc/config.yaml | 4 +- src/main.rs | 5 +-- src/safeappdb/global.rs | 93 ++++++++++++++++++++++++++++++++++------- 5 files changed, 126 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eeb5f26..28cb81f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,15 +15,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - [[package]] name = "annotate-snippets" version = "0.12.15" @@ -47,6 +38,15 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -65,6 +65,12 @@ version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + [[package]] name = "cfg-if" version = "1.0.4" @@ -89,6 +95,18 @@ dependencies = [ "encoding_rs", ] +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "serde", + "uncased", + "version_check", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -170,35 +188,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - [[package]] name = "rustversion" version = "1.0.22" @@ -209,6 +198,8 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" name = "safetyguard" version = "0.1.0" dependencies = [ + "figment", + "serde", "serde-saphyr", ] @@ -235,18 +226,18 @@ dependencies = [ [[package]] name = "serde-saphyr" -version = "0.0.23" +version = "0.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fbdfe7a27a1b1633dfc0c4c8e65940b8d819c5ddb9cca48ebc3223b00c8b14" +checksum = "75e214449d107a81daf1453eb46c9314457660509534883e82db6faca2034a8a" dependencies = [ "ahash", "annotate-snippets", "base64", "encoding_rs_io", + "figment", "getrandom", "nohash-hasher", "num-traits", - "regex", "saphyr-parser-bw", "serde", "smallvec", @@ -310,6 +301,15 @@ dependencies = [ "syn", ] +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/Cargo.toml b/Cargo.toml index e26c1df..9122a69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -serde-saphyr = "0.0.23" +serde-saphyr = { version = "0.0.25", features = ["serialize","deserialize","figment"] } +figment = { version = "0.10" } +serde = { version = "1.0.228", features = ["derive"] } diff --git a/doc/config.yaml b/doc/config.yaml index 1738998..f6b35a6 100644 --- a/doc/config.yaml +++ b/doc/config.yaml @@ -7,7 +7,7 @@ config_version: 1 global: - log_level: INFO + log_level: info check_interval_ms: 500 # cyclic time to check states of apps state_file: /var/lib/safetyguard/state.yaml log_file: /var/log/safetyguard.log @@ -67,8 +67,8 @@ apps: - service_name: safeapp # Runs this app, till system is safe. safemonitor: - heartbeat: - threads: heartbeat_update_interval_ms: 300 + threads: - main: heartbeat_interval_ms: 1000 - thread_b: diff --git a/src/main.rs b/src/main.rs index aac9ce3..729f031 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,4 @@ -mod safemonitor; -mod safeaction; -mod safeinstrument; -mod safeappdb; + fn main() { println!("Hello, world!"); diff --git a/src/safeappdb/global.rs b/src/safeappdb/global.rs index 87a0d83..907305b 100644 --- a/src/safeappdb/global.rs +++ b/src/safeappdb/global.rs @@ -1,23 +1,88 @@ +use figment::{Figment, providers::Format}; +use serde::{Serialize, Deserialize}; +#[derive(Debug, Serialize, Deserialize)] +pub struct Config { + config_version: u64, + global: Global, +} +#[derive(Debug, Serialize, Deserialize)] +pub struct Global { + log_level: LogLevel, + check_interval_ms: u64, + state_file: String, + log_file: String, + config_file: String, + config_file_gen: String, + mode: Mode, + endpoint: String, +} +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)] +#[serde(rename_all = "lowercase")] pub enum LogLevel { - debug, - info, - warn, - error, + DEDUG, + #[default] + INFO, + WARN, + ERROR, } +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] pub enum Mode { - debug, - release, + DEVELOPMENT, + PRODUCTION, } -pub struct Global { - pub loglevel: LogLevel, - pub log_file: String, - pub state_file: String, - pub config_file: String, - pub config_file_gen: String, - pub mode: Mode, - pub endpoint: String, + +impl Config { + pub fn get_config(fp : String ) -> Result> { + let cfg: Config = Figment::from(serde_saphyr::figment::Yaml::file(fp)) + .extract::()?; + Ok(cfg) + } } +#[cfg(test)] +mod tests { + use std::fs; + use std::io::Write; + use super::*; + + + #[test] + fn test_config() -> Result<(), Box> { + let yml = r#" +config_version: 1 +global: + log_level: info + check_interval_ms: 1000 + state_file: /tmp/safeappdb.state + log_file: /tmp/safeappdb.log + config_file: /tmp/safeappdb.config + config_file_gen: /tmp/safeappdb.config.gen + mode: development # Can be development. + endpoint: /tmp/saftyguard.sock +"#; + + let mut fp = fs::File::create("/tmp/tst.yaml")?; + fp.write_all(yml.as_bytes())?; + fp.sync_data()?; + + let config = Config::get_config("/tmp/tst.yaml".to_string())?; + println!("{config:#?}"); + + assert_eq!(config.global.check_interval_ms, 1000); + assert_eq!(config.global.state_file, "/tmp/safeappdb.state"); + assert_eq!(config.global.log_file, "/tmp/safeappdb.log"); + assert_eq!(config.global.config_file, "/tmp/safeappdb.config"); + assert_eq!(config.global.config_file_gen, "/tmp/safeappdb.config.gen"); + assert_eq!(config.global.endpoint, "/tmp/saftyguard.sock"); + assert_eq!(config.global.log_level, LogLevel::INFO); + assert_eq!(config.global.mode, Mode::DEVELOPMENT); + + fs::remove_file("/tmp/tst.yaml")?; + + Ok(()) + } +} \ No newline at end of file From bda51bd49aaae1897d512ebe83c45939264e663f Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Sun, 3 May 2026 09:48:35 +0200 Subject: [PATCH 05/13] Implement global, watchdog and recover yaml parsing Signed-off-by: Sathishkumar Duraisamy --- doc/config.yaml | 2 +- src/safeappdb/global.rs | 119 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/doc/config.yaml b/doc/config.yaml index f6b35a6..d7b788e 100644 --- a/doc/config.yaml +++ b/doc/config.yaml @@ -23,7 +23,7 @@ watchdog: pretimeout_sec: 5 kick_interval_ms: 3000 -recovery: +recover: system_reset_on_failure: true max_global_failures: 5 diff --git a/src/safeappdb/global.rs b/src/safeappdb/global.rs index 907305b..743266a 100644 --- a/src/safeappdb/global.rs +++ b/src/safeappdb/global.rs @@ -3,10 +3,22 @@ use serde::{Serialize, Deserialize}; #[derive(Debug, Serialize, Deserialize)] pub struct Config { + #[serde(default = "Config::default_config_version")] config_version: u64, + #[serde(default)] global: Global, + #[serde(default)] + watchdog: Watchdog, + #[serde(default)] + recover: Recover, +} +impl Config { + fn default_config_version() -> u64 { + 1 + } } #[derive(Debug, Serialize, Deserialize)] +#[serde(default)] pub struct Global { log_level: LogLevel, check_interval_ms: u64, @@ -18,11 +30,25 @@ pub struct Global { endpoint: String, } -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)] +impl Default for Global { + fn default() -> Self { + Self { + log_level: LogLevel::INFO, + check_interval_ms: 500, + state_file: "/tmp/safeappdb.state".to_string(), + log_file: "/tmp/safeappdb.log".to_string(), + config_file: "/tmp/safeappdb.config".to_string(), + config_file_gen: "/tmp/safeappdb.config.gen".to_string(), + mode: Mode::DEVELOPMENT, + endpoint: "/tmp/saftyguard.sock".to_string(), + } + } +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum LogLevel { DEDUG, - #[default] INFO, WARN, ERROR, @@ -35,6 +61,41 @@ pub enum Mode { PRODUCTION, } +#[derive(Debug, Serialize, Deserialize)] +#[serde(default)] +pub struct Watchdog { + device: String, + timeout_sec: u64, + pretimeout_sec: u64, + kick_interval_ms: u64, +} + +impl Default for Watchdog { + fn default() -> Self { + Self { + device: "/dev/watchdog".to_string(), + timeout_sec: 10, + pretimeout_sec: 5, + kick_interval_ms: 3000, + } + } +} +#[derive(Debug, Serialize, Deserialize)] +#[serde(default)] +pub struct Recover { + system_reset_on_failure: bool, + max_global_failures: u64, +} +impl Default for Recover { + fn default() -> Self { + Self { + system_reset_on_failure: true, + max_global_failures: 5, + } + } +} + + impl Config { pub fn get_config(fp : String ) -> Result> { let cfg: Config = Figment::from(serde_saphyr::figment::Yaml::file(fp)) @@ -63,11 +124,22 @@ global: config_file_gen: /tmp/safeappdb.config.gen mode: development # Can be development. endpoint: /tmp/saftyguard.sock + +watchdog: + device: /dev/watchdog2 + timeout_sec: 100 + pretimeout_sec: 15 + kick_interval_ms: 2000 + +recover: + system_reset_on_failure: false + max_global_failures: 6 + "#; let mut fp = fs::File::create("/tmp/tst.yaml")?; fp.write_all(yml.as_bytes())?; - fp.sync_data()?; + fp.sync_all()?; let config = Config::get_config("/tmp/tst.yaml".to_string())?; println!("{config:#?}"); @@ -81,8 +153,49 @@ global: assert_eq!(config.global.log_level, LogLevel::INFO); assert_eq!(config.global.mode, Mode::DEVELOPMENT); + assert_eq!(config.recover.system_reset_on_failure, false); + assert_eq!(config.recover.max_global_failures, 6); + + assert_eq!(config.watchdog.device, "/dev/watchdog2"); + assert_eq!(config.watchdog.timeout_sec, 100); + assert_eq!(config.watchdog.pretimeout_sec, 15); + assert_eq!(config.watchdog.kick_interval_ms, 2000); + fs::remove_file("/tmp/tst.yaml")?; + Ok(()) + } + #[test] + fn test_config_default() -> Result<(), Box> { + let yml = r#" +"#; + + let mut fp = fs::File::create("/tmp/tst2.yaml")?; + fp.write_all(yml.as_bytes())?; + fp.sync_all()?; + + let config = Config::get_config("/tmp/tst2.yaml".to_string())?; + println!("{config:#?}"); + + assert_eq!(config.global.check_interval_ms, 500); + assert_eq!(config.global.state_file, "/tmp/safeappdb.state"); + assert_eq!(config.global.log_file, "/tmp/safeappdb.log"); + assert_eq!(config.global.config_file, "/tmp/safeappdb.config"); + assert_eq!(config.global.config_file_gen, "/tmp/safeappdb.config.gen"); + assert_eq!(config.global.endpoint, "/tmp/saftyguard.sock"); + assert_eq!(config.global.log_level, LogLevel::INFO); + assert_eq!(config.global.mode, Mode::DEVELOPMENT); + + assert_eq!(config.recover.system_reset_on_failure, true); + assert_eq!(config.recover.max_global_failures, 5); + + assert_eq!(config.watchdog.device, "/dev/watchdog"); + assert_eq!(config.watchdog.timeout_sec, 10); + assert_eq!(config.watchdog.pretimeout_sec, 5); + assert_eq!(config.watchdog.kick_interval_ms, 3000); + + fs::remove_file("/tmp/tst2.yaml")?; + Ok(()) } } \ No newline at end of file From 57b181d0942b5216874467d0b1a5bdf160983243 Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Wed, 20 May 2026 15:32:01 +0200 Subject: [PATCH 06/13] Converted the project into workspace project Signed-off-by: Sathishkumar Duraisamy --- Cargo.toml | 12 ++---------- safetyguard/Cargo.toml | 10 ++++++++++ {src => safetyguard/src}/lib.rs | 0 {src => safetyguard/src}/main.rs | 0 {src => safetyguard/src}/safeaction.rs | 0 {src => safetyguard/src}/safeappdb.rs | 0 {src => safetyguard/src}/safeappdb/appsdb.rs | 0 {src => safetyguard/src}/safeappdb/global.rs | 0 {src => safetyguard/src}/safeinstrument.rs | 0 {src => safetyguard/src}/safemonitor.rs | 0 10 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 safetyguard/Cargo.toml rename {src => safetyguard/src}/lib.rs (100%) rename {src => safetyguard/src}/main.rs (100%) rename {src => safetyguard/src}/safeaction.rs (100%) rename {src => safetyguard/src}/safeappdb.rs (100%) rename {src => safetyguard/src}/safeappdb/appsdb.rs (100%) rename {src => safetyguard/src}/safeappdb/global.rs (100%) rename {src => safetyguard/src}/safeinstrument.rs (100%) rename {src => safetyguard/src}/safemonitor.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 9122a69..0ec84ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,2 @@ -[package] -name = "safetyguard" -version = "0.1.0" -edition = "2024" - -[dependencies] -serde-saphyr = { version = "0.0.25", features = ["serialize","deserialize","figment"] } -figment = { version = "0.10" } -serde = { version = "1.0.228", features = ["derive"] } - +[workspace] +members = ["safetyguard"] diff --git a/safetyguard/Cargo.toml b/safetyguard/Cargo.toml new file mode 100644 index 0000000..9122a69 --- /dev/null +++ b/safetyguard/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "safetyguard" +version = "0.1.0" +edition = "2024" + +[dependencies] +serde-saphyr = { version = "0.0.25", features = ["serialize","deserialize","figment"] } +figment = { version = "0.10" } +serde = { version = "1.0.228", features = ["derive"] } + diff --git a/src/lib.rs b/safetyguard/src/lib.rs similarity index 100% rename from src/lib.rs rename to safetyguard/src/lib.rs diff --git a/src/main.rs b/safetyguard/src/main.rs similarity index 100% rename from src/main.rs rename to safetyguard/src/main.rs diff --git a/src/safeaction.rs b/safetyguard/src/safeaction.rs similarity index 100% rename from src/safeaction.rs rename to safetyguard/src/safeaction.rs diff --git a/src/safeappdb.rs b/safetyguard/src/safeappdb.rs similarity index 100% rename from src/safeappdb.rs rename to safetyguard/src/safeappdb.rs diff --git a/src/safeappdb/appsdb.rs b/safetyguard/src/safeappdb/appsdb.rs similarity index 100% rename from src/safeappdb/appsdb.rs rename to safetyguard/src/safeappdb/appsdb.rs diff --git a/src/safeappdb/global.rs b/safetyguard/src/safeappdb/global.rs similarity index 100% rename from src/safeappdb/global.rs rename to safetyguard/src/safeappdb/global.rs diff --git a/src/safeinstrument.rs b/safetyguard/src/safeinstrument.rs similarity index 100% rename from src/safeinstrument.rs rename to safetyguard/src/safeinstrument.rs diff --git a/src/safemonitor.rs b/safetyguard/src/safemonitor.rs similarity index 100% rename from src/safemonitor.rs rename to safetyguard/src/safemonitor.rs From e3a4e9cf5cd6d4e8a5ac0650e6fccd70cc0934c7 Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Sun, 24 May 2026 22:02:00 +0200 Subject: [PATCH 07/13] Added code for shared memory structure layout and create function Signed-off-by: Sathishkumar Duraisamy --- Cargo.lock | 115 +++++++++++++++ Cargo.toml | 3 +- memfd_ringbuf/Cargo.toml | 10 ++ memfd_ringbuf/src/lib.rs | 17 +++ memfd_ringbuf/src/memfd_rb.rs | 263 ++++++++++++++++++++++++++++++++++ memfd_ringbuf/src/traits.rs | 3 + 6 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 memfd_ringbuf/Cargo.toml create mode 100644 memfd_ringbuf/src/lib.rs create mode 100644 memfd_ringbuf/src/memfd_rb.rs create mode 100644 memfd_ringbuf/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index 28cb81f..3f30718 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + [[package]] name = "bumpalo" version = "3.20.2" @@ -70,6 +76,20 @@ name = "bytemuck" version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "cfg-if" @@ -77,6 +97,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -95,6 +121,16 @@ dependencies = [ "encoding_rs", ] +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "figment" version = "0.10.19" @@ -137,12 +173,37 @@ version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "memchr" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memfd_ringbuf" +version = "0.1.0" +dependencies = [ + "bytemuck", + "memmap2", + "ringbuf", + "rustix", +] + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -164,6 +225,21 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -188,6 +264,30 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "ringbuf" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3ecbcab081b935fb9c618b07654924f27686b4aac8818e700580a83eedcb7f" +dependencies = [ + "crossbeam-utils", + "portable-atomic", + "portable-atomic-util", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -382,6 +482,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "wit-bindgen" version = "0.51.0" diff --git a/Cargo.toml b/Cargo.toml index 0ec84ab..95fb62d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,3 @@ [workspace] -members = ["safetyguard"] +members = ["safetyguard", "memfd_ringbuf"] +resolver = "3" \ No newline at end of file diff --git a/memfd_ringbuf/Cargo.toml b/memfd_ringbuf/Cargo.toml new file mode 100644 index 0000000..cfa2765 --- /dev/null +++ b/memfd_ringbuf/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "memfd_ringbuf" +version = "0.1.0" +edition = "2024" + +[dependencies] +ringbuf = { version = "0.5.0", features = []} +memmap2 = "0.9.10" +rustix = { version = "1.1.4", features = [ "fs", "mm"] } +bytemuck = { version = "1.25.0", features = ["derive"] } diff --git a/memfd_ringbuf/src/lib.rs b/memfd_ringbuf/src/lib.rs new file mode 100644 index 0000000..dc3384f --- /dev/null +++ b/memfd_ringbuf/src/lib.rs @@ -0,0 +1,17 @@ +mod traits; +mod memfd_rb; + +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/memfd_ringbuf/src/memfd_rb.rs b/memfd_ringbuf/src/memfd_rb.rs new file mode 100644 index 0000000..480d888 --- /dev/null +++ b/memfd_ringbuf/src/memfd_rb.rs @@ -0,0 +1,263 @@ +// Core +use std::{ + io, + mem::{size_of, MaybeUninit}, + os::fd::{ AsFd, FromRawFd, IntoRawFd }, + ptr::{self, NonNull}, + sync::atomic::{AtomicU32, AtomicU64, Ordering}, + cell::UnsafeCell, +}; +use std::fs::File; +// libraries +use memmap2::MmapMut; +use rustix::{ + fs::{ftruncate, memfd_create, MemfdFlags}, +}; +use ringbuf::storage::Storage; + +// Crate +use crate::traits::SharedPod; + +#[repr(C, align(64))] +pub struct CacheAligned(pub T); + +#[repr(C, align(64))] +pub struct Header { + pub magic: u64, + pub version: u32, + pub flags: u32, + pub total_size: u64, + pub ring_capacity: u64, + pub initialized: AtomicU32, +} + +#[repr(C)] +pub struct Slot { + pub sequence: u64, + pub crc32: u32, + pub _padding: u32, + pub value: UnsafeCell>, +} + +#[repr(C, align(64))] +pub struct SharedLayout { + pub header: Header, + pub read_index: CacheAligned, + pub write_index: CacheAligned, + pub storage: [Slot; N], +} + +pub struct MemfdStorage { + mmap: MmapMut, + ptr: NonNull>, +} + +impl MemfdStorage { + pub fn create(name: &str) -> io::Result { + const MAGIC: u64 = 0x53484D52494E4755; // SHMRINGU + + let fd = memfd_create(name, MemfdFlags::CLOEXEC | MemfdFlags::ALLOW_SEALING) + .map_err(io::Error::from)?; + + let total_size = size_of::>(); + + ftruncate(fd.as_fd(), total_size as u64) + .map_err(io::Error::from)?; + + /* Convert OwnedFD -> File. Becuase memmap2 works with file */ + let file = unsafe { + File::from_raw_fd(fd.into_raw_fd()) + }; + + let mut mmap = unsafe { + MmapMut::map_mut(&file)? + }; + let raw_ptr = mmap.as_mut_ptr() as *mut SharedLayout; + + unsafe { + ptr::write( + raw_ptr, + SharedLayout { + header: Header { + magic: MAGIC, + version: 1, + flags: 0, + total_size: total_size as u64, + ring_capacity: N as u64, + initialized: AtomicU32::new(0), + }, + + read_index: CacheAligned(AtomicU64::new(0)), + + write_index: CacheAligned(AtomicU64::new(0)), + + storage: std::array::from_fn(|i| Slot { + sequence: i as u64, + crc32: 0, + _padding: 0, + value: UnsafeCell::new(MaybeUninit::uninit()), + }), + }, + ); + + /* Marks as fully initialized state */ + (*raw_ptr) + .header + .initialized + .store(1, Ordering::Release); + + Ok(Self { + mmap, + ptr: NonNull::new(raw_ptr).unwrap(), + }) + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + use bytemuck::{Pod, Zeroable}; + use std::mem::{align_of, size_of}; + use std::sync::atomic::Ordering; + + #[repr(C)] + #[derive(Copy, Clone, Zeroable, Pod, Debug, PartialEq)] + struct TestMessage { + pub id: u64, + pub value: u32, + _padding: u32, + } + + unsafe impl SharedPod for TestMessage {} + + #[test] + fn create_memfd_storage() { + const N: usize = 128; + + let storage = + MemfdStorage::::create("test-ring") + .unwrap(); + + let layout = unsafe { + storage.ptr.as_ref() + }; + + /* + * Validate header + */ + assert_eq!( + layout.header.magic, + 0x53484D52494E4755 + ); + + assert_eq!(layout.header.version, 1); + + assert_eq!( + layout.header.ring_capacity, + N as u64 + ); + + assert_eq!( + layout.header.initialized.load(Ordering::Acquire), + 1 + ); + + /* + * Validate indexes + */ + assert_eq!( + layout.read_index.0.load(Ordering::Acquire), + 0 + ); + + assert_eq!( + layout.write_index.0.load(Ordering::Acquire), + 0 + ); + + /* + * Validate sequence initialization + */ + for (i, slot) in layout.storage.iter().enumerate() { + assert_eq!(slot.sequence, i as u64); + } + } + + #[test] + fn validate_alignment() { + assert_eq!( + align_of::
(), + 64 + ); + + assert_eq!( + align_of::>(), + 64 + ); + + assert_eq!( + align_of::>(), + 64 + ); + } + + #[test] + fn validate_sizes() { + println!( + "Header size: {}", + size_of::
() + ); + + println!( + "Slot size: {}", + size_of::>() + ); + + println!( + "SharedLayout size: {}", + size_of::>() + ); + + /* + * CacheAligned should occupy full cacheline. + */ + assert_eq!( + size_of::>(), + 64 + ); + } + + #[test] + fn write_and_read_slot() { + const N: usize = 16; + + let storage = + MemfdStorage::::create("rw-test") + .unwrap(); + + let layout = unsafe { + storage.ptr.as_ref() + }; + + let slot = &layout.storage[0]; + + let msg = TestMessage { + id: 42, + value: 777, + _padding: 0, + }; + + unsafe { + (*slot.value.get()).write(msg); + } + + let read_back = unsafe { + (*slot.value.get()).assume_init() + }; + + assert_eq!(msg, read_back); + } +} diff --git a/memfd_ringbuf/src/traits.rs b/memfd_ringbuf/src/traits.rs new file mode 100644 index 0000000..720de4d --- /dev/null +++ b/memfd_ringbuf/src/traits.rs @@ -0,0 +1,3 @@ +use bytemuck::Pod; + +pub unsafe trait SharedPod: Pod {} \ No newline at end of file From cb972ecc8e0e71f75842f58b5cc46e9c371e87ec Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Mon, 25 May 2026 00:03:51 +0200 Subject: [PATCH 08/13] Updated code for shared memory attach function, and cleaned up earlier code to use only with rustix library Signed-off-by: Sathishkumar Duraisamy --- memfd_ringbuf/src/memfd_rb.rs | 186 ++++++++++++++++++++++++++++++---- 1 file changed, 167 insertions(+), 19 deletions(-) diff --git a/memfd_ringbuf/src/memfd_rb.rs b/memfd_ringbuf/src/memfd_rb.rs index 480d888..21158f9 100644 --- a/memfd_ringbuf/src/memfd_rb.rs +++ b/memfd_ringbuf/src/memfd_rb.rs @@ -2,22 +2,25 @@ use std::{ io, mem::{size_of, MaybeUninit}, - os::fd::{ AsFd, FromRawFd, IntoRawFd }, + os::fd::{ AsFd, FromRawFd, IntoRawFd, OwnedFd }, ptr::{self, NonNull}, sync::atomic::{AtomicU32, AtomicU64, Ordering}, cell::UnsafeCell, }; -use std::fs::File; + // libraries use memmap2::MmapMut; use rustix::{ fs::{ftruncate, memfd_create, MemfdFlags}, + mm::{mmap, munmap, MapFlags, ProtFlags }, + io::dup, }; -use ringbuf::storage::Storage; +use ringbuf::storage::{Owning, Storage}; // Crate use crate::traits::SharedPod; +const MAGIC: u64 = u64::from_be_bytes(*b"SHMRINGU"); #[repr(C, align(64))] pub struct CacheAligned(pub T); @@ -48,31 +51,41 @@ pub struct SharedLayout { } pub struct MemfdStorage { - mmap: MmapMut, + fd: OwnedFd, ptr: NonNull>, + size: usize, } impl MemfdStorage { pub fn create(name: &str) -> io::Result { - const MAGIC: u64 = 0x53484D52494E4755; // SHMRINGU - let fd = memfd_create(name, MemfdFlags::CLOEXEC | MemfdFlags::ALLOW_SEALING) .map_err(io::Error::from)?; let total_size = size_of::>(); + /* Resize memfd */ ftruncate(fd.as_fd(), total_size as u64) .map_err(io::Error::from)?; - /* Convert OwnedFD -> File. Becuase memmap2 works with file */ - let file = unsafe { - File::from_raw_fd(fd.into_raw_fd()) - }; - - let mut mmap = unsafe { - MmapMut::map_mut(&file)? - }; - let raw_ptr = mmap.as_mut_ptr() as *mut SharedLayout; + let raw_ptr = unsafe { + mmap( + ptr::null_mut(), + total_size, + ProtFlags::READ | ProtFlags::WRITE, + MapFlags::SHARED, + fd.as_fd(), + 0, + ) + }.map_err(io::Error::from)? + as *mut SharedLayout; + + let ptr = NonNull::new(raw_ptr) + .ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidData, + "Mmap returned null pointer", + ) + })?; unsafe { ptr::write( @@ -101,17 +114,103 @@ impl MemfdStorage { ); /* Marks as fully initialized state */ - (*raw_ptr) + ptr.as_ref() .header .initialized .store(1, Ordering::Release); Ok(Self { - mmap, - ptr: NonNull::new(raw_ptr).unwrap(), + fd, + ptr, + size: total_size, }) } } + + pub fn attach(fd: OwnedFd) -> io::Result { + let total_size = size_of::>(); + + let raw_ptr = unsafe { + mmap( + ptr::null_mut(), + total_size, + ProtFlags::READ | ProtFlags::WRITE, + MapFlags::SHARED, + fd.as_fd(), + 0 + ) + }.map_err(io::Error::from)? as *mut SharedLayout; + + let ptr = NonNull::new(raw_ptr) + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "Null map pointer", + ) + })?; + + let cleanup = || unsafe { + munmap(raw_ptr.cast(), total_size).ok(); + }; + + unsafe { + let layout = ptr.as_ref(); + + if layout.header.magic != MAGIC { + cleanup(); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Invalid memfd magic", + )); + } + + /* Validate Version */ + if layout.header.version != 1 { + cleanup(); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Unsupported shared memory version", + )); + } + + if layout.header.ring_capacity != N as u64 { + cleanup(); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Ring capacity mismatch", + )); + } + + /* Wait/check initialization */ + if layout.header.initialized.load(Ordering::Acquire) != 1 { + cleanup(); + return Err(io::Error::new( + io::ErrorKind::WouldBlock, + "Shared memory not initialized", + )); + } + + Ok(Self { + fd, + ptr, + size: total_size, + }) + } + } + + pub fn dup_fd(&self) -> io::Result { + dup(self.fd.as_fd()).map_err(io::Error::from) + } + + pub fn layout( &self) -> &SharedLayout { + unsafe { self.ptr.as_ref() } + } +} + +impl Drop for MemfdStorage { + fn drop(&mut self) { + unsafe { + munmap(self.ptr.as_ptr().cast(), self.size).ok(); + } + } } @@ -150,7 +249,7 @@ mod tests { */ assert_eq!( layout.header.magic, - 0x53484D52494E4755 + MAGIC ); assert_eq!(layout.header.version, 1); @@ -260,4 +359,53 @@ mod tests { assert_eq!(msg, read_back); } + + #[test] + fn attach_existing_memfd() { + const N: usize = 64; + let storage = MemfdStorage::::create( + "attach-test", + ).unwrap(); + + let dup_fd = dup(storage.fd.as_fd()).unwrap(); + + /* Now use attach function to get the attached fd */ + let attached_storage = + MemfdStorage::::attach(dup_fd).unwrap(); + + assert_eq!( + attached_storage.layout().header.magic, + MAGIC + ); + + assert_eq!(attached_storage.layout().header.version, 1); + + /* write test */ + storage.layout() + .write_index + .0 + .store(42, Ordering::Release); + + /* Read from attached storage */ + let value = attached_storage.layout().write_index.0.load(Ordering::Acquire); + assert_eq!(value, 42); + + let msg = TestMessage { + id: 134, + value: 1466, + _padding: 0, + }; + + unsafe { + (*storage.layout().storage[0].value.get()).write(msg); + } + + /* Read back from attached fd */ + let read_back = unsafe { + (*attached_storage.layout().storage[0].value.get()).assume_init() + }; + + assert_eq!(msg, read_back); + + } } From af1fc3af6e55258d98369acf44846daf0d0b3adc Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Mon, 25 May 2026 00:50:52 +0200 Subject: [PATCH 09/13] Updated code by removing slot as ringbuf requires continous memory Signed-off-by: Sathishkumar Duraisamy --- memfd_ringbuf/src/memfd_rb.rs | 42 ++++++++--------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/memfd_ringbuf/src/memfd_rb.rs b/memfd_ringbuf/src/memfd_rb.rs index 21158f9..e534f78 100644 --- a/memfd_ringbuf/src/memfd_rb.rs +++ b/memfd_ringbuf/src/memfd_rb.rs @@ -34,20 +34,12 @@ pub struct Header { pub initialized: AtomicU32, } -#[repr(C)] -pub struct Slot { - pub sequence: u64, - pub crc32: u32, - pub _padding: u32, - pub value: UnsafeCell>, -} - #[repr(C, align(64))] pub struct SharedLayout { pub header: Header, pub read_index: CacheAligned, pub write_index: CacheAligned, - pub storage: [Slot; N], + pub storage: [MaybeUninit; N], } pub struct MemfdStorage { @@ -104,11 +96,8 @@ impl MemfdStorage { write_index: CacheAligned(AtomicU64::new(0)), - storage: std::array::from_fn(|i| Slot { - sequence: i as u64, - crc32: 0, - _padding: 0, - value: UnsafeCell::new(MaybeUninit::uninit()), + storage: std::array::from_fn(|i| { + MaybeUninit::uninit() }), }, ); @@ -277,12 +266,6 @@ mod tests { 0 ); - /* - * Validate sequence initialization - */ - for (i, slot) in layout.storage.iter().enumerate() { - assert_eq!(slot.sequence, i as u64); - } } #[test] @@ -310,11 +293,6 @@ mod tests { size_of::
() ); - println!( - "Slot size: {}", - size_of::>() - ); - println!( "SharedLayout size: {}", size_of::>() @@ -330,16 +308,14 @@ mod tests { } #[test] - fn write_and_read_slot() { + fn write_and_read_() { const N: usize = 16; let storage = MemfdStorage::::create("rw-test") .unwrap(); - let layout = unsafe { - storage.ptr.as_ref() - }; + let layout = storage.layout(); let slot = &layout.storage[0]; @@ -350,11 +326,11 @@ mod tests { }; unsafe { - (*slot.value.get()).write(msg); + slot.as_ptr().cast::().cast_mut().write(msg); } let read_back = unsafe { - (*slot.value.get()).assume_init() + slot.assume_init() }; assert_eq!(msg, read_back); @@ -397,12 +373,12 @@ mod tests { }; unsafe { - (*storage.layout().storage[0].value.get()).write(msg); + storage.layout().storage[0].as_ptr().cast::().cast_mut().write(msg); } /* Read back from attached fd */ let read_back = unsafe { - (*attached_storage.layout().storage[0].value.get()).assume_init() + attached_storage.layout().storage[0].assume_init() }; assert_eq!(msg, read_back); From e344c92150a478d39561bffc9e46f1adf35dfb53 Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Tue, 26 May 2026 12:10:27 +0200 Subject: [PATCH 10/13] Implemented storagetrait for memfd_storage Signed-off-by: Sathishkumar Duraisamy --- memfd_ringbuf/src/memfd_rb.rs | 53 +++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/memfd_ringbuf/src/memfd_rb.rs b/memfd_ringbuf/src/memfd_rb.rs index e534f78..b035200 100644 --- a/memfd_ringbuf/src/memfd_rb.rs +++ b/memfd_ringbuf/src/memfd_rb.rs @@ -2,20 +2,18 @@ use std::{ io, mem::{size_of, MaybeUninit}, - os::fd::{ AsFd, FromRawFd, IntoRawFd, OwnedFd }, - ptr::{self, NonNull}, + os::fd::{ AsFd, OwnedFd }, + ptr::{self, NonNull, addr_of_mut}, sync::atomic::{AtomicU32, AtomicU64, Ordering}, - cell::UnsafeCell, }; // libraries -use memmap2::MmapMut; use rustix::{ fs::{ftruncate, memfd_create, MemfdFlags}, mm::{mmap, munmap, MapFlags, ProtFlags }, io::dup, }; -use ringbuf::storage::{Owning, Storage}; +use ringbuf::storage::Storage; // Crate use crate::traits::SharedPod; @@ -202,6 +200,51 @@ impl Drop for MemfdStorage { } } +unsafe impl Storage for MemfdStorage { + type Item = T; + + fn len(&self) -> usize { + N + } + + fn as_mut_ptr(&self) -> *mut MaybeUninit { + unsafe { + let layout_ptr = self.ptr.as_ptr(); + (*layout_ptr).storage.as_mut_ptr() + } + } +} + +pub unsafe trait MemfdStorageTrait: Storage { + fn header_ptr(&self) -> *mut Header; + fn read_index_ptr(&self) -> *mut AtomicU64; + fn write_index_ptr(&self) -> *mut AtomicU64; +} + +unsafe impl MemfdStorageTrait for MemfdStorage { + fn header_ptr(&self) -> *mut Header { + unsafe { + let layout_ptr = self.ptr.as_ptr(); + addr_of_mut!((*layout_ptr).header) + } + } + + fn read_index_ptr(&self) -> *mut AtomicU64 { + unsafe { + let layout_ptr = self.ptr.as_ptr(); + addr_of_mut!((*layout_ptr).read_index.0) + } + } + + fn write_index_ptr(&self) -> *mut AtomicU64 { + unsafe { + let layout_ptr = self.ptr.as_ptr(); + addr_of_mut!((*layout_ptr).write_index.0) + } + } +} + + #[cfg(test)] mod tests { From e4559c48a279f2c82e3b9f700645826bca8f9b35 Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Tue, 26 May 2026 15:41:29 +0200 Subject: [PATCH 11/13] Implemented producer, consumer traits based on localRb Signed-off-by: Sathishkumar Duraisamy --- memfd_ringbuf/src/lib.rs | 7 +- memfd_ringbuf/src/memfd_rb.rs | 143 +++++++++++++++++++++++++++++++++- 2 files changed, 145 insertions(+), 5 deletions(-) diff --git a/memfd_ringbuf/src/lib.rs b/memfd_ringbuf/src/lib.rs index dc3384f..135cdbf 100644 --- a/memfd_ringbuf/src/lib.rs +++ b/memfd_ringbuf/src/lib.rs @@ -1,5 +1,8 @@ -mod traits; -mod memfd_rb; +pub mod traits; +pub mod memfd_rb; + +pub use memfd_rb::{MemfdRb, MemfdStorage, MemfdStorageTrait}; +pub use traits::SharedPod; pub fn add(left: u64, right: u64) -> u64 { left + right diff --git a/memfd_ringbuf/src/memfd_rb.rs b/memfd_ringbuf/src/memfd_rb.rs index b035200..c3fb9e3 100644 --- a/memfd_ringbuf/src/memfd_rb.rs +++ b/memfd_ringbuf/src/memfd_rb.rs @@ -4,7 +4,8 @@ use std::{ mem::{size_of, MaybeUninit}, os::fd::{ AsFd, OwnedFd }, ptr::{self, NonNull, addr_of_mut}, - sync::atomic::{AtomicU32, AtomicU64, Ordering}, + sync::atomic::{AtomicU32, AtomicU64, AtomicBool, Ordering}, + num::NonZeroUsize }; // libraries @@ -13,7 +14,11 @@ use rustix::{ mm::{mmap, munmap, MapFlags, ProtFlags }, io::dup, }; -use ringbuf::storage::Storage; +use ringbuf:: { + storage::Storage, + traits::{ Observer, Consumer, Producer, RingBuffer, SplitRef}, + wrap::{Prod, Cons}, +}; // Crate use crate::traits::SharedPod; @@ -37,6 +42,8 @@ pub struct SharedLayout { pub header: Header, pub read_index: CacheAligned, pub write_index: CacheAligned, + pub read_held: AtomicBool, + pub write_held: AtomicBool, pub storage: [MaybeUninit; N], } @@ -94,7 +101,11 @@ impl MemfdStorage { write_index: CacheAligned(AtomicU64::new(0)), - storage: std::array::from_fn(|i| { + read_held: AtomicBool::new(false), + + write_held: AtomicBool::new(false), + + storage: std::array::from_fn(|_i| { MaybeUninit::uninit() }), }, @@ -219,6 +230,8 @@ pub unsafe trait MemfdStorageTrait: Storage { fn header_ptr(&self) -> *mut Header; fn read_index_ptr(&self) -> *mut AtomicU64; fn write_index_ptr(&self) -> *mut AtomicU64; + fn read_held_ptr(&self) -> *mut u8; + fn write_held_ptr(&self) -> *mut u8; } unsafe impl MemfdStorageTrait for MemfdStorage { @@ -242,8 +255,132 @@ unsafe impl MemfdStorageTrait for MemfdStorage *mut u8 { + unsafe { + let layout_ptr = self.ptr.as_ptr(); + addr_of_mut!((*layout_ptr).read_held) as *mut u8 + } + } + + fn write_held_ptr(&self) -> *mut u8 { + unsafe { + let layout_ptr = self.ptr.as_ptr(); + addr_of_mut!((*layout_ptr).write_held) as *mut u8 + } + } +} + +pub struct MemfdRb { + storage: S, +} + +impl AsRef for MemfdRb { + fn as_ref(&self) -> &Self { + self + } +} + +impl Observer for MemfdRb { + type Item = S::Item; + + #[inline] + fn capacity(&self) -> NonZeroUsize { + NonZeroUsize::new(self.storage.len()).expect("Capacity must be non-zero") + } + + fn read_index(&self) -> usize { + unsafe { (*self.storage.read_index_ptr()).load(Ordering::Acquire) as usize } + } + + fn write_index(&self) -> usize { + unsafe { (*self.storage.write_index_ptr()).load(Ordering::Acquire) as usize } + } + + unsafe fn unsafe_slices(&self, start: usize, end: usize) + -> (&[MaybeUninit], &[MaybeUninit]) { + unsafe { + let (first, second) = self.unsafe_slices_mut(start, end); + (first, second) + } + } + + unsafe fn unsafe_slices_mut(&self, start: usize, end: usize) + -> (&mut [MaybeUninit], &mut [MaybeUninit]) { + let ptr = self.storage.as_mut_ptr(); + let len = self.storage.len(); + unsafe { + if start <= end { + (std::slice::from_raw_parts_mut(ptr.add(start), end - start), &mut []) + } else { + ( + std::slice::from_raw_parts_mut(ptr.add(start), len - start), + std::slice::from_raw_parts_mut(ptr, end), + ) + } + } + } + + fn read_is_held(&self) -> bool { + unsafe { + let ptr = self.storage.read_held_ptr() as *const AtomicBool; + (*ptr).load(Ordering::Acquire) + } + } + + fn write_is_held(&self) -> bool { + unsafe { + let ptr = self.storage.write_held_ptr() as *const AtomicBool; + (*ptr).load(Ordering::Acquire) + } + } } +impl Producer for MemfdRb { + unsafe fn set_write_index(&self, value: usize) { + unsafe { (*self.storage.write_index_ptr()).store(value as u64, Ordering::Release); } + } +} + +impl Consumer for MemfdRb { + unsafe fn set_read_index(&self, value: usize) { + unsafe { (*self.storage.read_index_ptr()).store(value as u64, Ordering::Release); } + } +} + +impl RingBuffer for MemfdRb { + unsafe fn hold_read(&self, flag: bool) -> bool { + unsafe { + let ptr = self.storage.read_held_ptr() as *const AtomicBool; + (*ptr).swap(flag, Ordering::AcqRel) + } + } + + unsafe fn hold_write(&self, flag: bool) -> bool { + unsafe { + let ptr = self.storage.write_held_ptr() as *const AtomicBool; + (*ptr).swap(flag, Ordering::AcqRel) + } + } +} + +impl SplitRef for MemfdRb { + type RefProd<'a> = Prod<&'a Self> where Self: 'a; + type RefCons<'a> = Cons<&'a Self> where Self: 'a; + + fn split_ref(&mut self) -> (Self::RefProd<'_>, Self::RefCons<'_>) { + (Prod::new(self), Cons::new(self)) + } +} + +impl Drop for MemfdRb { + fn drop(&mut self) { + unsafe { + self.hold_read(false); + self.hold_write(false); + } + } +} #[cfg(test)] From 851cf6586d4bce0f90e4f3829744ef97caed2798 Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Tue, 26 May 2026 22:23:03 +0200 Subject: [PATCH 12/13] Splitref trait is removed and replaced with get_producer and get_consumer. Splitref trait is not applicable for sharedmem based ringbuffer. Signed-off-by: Sathishkumar Duraisamy --- memfd_ringbuf/src/memfd_rb.rs | 64 ++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/memfd_ringbuf/src/memfd_rb.rs b/memfd_ringbuf/src/memfd_rb.rs index c3fb9e3..30d5fb4 100644 --- a/memfd_ringbuf/src/memfd_rb.rs +++ b/memfd_ringbuf/src/memfd_rb.rs @@ -14,10 +14,10 @@ use rustix::{ mm::{mmap, munmap, MapFlags, ProtFlags }, io::dup, }; -use ringbuf:: { +use ringbuf::{ storage::Storage, - traits::{ Observer, Consumer, Producer, RingBuffer, SplitRef}, - wrap::{Prod, Cons}, + traits::{Observer, Consumer, Producer, RingBuffer}, + wrap::{Prod, Cons} }; // Crate @@ -272,7 +272,7 @@ unsafe impl MemfdStorageTrait for MemfdStorage { - storage: S, + pub storage: S, } impl AsRef for MemfdRb { @@ -364,15 +364,6 @@ impl RingBuffer for MemfdRb { } } -impl SplitRef for MemfdRb { - type RefProd<'a> = Prod<&'a Self> where Self: 'a; - type RefCons<'a> = Cons<&'a Self> where Self: 'a; - - fn split_ref(&mut self) -> (Self::RefProd<'_>, Self::RefCons<'_>) { - (Prod::new(self), Cons::new(self)) - } -} - impl Drop for MemfdRb { fn drop(&mut self) { unsafe { @@ -382,6 +373,15 @@ impl Drop for MemfdRb { } } +impl MemfdRb { + pub fn get_producer(&self) -> Prod<&Self> { + Prod::new(self) + } + + pub fn get_consumer(&self) -> Cons<&Self> { + Cons::new(self) + } +} #[cfg(test)] mod tests { @@ -516,6 +516,44 @@ mod tests { assert_eq!(msg, read_back); } + #[test] + fn producer_consumer() { + const N: usize = 16; + let storage = MemfdStorage::::create("pc-test").unwrap(); + let rb = MemfdRb { storage }; + + let mut prod = rb.get_producer(); + let mut cons = rb.get_consumer(); + + let msg = TestMessage { id: 1, value: 100, _padding: 0 }; + prod.try_push(msg).unwrap(); + + let read = cons.try_pop().unwrap(); + assert_eq!(msg, read); + } + + + #[test] + fn producer_consumer_attach_storage() { + const N: usize = 16; + let storage = MemfdStorage::::create("pc-test-attach").unwrap(); + + let dup_fd = storage.dup_fd().unwrap(); + let attached_storage = MemfdStorage::::attach(dup_fd).unwrap(); + + let rb_prod = MemfdRb { storage }; + let rb_cons = MemfdRb { storage: attached_storage }; + + let mut prod = rb_prod.get_producer(); + let mut cons = rb_cons.get_consumer(); + + let msg = TestMessage { id: 1, value: 100, _padding: 0 }; + prod.try_push(msg).unwrap(); + + let read = cons.try_pop().unwrap(); + assert_eq!(msg, read); + } + #[test] fn attach_existing_memfd() { const N: usize = 64; From e79ebc087ba2d370457b94307fde2cb4fe8f4068 Mon Sep 17 00:00:00 2001 From: Sathishkumar Duraisamy Date: Fri, 29 May 2026 15:06:49 +0200 Subject: [PATCH 13/13] Added eventfd notifier Signed-off-by: Sathishkumar Duraisamy --- Cargo.lock | 10 --- memfd_ringbuf/Cargo.toml | 3 +- memfd_ringbuf/src/eventfd_notifier.rs | 116 ++++++++++++++++++++++++++ memfd_ringbuf/src/lib.rs | 4 +- memfd_ringbuf/src/traits.rs | 16 +++- 5 files changed, 135 insertions(+), 14 deletions(-) create mode 100644 memfd_ringbuf/src/eventfd_notifier.rs diff --git a/Cargo.lock b/Cargo.lock index 3f30718..2013c20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,20 +190,10 @@ name = "memfd_ringbuf" version = "0.1.0" dependencies = [ "bytemuck", - "memmap2", "ringbuf", "rustix", ] -[[package]] -name = "memmap2" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" -dependencies = [ - "libc", -] - [[package]] name = "nohash-hasher" version = "0.2.0" diff --git a/memfd_ringbuf/Cargo.toml b/memfd_ringbuf/Cargo.toml index cfa2765..e9676c4 100644 --- a/memfd_ringbuf/Cargo.toml +++ b/memfd_ringbuf/Cargo.toml @@ -5,6 +5,5 @@ edition = "2024" [dependencies] ringbuf = { version = "0.5.0", features = []} -memmap2 = "0.9.10" -rustix = { version = "1.1.4", features = [ "fs", "mm"] } +rustix = { version = "1.1.4", features = [ "fs", "mm", "event"] } bytemuck = { version = "1.25.0", features = ["derive"] } diff --git a/memfd_ringbuf/src/eventfd_notifier.rs b/memfd_ringbuf/src/eventfd_notifier.rs new file mode 100644 index 0000000..3261c6e --- /dev/null +++ b/memfd_ringbuf/src/eventfd_notifier.rs @@ -0,0 +1,116 @@ + +use rustix::{ + event::{eventfd, EventfdFlags}, + fd::OwnedFd, + io::{read, write, Result}, +}; +use ringbuf::traits::{Observer, Producer}; +use crate::traits::RbProducerNotify; + +pub struct EventFdNotifier { + pub fd: OwnedFd, +} + +impl EventFdNotifier { + pub fn new() -> Result { + let fd = eventfd(0, EventfdFlags::CLOEXEC | EventfdFlags::NONBLOCK)?; + Ok(Self { fd }) + } + + pub fn from_fd(fd: OwnedFd) -> Self { + Self { fd } + } + + pub fn wait(&self) -> Result { + let mut buf = [0u8; 8]; + read(&self.fd, &mut buf)?; + Ok(u64::from_ne_bytes(buf)) + } +} + +impl RbProducerNotify for EventFdNotifier { + fn notify(&self) -> Result<()> { + let val: u64 = 1; + write(&self.fd, &val.to_ne_bytes())?; + Ok(()) + } +} + +pub struct NotifyingProducer { + base: P, + notifier: N, +} + +impl NotifyingProducer { + pub fn new(base: P, notifier: N) -> Self { + Self { base, notifier } + } +} + +impl Observer for NotifyingProducer { + type Item = P::Item; + + fn capacity(&self) -> std::num::NonZeroUsize { + self.base.capacity() + } + + fn read_index(&self) -> usize { + self.base.read_index() + } + + fn write_index(&self) -> usize { + self.base.write_index() + } + + unsafe fn unsafe_slices(&self, start: usize, end: usize) -> (&[std::mem::MaybeUninit], &[std::mem::MaybeUninit]) { + unsafe { self.base.unsafe_slices(start, end) } + } + + unsafe fn unsafe_slices_mut(&self, start: usize, end: usize) -> (&mut [std::mem::MaybeUninit], &mut [std::mem::MaybeUninit]) { + unsafe { self.base.unsafe_slices_mut(start, end) } + } + + fn read_is_held(&self) -> bool { + self.base.read_is_held() + } + + fn write_is_held(&self) -> bool { + self.base.write_is_held() + } +} + +impl Producer for NotifyingProducer { + unsafe fn set_write_index(&self, value: usize) { + unsafe { self.base.set_write_index(value); } + let _ = self.notifier.notify(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::memfd_rb::MemfdRb; + use ringbuf::traits::{Producer, Consumer}; + + #[test] + fn test_eventfd_notify() { + let storage = crate::memfd_rb::MemfdStorage::::create("test_notify").unwrap(); + let rb = MemfdRb { storage }; + + let prod = rb.get_producer(); + let mut cons = rb.get_consumer(); + + let notifier = EventFdNotifier::new().unwrap(); + let mut notifying_prod = NotifyingProducer::new(prod, notifier); + + notifying_prod.try_push(42).unwrap(); + + let mut buf = [0u8; 8]; + rustix::io::read(¬ifying_prod.notifier.fd, &mut buf).unwrap(); + let val = u64::from_ne_bytes(buf); + assert_eq!(val, 1); + + assert_eq!(cons.try_pop(), Some(42)); + } +} + diff --git a/memfd_ringbuf/src/lib.rs b/memfd_ringbuf/src/lib.rs index 135cdbf..ae123b6 100644 --- a/memfd_ringbuf/src/lib.rs +++ b/memfd_ringbuf/src/lib.rs @@ -1,8 +1,10 @@ pub mod traits; pub mod memfd_rb; +pub mod eventfd_notifier; pub use memfd_rb::{MemfdRb, MemfdStorage, MemfdStorageTrait}; -pub use traits::SharedPod; +pub use traits::{SharedPod, RbProducerNotify}; +pub use eventfd_notifier::EventFdNotifier; pub fn add(left: u64, right: u64) -> u64 { left + right diff --git a/memfd_ringbuf/src/traits.rs b/memfd_ringbuf/src/traits.rs index 720de4d..3892ab6 100644 --- a/memfd_ringbuf/src/traits.rs +++ b/memfd_ringbuf/src/traits.rs @@ -1,3 +1,17 @@ use bytemuck::Pod; +use rustix::io::Result; -pub unsafe trait SharedPod: Pod {} \ No newline at end of file +pub unsafe trait SharedPod: Pod {} + +unsafe impl SharedPod for u32 {} +unsafe impl SharedPod for u64 {} +unsafe impl SharedPod for i32 {} +unsafe impl SharedPod for i64 {} +unsafe impl SharedPod for u8 {} +unsafe impl SharedPod for i8 {} +unsafe impl SharedPod for usize {} +unsafe impl SharedPod for isize {} + +pub trait RbProducerNotify { + fn notify(&self) -> Result<()>; +} \ No newline at end of file