From 09b4db5d3f4b1f3bebbefd5c55606fd621abff30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E5=98=89=E4=B9=90?= Date: Mon, 4 May 2026 21:13:17 +0800 Subject: [PATCH] =?UTF-8?q?[OHOS.with=20ai]=20=E5=B0=86=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E7=AE=A1=E7=90=86/=E9=85=8D=E7=BD=AE=E5=88=86=E4=BA=AB/?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E8=81=9A=E5=90=88/=E5=AE=9E=E4=BE=8B?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E8=A7=A3=E6=9E=90=E4=B8=8B=E6=B2=89=E8=87=B3?= =?UTF-8?q?=20Rust=20=E5=86=85=E6=A0=B8=EF=BC=8C=E6=94=B6=E6=95=9B?= =?UTF-8?q?=E8=81=8C=E8=B4=A3=E5=B9=B6=E6=8F=90=E5=8D=87=E6=80=A7=E8=83=BD?= =?UTF-8?q?=20(#2209)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add ohrs config store and startup error logging * feat: full ability core for ohos * feat: full ability core for ohos * feat: clean code --------- Co-authored-by: FrankHan --- easytier-contrib/easytier-ohrs/Cargo.lock | 676 ++++++++++++++---- easytier-contrib/easytier-ohrs/Cargo.toml | 10 + .../easytier-ohrs/src/config_repo.rs | 331 +++++++++ easytier-contrib/easytier-ohrs/src/lib.rs | 554 ++++++++++---- .../easytier-ohrs/src/native_log.rs | 96 --- easytier/src/proto/mod.rs | 5 +- 6 files changed, 1300 insertions(+), 372 deletions(-) create mode 100644 easytier-contrib/easytier-ohrs/src/config_repo.rs delete mode 100644 easytier-contrib/easytier-ohrs/src/native_log.rs diff --git a/easytier-contrib/easytier-ohrs/Cargo.lock b/easytier-contrib/easytier-ohrs/Cargo.lock index cc4a5ee2..86c7ed3f 100644 --- a/easytier-contrib/easytier-ohrs/Cargo.lock +++ b/easytier-contrib/easytier-ohrs/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[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.1" @@ -35,7 +26,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -52,6 +43,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy 0.8.27", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -228,6 +231,18 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" +[[package]] +name = "attohttpc" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" +dependencies = [ + "base64 0.22.1", + "http", + "log", + "url", +] + [[package]] name = "auto_impl" version = "1.3.0" @@ -245,21 +260,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base62" version = "2.2.3" @@ -326,6 +326,31 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bon" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47dbe92550676ee653353c310dfb9cf6ba17ee70396e1f7cf0a2020ad49b2fe" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.106", +] + [[package]] name = "boringtun-easytier" version = "0.6.1" @@ -471,7 +496,18 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", +] + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", ] [[package]] @@ -481,7 +517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", - "chacha20", + "chacha20 0.9.1", "cipher", "poly1305", "zeroize", @@ -498,7 +534,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -678,6 +714,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.3.0" @@ -807,7 +852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "fiat-crypto", "rustc_version", @@ -939,6 +984,17 @@ dependencies = [ "thiserror 2.0.16", ] +[[package]] +name = "delegate" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "deranged" version = "0.5.3" @@ -1066,6 +1122,17 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "dlopen2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" +dependencies = [ + "libc", + "once_cell", + "winapi", +] + [[package]] name = "dtor" version = "0.0.6" @@ -1083,7 +1150,7 @@ checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" [[package]] name = "easytier" -version = "2.6.0" +version = "2.6.3" dependencies = [ "anyhow", "arc-swap", @@ -1096,11 +1163,11 @@ dependencies = [ "auto_impl", "base64 0.22.1", "bitflags 2.9.4", + "bon", "boringtun-easytier", "bytecodec", "byteorder", "bytes", - "cfg-if", "cfg_aliases", "chrono", "cidr", @@ -1110,6 +1177,7 @@ dependencies = [ "crossbeam", "dashmap", "dbus", + "delegate", "derivative", "derive_builder", "derive_more", @@ -1118,10 +1186,10 @@ dependencies = [ "flume", "forwarded-header-value", "futures", - "gethostname", + "gethostname 0.5.0", "git-version", "globwalk", - "hashbrown 0.15.5", + "guarden", "hickory-client", "hickory-proto", "hickory-resolver", @@ -1132,13 +1200,15 @@ dependencies = [ "humansize", "humantime-serde", "idna", + "igd-next", "indoc", "itertools 0.14.0", "kcp-sys", "machine-uid", "multimap", + "natpmp", "netlink-packet-core", - "netlink-packet-route", + "netlink-packet-route 0.21.0", "netlink-packet-utils", "netlink-sys", "network-interface", @@ -1205,9 +1275,8 @@ dependencies = [ "wildmatch", "winapi", "windivert", - "windows 0.52.0", + "windows 0.62.2", "windows-service", - "windows-sys 0.52.0", "winreg 0.52.0", "x25519-dalek", "zerocopy 0.7.35", @@ -1219,16 +1288,26 @@ dependencies = [ name = "easytier-ohrs" version = "0.1.0" dependencies = [ + "async-trait", + "base64 0.22.1", "easytier", + "flate2", + "gethostname 1.1.0", + "ipnet", "napi-build-ohos", "napi-derive-ohos", "napi-ohos", "ohos-hilog-binding", "once_cell", + "prost-reflect", + "rusqlite", + "serde", "serde_json", + "tokio", "tracing", "tracing-core", "tracing-subscriber", + "url", "uuid", ] @@ -1390,6 +1469,18 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fastbloom" version = "0.14.1" @@ -1621,6 +1712,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.2", + "windows-link 0.2.1", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -1643,11 +1744,25 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasi 0.14.7+wasi-0.2.4", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasip2", + "wasip3", +] + [[package]] name = "ghash" version = "0.5.1" @@ -1658,12 +1773,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "git-version" version = "0.3.9" @@ -1714,6 +1823,28 @@ dependencies = [ "walkdir", ] +[[package]] +name = "guarden" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c7272e004bec8ea7fe50b2ec5451858695bb2743e897c353753fcb3415f4ef" +dependencies = [ + "futures", + "guarden-macros", + "tokio", +] + +[[package]] +name = "guarden-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d291d94f41471fe84384a426b3e2c9d22f960a351a5bf26aaa7cd75fbc02c88" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "h2" version = "0.4.12" @@ -1747,6 +1878,9 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" @@ -1765,6 +1899,15 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + [[package]] name = "heapless" version = "0.9.2" @@ -2055,7 +2198,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.6.3", "system-configuration", "tokio", "tower-service", @@ -2075,7 +2218,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.0", + "windows-core 0.62.2", ] [[package]] @@ -2173,6 +2316,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -2200,6 +2349,26 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "igd-next" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac9a3c8278f43b4cd8463380f4a25653ac843e5b177e1d3eaf849cc9ba10d4d" +dependencies = [ + "attohttpc", + "bytes", + "futures", + "http", + "http-body-util", + "hyper", + "hyper-util", + "log", + "rand 0.10.1", + "tokio", + "url", + "xmltree", +] + [[package]] name = "ignore" version = "0.4.23" @@ -2224,6 +2393,8 @@ checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", "hashbrown 0.16.0", + "serde", + "serde_core", ] [[package]] @@ -2253,17 +2424,6 @@ dependencies = [ "rustversion", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ip_network" version = "0.4.1" @@ -2426,6 +2586,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libbz2-rs-sys" version = "0.2.2" @@ -2434,9 +2600,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] name = "libc" -version = "0.2.175" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libdbus-sys" @@ -2455,7 +2621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -2494,6 +2660,17 @@ dependencies = [ "libc", ] +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-rs-sys" version = "0.5.2" @@ -2625,13 +2802,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -2743,6 +2920,35 @@ dependencies = [ "tempfile", ] +[[package]] +name = "natpmp" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77366fa8ce34e2e1322dd97da65f11a62f451bd3daae8be6993c00800f61dd07" +dependencies = [ + "async-trait", + "cc", + "netdev", + "tokio", +] + +[[package]] +name = "netdev" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f901362e84cd407be6f8cd9d3a46bccf09136b095792785401ea7d283c79b91d" +dependencies = [ + "dlopen2", + "ipnet", + "libc", + "netlink-packet-core", + "netlink-packet-route 0.17.1", + "netlink-sys", + "once_cell", + "system-configuration", + "windows-sys 0.52.0", +] + [[package]] name = "netlink-packet-core" version = "0.7.0" @@ -2754,6 +2960,20 @@ dependencies = [ "netlink-packet-utils", ] +[[package]] +name = "netlink-packet-route" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + [[package]] name = "netlink-packet-route" version = "0.21.0" @@ -2908,15 +3128,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "ohos-hilog-binding" version = "0.1.2" @@ -3232,7 +3443,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] @@ -3244,7 +3455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] @@ -3523,7 +3734,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.0", + "socket2 0.6.3", "thiserror 2.0.16", "tokio", "tracing", @@ -3574,7 +3785,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] @@ -3594,6 +3805,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "radix_trie" version = "0.2.1" @@ -3625,6 +3842,17 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20 0.10.0", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -3663,6 +3891,12 @@ dependencies = [ "getrandom 0.3.3", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rcgen" version = "0.12.1" @@ -3797,6 +4031,20 @@ dependencies = [ "portable-atomic-util", ] +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags 2.9.4", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rust-i18n" version = "3.1.5" @@ -3851,12 +4099,6 @@ dependencies = [ "triomphe", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -4170,7 +4412,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -4181,7 +4423,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -4290,12 +4532,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -4615,29 +4857,26 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.3", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -4684,6 +4923,7 @@ dependencies = [ "bytes", "futures-core", "futures-sink", + "futures-util", "pin-project-lite", "tokio", ] @@ -5146,7 +5386,16 @@ version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.46.0", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", ] [[package]] @@ -5221,6 +5470,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.9.4", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web-sys" version = "0.3.80" @@ -5375,27 +5658,29 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections", + "windows-collections 0.2.0", "windows-core 0.61.2", - "windows-future", + "windows-future 0.2.1", "windows-link 0.1.3", - "windows-numerics", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] @@ -5408,12 +5693,12 @@ dependencies = [ ] [[package]] -name = "windows-core" -version = "0.52.0" +name = "windows-collections" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-targets 0.52.6", + "windows-core 0.62.2", ] [[package]] @@ -5431,15 +5716,15 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.62.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.0", - "windows-result 0.4.0", - "windows-strings 0.5.0", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -5450,14 +5735,25 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -5466,9 +5762,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -5483,9 +5779,9 @@ checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" @@ -5497,6 +5793,16 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-registry" version = "0.5.3" @@ -5519,11 +5825,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -5548,11 +5854,11 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -5606,7 +5912,7 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -5681,6 +5987,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -5915,6 +6230,94 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap", + "prettyplease", + "syn 2.0.106", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.106", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.9.4", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + [[package]] name = "writeable" version = "0.6.1" @@ -5939,6 +6342,15 @@ version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +[[package]] +name = "xmltree" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" +dependencies = [ + "xml-rs", +] + [[package]] name = "yasna" version = "0.5.2" diff --git a/easytier-contrib/easytier-ohrs/Cargo.toml b/easytier-contrib/easytier-ohrs/Cargo.toml index 20c0c169..b1d65d9e 100644 --- a/easytier-contrib/easytier-ohrs/Cargo.toml +++ b/easytier-contrib/easytier-ohrs/Cargo.toml @@ -7,6 +7,10 @@ edition = "2024" crate-type=["cdylib"] [dependencies] +async-trait = "0.1" +base64 = "0.22" +flate2 = "1.1" +gethostname = "1.1" ohos-hilog-binding = {version = "*", features = ["redirect"]} easytier = { path = "../../easytier" } napi-derive-ohos = "1.1" @@ -26,10 +30,16 @@ napi-ohos = { version = "1.1", default-features = false, features = [ "web_stream", ] } once_cell = "1.21.3" +ipnet = "2.10" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.125" +prost-reflect = { version = "0.14.5", default-features = false, features = ["derive"] } +rusqlite = { version = "0.32", features = ["bundled"] } tracing-subscriber = "0.3.19" tracing-core = "0.1.33" tracing = "0.1.41" +tokio = { version = "1", features = ["rt-multi-thread", "sync", "time"] } +url = "2.5" uuid = { version = "1.5.0", features = [ "v4", "fast-rng", diff --git a/easytier-contrib/easytier-ohrs/src/config_repo.rs b/easytier-contrib/easytier-ohrs/src/config_repo.rs new file mode 100644 index 00000000..2406191a --- /dev/null +++ b/easytier-contrib/easytier-ohrs/src/config_repo.rs @@ -0,0 +1,331 @@ +use crate::config::storage::config_meta::{ + delete_config_meta, get_config_meta, init_config_meta_store, list_config_meta_entries, + open_db, upsert_config_meta_in_tx, +}; +use crate::config::types::stored_config::{ExportTomlResult, StoredConfigRecord}; +use super::{field_store, import_export, legacy_migration, validation}; +use easytier::common::config::ConfigLoader; +use easytier::proto::api::manage::NetworkConfig; +use ohos_hilog_binding::{hilog_debug, hilog_error}; +use rusqlite::params; +use serde_json::Value; +use std::path::PathBuf; +use std::sync::Mutex; + +static CONFIG_ROOT_DIR: Mutex> = Mutex::new(None); +pub(crate) const CONFIG_DIR_NAME: &str = "easytier-configs"; +pub(crate) const KERNEL_SOCKET_FILE_NAME: &str = "easytier-kernel.sock"; + +pub(crate) fn config_root_dir() -> Option { + CONFIG_ROOT_DIR + .lock() + .ok() + .and_then(|guard| guard.as_ref().cloned()) +} + +pub(crate) fn kernel_socket_path() -> Option { + config_root_dir().map(|root| root.join(KERNEL_SOCKET_FILE_NAME)) +} + +pub(crate) fn legacy_config_file_path(config_id: &str) -> Option { + legacy_migration::legacy_config_file_path(&config_root_dir(), CONFIG_DIR_NAME, config_id) +} + +pub fn init_config_store(root_dir: String) -> bool { + let root = PathBuf::from(root_dir); + let configs_dir = root.join(CONFIG_DIR_NAME); + if let Err(e) = std::fs::create_dir_all(&configs_dir) { + hilog_error!("[Rust] failed to create config dir {}: {}", configs_dir.display(), e); + return false; + } + + match CONFIG_ROOT_DIR.lock() { + Ok(mut guard) => { + *guard = Some(root.clone()); + } + Err(e) => { + hilog_error!("[Rust] failed to lock config root dir: {}", e); + return false; + } + } + + if !init_config_meta_store(root.to_string_lossy().into_owned()) { + return false; + } + + hilog_debug!("[Rust] initialized config repo at {}", configs_dir.display()); + true +} + +fn migrate_legacy_file_if_needed(config_id: &str) -> Option<()> { + legacy_migration::migrate_legacy_file_if_needed(&config_root_dir(), CONFIG_DIR_NAME, config_id, save_config_record) +} + +pub fn save_config_record( + config_id: String, + display_name: String, + config_json: String, +) -> Option { + let config = match validation::validate_config_json(&config_json, config_id.clone()) { + Ok(config) => config, + Err(e) => { + hilog_error!("[Rust] save_config_record failed {}", e); + return None; + } + }; + + let normalized_json = match serde_json::to_string(&config) { + Ok(raw) => raw, + Err(e) => { + hilog_error!("[Rust] failed to serialize normalized config {}: {}", config_id, e); + return None; + } + }; + + let fields = match validation::config_to_top_level_map(&config) { + Some(fields) => fields, + None => return None, + }; + + let conn = open_db()?; + let tx = conn.unchecked_transaction().ok()?; + let existing_meta = get_config_meta(&config_id); + let favorite = existing_meta.as_ref().map(|meta| meta.favorite).unwrap_or(false); + let temporary = existing_meta + .as_ref() + .map(|meta| meta.temporary) + .unwrap_or(false); + let meta = upsert_config_meta_in_tx(&tx, config_id.clone(), display_name, favorite, temporary)?; + + field_store::replace_config_fields(&tx, &config_id, fields)?; + + tx.commit().ok()?; + + if let Some(legacy_path) = legacy_config_file_path(&config_id) { + if legacy_path.exists() { + let _ = std::fs::remove_file(legacy_path); + } + } + + Some(StoredConfigRecord { + meta, + config_json: normalized_json, + }) +} + +pub fn load_config_json(config_id: &str) -> Option { + migrate_legacy_file_if_needed(config_id)?; + let object = field_store::load_config_map_from_db(config_id)?; + serde_json::to_string(&Value::Object(object)).ok() +} + +pub fn get_config_record(config_id: &str) -> Option { + let config_json = load_config_json(config_id)?; + let meta = get_config_meta(config_id)?; + Some(StoredConfigRecord { meta, config_json }) +} + +pub fn get_config_field_value(config_id: &str, field: &str) -> Option { + migrate_legacy_file_if_needed(config_id)?; + let conn = open_db()?; + conn.query_row( + "SELECT field_json FROM stored_config_fields + WHERE config_id = ?1 AND field_name = ?2", + params![config_id, field], + |row| row.get::<_, String>(0), + ) + .ok() +} + +pub fn set_config_field_value(config_id: &str, field: &str, json_value: &str) -> bool { + if field.contains('.') { + return false; + } + + let raw = match load_config_json(config_id) { + Some(raw) => raw, + None => return false, + }; + let mut value = match serde_json::from_str::(&raw) { + Ok(value) => value, + Err(_) => return false, + }; + let new_field_value = match serde_json::from_str::(json_value) { + Ok(value) => value, + Err(_) => return false, + }; + let object = match value.as_object_mut() { + Some(object) => object, + None => return false, + }; + object.insert(field.to_string(), new_field_value); + + let normalized = match serde_json::to_string(&value) { + Ok(raw) => raw, + Err(_) => return false, + }; + + let display_name = get_config_meta(config_id) + .map(|meta| meta.display_name) + .unwrap_or_else(|| config_id.to_string()); + + save_config_record(config_id.to_string(), display_name, normalized).is_some() +} + +pub fn get_display_name(config_id: &str) -> Option { + get_config_meta(config_id).map(|meta| meta.display_name) +} + +pub fn get_default_config_json() -> Option { + crate::build_default_network_config_json().ok() +} + +pub fn create_config_record(config_id: String, display_name: String) -> Option { + let raw = get_default_config_json()?; + let mut config = serde_json::from_str::(&raw).ok()?; + config.instance_id = Some(config_id.clone()); + let normalized_json = serde_json::to_string(&config).ok()?; + save_config_record(config_id, display_name, normalized_json) +} + +pub fn start_kernel_with_config_id(config_id: &str) -> bool { + let raw = match load_config_json(config_id) { + Some(raw) => raw, + None => return false, + }; + crate::run_network_instance_from_json(&raw) +} + +pub fn list_config_meta_json() -> String { + serde_json::to_string(&list_config_meta_entries().configs).unwrap_or_else(|_| "[]".to_string()) +} + +pub fn delete_config_record(config_id: &str) -> bool { + if let Some(path) = legacy_config_file_path(config_id) { + if path.exists() { + let _ = std::fs::remove_file(path); + } + } + + let conn = match open_db() { + Some(conn) => conn, + None => return false, + }; + if let Err(e) = conn.execute( + "DELETE FROM stored_config_fields WHERE config_id = ?1", + params![config_id], + ) { + hilog_error!("[Rust] failed to delete config fields {}: {}", config_id, e); + return false; + } + + delete_config_meta(config_id) +} + +pub fn export_config_toml(config_id: &str) -> Option { + let record = get_config_record(config_id)?; + import_export::export_config_toml_from_record(&record) +} + +pub fn import_toml_config(toml_text: String, display_name: Option) -> Option { + import_export::import_toml_to_record(toml_text, display_name, save_config_record) +} + +#[cfg(test)] +mod tests { + use super::*; + use rusqlite::params; + use std::path::PathBuf; + use std::time::{SystemTime, UNIX_EPOCH}; + + fn test_root() -> String { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + let dir = std::env::temp_dir().join(format!("easytier_ohrs_test_{}", unique)); + dir.to_string_lossy().into_owned() + } + + #[test] + fn save_get_export_delete_roundtrip() { + let root = test_root(); + assert!(init_config_store(root.clone())); + + let config_json = crate::build_default_network_config_json().expect("default config"); + let saved = save_config_record( + "cfg-1".to_string(), + "test-config".to_string(), + config_json, + ) + .expect("save config"); + + assert_eq!(saved.meta.config_id, "cfg-1"); + assert_eq!(saved.meta.display_name, "test-config"); + + let loaded = get_config_record("cfg-1").expect("load config"); + assert_eq!(loaded.meta.display_name, "test-config"); + assert!(loaded.config_json.contains("cfg-1")); + + let legacy_json_path = PathBuf::from(&root) + .join(CONFIG_DIR_NAME) + .join("cfg-1.json"); + assert!( + !legacy_json_path.exists(), + "config should no longer be persisted as a per-config json file" + ); + + let conn = open_db().expect("db should be open"); + let field_count: i64 = conn + .query_row( + "SELECT COUNT(*) FROM stored_config_fields WHERE config_id = ?1", + params!["cfg-1"], + |row| row.get(0), + ) + .expect("count config fields"); + assert!(field_count > 0, "config fields should be stored in sqlite"); + + let exported = export_config_toml("cfg-1").expect("export toml"); + assert!(exported.toml_text.contains("instance_id")); + + assert!(delete_config_record("cfg-1")); + assert!(get_config_record("cfg-1").is_none()); + } + + #[test] + fn set_config_field_updates_only_requested_top_level_field() { + let root = test_root(); + assert!(init_config_store(root)); + + let config_json = crate::build_default_network_config_json().expect("default config"); + save_config_record( + "cfg-field".to_string(), + "field-config".to_string(), + config_json, + ) + .expect("save config"); + + let before_network_name = get_config_field_value("cfg-field", "network_name"); + let before_instance_id = get_config_field_value("cfg-field", "instance_id") + .expect("instance id field should exist"); + + assert!(set_config_field_value( + "cfg-field", + "network_name", + "\"changed-network\"" + )); + + assert_eq!( + get_config_field_value("cfg-field", "network_name"), + Some("\"changed-network\"".to_string()) + ); + assert_eq!( + get_config_field_value("cfg-field", "instance_id"), + Some(before_instance_id) + ); + assert_ne!( + get_config_field_value("cfg-field", "network_name"), + before_network_name + ); + } +} diff --git a/easytier-contrib/easytier-ohrs/src/lib.rs b/easytier-contrib/easytier-ohrs/src/lib.rs index 352ce3c7..52140c6a 100644 --- a/easytier-contrib/easytier-ohrs/src/lib.rs +++ b/easytier-contrib/easytier-ohrs/src/lib.rs @@ -1,21 +1,357 @@ -mod native_log; +mod config; +mod exports; +mod kernel_bridge; +mod platform; +mod runtime; +use config::services::schema_service::{ + ConfigFieldMapping, NetworkConfigSchema, + get_network_config_field_mappings as build_network_config_field_mappings, + get_network_config_schema as build_network_config_schema, +}; +use config::services::share_link_service::{ + build_config_share_link as build_config_share_link_inner, + import_config_share_link as import_config_share_link_inner, + parse_config_share_link as parse_config_share_link_inner, +}; +use config::storage::config_meta::get_config_display_name; +use config::types::stored_config::{KeyValuePair, SharedConfigLinkPayload}; +use config::repository::{ + create_config_record, delete_config_record, export_config_toml, get_config_field_value, + get_default_config_json, import_toml_config, init_config_store as init_repo_store, + list_config_meta_json, save_config_record, set_config_field_value, start_kernel_with_config_id, +}; +use kernel_bridge::{ + aggregate_requested_tun_routes, + start_local_socket_server as start_local_socket_server_inner, + stop_local_socket_server as stop_local_socket_server_inner, +}; +use runtime::state::runtime_state::{ + RuntimeAggregateState, TunAggregateState, clear_tun_attached, mark_tun_attached, + runtime_instance_from_running_info, +}; use easytier::common::config::{ConfigFileControl, ConfigLoader, TomlConfigLoader}; use easytier::common::constants::EASYTIER_VERSION; use easytier::instance_manager::NetworkInstanceManager; use easytier::proto::api::manage::NetworkConfig; +use easytier::proto::api::manage::NetworkingMethod; +use easytier::web_client::{WebClient, WebClientHooks, run_web_client}; use napi_derive_ohos::napi; -use ohos_hilog_binding::{hilog_debug, hilog_error}; +use ohos_hilog_binding::{hilog_error, hilog_info}; +use std::collections::{HashMap, HashSet}; use std::format; +use std::sync::{Arc, Mutex}; +use tokio::runtime::{Builder, Runtime}; use uuid::Uuid; -static INSTANCE_MANAGER: once_cell::sync::Lazy = - once_cell::sync::Lazy::new(NetworkInstanceManager::new); +pub(crate) static INSTANCE_MANAGER: once_cell::sync::Lazy> = + once_cell::sync::Lazy::new(|| Arc::new(NetworkInstanceManager::new())); +static ASYNC_RUNTIME: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| { + Builder::new_multi_thread() + .enable_all() + .build() + .expect("tokio runtime for easytier-ohrs") +}); +static WEB_CLIENTS: once_cell::sync::Lazy>> = + once_cell::sync::Lazy::new(|| Mutex::new(HashMap::new())); -#[napi(object)] -pub struct KeyValuePair { - pub key: String, - pub value: String, +#[derive(Default)] +struct TrackedWebClientHooks { + instance_ids: Mutex>, +} + +struct ManagedWebClient { + _client: WebClient, + hooks: Arc, +} + +#[async_trait::async_trait] +impl WebClientHooks for TrackedWebClientHooks { + async fn post_run_network_instance(&self, id: &Uuid) -> Result<(), String> { + self.instance_ids + .lock() + .map_err(|err| err.to_string())? + .insert(*id); + Ok(()) + } + + async fn post_remove_network_instances(&self, ids: &[Uuid]) -> Result<(), String> { + let mut guard = self.instance_ids.lock().map_err(|err| err.to_string())?; + for id in ids { + guard.remove(id); + } + Ok(()) + } +} + +fn is_config_server_config(config: &NetworkConfig) -> bool { + matches!( + NetworkingMethod::try_from(config.networking_method.unwrap_or_default()).unwrap_or_default(), + NetworkingMethod::PublicServer + ) && config + .public_server_url + .as_ref() + .is_some_and(|url| !url.trim().is_empty()) +} + +fn stop_web_client(config_id: &str) -> bool { + let managed = match WEB_CLIENTS.lock() { + Ok(mut guard) => guard.remove(config_id), + Err(err) => { + hilog_error!("[Rust] stop_web_client lock failed {}", err); + return false; + } + }; + + let Some(managed) = managed else { + return false; + }; + + let tracked_ids = managed + .hooks + .instance_ids + .lock() + .map(|guard| guard.iter().copied().collect::>()) + .unwrap_or_default(); + drop(managed); + + if tracked_ids.is_empty() { + maybe_stop_local_socket_server(); + return true; + } + + let ret = INSTANCE_MANAGER + .delete_network_instance(tracked_ids) + .map(|_| true) + .unwrap_or_else(|err| { + hilog_error!("[Rust] stop config server instances failed {}: {}", config_id, err); + false + }); + maybe_stop_local_socket_server(); + ret +} + +fn ensure_local_socket_server_started() -> bool { + start_local_socket_server_inner() +} + +fn maybe_stop_local_socket_server() { + let no_local_instances = INSTANCE_MANAGER.list_network_instance_ids().is_empty(); + let no_web_clients = WEB_CLIENTS.lock().map(|guard| guard.is_empty()).unwrap_or(false); + if no_local_instances && no_web_clients { + let _ = stop_local_socket_server_inner(); + } +} + +fn run_config_server_instance(config_id: &str, config: &NetworkConfig) -> bool { + if INSTANCE_MANAGER.list_network_instance_ids().iter().next().is_some() { + hilog_error!("[Rust] there is a running instance!"); + return false; + } + + let Some(config_server_url) = config.public_server_url.clone() else { + hilog_error!("[Rust] public_server_url missing for config server mode"); + return false; + }; + let hooks = Arc::new(TrackedWebClientHooks::default()); + let secure_mode = config.secure_mode.as_ref().map(|mode| mode.enabled).unwrap_or(false); + let hostname = config.hostname.clone(); + + if !ensure_local_socket_server_started() { + return false; + } + + let client = ASYNC_RUNTIME.block_on(run_web_client( + &config_server_url, + None, + hostname, + secure_mode, + INSTANCE_MANAGER.clone(), + Some(hooks.clone()), + )); + + let client = match client { + Ok(client) => client, + Err(err) => { + hilog_error!("[Rust] start config server failed {}", err); + return false; + } + }; + + match WEB_CLIENTS.lock() { + Ok(mut guard) => { + guard.insert( + config_id.to_string(), + ManagedWebClient { + _client: client, + hooks, + }, + ); + true + } + Err(err) => { + hilog_error!("[Rust] store config server client failed {}", err); + false + } + } +} + +pub(crate) fn build_default_network_config_json() -> Result { + let config = NetworkConfig::new_from_config(TomlConfigLoader::default()) + .map_err(|e| format!("default_network_config failed {}", e))?; + serde_json::to_string(&config).map_err(|e| format!("default_network_config failed {}", e)) +} + +fn convert_toml_to_network_config_inner(toml_text: &str) -> Result { + let config = NetworkConfig::new_from_config(TomlConfigLoader::new_from_str(toml_text).map_err(|e| e.to_string())?) + .map_err(|e| e.to_string())?; + serde_json::to_string(&config).map_err(|e| e.to_string()) +} + +fn parse_network_config_inner(cfg_json: &str) -> bool { + serde_json::from_str::(cfg_json) + .ok() + .and_then(|cfg| cfg.gen_config().ok()) + .is_some() +} + +pub(crate) fn run_network_instance_from_json(cfg_json: &str) -> bool { + let config = match serde_json::from_str::(cfg_json) { + Ok(cfg) => cfg, + Err(e) => { + hilog_error!("[Rust] parse config failed {}", e); + return false; + } + }; + + if is_config_server_config(&config) { + let Some(config_id) = config.instance_id.as_deref() else { + hilog_error!("[Rust] config server config missing instance id"); + return false; + }; + return run_config_server_instance(config_id, &config); + } + + let cfg = match config.gen_config() { + Ok(toml) => toml, + Err(e) => { + hilog_error!("[Rust] parse config failed {}", e); + return false; + } + }; + + if !INSTANCE_MANAGER.list_network_instance_ids().is_empty() { + hilog_error!("[Rust] there is a running instance!"); + return false; + } + + if !ensure_local_socket_server_started() { + return false; + } + + let inst_id = cfg.get_id(); + if INSTANCE_MANAGER.list_network_instance_ids().contains(&inst_id) { + hilog_error!("[Rust] instance {} already exists", inst_id); + return false; + } + + match INSTANCE_MANAGER.run_network_instance(cfg, false, ConfigFileControl::STATIC_CONFIG) { + Ok(_) => true, + Err(err) => { + hilog_error!("[Rust] start_kernel failed for {}: {}", inst_id, err); + false + } + } +} + +fn parse_instance_uuid(config_id: &str) -> Option { + match Uuid::parse_str(config_id) { + Ok(uuid) => Some(uuid), + Err(err) => { + hilog_error!("[Rust] invalid config_id {}: {}", config_id, err); + None + } + } +} + +#[napi] +pub fn init_config_store(root_dir: String) -> bool { + exports::config_api::init_config_store(root_dir) +} + +#[napi] +pub fn list_configs() -> String { + exports::config_api::list_configs() +} + +#[napi] +pub fn get_config_display_name_by_id(config_id: String) -> Option { + get_config_display_name(&config_id) +} + +#[napi] +pub fn save_config(config_id: String, display_name: String, config_json: String) -> bool { + exports::config_api::save_config(config_id, display_name, config_json) +} + +#[napi] +pub fn create_config(config_id: String, display_name: String) -> bool { + exports::config_api::create_config(config_id, display_name) +} + +#[napi] +pub fn rename_stored_config(config_id: String, display_name: String) -> bool { + config::storage::config_meta::set_config_display_name(config_id, display_name).is_some() +} + +#[napi] +pub fn delete_stored_config_meta(config_id: String) -> bool { + exports::config_api::delete_stored_config_meta(config_id) +} + +#[napi] +pub fn get_config(config_id: String) -> Option { + exports::config_api::get_config(config_id) +} + +#[napi] +pub fn get_default_config() -> Option { + exports::config_api::get_default_config() +} + +#[napi] +pub fn get_config_field(config_id: String, field: String) -> Option { + exports::config_api::get_config_field(config_id, field) +} + +#[napi] +pub fn set_config_field(config_id: String, field: String, json_value: String) -> bool { + exports::config_api::set_config_field(config_id, field, json_value) +} + +#[napi] +pub fn import_toml(toml_text: String, display_name: Option) -> Option { + exports::config_api::import_toml(toml_text, display_name) +} + +#[napi] +pub fn export_toml(config_id: String) -> Option { + exports::config_api::export_toml(config_id) +} + +#[napi] +pub fn start_kernel(config_id: String) -> bool { + exports::runtime_api::start_kernel(config_id, start_kernel_with_config_id) +} + +#[napi] +pub fn stop_kernel(config_id: String) -> bool { + exports::runtime_api::stop_kernel(config_id, stop_web_client, parse_instance_uuid, maybe_stop_local_socket_server) +} + +#[napi] +pub fn stop_network_instance(config_ids: Vec) -> bool { + exports::runtime_api::stop_network_instance(config_ids, stop_kernel) } #[napi] @@ -23,163 +359,95 @@ pub fn easytier_version() -> String { EASYTIER_VERSION.to_string() } -#[napi] -pub fn set_tun_fd(inst_id: String, fd: i32) -> bool { - match Uuid::try_parse(&inst_id) { - Ok(uuid) => match INSTANCE_MANAGER.set_tun_fd(&uuid, fd) { - Ok(_) => { - hilog_debug!("[Rust] set tun fd {} to {}.", fd, inst_id); - true - } - Err(e) => { - hilog_error!("[Rust] cant set tun fd {} to {}. {}", fd, inst_id, e); - false - } - }, - Err(e) => { - hilog_error!("[Rust] cant covert {} to uuid. {}", inst_id, e); - false - } - } -} - #[napi] pub fn default_network_config() -> String { - match NetworkConfig::new_from_config(TomlConfigLoader::default()) { - Ok(result) => serde_json::to_string(&result).unwrap_or_else(|e| format!("ERROR {}", e)), - Err(e) => { - hilog_error!("[Rust] default_network_config failed {}", e); - format!("ERROR {}", e) - } - } + get_default_config().unwrap_or_else(|| "{}".to_string()) } #[napi] -pub fn convert_toml_to_network_config(cfg_str: String) -> String { - match TomlConfigLoader::new_from_str(&cfg_str) { - Ok(cfg) => match NetworkConfig::new_from_config(cfg) { - Ok(result) => serde_json::to_string(&result).unwrap_or_else(|e| format!("ERROR {}", e)), - Err(e) => { - hilog_error!("[Rust] convert_toml_to_network_config failed {}", e); - format!("ERROR {}", e) - } - }, - Err(e) => { - hilog_error!("[Rust] convert_toml_to_network_config failed {}", e); - format!("ERROR {}", e) - } - } +pub fn convert_toml_to_network_config(toml_text: String) -> String { + convert_toml_to_network_config_inner(&toml_text) + .unwrap_or_else(|err| format!("ERROR: {err}")) } #[napi] pub fn parse_network_config(cfg_json: String) -> bool { - match serde_json::from_str::(&cfg_json) { - Ok(cfg) => match cfg.gen_config() { - Ok(toml) => { - hilog_debug!("[Rust] Convert to Toml {}", toml.dump()); - true - } - Err(e) => { - hilog_error!("[Rust] parse config failed {}", e); - false - } - }, - Err(e) => { - hilog_error!("[Rust] parse config failed {}", e); - false - } - } + parse_network_config_inner(&cfg_json) } #[napi] pub fn run_network_instance(cfg_json: String) -> bool { - let cfg = match serde_json::from_str::(&cfg_json) { - Ok(cfg) => match cfg.gen_config() { - Ok(toml) => toml, - Err(e) => { - hilog_error!("[Rust] parse config failed {}", e); - return false; - } - }, - Err(e) => { - hilog_error!("[Rust] parse config failed {}", e); - return false; - } - }; - - if INSTANCE_MANAGER.list_network_instance_ids().len() > 0 { - hilog_error!("[Rust] there is a running instance!"); - return false; - } - - let inst_id = cfg.get_id(); - if INSTANCE_MANAGER - .list_network_instance_ids() - .contains(&inst_id) - { - return false; - } - INSTANCE_MANAGER - .run_network_instance(cfg, false, ConfigFileControl::STATIC_CONFIG) - .unwrap(); - true -} - -#[napi] -pub fn stop_network_instance(inst_names: Vec) { - INSTANCE_MANAGER - .delete_network_instance( - inst_names - .into_iter() - .filter_map(|s| Uuid::parse_str(&s).ok()) - .collect(), - ) - .unwrap(); - hilog_debug!("[Rust] stop_network_instance"); + run_network_instance_from_json(&cfg_json) } #[napi] pub fn collect_network_infos() -> Vec { - let mut result = Vec::new(); - match INSTANCE_MANAGER.collect_network_infos_sync() { - Ok(map) => { - for (uuid, info) in map.iter() { - // convert value to json string - let value = match serde_json::to_string(&info) { - Ok(value) => value, - Err(e) => { - hilog_error!("[Rust] failed to serialize instance {} info: {}", uuid, e); - continue; - } - }; - result.push(KeyValuePair { - key: uuid.clone().to_string(), - value: value.clone(), - }); - } - } - Err(_) => {} - } - result + exports::runtime_api::collect_network_infos() } #[napi] -pub fn collect_running_network() -> Vec { - INSTANCE_MANAGER - .list_network_instance_ids() - .clone() - .into_iter() - .map(|id| id.to_string()) - .collect() +pub fn set_tun_fd(config_id: String, fd: i32) -> bool { + exports::runtime_api::set_tun_fd(config_id, fd, parse_instance_uuid) } #[napi] -pub fn is_running_network(inst_id: String) -> bool { - match Uuid::try_parse(&inst_id) { - Ok(uuid) => INSTANCE_MANAGER.list_network_instance_ids().contains(&uuid), - Err(e) => { - hilog_error!("[Rust] cant covert {} to uuid. {}", inst_id, e); - false - } +pub fn get_network_config_schema() -> NetworkConfigSchema { + build_network_config_schema() +} + +#[napi] +pub fn get_network_config_field_mappings() -> Vec { + build_network_config_field_mappings() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn exported_plain_object_schema_contains_core_networkconfig_metadata() { + let schema = get_network_config_schema(); + assert_eq!(schema.name, "NetworkConfig"); + assert_eq!(schema.node_kind, "schema"); + assert!(schema + .children + .iter() + .any(|field| field.name == "network_name")); + let secure_mode = schema + .children + .iter() + .find(|field| field.name == "secure_mode") + .expect("secure_mode field"); + assert!(secure_mode + .children + .iter() + .any(|field| field.name == "enabled")); } } + +#[napi] +pub fn get_runtime_snapshot() -> RuntimeAggregateState { + exports::runtime_api::get_runtime_snapshot() +} + +pub(crate) fn get_runtime_snapshot_inner() -> RuntimeAggregateState { + exports::runtime_api::get_runtime_snapshot_inner() +} + +#[napi] +pub fn build_config_share_link(config_id: String, only_start: Option) -> Option { + build_config_share_link_inner(&config_id, None, only_start.unwrap_or(false)) +} + +#[napi] +pub fn parse_config_share_link(share_link: String) -> Option { + parse_config_share_link_inner(&share_link) +} + +#[napi] +pub fn import_config_share_link( + share_link: String, + display_name_override: Option, +) -> Option { + import_config_share_link_inner(&share_link, display_name_override) +} diff --git a/easytier-contrib/easytier-ohrs/src/native_log.rs b/easytier-contrib/easytier-ohrs/src/native_log.rs deleted file mode 100644 index abfbc611..00000000 --- a/easytier-contrib/easytier-ohrs/src/native_log.rs +++ /dev/null @@ -1,96 +0,0 @@ -use napi_derive_ohos::napi; -use ohos_hilog_binding::{ - LogOptions, hilog_debug, hilog_error, hilog_info, hilog_warn, set_global_options, -}; -use std::collections::HashMap; -use std::panic; -use tracing::{Event, Subscriber}; -use tracing_core::Level; -use tracing_subscriber::layer::{Context, Layer}; -use tracing_subscriber::prelude::*; - -static INITIALIZED: std::sync::Once = std::sync::Once::new(); -fn panic_hook(info: &panic::PanicHookInfo) { - hilog_error!("RUST PANIC: {}", info); -} - -#[napi] -pub fn init_panic_hook() { - INITIALIZED.call_once(|| { - panic::set_hook(Box::new(panic_hook)); - }); -} - -#[napi] -pub fn hilog_global_options(domain: u32, tag: String) { - ohos_hilog_binding::forward_stdio_to_hilog(); - set_global_options(LogOptions { - domain, - tag: Box::leak(tag.clone().into_boxed_str()), - }) -} - -#[napi] -pub fn init_tracing_subscriber() { - tracing_subscriber::registry() - .with(CallbackLayer { - callback: Box::new(tracing_callback), - }) - .init(); -} - -fn tracing_callback(event: &Event, fields: HashMap) { - let metadata = event.metadata(); - #[cfg(target_env = "ohos")] - { - let loc = metadata.target().split("::").last().unwrap(); - match *metadata.level() { - Level::TRACE => { - hilog_debug!("[{}] {:?}", loc, fields.values().collect::>()); - } - Level::DEBUG => { - hilog_debug!("[{}] {:?}", loc, fields.values().collect::>()); - } - Level::INFO => { - hilog_info!("[{}] {:?}", loc, fields.values().collect::>()); - } - Level::WARN => { - hilog_warn!("[{}] {:?}", loc, fields.values().collect::>()); - } - Level::ERROR => { - hilog_error!("[{}] {:?}", loc, fields.values().collect::>()); - } - } - } -} - -struct CallbackLayer { - callback: Box) + Send + Sync>, -} - -impl Layer for CallbackLayer { - fn on_event(&self, event: &Event, _ctx: Context) { - // 使用 fmt::format::FmtSpan 提取字段值 - let mut fields = HashMap::new(); - let mut visitor = FieldCollector(&mut fields); - event.record(&mut visitor); - (self.callback)(event, fields); - } -} - -struct FieldCollector<'a>(&'a mut HashMap); - -impl<'a> tracing::field::Visit for FieldCollector<'a> { - fn record_i64(&mut self, field: &tracing::field::Field, value: i64) { - self.0.insert(field.name().to_string(), value.to_string()); - } - - fn record_str(&mut self, field: &tracing::field::Field, value: &str) { - self.0.insert(field.name().to_string(), value.to_string()); - } - - fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { - self.0 - .insert(field.name().to_string(), format!("{:?}", value)); - } -} diff --git a/easytier/src/proto/mod.rs b/easytier/src/proto/mod.rs index cb455a89..3315a5da 100644 --- a/easytier/src/proto/mod.rs +++ b/easytier/src/proto/mod.rs @@ -14,5 +14,8 @@ pub mod web; pub mod tests; pub mod utils; -const DESCRIPTOR_POOL_BYTES: &[u8] = +pub const DESCRIPTOR_POOL_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin")); + +pub const ALL_DESCRIPTOR_BYTES: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/descriptors.bin"));