mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-04 10:05:42 +00:00
[OHOS] fix: 修复内存泄露问题,并重构日志管理,预防性修复数据库初始化异常问题 (#2328)
* fix: leak memory feat: new log manager * fix: fail to init db * fix: fail to init db * fix: cargo format
This commit is contained in:
+112
-64
@@ -1237,18 +1237,18 @@ dependencies = [
|
|||||||
"ordered_hash_map",
|
"ordered_hash_map",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"paste",
|
"paste",
|
||||||
|
"pbjson",
|
||||||
|
"pbjson-build",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"petgraph 0.8.2",
|
"petgraph",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pnet",
|
"pnet",
|
||||||
"prefix-trie",
|
"prefix-trie",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"prost",
|
"prost 0.14.3",
|
||||||
"prost-build",
|
"prost-build",
|
||||||
"prost-reflect",
|
"prost-reflect 0.16.4",
|
||||||
"prost-reflect-build",
|
"prost-reflect-build",
|
||||||
"prost-wkt",
|
|
||||||
"prost-wkt-build",
|
|
||||||
"prost-wkt-types",
|
"prost-wkt-types",
|
||||||
"quinn",
|
"quinn",
|
||||||
"quinn-plaintext",
|
"quinn-plaintext",
|
||||||
@@ -1318,9 +1318,8 @@ dependencies = [
|
|||||||
"napi-build-ohos",
|
"napi-build-ohos",
|
||||||
"napi-derive-ohos",
|
"napi-derive-ohos",
|
||||||
"napi-ohos",
|
"napi-ohos",
|
||||||
"ohos-hilog-binding",
|
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"prost-reflect",
|
"prost-reflect 0.14.7",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -3165,22 +3164,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ohos-hilog-binding"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "860d1e3c2c5e3217d819a16c815d2d4dcbc7610285d2612d08745a29c353a503"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"ohos-hilogs-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ohos-hilogs-sys"
|
|
||||||
version = "0.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed07615005d0f8d7bcf901f89c8ff4870666a9bdb00382f588af383f40c160b7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.3"
|
version = "1.21.3"
|
||||||
@@ -3308,6 +3291,28 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbjson"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8edd1efdd8ab23ba9cb9ace3d9987a72663d5d7c9f74fa00b51d6213645cf6c"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbjson-build"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ed4d5c6ae95e08ac768883c8401cf0e8deb4e6e1d6a4e1fd3d2ec4f0ec63200"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.5.0",
|
||||||
|
"itertools 0.14.0",
|
||||||
|
"prost 0.14.3",
|
||||||
|
"prost-types 0.14.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbkdf2"
|
name = "pbkdf2"
|
||||||
version = "0.12.2"
|
version = "0.12.2"
|
||||||
@@ -3334,16 +3339,6 @@ version = "2.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "petgraph"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
|
|
||||||
dependencies = [
|
|
||||||
"fixedbitset",
|
|
||||||
"indexmap",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "petgraph"
|
name = "petgraph"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -3631,24 +3626,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
|
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"prost-derive",
|
"prost-derive 0.13.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"prost-derive 0.14.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-build"
|
name = "prost-build"
|
||||||
version = "0.13.5"
|
version = "0.14.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
|
checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"log",
|
"log",
|
||||||
"multimap",
|
"multimap",
|
||||||
"once_cell",
|
"petgraph",
|
||||||
"petgraph 0.7.1",
|
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"prost",
|
"prost 0.14.3",
|
||||||
"prost-types",
|
"prost-types 0.14.3",
|
||||||
"regex",
|
"regex",
|
||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
@@ -3667,6 +3671,19 @@ dependencies = [
|
|||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost-derive"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"itertools 0.14.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-reflect"
|
name = "prost-reflect"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@@ -3674,19 +3691,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "7b5edd582b62f5cde844716e66d92565d7faf7ab1445c8cebce6e00fba83ddb2"
|
checksum = "7b5edd582b62f5cde844716e66d92565d7faf7ab1445c8cebce6e00fba83ddb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"prost",
|
"prost 0.13.5",
|
||||||
"prost-reflect-derive",
|
"prost-reflect-derive 0.14.0",
|
||||||
"prost-types",
|
"prost-types 0.13.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost-reflect"
|
||||||
|
version = "0.16.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "590aa145fee8f7a26b5a6055365e7c5e89a5c1caae9869de76ec0ee73181a2f9"
|
||||||
|
dependencies = [
|
||||||
|
"prost 0.14.3",
|
||||||
|
"prost-reflect-derive 0.16.0",
|
||||||
|
"prost-types 0.14.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-reflect-build"
|
name = "prost-reflect-build"
|
||||||
version = "0.14.0"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "50e2537231d94dd2778920c2ada37dd9eb1ac0325bb3ee3ee651bd44c1134123"
|
checksum = "8214ae2c30bbac390db0134d08300e770ef89b6d4e5abf855e8d300eded87e28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"prost-build",
|
"prost-build",
|
||||||
"prost-reflect",
|
"prost-reflect 0.16.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3700,24 +3728,44 @@ dependencies = [
|
|||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost-reflect-derive"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b6d90e29fa6c0d13c2c19ba5e4b3fb0efbf5975d27bcf4e260b7b15455bcabe"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-types"
|
name = "prost-types"
|
||||||
version = "0.13.5"
|
version = "0.13.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
|
checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"prost",
|
"prost 0.13.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost-types"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7"
|
||||||
|
dependencies = [
|
||||||
|
"prost 0.14.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-wkt"
|
name = "prost-wkt"
|
||||||
version = "0.6.1"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "497e1e938f0c09ef9cabe1d49437b4016e03e8f82fbbe5d1c62a9b61b9decae1"
|
checksum = "cd3de5e9c9e84fcb5efa204b8e283d23e615a8bc8c777bf1d6622bb01dc61445"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"inventory",
|
"inventory",
|
||||||
"prost",
|
"prost 0.14.3",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -3726,27 +3774,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-wkt-build"
|
name = "prost-wkt-build"
|
||||||
version = "0.6.1"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07b8bf115b70a7aa5af1fd5d6e9418492e9ccb6e4785e858c938e28d132a884b"
|
checksum = "fe500dc80e757a75e1e8fb7290e448d62dfba3105ece1d058579cb00b58151cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"prost",
|
"prost 0.14.3",
|
||||||
"prost-build",
|
"prost-build",
|
||||||
"prost-types",
|
"prost-types 0.14.3",
|
||||||
"quote",
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-wkt-types"
|
name = "prost-wkt-types"
|
||||||
version = "0.6.1"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8cdde6df0a98311c839392ca2f2f0bcecd545f86a62b4e3c6a49c336e970fe5"
|
checksum = "13807eaa7e15833d06e899008371926201cdcd11d74b6d490f49130cdb3f415e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"prost",
|
"prost 0.14.3",
|
||||||
"prost-build",
|
"prost-build",
|
||||||
"prost-types",
|
"prost-types 0.14.3",
|
||||||
"prost-wkt",
|
"prost-wkt",
|
||||||
"prost-wkt-build",
|
"prost-wkt-build",
|
||||||
"regex",
|
"regex",
|
||||||
@@ -3835,9 +3883,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.40"
|
version = "1.0.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@@ -3974,9 +4022,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.11.2"
|
version = "1.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
|
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -3986,9 +4034,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.10"
|
version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
|
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ async-trait = "0.1"
|
|||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
flate2 = "1.1"
|
flate2 = "1.1"
|
||||||
gethostname = "1.1"
|
gethostname = "1.1"
|
||||||
ohos-hilog-binding = {version = "*", features = ["redirect"]}
|
|
||||||
easytier = { path = "../../easytier" }
|
easytier = { path = "../../easytier" }
|
||||||
napi-derive-ohos = "1.1"
|
napi-derive-ohos = "1.1"
|
||||||
napi-ohos = { version = "1.1", default-features = false, features = [
|
napi-ohos = { version = "1.1", default-features = false, features = [
|
||||||
|
|||||||
@@ -1,15 +1,49 @@
|
|||||||
use crate::config::types::stored_config::{
|
use crate::config::types::stored_config::{
|
||||||
SnapshotImportResult, StoredConfigList, StoredConfigMeta,
|
SnapshotImportResult, StoredConfigList, StoredConfigMeta,
|
||||||
};
|
};
|
||||||
use ohos_hilog_binding::{hilog_debug, hilog_error};
|
use once_cell::sync::Lazy;
|
||||||
use rusqlite::{Connection, OptionalExtension, params};
|
use rusqlite::{Connection, OptionalExtension, params};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Mutex;
|
use std::sync::{Mutex, MutexGuard};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
static CONFIG_DB_PATH: Mutex<Option<PathBuf>> = Mutex::new(None);
|
static CONFIG_DB_PATH: Mutex<Option<PathBuf>> = Mutex::new(None);
|
||||||
|
static CONFIG_DB_CONNECTION: Lazy<Mutex<Option<CachedConfigDb>>> = Lazy::new(|| Mutex::new(None));
|
||||||
const CONFIG_DB_FILE_NAME: &str = "easytier-config-store.db";
|
const CONFIG_DB_FILE_NAME: &str = "easytier-config-store.db";
|
||||||
|
|
||||||
|
struct CachedConfigDb {
|
||||||
|
path: PathBuf,
|
||||||
|
conn: Connection,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ConfigDbGuard<'a> {
|
||||||
|
guard: MutexGuard<'a, Option<CachedConfigDb>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ConfigDbGuard<'_> {
|
||||||
|
type Target = Connection;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self
|
||||||
|
.guard
|
||||||
|
.as_ref()
|
||||||
|
.expect("config db connection guard must contain a connection")
|
||||||
|
.conn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for ConfigDbGuard<'_> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self
|
||||||
|
.guard
|
||||||
|
.as_mut()
|
||||||
|
.expect("config db connection guard must contain a connection")
|
||||||
|
.conn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct StoredConfigMetaRecord {
|
struct StoredConfigMetaRecord {
|
||||||
config_id: String,
|
config_id: String,
|
||||||
@@ -79,31 +113,176 @@ fn init_schema(conn: &Connection) -> rusqlite::Result<()> {
|
|||||||
);
|
);
|
||||||
CREATE INDEX IF NOT EXISTS idx_stored_config_fields_config_id
|
CREATE INDEX IF NOT EXISTS idx_stored_config_fields_config_id
|
||||||
ON stored_config_fields(config_id);",
|
ON stored_config_fields(config_id);",
|
||||||
)
|
)?;
|
||||||
|
|
||||||
|
ensure_column(
|
||||||
|
conn,
|
||||||
|
"stored_configs",
|
||||||
|
"favorite",
|
||||||
|
"ALTER TABLE stored_configs ADD COLUMN favorite INTEGER NOT NULL DEFAULT 0;",
|
||||||
|
)?;
|
||||||
|
ensure_column(
|
||||||
|
conn,
|
||||||
|
"stored_configs",
|
||||||
|
"temporary",
|
||||||
|
"ALTER TABLE stored_configs ADD COLUMN temporary INTEGER NOT NULL DEFAULT 0;",
|
||||||
|
)?;
|
||||||
|
ensure_column(
|
||||||
|
conn,
|
||||||
|
"stored_config_fields",
|
||||||
|
"updated_at",
|
||||||
|
"ALTER TABLE stored_config_fields ADD COLUMN updated_at TEXT NOT NULL DEFAULT '0';",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if !validate_store_schema(conn)? {
|
||||||
|
return Err(rusqlite::Error::InvalidQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.execute_batch("PRAGMA user_version = 1;")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn open_db() -> Option<Connection> {
|
fn table_columns(conn: &Connection, table_name: &str) -> rusqlite::Result<HashSet<String>> {
|
||||||
let path = db_file_path()?;
|
let mut stmt = conn.prepare(&format!("PRAGMA table_info({})", table_name))?;
|
||||||
let conn = match Connection::open(&path) {
|
let rows = stmt.query_map([], |row| row.get::<_, String>(1))?;
|
||||||
|
let mut columns = HashSet::new();
|
||||||
|
for row in rows {
|
||||||
|
columns.insert(row?);
|
||||||
|
}
|
||||||
|
Ok(columns)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_column(
|
||||||
|
conn: &Connection,
|
||||||
|
table_name: &str,
|
||||||
|
column_name: &str,
|
||||||
|
alter_sql: &str,
|
||||||
|
) -> rusqlite::Result<()> {
|
||||||
|
let columns = table_columns(conn, table_name)?;
|
||||||
|
if !columns.contains(column_name) {
|
||||||
|
conn.execute_batch(alter_sql)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_store_schema(conn: &Connection) -> rusqlite::Result<bool> {
|
||||||
|
let meta_columns = table_columns(conn, "stored_configs")?;
|
||||||
|
let field_columns = table_columns(conn, "stored_config_fields")?;
|
||||||
|
let required_meta = [
|
||||||
|
"config_id",
|
||||||
|
"display_name",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"favorite",
|
||||||
|
"temporary",
|
||||||
|
];
|
||||||
|
let required_fields = ["config_id", "field_name", "field_json", "updated_at"];
|
||||||
|
|
||||||
|
Ok(required_meta
|
||||||
|
.iter()
|
||||||
|
.all(|column| meta_columns.contains(*column))
|
||||||
|
&& required_fields
|
||||||
|
.iter()
|
||||||
|
.all(|column| field_columns.contains(*column)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_db_file_if_exists(path: &Path) -> bool {
|
||||||
|
if !path.exists() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let target = PathBuf::from(format!(
|
||||||
|
"{}.corrupt.{}",
|
||||||
|
path.to_string_lossy(),
|
||||||
|
now_ts_string()
|
||||||
|
));
|
||||||
|
match std::fs::rename(path, &target) {
|
||||||
|
Ok(_) => true,
|
||||||
|
Err(e) => {
|
||||||
|
ohrs_log_error!(
|
||||||
|
"[Rust] failed to move corrupt config db {} to {}: {}",
|
||||||
|
path.display(),
|
||||||
|
target.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recover_config_db_files(path: &Path) -> bool {
|
||||||
|
let main_ok = move_db_file_if_exists(path);
|
||||||
|
let wal_ok = move_db_file_if_exists(Path::new(&format!("{}-wal", path.to_string_lossy())));
|
||||||
|
let shm_ok = move_db_file_if_exists(Path::new(&format!("{}-shm", path.to_string_lossy())));
|
||||||
|
main_ok && wal_ok && shm_ok
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_connection(path: &Path) -> Option<Connection> {
|
||||||
|
let conn = match Connection::open(path) {
|
||||||
Ok(conn) => conn,
|
Ok(conn) => conn,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!("[Rust] failed to open config db {}: {}", path.display(), e);
|
ohrs_log_error!("[Rust] failed to open config db {}: {}", path.display(), e);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = init_schema(&conn) {
|
if let Err(e) = init_schema(&conn) {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to initialize config db {}: {}",
|
"[Rust] failed to initialize config db {}: {}",
|
||||||
path.display(),
|
path.display(),
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
return None;
|
drop(conn);
|
||||||
|
if !recover_config_db_files(path) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let recovered = match Connection::open(path) {
|
||||||
|
Ok(conn) => conn,
|
||||||
|
Err(e) => {
|
||||||
|
ohrs_log_error!(
|
||||||
|
"[Rust] failed to open recovered config db {}: {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Err(e) = init_schema(&recovered) {
|
||||||
|
ohrs_log_error!(
|
||||||
|
"[Rust] failed to initialize recovered config db {}: {}",
|
||||||
|
path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some(recovered);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(conn)
|
Some(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn open_db() -> Option<ConfigDbGuard<'static>> {
|
||||||
|
let path = db_file_path()?;
|
||||||
|
let mut guard = match CONFIG_DB_CONNECTION.lock() {
|
||||||
|
Ok(guard) => guard,
|
||||||
|
Err(e) => {
|
||||||
|
ohrs_log_error!("[Rust] failed to lock config db connection: {}", e);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let should_open = guard
|
||||||
|
.as_ref()
|
||||||
|
.map(|cached| cached.path != path || !cached.path.exists())
|
||||||
|
.unwrap_or(true);
|
||||||
|
if should_open {
|
||||||
|
let conn = open_connection(&path)?;
|
||||||
|
|
||||||
|
*guard = Some(CachedConfigDb { path, conn });
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(ConfigDbGuard { guard })
|
||||||
|
}
|
||||||
|
|
||||||
fn row_to_meta(row: &rusqlite::Row<'_>) -> rusqlite::Result<StoredConfigMetaRecord> {
|
fn row_to_meta(row: &rusqlite::Row<'_>) -> rusqlite::Result<StoredConfigMetaRecord> {
|
||||||
Ok(StoredConfigMetaRecord {
|
Ok(StoredConfigMetaRecord {
|
||||||
config_id: row.get(0)?,
|
config_id: row.get(0)?,
|
||||||
@@ -250,7 +429,7 @@ fn ensure_parent_dir(path: &Path) -> bool {
|
|||||||
Some(parent) => match std::fs::create_dir_all(parent) {
|
Some(parent) => match std::fs::create_dir_all(parent) {
|
||||||
Ok(_) => true,
|
Ok(_) => true,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to create snapshot parent {}: {}",
|
"[Rust] failed to create snapshot parent {}: {}",
|
||||||
parent.display(),
|
parent.display(),
|
||||||
e
|
e
|
||||||
@@ -276,7 +455,7 @@ fn to_meta(record: StoredConfigMetaRecord) -> StoredConfigMeta {
|
|||||||
pub fn init_config_meta_store(root_dir: String) -> bool {
|
pub fn init_config_meta_store(root_dir: String) -> bool {
|
||||||
let root = PathBuf::from(root_dir);
|
let root = PathBuf::from(root_dir);
|
||||||
if let Err(e) = std::fs::create_dir_all(&root) {
|
if let Err(e) = std::fs::create_dir_all(&root) {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to create config db dir {}: {}",
|
"[Rust] failed to create config db dir {}: {}",
|
||||||
root.display(),
|
root.display(),
|
||||||
e
|
e
|
||||||
@@ -290,7 +469,7 @@ pub fn init_config_meta_store(root_dir: String) -> bool {
|
|||||||
*guard = Some(db_path.clone());
|
*guard = Some(db_path.clone());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!("[Rust] failed to lock config db path: {}", e);
|
ohrs_log_error!("[Rust] failed to lock config db path: {}", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,7 +478,7 @@ pub fn init_config_meta_store(root_dir: String) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hilog_debug!("[Rust] initialized config db at {}", db_path.display());
|
ohrs_log_debug!("[Rust] initialized config db at {}", db_path.display());
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +493,7 @@ pub fn export_config_store_snapshot(target_path: String) -> bool {
|
|||||||
let mut dst = match Connection::open(&target) {
|
let mut dst = match Connection::open(&target) {
|
||||||
Ok(conn) => conn,
|
Ok(conn) => conn,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to open snapshot target {}: {}",
|
"[Rust] failed to open snapshot target {}: {}",
|
||||||
target.display(),
|
target.display(),
|
||||||
e
|
e
|
||||||
@@ -323,7 +502,7 @@ pub fn export_config_store_snapshot(target_path: String) -> bool {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Err(e) = init_schema(&dst) {
|
if let Err(e) = init_schema(&dst) {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to init snapshot schema {}: {}",
|
"[Rust] failed to init snapshot schema {}: {}",
|
||||||
target.display(),
|
target.display(),
|
||||||
e
|
e
|
||||||
@@ -333,7 +512,7 @@ pub fn export_config_store_snapshot(target_path: String) -> bool {
|
|||||||
match copy_snapshot_tables(&src, &mut dst) {
|
match copy_snapshot_tables(&src, &mut dst) {
|
||||||
Ok(_) => true,
|
Ok(_) => true,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to export snapshot {}: {}",
|
"[Rust] failed to export snapshot {}: {}",
|
||||||
target.display(),
|
target.display(),
|
||||||
e
|
e
|
||||||
@@ -348,7 +527,7 @@ pub fn import_config_store_snapshot_with_result(source_path: String) -> Snapshot
|
|||||||
let src = match Connection::open(&source) {
|
let src = match Connection::open(&source) {
|
||||||
Ok(conn) => conn,
|
Ok(conn) => conn,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to open snapshot source {}: {}",
|
"[Rust] failed to open snapshot source {}: {}",
|
||||||
source.display(),
|
source.display(),
|
||||||
e
|
e
|
||||||
@@ -357,7 +536,7 @@ pub fn import_config_store_snapshot_with_result(source_path: String) -> Snapshot
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !validate_snapshot_schema(&src) {
|
if !validate_snapshot_schema(&src) {
|
||||||
hilog_error!("[Rust] invalid snapshot schema {}", source.display());
|
ohrs_log_error!("[Rust] invalid snapshot schema {}", source.display());
|
||||||
return snapshot_import_err(
|
return snapshot_import_err(
|
||||||
"invalid_snapshot_schema",
|
"invalid_snapshot_schema",
|
||||||
format!("invalid snapshot schema: {}", source.display()),
|
format!("invalid snapshot schema: {}", source.display()),
|
||||||
@@ -367,7 +546,7 @@ pub fn import_config_store_snapshot_with_result(source_path: String) -> Snapshot
|
|||||||
let (meta_rows, field_rows) = match read_snapshot_tables(&src) {
|
let (meta_rows, field_rows) = match read_snapshot_tables(&src) {
|
||||||
Ok(rows) => rows,
|
Ok(rows) => rows,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to read snapshot source {}: {}",
|
"[Rust] failed to read snapshot source {}: {}",
|
||||||
source.display(),
|
source.display(),
|
||||||
e
|
e
|
||||||
@@ -385,7 +564,7 @@ pub fn import_config_store_snapshot_with_result(source_path: String) -> Snapshot
|
|||||||
match write_snapshot_tables(&mut dst, meta_rows, field_rows) {
|
match write_snapshot_tables(&mut dst, meta_rows, field_rows) {
|
||||||
Ok(_) => snapshot_import_ok(),
|
Ok(_) => snapshot_import_ok(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to import snapshot {}: {}",
|
"[Rust] failed to import snapshot {}: {}",
|
||||||
source.display(),
|
source.display(),
|
||||||
e
|
e
|
||||||
@@ -399,6 +578,41 @@ pub fn import_config_store_snapshot(source_path: String) -> bool {
|
|||||||
import_config_store_snapshot_with_result(source_path).ok
|
import_config_store_snapshot_with_result(source_path).ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_config_meta_store() -> bool {
|
||||||
|
let Some(conn) = open_db() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let tx = match conn.unchecked_transaction() {
|
||||||
|
Ok(tx) => tx,
|
||||||
|
Err(e) => {
|
||||||
|
ohrs_log_error!(
|
||||||
|
"[Rust] failed to start config store reset transaction: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = tx.execute("DELETE FROM stored_config_fields", []) {
|
||||||
|
ohrs_log_error!("[Rust] failed to reset config fields: {}", e);
|
||||||
|
let _ = tx.rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let Err(e) = tx.execute("DELETE FROM stored_configs", []) {
|
||||||
|
ohrs_log_error!("[Rust] failed to reset config meta: {}", e);
|
||||||
|
let _ = tx.rollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match tx.commit() {
|
||||||
|
Ok(_) => true,
|
||||||
|
Err(e) => {
|
||||||
|
ohrs_log_error!("[Rust] failed to commit config store reset: {}", e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list_config_meta_entries() -> StoredConfigList {
|
pub fn list_config_meta_entries() -> StoredConfigList {
|
||||||
let Some(conn) = open_db() else {
|
let Some(conn) = open_db() else {
|
||||||
return StoredConfigList { configs: vec![] };
|
return StoredConfigList { configs: vec![] };
|
||||||
@@ -411,7 +625,7 @@ pub fn list_config_meta_entries() -> StoredConfigList {
|
|||||||
) {
|
) {
|
||||||
Ok(stmt) => stmt,
|
Ok(stmt) => stmt,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!("[Rust] failed to prepare list meta query: {}", e);
|
ohrs_log_error!("[Rust] failed to prepare list meta query: {}", e);
|
||||||
return StoredConfigList { configs: vec![] };
|
return StoredConfigList { configs: vec![] };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -419,7 +633,7 @@ pub fn list_config_meta_entries() -> StoredConfigList {
|
|||||||
let rows = match stmt.query_map([], row_to_meta) {
|
let rows = match stmt.query_map([], row_to_meta) {
|
||||||
Ok(rows) => rows,
|
Ok(rows) => rows,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!("[Rust] failed to list config meta rows: {}", e);
|
ohrs_log_error!("[Rust] failed to list config meta rows: {}", e);
|
||||||
return StoredConfigList { configs: vec![] };
|
return StoredConfigList { configs: vec![] };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -438,59 +652,6 @@ pub fn get_config_meta(config_id: &str) -> Option<StoredConfigMeta> {
|
|||||||
load_meta_record(&conn, config_id).map(to_meta)
|
load_meta_record(&conn, config_id).map(to_meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upsert_config_meta(
|
|
||||||
config_id: String,
|
|
||||||
display_name: String,
|
|
||||||
favorite: bool,
|
|
||||||
temporary: bool,
|
|
||||||
) -> StoredConfigMeta {
|
|
||||||
let now = now_ts_string();
|
|
||||||
let Some(conn) = open_db() else {
|
|
||||||
return StoredConfigMeta {
|
|
||||||
config_id,
|
|
||||||
display_name,
|
|
||||||
created_at: now.clone(),
|
|
||||||
updated_at: now,
|
|
||||||
favorite,
|
|
||||||
temporary,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
let created_at = load_meta_record(&conn, &config_id)
|
|
||||||
.map(|record| record.created_at)
|
|
||||||
.unwrap_or_else(|| now.clone());
|
|
||||||
|
|
||||||
if let Err(e) = conn.execute(
|
|
||||||
"INSERT INTO stored_configs (
|
|
||||||
config_id, display_name, created_at, updated_at, favorite, temporary
|
|
||||||
) VALUES (?1, ?2, ?3, ?4, ?5, ?6)
|
|
||||||
ON CONFLICT(config_id) DO UPDATE SET
|
|
||||||
display_name = excluded.display_name,
|
|
||||||
updated_at = excluded.updated_at,
|
|
||||||
favorite = excluded.favorite,
|
|
||||||
temporary = excluded.temporary",
|
|
||||||
params![
|
|
||||||
config_id,
|
|
||||||
display_name,
|
|
||||||
created_at,
|
|
||||||
now,
|
|
||||||
if favorite { 1 } else { 0 },
|
|
||||||
if temporary { 1 } else { 0 }
|
|
||||||
],
|
|
||||||
) {
|
|
||||||
hilog_error!("[Rust] failed to upsert config meta: {}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
get_config_meta(&config_id).unwrap_or(StoredConfigMeta {
|
|
||||||
config_id,
|
|
||||||
display_name,
|
|
||||||
created_at,
|
|
||||||
updated_at: now,
|
|
||||||
favorite,
|
|
||||||
temporary,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn upsert_config_meta_in_tx(
|
pub(crate) fn upsert_config_meta_in_tx(
|
||||||
tx: &rusqlite::Transaction<'_>,
|
tx: &rusqlite::Transaction<'_>,
|
||||||
config_id: String,
|
config_id: String,
|
||||||
@@ -614,20 +775,3 @@ pub fn set_config_favorite(config_id: String, favorite: bool) -> Option<StoredCo
|
|||||||
tx.commit().ok()?;
|
tx.commit().ok()?;
|
||||||
Some(meta)
|
Some(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_config_meta(config_id: &str) -> bool {
|
|
||||||
let Some(conn) = open_db() else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
match conn.execute(
|
|
||||||
"DELETE FROM stored_configs WHERE config_id = ?1",
|
|
||||||
params![config_id],
|
|
||||||
) {
|
|
||||||
Ok(rows) => rows > 0,
|
|
||||||
Err(e) => {
|
|
||||||
hilog_error!("[Rust] failed to delete config meta {}: {}", config_id, e);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -35,14 +35,6 @@ pub struct ExportTomlResult {
|
|||||||
pub toml_text: String,
|
pub toml_text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[napi(object)]
|
|
||||||
pub struct StoredConfigSummary {
|
|
||||||
pub config_id: String,
|
|
||||||
pub display_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[napi(object)]
|
#[napi(object)]
|
||||||
|
|||||||
@@ -1,21 +1,74 @@
|
|||||||
use super::{field_store, import_export, legacy_migration, validation};
|
use super::{field_store, import_export, legacy_migration, validation};
|
||||||
use crate::config::storage::config_meta::{
|
use crate::config::storage::config_meta::{
|
||||||
delete_config_meta, get_config_meta, init_config_meta_store, list_config_meta_entries, open_db,
|
get_config_meta, init_config_meta_store, list_config_meta_entries, open_db,
|
||||||
upsert_config_meta_in_tx,
|
reset_config_meta_store, upsert_config_meta_in_tx,
|
||||||
};
|
};
|
||||||
use crate::config::types::stored_config::{ExportTomlResult, StoredConfigRecord};
|
use crate::config::types::stored_config::{ExportTomlResult, StoredConfigRecord};
|
||||||
use easytier::common::config::ConfigLoader;
|
|
||||||
use easytier::proto::api::manage::NetworkConfig;
|
use easytier::proto::api::manage::NetworkConfig;
|
||||||
use ohos_hilog_binding::{hilog_debug, hilog_error};
|
use once_cell::sync::Lazy;
|
||||||
use rusqlite::params;
|
use rusqlite::params;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
static CONFIG_ROOT_DIR: Mutex<Option<PathBuf>> = Mutex::new(None);
|
static CONFIG_ROOT_DIR: Mutex<Option<PathBuf>> = Mutex::new(None);
|
||||||
|
static RUNTIME_CONFIG_SNAPSHOTS: Lazy<Mutex<HashMap<String, RuntimeConfigSnapshot>>> =
|
||||||
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||||
pub(crate) const CONFIG_DIR_NAME: &str = "easytier-configs";
|
pub(crate) const CONFIG_DIR_NAME: &str = "easytier-configs";
|
||||||
pub(crate) const KERNEL_SOCKET_FILE_NAME: &str = "easytier-kernel.sock";
|
pub(crate) const KERNEL_SOCKET_FILE_NAME: &str = "easytier-kernel.sock";
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct RuntimeConfigSnapshot {
|
||||||
|
pub display_name: String,
|
||||||
|
pub config: NetworkConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn cache_runtime_config_snapshot(
|
||||||
|
config_id: String,
|
||||||
|
display_name: String,
|
||||||
|
config: NetworkConfig,
|
||||||
|
) {
|
||||||
|
if let Ok(mut guard) = RUNTIME_CONFIG_SNAPSHOTS.lock() {
|
||||||
|
guard.insert(
|
||||||
|
config_id,
|
||||||
|
RuntimeConfigSnapshot {
|
||||||
|
display_name,
|
||||||
|
config,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clear_runtime_config_snapshot(config_id: &str) {
|
||||||
|
if let Ok(mut guard) = RUNTIME_CONFIG_SNAPSHOTS.lock() {
|
||||||
|
guard.remove(config_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_runtime_config_snapshot(config_id: &str) -> Option<RuntimeConfigSnapshot> {
|
||||||
|
RUNTIME_CONFIG_SNAPSHOTS
|
||||||
|
.lock()
|
||||||
|
.ok()
|
||||||
|
.and_then(|guard| guard.get(config_id).cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_runtime_config_route_overrides(config_id: &str) -> (Vec<String>, Vec<String>) {
|
||||||
|
RUNTIME_CONFIG_SNAPSHOTS
|
||||||
|
.lock()
|
||||||
|
.ok()
|
||||||
|
.and_then(|guard| {
|
||||||
|
guard.get(config_id).map(|snapshot| {
|
||||||
|
(
|
||||||
|
snapshot.config.routes.clone(),
|
||||||
|
snapshot.config.proxy_cidrs.clone(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn config_root_dir() -> Option<PathBuf> {
|
pub(crate) fn config_root_dir() -> Option<PathBuf> {
|
||||||
CONFIG_ROOT_DIR
|
CONFIG_ROOT_DIR
|
||||||
.lock()
|
.lock()
|
||||||
@@ -35,7 +88,7 @@ pub fn init_config_store(root_dir: String) -> bool {
|
|||||||
let root = PathBuf::from(root_dir);
|
let root = PathBuf::from(root_dir);
|
||||||
let configs_dir = root.join(CONFIG_DIR_NAME);
|
let configs_dir = root.join(CONFIG_DIR_NAME);
|
||||||
if let Err(e) = std::fs::create_dir_all(&configs_dir) {
|
if let Err(e) = std::fs::create_dir_all(&configs_dir) {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to create config dir {}: {}",
|
"[Rust] failed to create config dir {}: {}",
|
||||||
configs_dir.display(),
|
configs_dir.display(),
|
||||||
e
|
e
|
||||||
@@ -48,7 +101,7 @@ pub fn init_config_store(root_dir: String) -> bool {
|
|||||||
*guard = Some(root.clone());
|
*guard = Some(root.clone());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!("[Rust] failed to lock config root dir: {}", e);
|
ohrs_log_error!("[Rust] failed to lock config root dir: {}", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,13 +110,23 @@ pub fn init_config_store(root_dir: String) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hilog_debug!(
|
ohrs_log_debug!(
|
||||||
"[Rust] initialized config repo at {}",
|
"[Rust] initialized config repo at {}",
|
||||||
configs_dir.display()
|
configs_dir.display()
|
||||||
);
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_config_store() -> bool {
|
||||||
|
if !reset_config_meta_store() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if let Ok(mut guard) = RUNTIME_CONFIG_SNAPSHOTS.lock() {
|
||||||
|
guard.clear();
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn migrate_legacy_file_if_needed(config_id: &str) -> Option<()> {
|
fn migrate_legacy_file_if_needed(config_id: &str) -> Option<()> {
|
||||||
if validation::validate_config_id(config_id).is_err() {
|
if validation::validate_config_id(config_id).is_err() {
|
||||||
return None;
|
return None;
|
||||||
@@ -84,7 +147,7 @@ pub fn save_config_record(
|
|||||||
let config = match validation::validate_config_json(&config_json, config_id.clone()) {
|
let config = match validation::validate_config_json(&config_json, config_id.clone()) {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!("[Rust] save_config_record failed {}", e);
|
ohrs_log_error!("[Rust] save_config_record failed {}", e);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -92,7 +155,7 @@ pub fn save_config_record(
|
|||||||
let normalized_json = match serde_json::to_string(&config) {
|
let normalized_json = match serde_json::to_string(&config) {
|
||||||
Ok(raw) => raw,
|
Ok(raw) => raw,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to serialize normalized config {}: {}",
|
"[Rust] failed to serialize normalized config {}: {}",
|
||||||
config_id,
|
config_id,
|
||||||
e
|
e
|
||||||
@@ -108,15 +171,15 @@ pub fn save_config_record(
|
|||||||
|
|
||||||
let conn = open_db()?;
|
let conn = open_db()?;
|
||||||
let tx = conn.unchecked_transaction().ok()?;
|
let tx = conn.unchecked_transaction().ok()?;
|
||||||
let existing_meta = get_config_meta(&config_id);
|
let existing_meta = tx
|
||||||
let favorite = existing_meta
|
.query_row(
|
||||||
.as_ref()
|
"SELECT favorite, temporary FROM stored_configs WHERE config_id = ?1",
|
||||||
.map(|meta| meta.favorite)
|
params![config_id.clone()],
|
||||||
.unwrap_or(false);
|
|row| Ok((row.get::<_, i64>(0)? != 0, row.get::<_, i64>(1)? != 0)),
|
||||||
let temporary = existing_meta
|
)
|
||||||
.as_ref()
|
.ok();
|
||||||
.map(|meta| meta.temporary)
|
let favorite = existing_meta.map(|meta| meta.0).unwrap_or(false);
|
||||||
.unwrap_or(false);
|
let temporary = existing_meta.map(|meta| meta.1).unwrap_or(false);
|
||||||
let meta = upsert_config_meta_in_tx(&tx, config_id.clone(), display_name, favorite, temporary)?;
|
let meta = upsert_config_meta_in_tx(&tx, config_id.clone(), display_name, favorite, temporary)?;
|
||||||
|
|
||||||
field_store::replace_config_fields(&tx, &config_id, fields)?;
|
field_store::replace_config_fields(&tx, &config_id, fields)?;
|
||||||
@@ -150,16 +213,32 @@ pub fn get_config_record(config_id: &str) -> Option<StoredConfigRecord> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config_field_value(config_id: &str, field: &str) -> Option<String> {
|
pub fn get_config_field_value(config_id: &str, field: &str) -> Option<String> {
|
||||||
|
let total_start = Instant::now();
|
||||||
validation::validate_config_id(config_id).ok()?;
|
validation::validate_config_id(config_id).ok()?;
|
||||||
migrate_legacy_file_if_needed(config_id)?;
|
migrate_legacy_file_if_needed(config_id)?;
|
||||||
|
let open_start = Instant::now();
|
||||||
let conn = open_db()?;
|
let conn = open_db()?;
|
||||||
conn.query_row(
|
let open_elapsed = open_start.elapsed();
|
||||||
"SELECT field_json FROM stored_config_fields
|
let query_start = Instant::now();
|
||||||
|
let result = conn
|
||||||
|
.query_row(
|
||||||
|
"SELECT field_json FROM stored_config_fields
|
||||||
WHERE config_id = ?1 AND field_name = ?2",
|
WHERE config_id = ?1 AND field_name = ?2",
|
||||||
params![config_id, field],
|
params![config_id, field],
|
||||||
|row| row.get::<_, String>(0),
|
|row| row.get::<_, String>(0),
|
||||||
)
|
)
|
||||||
.ok()
|
.ok();
|
||||||
|
ohrs_log_debug!(
|
||||||
|
"[Rust] get_config_field_value config={} field={} found={} open_ms={} query_ms={} total_ms={} len={}",
|
||||||
|
config_id,
|
||||||
|
field,
|
||||||
|
result.is_some(),
|
||||||
|
open_elapsed.as_millis(),
|
||||||
|
query_start.elapsed().as_millis(),
|
||||||
|
total_start.elapsed().as_millis(),
|
||||||
|
result.as_ref().map(|value| value.len()).unwrap_or(0)
|
||||||
|
);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_config_field_value(config_id: &str, field: &str, json_value: &str) -> bool {
|
pub fn set_config_field_value(config_id: &str, field: &str, json_value: &str) -> bool {
|
||||||
@@ -200,11 +279,6 @@ pub fn set_config_field_value(config_id: &str, field: &str, json_value: &str) ->
|
|||||||
save_config_record(config_id.to_string(), display_name, normalized).is_some()
|
save_config_record(config_id.to_string(), display_name, normalized).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_display_name(config_id: &str) -> Option<String> {
|
|
||||||
validation::validate_config_id(config_id).ok()?;
|
|
||||||
get_config_meta(config_id).map(|meta| meta.display_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_default_config_json() -> Option<String> {
|
pub fn get_default_config_json() -> Option<String> {
|
||||||
crate::build_default_network_config_json().ok()
|
crate::build_default_network_config_json().ok()
|
||||||
}
|
}
|
||||||
@@ -226,7 +300,14 @@ pub fn start_kernel_with_config_id(config_id: &str) -> bool {
|
|||||||
Some(raw) => raw,
|
Some(raw) => raw,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
crate::run_network_instance_from_json(&raw)
|
let display_name = get_config_meta(config_id)
|
||||||
|
.map(|meta| meta.display_name)
|
||||||
|
.unwrap_or_else(|| config_id.to_string());
|
||||||
|
let started = crate::run_network_instance_from_json(&raw);
|
||||||
|
if started && let Ok(config) = serde_json::from_str::<NetworkConfig>(&raw) {
|
||||||
|
cache_runtime_config_snapshot(config_id.to_string(), display_name, config);
|
||||||
|
}
|
||||||
|
started
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_config_meta_json() -> String {
|
pub fn list_config_meta_json() -> String {
|
||||||
@@ -251,11 +332,20 @@ pub fn delete_config_record(config_id: &str) -> bool {
|
|||||||
"DELETE FROM stored_config_fields WHERE config_id = ?1",
|
"DELETE FROM stored_config_fields WHERE config_id = ?1",
|
||||||
params![config_id],
|
params![config_id],
|
||||||
) {
|
) {
|
||||||
hilog_error!("[Rust] failed to delete config fields {}: {}", config_id, e);
|
ohrs_log_error!("[Rust] failed to delete config fields {}: {}", config_id, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_config_meta(config_id)
|
match conn.execute(
|
||||||
|
"DELETE FROM stored_configs WHERE config_id = ?1",
|
||||||
|
params![config_id],
|
||||||
|
) {
|
||||||
|
Ok(rows) => rows > 0,
|
||||||
|
Err(e) => {
|
||||||
|
ohrs_log_error!("[Rust] failed to delete config meta {}: {}", config_id, e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export_config_toml(config_id: &str) -> Option<ExportTomlResult> {
|
pub fn export_config_toml(config_id: &str) -> Option<ExportTomlResult> {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::config::storage::config_meta::{now_ts_string, open_db};
|
use crate::config::storage::config_meta::{now_ts_string, open_db};
|
||||||
use ohos_hilog_binding::hilog_error;
|
|
||||||
use rusqlite::{Connection, params};
|
use rusqlite::{Connection, params};
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ pub(super) fn replace_config_fields(
|
|||||||
"DELETE FROM stored_config_fields WHERE config_id = ?1",
|
"DELETE FROM stored_config_fields WHERE config_id = ?1",
|
||||||
params![config_id],
|
params![config_id],
|
||||||
) {
|
) {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to clear existing config fields {}: {}",
|
"[Rust] failed to clear existing config fields {}: {}",
|
||||||
config_id,
|
config_id,
|
||||||
e
|
e
|
||||||
@@ -58,7 +57,7 @@ pub(super) fn replace_config_fields(
|
|||||||
VALUES (?1, ?2, ?3, ?4)",
|
VALUES (?1, ?2, ?3, ?4)",
|
||||||
params![config_id, field_name, field_json, now_ts_string()],
|
params![config_id, field_name, field_json, now_ts_string()],
|
||||||
) {
|
) {
|
||||||
hilog_error!("[Rust] failed to persist config field {}: {}", config_id, e);
|
ohrs_log_error!("[Rust] failed to persist config field {}: {}", config_id, e);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::config::storage::config_meta::get_config_meta;
|
use crate::config::storage::config_meta::get_config_meta;
|
||||||
use ohos_hilog_binding::hilog_error;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::validation;
|
use super::validation;
|
||||||
@@ -10,7 +9,7 @@ pub(super) fn legacy_config_file_path(
|
|||||||
config_id: &str,
|
config_id: &str,
|
||||||
) -> Option<PathBuf> {
|
) -> Option<PathBuf> {
|
||||||
if !validation::is_valid_config_id(config_id) {
|
if !validation::is_valid_config_id(config_id) {
|
||||||
hilog_error!("[Rust] invalid legacy config_id {}", config_id);
|
ohrs_log_error!("[Rust] invalid legacy config_id {}", config_id);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
root_dir.as_ref().map(|root| {
|
root_dir.as_ref().map(|root| {
|
||||||
@@ -41,7 +40,7 @@ pub(super) fn migrate_legacy_file_if_needed(
|
|||||||
save_config_record(config_id.to_string(), display_name, raw)?;
|
save_config_record(config_id.to_string(), display_name, raw)?;
|
||||||
|
|
||||||
if let Err(e) = std::fs::remove_file(&legacy_path) {
|
if let Err(e) = std::fs::remove_file(&legacy_path) {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] failed to remove legacy config file {}: {}",
|
"[Rust] failed to remove legacy config file {}: {}",
|
||||||
legacy_path.display(),
|
legacy_path.display(),
|
||||||
e
|
e
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ pub(crate) fn init_config_store(root_dir: String) -> bool {
|
|||||||
config::repository::init_config_store(root_dir)
|
config::repository::init_config_store(root_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reset_config_store() -> bool {
|
||||||
|
config::repository::reset_config_store()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn list_configs() -> String {
|
pub(crate) fn list_configs() -> String {
|
||||||
config::repository::list_config_meta_json()
|
config::repository::list_config_meta_json()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
use crate::config::repository::load_config_json;
|
use crate::config::repository::{clear_runtime_config_snapshot, get_runtime_config_snapshot};
|
||||||
use crate::config::storage::config_meta::get_config_display_name;
|
|
||||||
use crate::config::types::stored_config::KeyValuePair;
|
use crate::config::types::stored_config::KeyValuePair;
|
||||||
use crate::kernel_bridge::{
|
use crate::kernel_bridge::{
|
||||||
aggregate_requested_tun_routes, start_local_socket_server as start_local_socket_server_inner,
|
aggregate_requested_tun_routes, start_local_socket_server as start_local_socket_server_inner,
|
||||||
stop_local_socket_server as stop_local_socket_server_inner,
|
stop_local_socket_server as stop_local_socket_server_inner,
|
||||||
};
|
};
|
||||||
use crate::runtime::state::runtime_state::{
|
use crate::runtime::state::runtime_state::{
|
||||||
RuntimeAggregateState, TunAggregateState, clear_tun_attached, mark_tun_attached,
|
RuntimeAggregateState, RuntimeInstanceState, TunAggregateState, clear_tun_attached,
|
||||||
|
is_tun_attached, mark_tun_attached, runtime_instance_from_config_snapshot,
|
||||||
runtime_instance_from_running_info,
|
runtime_instance_from_running_info,
|
||||||
};
|
};
|
||||||
use crate::{ASYNC_RUNTIME, EASYTIER_VERSION, INSTANCE_MANAGER, WEB_CLIENTS};
|
use crate::{ASYNC_RUNTIME, INSTANCE_MANAGER, WEB_CLIENTS};
|
||||||
use easytier::proto::api::manage::NetworkConfig;
|
|
||||||
use ohos_hilog_binding::{hilog_error, hilog_info};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub(crate) fn start_kernel(
|
pub(crate) fn start_kernel(
|
||||||
config_id: String,
|
config_id: String,
|
||||||
@@ -29,9 +26,12 @@ pub(crate) fn stop_kernel(
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
clear_tun_attached(&config_id);
|
clear_tun_attached(&config_id);
|
||||||
if stop_web_client(&config_id) {
|
if stop_web_client(&config_id) {
|
||||||
|
clear_runtime_config_snapshot(&config_id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = stop_local_socket_server_inner();
|
||||||
|
|
||||||
let Some(instance_id) = parse_instance_uuid(&config_id) else {
|
let Some(instance_id) = parse_instance_uuid(&config_id) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -40,9 +40,20 @@ pub(crate) fn stop_kernel(
|
|||||||
.delete_network_instance(vec![instance_id])
|
.delete_network_instance(vec![instance_id])
|
||||||
.map(|_| true)
|
.map(|_| true)
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
hilog_error!("[Rust] stop_kernel failed {}: {}", config_id, err);
|
ohrs_log_error!("[Rust] stop_kernel failed {}: {}", config_id, err);
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
|
if ret {
|
||||||
|
clear_runtime_config_snapshot(&config_id);
|
||||||
|
}
|
||||||
|
let has_active_instances = !INSTANCE_MANAGER.list_network_instance_ids().is_empty();
|
||||||
|
let has_web_clients = WEB_CLIENTS
|
||||||
|
.lock()
|
||||||
|
.map(|guard| !guard.is_empty())
|
||||||
|
.unwrap_or(false);
|
||||||
|
if has_active_instances || has_web_clients {
|
||||||
|
let _ = start_local_socket_server_inner();
|
||||||
|
}
|
||||||
maybe_stop_local_socket_server();
|
maybe_stop_local_socket_server();
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
@@ -59,10 +70,10 @@ pub(crate) fn stop_network_instance(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_network_infos() -> Vec<KeyValuePair> {
|
pub(crate) fn collect_network_infos() -> Vec<KeyValuePair> {
|
||||||
let infos = match INSTANCE_MANAGER.collect_network_infos_sync() {
|
let infos = match ASYNC_RUNTIME.block_on(INSTANCE_MANAGER.collect_network_infos()) {
|
||||||
Ok(infos) => infos,
|
Ok(infos) => infos,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] collect network infos failed {}", err);
|
ohrs_log_error!("[Rust] collect network infos failed {}", err);
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -86,7 +97,7 @@ pub(crate) fn set_tun_fd(
|
|||||||
parse_instance_uuid: impl Fn(&str) -> Option<uuid::Uuid>,
|
parse_instance_uuid: impl Fn(&str) -> Option<uuid::Uuid>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let Some(instance_id) = parse_instance_uuid(&config_id) else {
|
let Some(instance_id) = parse_instance_uuid(&config_id) else {
|
||||||
hilog_error!("[Rust] set_tun_fd invalid instance id: {}", config_id);
|
ohrs_log_error!("[Rust] set_tun_fd invalid instance id: {}", config_id);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -94,7 +105,7 @@ pub(crate) fn set_tun_fd(
|
|||||||
.set_tun_fd(&instance_id, fd)
|
.set_tun_fd(&instance_id, fd)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
mark_tun_attached(&config_id);
|
mark_tun_attached(&config_id);
|
||||||
hilog_info!(
|
ohrs_log_info!(
|
||||||
"[Rust] set_tun_fd success instance={} fd={} marked_attached=true",
|
"[Rust] set_tun_fd success instance={} fd={} marked_attached=true",
|
||||||
config_id,
|
config_id,
|
||||||
fd
|
fd
|
||||||
@@ -102,7 +113,7 @@ pub(crate) fn set_tun_fd(
|
|||||||
true
|
true
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
hilog_error!("[Rust] set_tun_fd failed {}: {}", config_id, err);
|
ohrs_log_error!("[Rust] set_tun_fd failed {}: {}", config_id, err);
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -112,10 +123,10 @@ pub(crate) fn get_runtime_snapshot() -> RuntimeAggregateState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_runtime_snapshot_inner() -> RuntimeAggregateState {
|
pub(crate) fn get_runtime_snapshot_inner() -> RuntimeAggregateState {
|
||||||
let infos = match INSTANCE_MANAGER.collect_network_infos_sync() {
|
let infos = match ASYNC_RUNTIME.block_on(INSTANCE_MANAGER.collect_network_infos()) {
|
||||||
Ok(infos) => infos,
|
Ok(infos) => infos,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] collect network infos failed {}", err);
|
ohrs_log_error!("[Rust] collect network infos failed {}", err);
|
||||||
return RuntimeAggregateState {
|
return RuntimeAggregateState {
|
||||||
instances: vec![],
|
instances: vec![],
|
||||||
tun: TunAggregateState {
|
tun: TunAggregateState {
|
||||||
@@ -129,30 +140,67 @@ pub(crate) fn get_runtime_snapshot_inner() -> RuntimeAggregateState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let mut live_infos = infos
|
||||||
|
.into_iter()
|
||||||
|
.map(|(instance_id, info)| (instance_id.to_string(), info))
|
||||||
|
.collect::<std::collections::HashMap<_, _>>();
|
||||||
|
let mut active_config_ids = live_infos.keys().cloned().collect::<Vec<_>>();
|
||||||
|
if let Ok(guard) = WEB_CLIENTS.lock() {
|
||||||
|
for config_id in guard.keys() {
|
||||||
|
if !active_config_ids.iter().any(|value| value == config_id) {
|
||||||
|
active_config_ids.push(config_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut instances = Vec::with_capacity(infos.len());
|
let mut instances = Vec::with_capacity(active_config_ids.len());
|
||||||
for (instance_uuid, info) in infos {
|
for config_id in active_config_ids {
|
||||||
let config_id = instance_uuid.to_string();
|
if let Some(info) = live_infos.remove(&config_id) {
|
||||||
let display_name = get_config_display_name(&config_id).unwrap_or_else(|| config_id.clone());
|
let snapshot = get_runtime_config_snapshot(&config_id);
|
||||||
let config_json = load_config_json(&config_id);
|
let display_name = snapshot
|
||||||
let stored_config = config_json
|
.as_ref()
|
||||||
.as_deref()
|
.map(|snapshot| snapshot.display_name.clone())
|
||||||
.and_then(|raw| serde_json::from_str::<NetworkConfig>(raw).ok());
|
.unwrap_or_else(|| config_id.clone());
|
||||||
let magic_dns_enabled = stored_config
|
let magic_dns_enabled = snapshot
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|cfg| cfg.enable_magic_dns)
|
.and_then(|snapshot| snapshot.config.enable_magic_dns)
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
let need_exit_node = stored_config
|
let need_exit_node = snapshot
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|cfg| !cfg.exit_nodes.is_empty())
|
.map(|snapshot| !snapshot.config.exit_nodes.is_empty())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
instances.push(runtime_instance_from_running_info(
|
instances.push(runtime_instance_from_running_info(
|
||||||
config_id,
|
config_id,
|
||||||
display_name,
|
display_name,
|
||||||
magic_dns_enabled,
|
magic_dns_enabled,
|
||||||
need_exit_node,
|
need_exit_node,
|
||||||
info,
|
info,
|
||||||
));
|
));
|
||||||
|
} else if let Some(snapshot) = get_runtime_config_snapshot(&config_id) {
|
||||||
|
instances.push(runtime_instance_from_config_snapshot(
|
||||||
|
config_id,
|
||||||
|
snapshot.display_name,
|
||||||
|
snapshot.config,
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
let tun_attached = is_tun_attached(&config_id);
|
||||||
|
instances.push(RuntimeInstanceState {
|
||||||
|
config_id: config_id.clone(),
|
||||||
|
instance_id: config_id.clone(),
|
||||||
|
display_name: config_id.clone(),
|
||||||
|
running: true,
|
||||||
|
tun_required: tun_attached,
|
||||||
|
tun_attached,
|
||||||
|
magic_dns_enabled: false,
|
||||||
|
need_exit_node: false,
|
||||||
|
error_message: None,
|
||||||
|
my_node_info: None,
|
||||||
|
events: Vec::new(),
|
||||||
|
routes: Vec::new(),
|
||||||
|
peers: Vec::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instances.sort_by(|a, b| {
|
instances.sort_by(|a, b| {
|
||||||
|
|||||||
@@ -3,4 +3,6 @@ mod routing;
|
|||||||
mod socket_server;
|
mod socket_server;
|
||||||
|
|
||||||
pub(crate) use routing::aggregate_requested_tun_routes;
|
pub(crate) use routing::aggregate_requested_tun_routes;
|
||||||
pub use socket_server::{start_local_socket_server, stop_local_socket_server};
|
pub use socket_server::{
|
||||||
|
set_snapshot_broadcast_enabled, start_local_socket_server, stop_local_socket_server,
|
||||||
|
};
|
||||||
|
|||||||
@@ -48,3 +48,37 @@ pub(crate) fn broadcast_local_socket_message(
|
|||||||
*clients = active_clients;
|
*clients = active_clients;
|
||||||
delivered
|
delivered
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn send_local_socket_json_payload_message(
|
||||||
|
stream: &mut UnixStream,
|
||||||
|
message_type: &str,
|
||||||
|
payload_json: &str,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
let message_type_json = serde_json::to_string(message_type)
|
||||||
|
.map_err(|err| Error::new(ErrorKind::InvalidData, err.to_string()))?;
|
||||||
|
let mut raw = Vec::with_capacity(message_type_json.len() + payload_json.len() + 38);
|
||||||
|
raw.extend_from_slice(b"{\"messageType\":");
|
||||||
|
raw.extend_from_slice(message_type_json.as_bytes());
|
||||||
|
raw.extend_from_slice(b",\"payloadJson\":");
|
||||||
|
raw.extend_from_slice(payload_json.as_bytes());
|
||||||
|
raw.extend_from_slice(b"}\n");
|
||||||
|
stream.write_all(&raw)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn broadcast_local_socket_json_payload_message(
|
||||||
|
clients: &mut Vec<UnixStream>,
|
||||||
|
message_type: &str,
|
||||||
|
payload_json: &str,
|
||||||
|
) -> bool {
|
||||||
|
let mut active_clients = Vec::with_capacity(clients.len());
|
||||||
|
let mut delivered = false;
|
||||||
|
for mut client in clients.drain(..) {
|
||||||
|
if send_local_socket_json_payload_message(&mut client, message_type, payload_json).is_ok() {
|
||||||
|
delivered = true;
|
||||||
|
active_clients.push(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*clients = active_clients;
|
||||||
|
delivered
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +1,12 @@
|
|||||||
use crate::config::repository::load_config_json;
|
use crate::config::repository::get_runtime_config_route_overrides;
|
||||||
use crate::runtime::state::runtime_state::RuntimeInstanceState;
|
use crate::runtime::state::runtime_state::RuntimeInstanceState;
|
||||||
use easytier::proto::api::manage::NetworkConfig;
|
|
||||||
use ipnet::IpNet;
|
use ipnet::IpNet;
|
||||||
use ohos_hilog_binding::hilog_debug;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
pub(crate) fn load_manual_routes(config_id: &str) -> Vec<String> {
|
|
||||||
load_config_json(config_id)
|
|
||||||
.and_then(|raw| serde_json::from_str::<NetworkConfig>(&raw).ok())
|
|
||||||
.map(|config| config.routes)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn normalize_route_cidr(route: &str) -> Option<String> {
|
fn normalize_route_cidr(route: &str) -> Option<String> {
|
||||||
route
|
let normalized = route.split("->").next().unwrap_or(route).trim();
|
||||||
|
normalized
|
||||||
.parse::<IpNet>()
|
.parse::<IpNet>()
|
||||||
.ok()
|
.ok()
|
||||||
.map(|network| match network {
|
.map(|network| match network {
|
||||||
@@ -22,7 +14,7 @@ fn normalize_route_cidr(route: &str) -> Option<String> {
|
|||||||
IpNet::V6(net) => net.trunc().to_string(),
|
IpNet::V6(net) => net.trunc().to_string(),
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
route.parse::<IpAddr>().ok().map(|addr| match addr {
|
normalized.parse::<IpAddr>().ok().map(|addr| match addr {
|
||||||
IpAddr::V4(ip) => format!("{}/32", ip),
|
IpAddr::V4(ip) => format!("{}/32", ip),
|
||||||
IpAddr::V6(ip) => format!("{}/128", ip),
|
IpAddr::V6(ip) => format!("{}/128", ip),
|
||||||
})
|
})
|
||||||
@@ -67,8 +59,9 @@ pub(crate) fn aggregate_tun_routes(instance: &RuntimeInstanceState) -> Vec<Strin
|
|||||||
.my_node_info
|
.my_node_info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|info| info.virtual_ipv4_cidr.clone());
|
.and_then(|info| info.virtual_ipv4_cidr.clone());
|
||||||
let manual_routes = load_manual_routes(&instance.config_id);
|
let (manual_routes, config_proxy_cidrs) =
|
||||||
let proxy_cidrs = instance
|
get_runtime_config_route_overrides(&instance.config_id);
|
||||||
|
let runtime_proxy_cidrs = instance
|
||||||
.routes
|
.routes
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|route| route.proxy_cidrs.iter().cloned())
|
.flat_map(|route| route.proxy_cidrs.iter().cloned())
|
||||||
@@ -80,15 +73,9 @@ pub(crate) fn aggregate_tun_routes(instance: &RuntimeInstanceState) -> Vec<Strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
raw_routes.extend(manual_routes.iter().cloned());
|
raw_routes.extend(manual_routes.iter().cloned());
|
||||||
raw_routes.extend(proxy_cidrs.iter().cloned());
|
raw_routes.extend(config_proxy_cidrs.iter().cloned());
|
||||||
let aggregated_routes = simplify_routes(raw_routes);
|
raw_routes.extend(runtime_proxy_cidrs.iter().cloned());
|
||||||
hilog_debug!(
|
simplify_routes(raw_routes)
|
||||||
"[Rust] aggregate_tun_routes instance={} proxy_cidrs={:?} aggregated_routes={:?}",
|
|
||||||
instance.instance_id,
|
|
||||||
proxy_cidrs,
|
|
||||||
aggregated_routes
|
|
||||||
);
|
|
||||||
aggregated_routes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn aggregate_requested_tun_routes(instances: &[RuntimeInstanceState]) -> Vec<String> {
|
pub(crate) fn aggregate_requested_tun_routes(instances: &[RuntimeInstanceState]) -> Vec<String> {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use super::protocol::{TunRequestPayload, broadcast_local_socket_message};
|
use super::protocol::{
|
||||||
|
TunRequestPayload, broadcast_local_socket_json_payload_message, broadcast_local_socket_message,
|
||||||
|
};
|
||||||
|
use crate::INSTANCE_MANAGER;
|
||||||
use crate::config::repository::kernel_socket_path;
|
use crate::config::repository::kernel_socket_path;
|
||||||
use crate::get_runtime_snapshot_inner;
|
use crate::get_runtime_snapshot_inner;
|
||||||
use crate::kernel_bridge::routing::aggregate_tun_routes;
|
use crate::kernel_bridge::routing::aggregate_tun_routes;
|
||||||
use ohos_hilog_binding::{hilog_error, hilog_info};
|
use easytier::common::global_ctx::{EventBusSubscriber, GlobalCtxEvent};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
@@ -11,7 +14,7 @@ use std::path::PathBuf;
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::thread::{self, JoinHandle};
|
use std::thread::{self, JoinHandle};
|
||||||
use std::time::Duration;
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
struct LocalSocketState {
|
struct LocalSocketState {
|
||||||
stop_flag: std::sync::Arc<AtomicBool>,
|
stop_flag: std::sync::Arc<AtomicBool>,
|
||||||
@@ -20,12 +23,77 @@ struct LocalSocketState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static LOCAL_SOCKET_STATE: Lazy<Mutex<Option<LocalSocketState>>> = Lazy::new(|| Mutex::new(None));
|
static LOCAL_SOCKET_STATE: Lazy<Mutex<Option<LocalSocketState>>> = Lazy::new(|| Mutex::new(None));
|
||||||
|
static SNAPSHOT_BROADCAST_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||||
|
const SOCKET_TICK_INTERVAL: Duration = Duration::from_millis(250);
|
||||||
|
const TUN_FAST_CHECK_WINDOW: Duration = Duration::from_secs(8);
|
||||||
|
const EVENT_RECEIVER_SYNC_INTERVAL: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
|
pub fn set_snapshot_broadcast_enabled(enabled: bool) {
|
||||||
|
SNAPSHOT_BROADCAST_ENABLED.store(enabled, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sync_tun_event_receivers(receivers: &mut HashMap<String, EventBusSubscriber>) {
|
||||||
|
let mut active_instance_ids = HashSet::new();
|
||||||
|
for instance in INSTANCE_MANAGER.iter() {
|
||||||
|
let instance_id = instance.key().to_string();
|
||||||
|
active_instance_ids.insert(instance_id.clone());
|
||||||
|
if !receivers.contains_key(&instance_id)
|
||||||
|
&& let Some(receiver) = instance.value().subscribe_event()
|
||||||
|
{
|
||||||
|
receivers.insert(instance_id, receiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
receivers.retain(|instance_id, _| active_instance_ids.contains(instance_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_needs_tun_refresh(event: &GlobalCtxEvent) -> bool {
|
||||||
|
matches!(
|
||||||
|
event,
|
||||||
|
GlobalCtxEvent::DhcpIpv4Changed(_, _)
|
||||||
|
| GlobalCtxEvent::DhcpIpv4Conflicted(_)
|
||||||
|
| GlobalCtxEvent::PublicIpv6Changed(_, _)
|
||||||
|
| GlobalCtxEvent::PublicIpv6RoutesUpdated(_, _)
|
||||||
|
| GlobalCtxEvent::ProxyCidrsUpdated(_, _)
|
||||||
|
| GlobalCtxEvent::ConfigPatched(_)
|
||||||
|
| GlobalCtxEvent::PeerAdded(_)
|
||||||
|
| GlobalCtxEvent::PeerRemoved(_)
|
||||||
|
| GlobalCtxEvent::PeerConnAdded(_)
|
||||||
|
| GlobalCtxEvent::PeerConnRemoved(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain_tun_refresh_events(receivers: &mut HashMap<String, EventBusSubscriber>) -> bool {
|
||||||
|
let mut refresh_needed = false;
|
||||||
|
let mut closed_receivers = Vec::new();
|
||||||
|
for (instance_id, receiver) in receivers.iter_mut() {
|
||||||
|
loop {
|
||||||
|
match receiver.try_recv() {
|
||||||
|
Ok(event) => {
|
||||||
|
refresh_needed = event_needs_tun_refresh(&event) || refresh_needed;
|
||||||
|
}
|
||||||
|
Err(tokio::sync::broadcast::error::TryRecvError::Empty) => break,
|
||||||
|
Err(tokio::sync::broadcast::error::TryRecvError::Lagged(_)) => {
|
||||||
|
refresh_needed = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(tokio::sync::broadcast::error::TryRecvError::Closed) => {
|
||||||
|
closed_receivers.push(instance_id.clone());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for instance_id in closed_receivers {
|
||||||
|
receivers.remove(&instance_id);
|
||||||
|
}
|
||||||
|
refresh_needed
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start_local_socket_server() -> bool {
|
pub fn start_local_socket_server() -> bool {
|
||||||
let socket_path = match kernel_socket_path() {
|
let socket_path = match kernel_socket_path() {
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
None => {
|
None => {
|
||||||
hilog_error!("[Rust] kernel socket path unavailable");
|
ohrs_log_error!("[Rust] kernel socket path unavailable");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -34,7 +102,7 @@ pub fn start_local_socket_server() -> bool {
|
|||||||
Ok(guard) if guard.is_some() => return true,
|
Ok(guard) if guard.is_some() => return true,
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] lock localsocket state failed: {}", err);
|
ohrs_log_error!("[Rust] lock localsocket state failed: {}", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,7 +114,7 @@ pub fn start_local_socket_server() -> bool {
|
|||||||
let listener = match UnixListener::bind(&socket_path) {
|
let listener = match UnixListener::bind(&socket_path) {
|
||||||
Ok(listener) => listener,
|
Ok(listener) => listener,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] bind localsocket failed {}: {}",
|
"[Rust] bind localsocket failed {}: {}",
|
||||||
socket_path.display(),
|
socket_path.display(),
|
||||||
err
|
err
|
||||||
@@ -55,7 +123,7 @@ pub fn start_local_socket_server() -> bool {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Err(err) = listener.set_nonblocking(true) {
|
if let Err(err) = listener.set_nonblocking(true) {
|
||||||
hilog_error!("[Rust] set localsocket nonblocking failed: {}", err);
|
ohrs_log_error!("[Rust] set localsocket nonblocking failed: {}", err);
|
||||||
let _ = std::fs::remove_file(&socket_path);
|
let _ = std::fs::remove_file(&socket_path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -66,6 +134,10 @@ pub fn start_local_socket_server() -> bool {
|
|||||||
let mut last_snapshot_json = String::new();
|
let mut last_snapshot_json = String::new();
|
||||||
let mut delivered_tun_requests = HashSet::new();
|
let mut delivered_tun_requests = HashSet::new();
|
||||||
let mut last_tun_route_signatures = HashMap::<String, String>::new();
|
let mut last_tun_route_signatures = HashMap::<String, String>::new();
|
||||||
|
let mut tun_fast_until = Instant::now() + TUN_FAST_CHECK_WINDOW;
|
||||||
|
let mut tun_bootstrap_done = false;
|
||||||
|
let mut last_event_receiver_sync_at: Option<Instant> = None;
|
||||||
|
let mut tun_event_receivers = HashMap::<String, EventBusSubscriber>::new();
|
||||||
let mut clients = Vec::<UnixStream>::new();
|
let mut clients = Vec::<UnixStream>::new();
|
||||||
|
|
||||||
while !worker_stop_flag.load(Ordering::Relaxed) {
|
while !worker_stop_flag.load(Ordering::Relaxed) {
|
||||||
@@ -75,36 +147,86 @@ pub fn start_local_socket_server() -> bool {
|
|||||||
Ok((stream, _addr)) => {
|
Ok((stream, _addr)) => {
|
||||||
accepted_client = true;
|
accepted_client = true;
|
||||||
clients.push(stream);
|
clients.push(stream);
|
||||||
|
tun_fast_until = Instant::now() + TUN_FAST_CHECK_WINDOW;
|
||||||
|
tun_bootstrap_done = false;
|
||||||
}
|
}
|
||||||
Err(err) if err.kind() == ErrorKind::WouldBlock => break,
|
Err(err) if err.kind() == ErrorKind::WouldBlock => break,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] accept localsocket failed: {}", err);
|
ohrs_log_error!("[Rust] accept localsocket failed: {}", err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let snapshot = get_runtime_snapshot_inner();
|
let snapshot_enabled = SNAPSHOT_BROADCAST_ENABLED.load(Ordering::Relaxed);
|
||||||
let snapshot_json = match serde_json::to_string(&snapshot) {
|
if clients.is_empty() {
|
||||||
Ok(json) => json,
|
if !last_snapshot_json.is_empty() {
|
||||||
Err(err) => {
|
last_snapshot_json.clear();
|
||||||
hilog_error!("[Rust] serialize runtime snapshot failed: {}", err);
|
|
||||||
thread::sleep(Duration::from_millis(250));
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
};
|
delivered_tun_requests.clear();
|
||||||
|
last_tun_route_signatures.clear();
|
||||||
if accepted_client || snapshot_json != last_snapshot_json {
|
tun_event_receivers.clear();
|
||||||
let _ = broadcast_local_socket_message(
|
last_event_receiver_sync_at = None;
|
||||||
&mut clients,
|
tun_bootstrap_done = false;
|
||||||
"runtime_snapshot",
|
thread::sleep(SOCKET_TICK_INTERVAL);
|
||||||
&snapshot_json,
|
continue;
|
||||||
);
|
|
||||||
last_snapshot_json = snapshot_json;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let now = Instant::now();
|
||||||
|
let should_sync_event_receivers = accepted_client
|
||||||
|
|| last_event_receiver_sync_at
|
||||||
|
.map(|last| now.duration_since(last) >= EVENT_RECEIVER_SYNC_INTERVAL)
|
||||||
|
.unwrap_or(true);
|
||||||
|
if should_sync_event_receivers {
|
||||||
|
sync_tun_event_receivers(&mut tun_event_receivers);
|
||||||
|
last_event_receiver_sync_at = Some(now);
|
||||||
|
}
|
||||||
|
if drain_tun_refresh_events(&mut tun_event_receivers) {
|
||||||
|
tun_bootstrap_done = false;
|
||||||
|
tun_fast_until = now + TUN_FAST_CHECK_WINDOW;
|
||||||
|
}
|
||||||
|
let should_collect_snapshot = snapshot_enabled
|
||||||
|
|| accepted_client
|
||||||
|
|| (!tun_bootstrap_done && now < tun_fast_until);
|
||||||
|
if !should_collect_snapshot {
|
||||||
|
if !last_snapshot_json.is_empty() {
|
||||||
|
last_snapshot_json.clear();
|
||||||
|
}
|
||||||
|
thread::sleep(SOCKET_TICK_INTERVAL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let snapshot = get_runtime_snapshot_inner();
|
||||||
|
if snapshot_enabled {
|
||||||
|
let snapshot_json = match serde_json::to_string(&snapshot) {
|
||||||
|
Ok(json) => json,
|
||||||
|
Err(err) => {
|
||||||
|
ohrs_log_error!("[Rust] serialize runtime snapshot failed: {}", err);
|
||||||
|
thread::sleep(SOCKET_TICK_INTERVAL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if accepted_client || snapshot_json != last_snapshot_json {
|
||||||
|
let _ = broadcast_local_socket_json_payload_message(
|
||||||
|
&mut clients,
|
||||||
|
"runtime_snapshot",
|
||||||
|
&snapshot_json,
|
||||||
|
);
|
||||||
|
last_snapshot_json = snapshot_json;
|
||||||
|
}
|
||||||
|
} else if !last_snapshot_json.is_empty() {
|
||||||
|
last_snapshot_json.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut saw_running_instance = false;
|
||||||
|
let mut saw_tun_candidate = false;
|
||||||
for instance in snapshot.instances.iter() {
|
for instance in snapshot.instances.iter() {
|
||||||
|
if instance.running {
|
||||||
|
saw_running_instance = true;
|
||||||
|
}
|
||||||
if instance.running && instance.tun_required {
|
if instance.running && instance.tun_required {
|
||||||
|
saw_tun_candidate = true;
|
||||||
let virtual_ipv4 = instance
|
let virtual_ipv4 = instance
|
||||||
.my_node_info
|
.my_node_info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -120,9 +242,16 @@ pub fn start_local_socket_server() -> bool {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let aggregated_routes = aggregate_tun_routes(instance);
|
let aggregated_routes = aggregate_tun_routes(instance);
|
||||||
let route_signature = serde_json::to_string(&aggregated_routes)
|
let route_signature = serde_json::to_string(&(
|
||||||
.unwrap_or_else(|_| "[]".to_string());
|
&virtual_ipv4,
|
||||||
let should_send = !delivered_tun_requests.contains(&instance.instance_id)
|
&virtual_ipv4_cidr,
|
||||||
|
&aggregated_routes,
|
||||||
|
instance.magic_dns_enabled,
|
||||||
|
instance.need_exit_node,
|
||||||
|
))
|
||||||
|
.unwrap_or_else(|_| "[]".to_string());
|
||||||
|
let should_send = accepted_client
|
||||||
|
|| !delivered_tun_requests.contains(&instance.instance_id)
|
||||||
|| last_tun_route_signatures
|
|| last_tun_route_signatures
|
||||||
.get(&instance.instance_id)
|
.get(&instance.instance_id)
|
||||||
.map(|value| value != &route_signature)
|
.map(|value| value != &route_signature)
|
||||||
@@ -143,7 +272,7 @@ pub fn start_local_socket_server() -> bool {
|
|||||||
let payload_json = match serde_json::to_string(&payload) {
|
let payload_json = match serde_json::to_string(&payload) {
|
||||||
Ok(json) => json,
|
Ok(json) => json,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] serialize tun request failed: {}", err);
|
ohrs_log_error!("[Rust] serialize tun request failed: {}", err);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -157,8 +286,15 @@ pub fn start_local_socket_server() -> bool {
|
|||||||
last_tun_route_signatures.remove(&instance.instance_id);
|
last_tun_route_signatures.remove(&instance.instance_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !snapshot_enabled
|
||||||
|
&& (!delivered_tun_requests.is_empty()
|
||||||
|
|| (saw_running_instance && !saw_tun_candidate)
|
||||||
|
|| now >= tun_fast_until)
|
||||||
|
{
|
||||||
|
tun_bootstrap_done = true;
|
||||||
|
}
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(SOCKET_TICK_INTERVAL);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,7 +308,7 @@ pub fn start_local_socket_server() -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] lock localsocket state failed: {}", err);
|
ohrs_log_error!("[Rust] lock localsocket state failed: {}", err);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,7 +318,7 @@ pub fn stop_local_socket_server() -> bool {
|
|||||||
let state = match LOCAL_SOCKET_STATE.lock() {
|
let state = match LOCAL_SOCKET_STATE.lock() {
|
||||||
Ok(mut guard) => guard.take(),
|
Ok(mut guard) => guard.take(),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] lock localsocket state failed: {}", err);
|
ohrs_log_error!("[Rust] lock localsocket state failed: {}", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,46 @@
|
|||||||
|
macro_rules! ohrs_log_error {
|
||||||
|
($($arg:tt)*) => {{
|
||||||
|
if $crate::platform::logging::log_manager::app_log_enabled(5) {
|
||||||
|
$crate::platform::logging::log_manager::record_app_log(
|
||||||
|
5,
|
||||||
|
"RustOhrs",
|
||||||
|
&std::format!($($arg)*),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! ohrs_log_info {
|
||||||
|
($($arg:tt)*) => {{
|
||||||
|
if $crate::platform::logging::log_manager::app_log_enabled(4) {
|
||||||
|
$crate::platform::logging::log_manager::record_app_log(
|
||||||
|
4,
|
||||||
|
"RustOhrs",
|
||||||
|
&std::format!($($arg)*),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! ohrs_log_debug {
|
||||||
|
($($arg:tt)*) => {{
|
||||||
|
if $crate::platform::logging::log_manager::app_log_enabled(3) {
|
||||||
|
$crate::platform::logging::log_manager::record_app_log(
|
||||||
|
3,
|
||||||
|
"RustOhrs",
|
||||||
|
&std::format!($($arg)*),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod exports;
|
mod exports;
|
||||||
mod kernel_bridge;
|
mod kernel_bridge;
|
||||||
mod platform;
|
mod platform;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
|
|
||||||
use config::repository::{
|
use config::repository::{cache_runtime_config_snapshot, start_kernel_with_config_id};
|
||||||
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 config::services::schema_service::{
|
use config::services::schema_service::{
|
||||||
ConfigFieldMapping, NetworkConfigSchema,
|
ConfigFieldMapping, NetworkConfigSchema,
|
||||||
get_network_config_field_mappings as build_network_config_field_mappings,
|
get_network_config_field_mappings as build_network_config_field_mappings,
|
||||||
@@ -31,15 +63,11 @@ use easytier::proto::api::manage::NetworkConfig;
|
|||||||
use easytier::proto::api::manage::NetworkingMethod;
|
use easytier::proto::api::manage::NetworkingMethod;
|
||||||
use easytier::web_client::{WebClient, WebClientHooks, run_web_client};
|
use easytier::web_client::{WebClient, WebClientHooks, run_web_client};
|
||||||
use kernel_bridge::{
|
use kernel_bridge::{
|
||||||
aggregate_requested_tun_routes, start_local_socket_server as start_local_socket_server_inner,
|
set_snapshot_broadcast_enabled, start_local_socket_server as start_local_socket_server_inner,
|
||||||
stop_local_socket_server as stop_local_socket_server_inner,
|
stop_local_socket_server as stop_local_socket_server_inner,
|
||||||
};
|
};
|
||||||
use napi_derive_ohos::napi;
|
use napi_derive_ohos::napi;
|
||||||
use ohos_hilog_binding::{hilog_error, hilog_info};
|
use runtime::state::runtime_state::RuntimeAggregateState;
|
||||||
use runtime::state::runtime_state::{
|
|
||||||
RuntimeAggregateState, TunAggregateState, clear_tun_attached, mark_tun_attached,
|
|
||||||
runtime_instance_from_running_info,
|
|
||||||
};
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::format;
|
use std::format;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
@@ -101,7 +129,7 @@ fn stop_web_client(config_id: &str) -> bool {
|
|||||||
let managed = match WEB_CLIENTS.lock() {
|
let managed = match WEB_CLIENTS.lock() {
|
||||||
Ok(mut guard) => guard.remove(config_id),
|
Ok(mut guard) => guard.remove(config_id),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] stop_web_client lock failed {}", err);
|
ohrs_log_error!("[Rust] stop_web_client lock failed {}", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -127,7 +155,7 @@ fn stop_web_client(config_id: &str) -> bool {
|
|||||||
.delete_network_instance(tracked_ids)
|
.delete_network_instance(tracked_ids)
|
||||||
.map(|_| true)
|
.map(|_| true)
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
hilog_error!(
|
ohrs_log_error!(
|
||||||
"[Rust] stop config server instances failed {}: {}",
|
"[Rust] stop config server instances failed {}: {}",
|
||||||
config_id,
|
config_id,
|
||||||
err
|
err
|
||||||
@@ -160,12 +188,12 @@ fn run_config_server_instance(config_id: &str, config: &NetworkConfig) -> bool {
|
|||||||
.next()
|
.next()
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
hilog_error!("[Rust] there is a running instance!");
|
ohrs_log_error!("[Rust] there is a running instance!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(config_server_url) = config.public_server_url.clone() else {
|
let Some(config_server_url) = config.public_server_url.clone() else {
|
||||||
hilog_error!("[Rust] public_server_url missing for config server mode");
|
ohrs_log_error!("[Rust] public_server_url missing for config server mode");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let hooks = Arc::new(TrackedWebClientHooks::default());
|
let hooks = Arc::new(TrackedWebClientHooks::default());
|
||||||
@@ -192,7 +220,7 @@ fn run_config_server_instance(config_id: &str, config: &NetworkConfig) -> bool {
|
|||||||
let client = match client {
|
let client = match client {
|
||||||
Ok(client) => client,
|
Ok(client) => client,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] start config server failed {}", err);
|
ohrs_log_error!("[Rust] start config server failed {}", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -209,7 +237,7 @@ fn run_config_server_instance(config_id: &str, config: &NetworkConfig) -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] store config server client failed {}", err);
|
ohrs_log_error!("[Rust] store config server client failed {}", err);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,29 +268,33 @@ pub(crate) fn run_network_instance_from_json(cfg_json: &str) -> bool {
|
|||||||
let config = match serde_json::from_str::<NetworkConfig>(cfg_json) {
|
let config = match serde_json::from_str::<NetworkConfig>(cfg_json) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!("[Rust] parse config failed {}", e);
|
ohrs_log_error!("[Rust] parse config failed {}", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_config_server_config(&config) {
|
if is_config_server_config(&config) {
|
||||||
let Some(config_id) = config.instance_id.as_deref() else {
|
let Some(config_id) = config.instance_id.as_deref() else {
|
||||||
hilog_error!("[Rust] config server config missing instance id");
|
ohrs_log_error!("[Rust] config server config missing instance id");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
return run_config_server_instance(config_id, &config);
|
let started = run_config_server_instance(config_id, &config);
|
||||||
|
if started {
|
||||||
|
cache_runtime_config_snapshot(config_id.to_string(), config_id.to_string(), config);
|
||||||
|
}
|
||||||
|
return started;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cfg = match config.gen_config() {
|
let cfg = match config.gen_config() {
|
||||||
Ok(toml) => toml,
|
Ok(toml) => toml,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
hilog_error!("[Rust] parse config failed {}", e);
|
ohrs_log_error!("[Rust] parse config failed {}", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !INSTANCE_MANAGER.list_network_instance_ids().is_empty() {
|
if !INSTANCE_MANAGER.list_network_instance_ids().is_empty() {
|
||||||
hilog_error!("[Rust] there is a running instance!");
|
ohrs_log_error!("[Rust] there is a running instance!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,14 +307,17 @@ pub(crate) fn run_network_instance_from_json(cfg_json: &str) -> bool {
|
|||||||
.list_network_instance_ids()
|
.list_network_instance_ids()
|
||||||
.contains(&inst_id)
|
.contains(&inst_id)
|
||||||
{
|
{
|
||||||
hilog_error!("[Rust] instance {} already exists", inst_id);
|
ohrs_log_error!("[Rust] instance {} already exists", inst_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
match INSTANCE_MANAGER.run_network_instance(cfg, false, ConfigFileControl::STATIC_CONFIG) {
|
match INSTANCE_MANAGER.run_network_instance(cfg, false, ConfigFileControl::STATIC_CONFIG) {
|
||||||
Ok(_) => true,
|
Ok(_) => {
|
||||||
|
cache_runtime_config_snapshot(inst_id.to_string(), inst_id.to_string(), config);
|
||||||
|
true
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] start_kernel failed for {}: {}", inst_id, err);
|
ohrs_log_error!("[Rust] start_kernel failed for {}: {}", inst_id, err);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,7 +327,7 @@ fn parse_instance_uuid(config_id: &str) -> Option<Uuid> {
|
|||||||
match Uuid::parse_str(config_id) {
|
match Uuid::parse_str(config_id) {
|
||||||
Ok(uuid) => Some(uuid),
|
Ok(uuid) => Some(uuid),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
hilog_error!("[Rust] invalid config_id {}: {}", config_id, err);
|
ohrs_log_error!("[Rust] invalid config_id {}: {}", config_id, err);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,6 +338,11 @@ pub fn init_config_store(root_dir: String) -> bool {
|
|||||||
exports::config_api::init_config_store(root_dir)
|
exports::config_api::init_config_store(root_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn reset_config_store() -> bool {
|
||||||
|
exports::config_api::reset_config_store()
|
||||||
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub fn list_configs() -> String {
|
pub fn list_configs() -> String {
|
||||||
exports::config_api::list_configs()
|
exports::config_api::list_configs()
|
||||||
@@ -482,6 +522,11 @@ pub fn get_runtime_snapshot() -> RuntimeAggregateState {
|
|||||||
exports::runtime_api::get_runtime_snapshot()
|
exports::runtime_api::get_runtime_snapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_kernel_snapshot_enabled(enabled: bool) {
|
||||||
|
set_snapshot_broadcast_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_runtime_snapshot_inner() -> RuntimeAggregateState {
|
pub(crate) fn get_runtime_snapshot_inner() -> RuntimeAggregateState {
|
||||||
exports::runtime_api::get_runtime_snapshot_inner()
|
exports::runtime_api::get_runtime_snapshot_inner()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,266 @@
|
|||||||
|
use napi_derive_ohos::napi;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::fs::{self, OpenOptions};
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
const LOG_DIR_NAME: &str = "easytier-logs";
|
||||||
|
const LOG_FILE_PREFIX: &str = "easytier-";
|
||||||
|
const LOG_FILE_SUFFIX: &str = ".log";
|
||||||
|
const MAX_LOG_FILES: usize = 10;
|
||||||
|
const MAX_MEMORY_LINES: usize = 500;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LogOptions {
|
||||||
|
core_log: bool,
|
||||||
|
debug_log: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LogOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
core_log: false,
|
||||||
|
debug_log: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct LogManagerState {
|
||||||
|
log_dir: Option<PathBuf>,
|
||||||
|
active_file: Option<PathBuf>,
|
||||||
|
lines: VecDeque<String>,
|
||||||
|
options: LogOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOG_MANAGER: Lazy<Mutex<LogManagerState>> =
|
||||||
|
Lazy::new(|| Mutex::new(LogManagerState::default()));
|
||||||
|
static CORE_LOG_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||||
|
static DEBUG_LOG_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
fn now_millis() -> u128 {
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.map(|duration| duration.as_millis())
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sanitize_name(raw: &str) -> String {
|
||||||
|
let value = raw
|
||||||
|
.chars()
|
||||||
|
.map(|ch| {
|
||||||
|
if ch.is_ascii_alphanumeric() || ch == '-' || ch == '_' {
|
||||||
|
ch
|
||||||
|
} else {
|
||||||
|
'-'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<String>();
|
||||||
|
if value.is_empty() {
|
||||||
|
"process".to_string()
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_dir(root_dir: &str) -> PathBuf {
|
||||||
|
Path::new(root_dir).join(LOG_DIR_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_log_file(path: &Path) -> bool {
|
||||||
|
path.file_name()
|
||||||
|
.and_then(|name| name.to_str())
|
||||||
|
.map(|name| name.starts_with(LOG_FILE_PREFIX) && name.ends_with(LOG_FILE_SUFFIX))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sorted_log_files(dir: &Path) -> Vec<PathBuf> {
|
||||||
|
let mut files = fs::read_dir(dir)
|
||||||
|
.ok()
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|entries| entries.filter_map(|entry| entry.ok()))
|
||||||
|
.map(|entry| entry.path())
|
||||||
|
.filter(|path| is_log_file(path))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
files.sort_by(|left, right| {
|
||||||
|
left.file_name()
|
||||||
|
.and_then(|name| name.to_str())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.cmp(
|
||||||
|
right
|
||||||
|
.file_name()
|
||||||
|
.and_then(|name| name.to_str())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
files
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup_old_logs(dir: &Path) {
|
||||||
|
let files = sorted_log_files(dir);
|
||||||
|
let overflow = files.len().saturating_sub(MAX_LOG_FILES);
|
||||||
|
for path in files.into_iter().take(overflow) {
|
||||||
|
let _ = fs::remove_file(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_memory_line(state: &mut LogManagerState, line: String) {
|
||||||
|
state.lines.push_back(line);
|
||||||
|
while state.lines.len() > MAX_MEMORY_LINES {
|
||||||
|
state.lines.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_log_file(path: &Path, line: &str) {
|
||||||
|
if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(path) {
|
||||||
|
let _ = writeln!(file, "{}", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_record_debug(level: i32) -> bool {
|
||||||
|
level <= 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_line(level: i32, target: &str, message: &str) -> String {
|
||||||
|
format!("{}[{}] {}", level, target, message.replace('\n', "\\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn configure(core_log: bool, debug_log: bool) {
|
||||||
|
CORE_LOG_ENABLED.store(core_log, Ordering::Relaxed);
|
||||||
|
DEBUG_LOG_ENABLED.store(debug_log, Ordering::Relaxed);
|
||||||
|
if let Ok(mut guard) = LOG_MANAGER.lock() {
|
||||||
|
guard.options.core_log = core_log;
|
||||||
|
guard.options.debug_log = debug_log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn app_log_enabled(level: i32) -> bool {
|
||||||
|
!should_record_debug(level) || DEBUG_LOG_ENABLED.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn core_log_enabled(level: i32) -> bool {
|
||||||
|
CORE_LOG_ENABLED.load(Ordering::Relaxed) && app_log_enabled(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn record_app_log(level: i32, target: &str, message: &str) {
|
||||||
|
if !app_log_enabled(level) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Ok(mut guard) = LOG_MANAGER.lock() {
|
||||||
|
let line = format_line(level, target, message);
|
||||||
|
if let Some(path) = guard.active_file.as_ref() {
|
||||||
|
append_log_file(path, &line);
|
||||||
|
}
|
||||||
|
push_memory_line(&mut guard, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn record_core_log(level: i32, target: &str, message: &str) {
|
||||||
|
if !core_log_enabled(level) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Ok(mut guard) = LOG_MANAGER.lock() {
|
||||||
|
let line = format_line(level, target, message);
|
||||||
|
if let Some(path) = guard.active_file.as_ref() {
|
||||||
|
append_log_file(path, &line);
|
||||||
|
}
|
||||||
|
push_memory_line(&mut guard, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn init_log_manager(root_dir: String, process_name: String) -> bool {
|
||||||
|
let dir = log_dir(&root_dir);
|
||||||
|
if fs::create_dir_all(&dir).is_err() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if LOG_MANAGER
|
||||||
|
.lock()
|
||||||
|
.map(|guard| guard.active_file.is_some())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
cleanup_old_logs(&dir);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let active_file = dir.join(format!(
|
||||||
|
"{}{}-{}-{}{}",
|
||||||
|
LOG_FILE_PREFIX,
|
||||||
|
now_millis(),
|
||||||
|
std::process::id(),
|
||||||
|
sanitize_name(&process_name),
|
||||||
|
LOG_FILE_SUFFIX
|
||||||
|
));
|
||||||
|
if OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&active_file)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut guard) = LOG_MANAGER.lock() {
|
||||||
|
guard.log_dir = Some(dir.clone());
|
||||||
|
guard.active_file = Some(active_file);
|
||||||
|
guard.lines.clear();
|
||||||
|
}
|
||||||
|
cleanup_old_logs(&dir);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn configure_log_manager(core_log: bool, debug_log: bool) {
|
||||||
|
configure(core_log, debug_log);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn write_app_log(level: i32, target: String, message: String) {
|
||||||
|
record_app_log(level, &target, &message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn drain_log_lines() -> Vec<String> {
|
||||||
|
LOG_MANAGER
|
||||||
|
.lock()
|
||||||
|
.map(|mut guard| guard.lines.drain(..).collect())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn export_log_archive(target_path: String) -> bool {
|
||||||
|
let log_dir = LOG_MANAGER
|
||||||
|
.lock()
|
||||||
|
.ok()
|
||||||
|
.and_then(|guard| guard.log_dir.clone());
|
||||||
|
let Some(log_dir) = log_dir else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let files = sorted_log_files(&log_dir);
|
||||||
|
let mut output = match OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.write(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(&target_path)
|
||||||
|
{
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
for path in files {
|
||||||
|
let name = path
|
||||||
|
.file_name()
|
||||||
|
.and_then(|value| value.to_str())
|
||||||
|
.unwrap_or("unknown.log");
|
||||||
|
let _ = writeln!(output, "===== {} =====", name);
|
||||||
|
if let Ok(content) = fs::read_to_string(&path) {
|
||||||
|
let _ = writeln!(output, "{}", content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
pub(crate) mod log_manager;
|
||||||
pub(crate) mod native_log;
|
pub(crate) mod native_log;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
|
use super::log_manager;
|
||||||
use napi_derive_ohos::napi;
|
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::collections::HashMap;
|
||||||
use std::panic;
|
use std::panic;
|
||||||
use tracing::{Event, Subscriber};
|
use tracing::{Event, Subscriber};
|
||||||
@@ -10,8 +8,9 @@ use tracing_subscriber::layer::{Context, Layer};
|
|||||||
use tracing_subscriber::prelude::*;
|
use tracing_subscriber::prelude::*;
|
||||||
|
|
||||||
static INITIALIZED: std::sync::Once = std::sync::Once::new();
|
static INITIALIZED: std::sync::Once = std::sync::Once::new();
|
||||||
|
static TRACING_INITIALIZED: std::sync::Once = std::sync::Once::new();
|
||||||
fn panic_hook(info: &panic::PanicHookInfo) {
|
fn panic_hook(info: &panic::PanicHookInfo) {
|
||||||
hilog_error!("RUST PANIC: {}", info);
|
log_manager::record_core_log(5, "RustPanic", &format!("{}", info));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
@@ -23,45 +22,40 @@ pub fn init_panic_hook() {
|
|||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub fn hilog_global_options(domain: u32, tag: String) {
|
pub fn hilog_global_options(domain: u32, tag: String) {
|
||||||
ohos_hilog_binding::forward_stdio_to_hilog();
|
let _ = domain;
|
||||||
set_global_options(LogOptions {
|
let _ = tag;
|
||||||
domain,
|
|
||||||
tag: Box::leak(tag.clone().into_boxed_str()),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub fn init_tracing_subscriber() {
|
pub fn init_tracing_subscriber() {
|
||||||
tracing_subscriber::registry()
|
TRACING_INITIALIZED.call_once(|| {
|
||||||
.with(CallbackLayer {
|
let _ = tracing_subscriber::registry()
|
||||||
callback: Box::new(tracing_callback),
|
.with(CallbackLayer {
|
||||||
})
|
callback: Box::new(tracing_callback),
|
||||||
.init();
|
})
|
||||||
|
.try_init();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tracing_callback(event: &Event, fields: HashMap<String, String>) {
|
fn tracing_callback(event: &Event, fields: HashMap<String, String>) {
|
||||||
let metadata = event.metadata();
|
let metadata = event.metadata();
|
||||||
#[cfg(target_env = "ohos")]
|
let loc = metadata
|
||||||
{
|
.target()
|
||||||
let loc = metadata.target().split("::").last().unwrap();
|
.split("::")
|
||||||
match *metadata.level() {
|
.last()
|
||||||
Level::TRACE => {
|
.unwrap_or(metadata.target());
|
||||||
hilog_debug!("[{}] {:?}", loc, fields.values().collect::<Vec<_>>());
|
let level = match *metadata.level() {
|
||||||
}
|
Level::TRACE => 2,
|
||||||
Level::DEBUG => {
|
Level::DEBUG => 3,
|
||||||
hilog_debug!("[{}] {:?}", loc, fields.values().collect::<Vec<_>>());
|
Level::INFO => 4,
|
||||||
}
|
Level::WARN => 6,
|
||||||
Level::INFO => {
|
Level::ERROR => 5,
|
||||||
hilog_info!("[{}] {:?}", loc, fields.values().collect::<Vec<_>>());
|
};
|
||||||
}
|
if !log_manager::core_log_enabled(level) {
|
||||||
Level::WARN => {
|
return;
|
||||||
hilog_warn!("[{}] {:?}", loc, fields.values().collect::<Vec<_>>());
|
|
||||||
}
|
|
||||||
Level::ERROR => {
|
|
||||||
hilog_error!("[{}] {:?}", loc, fields.values().collect::<Vec<_>>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let values = fields.values().cloned().collect::<Vec<_>>().join(" ");
|
||||||
|
log_manager::record_core_log(level, &format!("Rust:{}", loc), &values);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CallbackLayer {
|
struct CallbackLayer {
|
||||||
@@ -70,6 +64,16 @@ struct CallbackLayer {
|
|||||||
|
|
||||||
impl<S: Subscriber> Layer<S> for CallbackLayer {
|
impl<S: Subscriber> Layer<S> for CallbackLayer {
|
||||||
fn on_event(&self, event: &Event, _ctx: Context<S>) {
|
fn on_event(&self, event: &Event, _ctx: Context<S>) {
|
||||||
|
let level = match *event.metadata().level() {
|
||||||
|
Level::TRACE => 2,
|
||||||
|
Level::DEBUG => 3,
|
||||||
|
Level::INFO => 4,
|
||||||
|
Level::WARN => 6,
|
||||||
|
Level::ERROR => 5,
|
||||||
|
};
|
||||||
|
if !log_manager::core_log_enabled(level) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 使用 fmt::format::FmtSpan 提取字段值
|
// 使用 fmt::format::FmtSpan 提取字段值
|
||||||
let mut fields = HashMap::new();
|
let mut fields = HashMap::new();
|
||||||
let mut visitor = FieldCollector(&mut fields);
|
let mut visitor = FieldCollector(&mut fields);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use napi_derive_ohos::napi;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
static ATTACHED_TUN_INSTANCE_IDS: once_cell::sync::Lazy<Mutex<HashSet<String>>> =
|
static ATTACHED_TUN_INSTANCE_IDS: once_cell::sync::Lazy<Mutex<HashSet<String>>> =
|
||||||
once_cell::sync::Lazy::new(|| Mutex::new(HashSet::new()));
|
once_cell::sync::Lazy::new(|| Mutex::new(HashSet::new()));
|
||||||
@@ -158,6 +159,136 @@ fn stringify_uuid(value: Option<common::Uuid>) -> Option<String> {
|
|||||||
value.map(|v| v.to_string())
|
value.map(|v| v.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn non_empty_string(value: Option<String>) -> Option<String> {
|
||||||
|
value.and_then(|raw| {
|
||||||
|
let trimmed = raw.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(trimmed.to_string())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_virtual_ipv4_cidr(config: &api::manage::NetworkConfig) -> Option<String> {
|
||||||
|
non_empty_string(config.virtual_ipv4.clone())
|
||||||
|
.map(|ipv4| format!("{}/{}", ipv4, config.network_length.unwrap_or(24)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_endpoint_urls(config: &api::manage::NetworkConfig) -> Vec<String> {
|
||||||
|
let mut urls = Vec::new();
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
if let Some(url) = non_empty_string(config.public_server_url.clone())
|
||||||
|
&& seen.insert(url.clone())
|
||||||
|
{
|
||||||
|
urls.push(url);
|
||||||
|
}
|
||||||
|
for raw in &config.peer_urls {
|
||||||
|
let trimmed = raw.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let value = trimmed.to_string();
|
||||||
|
if seen.insert(value.clone()) {
|
||||||
|
urls.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
urls
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_url(url: &str) -> Option<Url> {
|
||||||
|
Url::parse(url).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_scheme(url: &str) -> Option<String> {
|
||||||
|
endpoint_url(url)
|
||||||
|
.map(|parsed| parsed.scheme().to_string())
|
||||||
|
.or_else(|| {
|
||||||
|
let scheme = url.split("://").next().unwrap_or("").trim();
|
||||||
|
(!scheme.is_empty()).then_some(scheme.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_label(url: &str) -> String {
|
||||||
|
if let Some(parsed) = endpoint_url(url)
|
||||||
|
&& let Some(host) = parsed.host_str()
|
||||||
|
{
|
||||||
|
return format!("[Config] {}", host);
|
||||||
|
}
|
||||||
|
format!("[Config] {}", url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_remote_display(url: &str) -> String {
|
||||||
|
if let Some(parsed) = endpoint_url(url)
|
||||||
|
&& let Some(host) = parsed.host_str()
|
||||||
|
{
|
||||||
|
return parsed
|
||||||
|
.port()
|
||||||
|
.map(|port| format!("{}:{}", host, port))
|
||||||
|
.unwrap_or_else(|| host.to_string());
|
||||||
|
}
|
||||||
|
url.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configured_peer_id(index: usize) -> i64 {
|
||||||
|
9_000_000 + index as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configured_route_views(endpoints: &[String], public_server_url: Option<&str>) -> Vec<RouteView> {
|
||||||
|
endpoints
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, endpoint)| RouteView {
|
||||||
|
peer_id: configured_peer_id(index),
|
||||||
|
hostname: Some(endpoint_label(endpoint)),
|
||||||
|
ipv4: Some(endpoint_remote_display(endpoint)),
|
||||||
|
ipv4_cidr: None,
|
||||||
|
ipv6_cidr: None,
|
||||||
|
proxy_cidrs: Vec::new(),
|
||||||
|
next_hop_peer_id: None,
|
||||||
|
cost: Some(0),
|
||||||
|
path_latency: None,
|
||||||
|
udp_nat_type: None,
|
||||||
|
tcp_nat_type: None,
|
||||||
|
inst_id: None,
|
||||||
|
version: None,
|
||||||
|
is_public_server: public_server_url.map(|url| url == endpoint),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configured_peer_views(endpoints: &[String]) -> Vec<PeerInfo> {
|
||||||
|
endpoints
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, endpoint)| {
|
||||||
|
let conn_id = format!("configured-peer-{}", index);
|
||||||
|
PeerInfo {
|
||||||
|
peer_id: configured_peer_id(index),
|
||||||
|
default_conn_id: Some(conn_id.clone()),
|
||||||
|
directly_connected_conns: vec![conn_id.clone()],
|
||||||
|
conns: vec![PeerConnInfo {
|
||||||
|
conn_id,
|
||||||
|
my_peer_id: 0,
|
||||||
|
peer_id: configured_peer_id(index),
|
||||||
|
features: Vec::new(),
|
||||||
|
tunnel_type: endpoint_scheme(endpoint),
|
||||||
|
local_addr: None,
|
||||||
|
remote_addr: Some(endpoint.clone()),
|
||||||
|
resolved_remote_addr: Some(endpoint_remote_display(endpoint)),
|
||||||
|
stats: None,
|
||||||
|
loss_rate: None,
|
||||||
|
is_client: true,
|
||||||
|
network_name: None,
|
||||||
|
is_closed: false,
|
||||||
|
secure_auth_level: None,
|
||||||
|
peer_identity_type: None,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn optional_u32_to_i64(value: Option<u32>) -> Option<i64> {
|
fn optional_u32_to_i64(value: Option<u32>) -> Option<i64> {
|
||||||
value.map(|v| v as i64)
|
value.map(|v| v as i64)
|
||||||
}
|
}
|
||||||
@@ -291,3 +422,43 @@ pub fn runtime_instance_from_running_info(
|
|||||||
peers: info.peers.into_iter().map(peer_to_view).collect(),
|
peers: info.peers.into_iter().map(peer_to_view).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn runtime_instance_from_config_snapshot(
|
||||||
|
config_id: String,
|
||||||
|
display_name: String,
|
||||||
|
config: api::manage::NetworkConfig,
|
||||||
|
running: bool,
|
||||||
|
) -> RuntimeInstanceState {
|
||||||
|
let tun_attached = running && is_tun_attached(&config_id);
|
||||||
|
let tun_required =
|
||||||
|
running && (config.dev_name.as_deref().unwrap_or("") != "no_tun" || tun_attached);
|
||||||
|
let endpoint_urls = config_endpoint_urls(&config);
|
||||||
|
let public_server_url = non_empty_string(config.public_server_url.clone());
|
||||||
|
let my_node_info = MyNodeInfo {
|
||||||
|
virtual_ipv4: non_empty_string(config.virtual_ipv4.clone()),
|
||||||
|
virtual_ipv4_cidr: config_virtual_ipv4_cidr(&config),
|
||||||
|
hostname: non_empty_string(config.hostname.clone()),
|
||||||
|
version: None,
|
||||||
|
peer_id: None,
|
||||||
|
listeners: config.listener_urls.clone(),
|
||||||
|
vpn_portal_cfg: None,
|
||||||
|
udp_nat_type: None,
|
||||||
|
tcp_nat_type: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
RuntimeInstanceState {
|
||||||
|
config_id: config_id.clone(),
|
||||||
|
instance_id: config_id,
|
||||||
|
display_name,
|
||||||
|
running,
|
||||||
|
tun_required,
|
||||||
|
tun_attached,
|
||||||
|
magic_dns_enabled: config.enable_magic_dns.unwrap_or(false),
|
||||||
|
need_exit_node: !config.exit_nodes.is_empty(),
|
||||||
|
error_message: None,
|
||||||
|
my_node_info: Some(my_node_info),
|
||||||
|
events: Vec::new(),
|
||||||
|
routes: configured_route_views(&endpoint_urls, public_server_url.as_deref()),
|
||||||
|
peers: configured_peer_views(&endpoint_urls),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user