diff --git a/.gitignore b/.gitignore index 346f2ce..7df4ed0 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,4 @@ coverage/ # === Debug/profiler === *.profraw *.profdata -flamegraph.svg +flamegraph.svg \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 8c46f2f..39b0782 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,19 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -100,6 +113,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -145,7 +167,7 @@ source = "git+https://github.com/aya-rs/aya?tag=aya-v0.13.1#2791badd947e3abb459e dependencies = [ "bytes", "core-error", - "hashbrown", + "hashbrown 0.15.4", "log", "object 0.36.7", "thiserror", @@ -166,6 +188,18 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bitflags" version = "1.3.2" @@ -177,6 +211,18 @@ name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] name = "bumpalo" @@ -266,10 +312,10 @@ version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -311,6 +357,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + [[package]] name = "console" version = "0.15.11" @@ -324,6 +380,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "core-error" version = "0.0.0" @@ -339,6 +401,30 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -367,6 +453,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -415,15 +510,76 @@ dependencies = [ ] [[package]] -name = "eclipta-agent" -version = "0.2.1" +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "chrono", - "hostname 0.3.1", - "serde", - "serde_json", + "generic-array", + "typenum", ] +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "eclipta-cli" version = "1.0.0" @@ -435,19 +591,24 @@ dependencies = [ "chrono", "clap", "color-eyre", + "colored", "console", "crossterm 0.27.0", + "dirs", + "dotenvy", "figlet-rs", - "hostname 0.4.1", + "hostname", "humantime", "log", "nix", "serde", "serde_json", + "sqlx", "sysinfo", "tokio", "tracing-subscriber", "tui", + "uuid", ] [[package]] @@ -455,6 +616,9 @@ name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] [[package]] name = "encode_unicode" @@ -468,6 +632,33 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "eyre" version = "0.6.12" @@ -478,24 +669,165 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "figlet-rs" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4742a071cd9694fc86f9fa1a08fa3e53d40cc899d7ee532295da2d085639fbc5" +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + [[package]] name = "hashbrown" version = "0.15.4" @@ -507,6 +839,24 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.5.0" @@ -514,14 +864,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "hostname" -version = "0.3.1" +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "libc", - "match_cfg", - "winapi", + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", ] [[package]] @@ -566,48 +938,158 @@ dependencies = [ ] [[package]] -name = "indenter" -version = "0.3.3" +name = "icu_collections" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] [[package]] -name = "indexmap" -version = "2.10.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ - "equivalent", - "hashbrown", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.1" +name = "icu_normalizer" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] [[package]] -name = "itoa" -version = "1.0.15" +name = "icu_normalizer_data" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] -name = "js-sys" -version = "0.3.77" +name = "icu_properties" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ - "once_cell", - "wasm-bindgen", + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", ] [[package]] -name = "lazy_static" -version = "1.5.0" +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -615,6 +1097,46 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +dependencies = [ + "bitflags 2.9.1", + "libc", + "redox_syscall", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lock_api" version = "0.4.13" @@ -632,10 +1154,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] -name = "match_cfg" -version = "0.1.0" +name = "md-5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] [[package]] name = "memchr" @@ -643,6 +1169,12 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -660,7 +1192,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -671,7 +1203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -687,6 +1219,16 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -706,6 +1248,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -713,6 +1292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -731,7 +1311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", - "hashbrown", + "hashbrown 0.15.4", "indexmap", "memchr", ] @@ -748,6 +1328,12 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -783,12 +1369,84 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -807,6 +1465,42 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + [[package]] name = "rayon" version = "1.10.0" @@ -829,19 +1523,107 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags 2.9.1", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-demangle" version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.21" @@ -860,6 +1642,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "serde" version = "1.0.219" @@ -877,77 +1669,385 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +dependencies = [ + "libc", + "mio 0.8.11", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash", + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", + "webpki-roots", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", ] [[package]] -name = "serde_json" -version = "1.0.140" +name = "sqlx-mysql" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" dependencies = [ + "atoi", + "base64", + "bitflags 2.9.1", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", "itoa", + "log", + "md-5", "memchr", - "ryu", + "once_cell", + "percent-encoding", + "rand", + "rsa", "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", ] [[package]] -name = "sharded-slab" -version = "0.1.7" +name = "sqlx-postgres" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ - "lazy_static", + "atoi", + "base64", + "bitflags 2.9.1", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", ] [[package]] -name = "shlex" -version = "1.3.0" +name = "sqlx-sqlite" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "tracing", + "url", + "urlencoding", +] [[package]] -name = "signal-hook" -version = "0.3.18" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "signal-hook-mio" -version = "0.2.4" +name = "stringprep" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "libc", - "mio 0.8.11", - "signal-hook", + "unicode-bidi", + "unicode-normalization", + "unicode-properties", ] [[package]] -name = "signal-hook-registry" -version = "1.4.5" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" -dependencies = [ - "libc", -] +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "smallvec" -version = "1.15.1" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] -name = "strsim" -version = "0.11.1" +name = "syn" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] name = "syn" @@ -960,6 +2060,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "sysinfo" version = "0.30.13" @@ -975,6 +2086,19 @@ dependencies = [ "windows", ] +[[package]] +name = "tempfile" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -992,7 +2116,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1004,6 +2128,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.45.1" @@ -1016,19 +2165,56 @@ dependencies = [ "mio 1.0.4", "pin-project-lite", "signal-hook-registry", + "socket2", + "tokio-macros", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "tracing-core" version = "0.1.34" @@ -1087,12 +2273,39 @@ dependencies = [ "unicode-width 0.1.14", ] +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -1111,6 +2324,35 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "user" version = "0.1.0" @@ -1119,18 +2361,41 @@ dependencies = [ "aya 0.11.0", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -1143,6 +2408,21 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -1165,7 +2445,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -1187,7 +2467,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1201,6 +2481,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1263,7 +2559,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1274,7 +2570,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1448,3 +2744,122 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] diff --git a/Cargo.toml b/Cargo.toml index 4eae3cb..a18ab6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [workspace] members = [ "eclipta-cli", - "eclipta-agents", "ebpf-demo/user" ] diff --git a/eclipta-agents/.gitignore b/eclipta-agents/.gitignore deleted file mode 100644 index 58d814f..0000000 --- a/eclipta-agents/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# === Rust (for all crates) === -/target -**/target -Cargo.lock - -# === eBPF-specific === -*.o -*.elf - -# === Editor & IDE === -*.swp -*.swo -*.bak -*.tmp -*.log - -# VSCode -.vscode/ - -# JetBrains -.idea/ - -# === System files === -.DS_Store -Thumbs.db - -# === Shell scripts logs/output === -*.out -*.err diff --git a/eclipta-agents/Cargo.toml b/eclipta-agents/Cargo.toml deleted file mode 100644 index 3b5f1ff..0000000 --- a/eclipta-agents/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "eclipta-agent" -version = "0.2.1" -edition = "2021" - -[dependencies] -chrono = "0.4" -hostname = "0.3" -serde = { version = "1", features = ["derive"] } -serde_json = "1" - -[[bin]] -name = "eclipta-agent" -path = "src/main.rs" diff --git a/eclipta-agents/src/main.rs b/eclipta-agents/src/main.rs deleted file mode 100644 index cd75bdd..0000000 --- a/eclipta-agents/src/main.rs +++ /dev/null @@ -1,201 +0,0 @@ -use std::{ fs, thread, time::Duration }; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; -use std::process::Command; - -use chrono::Utc; -use serde::Serialize; - -#[derive(Serialize)] -struct AgentStatus { - id: String, - hostname: String, - kernel: String, - version: String, - last_seen: String, - uptime_secs: u64, - cpu_load: [f32; 3], - mem_used_mb: u64, - mem_total_mb: u64, - process_count: usize, - disk_used_mb: u64, - disk_total_mb: u64, - net_rx_kb: u64, - net_tx_kb: u64, - tcp_connections: usize, - alert: bool, -} - -fn main() { - let agent_id = "agent-001"; - let interval = Duration::from_secs(5); - let status_dir = PathBuf::from("/run/eclipta"); - fs::create_dir_all(&status_dir).ok(); - - loop { - let hostname = get_hostname(); - let kernel = get_kernel(); - let version = env!("CARGO_PKG_VERSION").to_string(); - let now = Utc::now().to_rfc3339(); - let uptime_secs = get_uptime_secs(); - - let cpu_load = get_cpu_load(); - let (mem_used_mb, mem_total_mb) = get_memory_mb(); - let (disk_used_mb, disk_total_mb) = get_disk_usage(); - let (net_rx_kb, net_tx_kb) = get_network_io(); - let process_count = get_process_count(); - let tcp_connections = get_tcp_connection_count(); - - let alert = should_alert(&cpu_load, mem_used_mb, mem_total_mb); - fs::write(status_dir.join(format!("{agent_id}.pid")), std::process::id().to_string()).ok(); - - let status = AgentStatus { - id: agent_id.to_string(), - hostname, - kernel, - version, - last_seen: now, - uptime_secs, - cpu_load, - mem_used_mb, - mem_total_mb, - process_count, - disk_used_mb, - disk_total_mb, - net_rx_kb, - net_tx_kb, - tcp_connections, - alert, - }; - - if let Ok(json) = serde_json::to_string_pretty(&status) { - let path = status_dir.join(format!("{agent_id}.json")); - if let Ok(mut file) = File::create(path) { - let _ = file.write_all(json.as_bytes()); - println!("✅ Agent heartbeat written."); - } - } - - thread::sleep(interval); - } -} - -fn get_hostname() -> String { - hostname::get().unwrap_or_default().to_string_lossy().to_string() -} - -fn get_kernel() -> String { - Command::new("uname") - .arg("-r") - .output() - .map(|out| String::from_utf8_lossy(&out.stdout).trim().to_string()) - .unwrap_or_else(|_| "unknown".to_string()) -} - -fn get_uptime_secs() -> u64 { - fs::read_to_string("/proc/uptime") - .ok() - .and_then(|s| - s - .split('.') - .next() - .map(|v| v.parse().unwrap_or(0)) - ) - .unwrap_or(0) -} - -fn get_cpu_load() -> [f32; 3] { - fs::read_to_string("/proc/loadavg") - .ok() - .and_then(|s| { - let parts: Vec<&str> = s.split_whitespace().collect(); - if parts.len() >= 3 { - Some([ - parts[0].parse().unwrap_or(0.0), - parts[1].parse().unwrap_or(0.0), - parts[2].parse().unwrap_or(0.0), - ]) - } else { - None - } - }) - .unwrap_or([0.0, 0.0, 0.0]) -} - -fn get_memory_mb() -> (u64, u64) { - let data = fs::read_to_string("/proc/meminfo").unwrap_or_default(); - let mut total: u64 = 0; - let mut free: u64 = 0; - - for line in data.lines() { - if line.starts_with("MemTotal:") { - total = line.split_whitespace().nth(1).unwrap_or("0").parse().unwrap_or(0); - } else if line.starts_with("MemAvailable:") { - free = line.split_whitespace().nth(1).unwrap_or("0").parse().unwrap_or(0); - } - } - - let used = total.saturating_sub(free); - (used / 1024, total / 1024) // return in MB -} - -fn get_disk_usage() -> (u64, u64) { - let output = Command::new("df").arg("-BM").arg("/").output(); - if let Ok(out) = output { - let lines = String::from_utf8_lossy(&out.stdout); - for line in lines.lines().skip(1) { - let parts: Vec<&str> = line.split_whitespace().collect(); - if parts.len() >= 5 { - let total = parts[1].trim_end_matches('M').parse().unwrap_or(0); - let used = parts[2].trim_end_matches('M').parse().unwrap_or(0); - return (used, total); - } - } - } - (0, 0) -} - -fn get_network_io() -> (u64, u64) { - let data = fs::read_to_string("/proc/net/dev").unwrap_or_default(); - let mut rx = 0; - let mut tx = 0; - for line in data.lines().skip(2) { - let parts: Vec<&str> = line.split_whitespace().collect(); - if parts.len() >= 17 { - rx += parts[1].parse::().unwrap_or(0); // RX bytes - tx += parts[9].parse::().unwrap_or(0); // TX bytes - } - } - (rx / 1024, tx / 1024) // Return in KB -} - -fn get_process_count() -> usize { - let entries = match fs::read_dir("/proc") { - Ok(entries) => entries, - Err(_) => { - return 0; - } - }; - - entries - .filter_map(|e| e.ok()) - .filter(|e| { - e.file_name() - .to_string_lossy() - .chars() - .all(|c| c.is_ascii_digit()) - }) - .count() -} - -fn get_tcp_connection_count() -> usize { - fs::read_to_string("/proc/net/tcp") - .map(|content| content.lines().skip(1).count()) - .unwrap_or(0) -} - -fn should_alert(cpu: &[f32; 3], used: u64, total: u64) -> bool { - let mem_percent = ((used as f32) / (total as f32)) * 100.0; - cpu[0] > 2.0 || mem_percent > 90.0 -} diff --git a/eclipta-cli/Cargo.toml b/eclipta-cli/Cargo.toml index 1b99bb6..ee16ebe 100644 --- a/eclipta-cli/Cargo.toml +++ b/eclipta-cli/Cargo.toml @@ -13,7 +13,7 @@ console = "0.15" nix = { version = "0.30", features = ["user", "signal", "process", "resource"] } anyhow = "1" log = "0.4" -tokio = { version = "1.38", features = ["rt-multi-thread", "time", "signal", "fs", "io-util"] } +tokio = { version = "1.38", features = ["rt-multi-thread", "time", "signal", "fs", "io-util", "macros" ] } byteorder = "1" bytes = "1.10.1" serde_json = "1.0" @@ -24,3 +24,9 @@ crossterm = "0.27" chrono = { version = "0.4", features = ["serde"] } hostname = "0.4.1" humantime = "2.1" +dirs = "5.0" +# sqlx = { version = "0.8", features = ["postgres", "runtime-tokio-rustls", "macros"] } +sqlx = { version = "0.7.4", features = ["postgres", "runtime-tokio-rustls"] } +dotenvy = "0.15" +uuid = { version = "1.8", features = ["v4"] } +colored = "2.1" diff --git a/eclipta-cli/src/commands/agent_logs.rs b/eclipta-cli/src/commands/agent_logs.rs deleted file mode 100644 index ea7c360..0000000 --- a/eclipta-cli/src/commands/agent_logs.rs +++ /dev/null @@ -1,66 +0,0 @@ -use clap::Args; -use std::path::PathBuf; -use tokio::fs::File; -use tokio::io::{AsyncBufReadExt, BufReader}; -use crate::utils::logger::{error, info}; -use tokio::time::{sleep, Duration}; -use std::fs; -use std::ffi::OsStr; - -#[derive(Args, Debug)] -pub struct AgentLogsOptions { - /// Agent ID like agent-001 (optional) - #[arg(long)] - pub id: Option, -} - -pub async fn handle_agent_logs(opts: AgentLogsOptions) { - let log_dir = PathBuf::from("/run/eclipta/logs"); - - if !log_dir.exists() { - error("Log directory not found: /run/eclipta/logs"); - return; - } - - let files: Vec = if let Some(id) = &opts.id { - let p = log_dir.join(format!("{}.log", id)); - if !p.exists() { - error(&format!("Log file not found: {}", p.display())); - return; - } - vec![p] - } else { - match fs::read_dir(&log_dir) { - Ok(entries) => entries - .flatten() - .filter(|e| e.path().extension() == Some(OsStr::new("log"))) - .map(|e| e.path()) - .collect(), - Err(e) => { - error(&format!("Failed to read log directory: {}", e)); - return; - } - } - }; - - for file in files { - let file_name = file.file_name().unwrap().to_string_lossy().to_string(); - let f = File::open(&file).await.unwrap(); - let reader = BufReader::new(f); - let mut lines = reader.lines(); - - info(&format!("Tailing logs from: {}", file_name)); - - tokio::spawn(async move { - while let Ok(Some(line)) = lines.next_line().await { - println!(" [{}] {}", file_name, line); - sleep(Duration::from_millis(200)).await; - } - }); - } - - // Keep the program running to tail - loop { - sleep(Duration::from_secs(1)).await; - } -} diff --git a/eclipta-cli/src/commands/agents.rs b/eclipta-cli/src/commands/agents.rs deleted file mode 100644 index d725be8..0000000 --- a/eclipta-cli/src/commands/agents.rs +++ /dev/null @@ -1,105 +0,0 @@ -use clap::Args; -use std::{fs, path::PathBuf}; -use serde::{Deserialize, Serialize}; -use crate::utils::logger::{info, success, error}; - -#[derive(Args, Debug)] -pub struct AgentOptions { - #[arg(long)] - pub json: bool, - - #[arg(long)] - pub verbose: bool, -} - -#[derive(Debug, Deserialize, Serialize)] -struct AgentStatus { - id: String, - hostname: String, - kernel: String, - version: String, - last_seen: String, - uptime_secs: u64, -} - -pub fn handle_agents(opts: AgentOptions) { - let dir = PathBuf::from("/run/eclipta"); - - let files = match fs::read_dir(&dir) { - Ok(f) => f, - Err(e) => { - error(&format!("Failed to read /run/eclipta: {}", e)); - return; - } - }; - - let mut agents = Vec::new(); - - for entry in files.flatten() { - let path = entry.path(); - if path.extension().and_then(|s| s.to_str()) != Some("json") { - continue; - } - - if let Ok(content) = fs::read_to_string(&path) { - if let Ok(agent) = serde_json::from_str::(&content) { - agents.push(agent); - } - } - } - - if agents.is_empty() { - info("No agents found."); - return; - } - - if opts.json { - let out = serde_json::to_string_pretty(&agents).unwrap(); - println!("{}", out); - return; - } - - success("Connected Agents:"); - for agent in &agents { - let status = agent.status_string(); - let badge = match status { - "online" => "🟢", - "offline" => "🔴", - _ => "❓", - }; - - println!( - "• [{} {}] {} - {} (uptime: {}s)", - badge, - status, - agent.id, - agent.hostname, - agent.uptime_secs - ); - - if opts.verbose { - println!(" └─ Kernel: {} | Version: {}", agent.kernel, agent.version); - } - } -} - -impl AgentStatus { - fn status_string(&self) -> &'static str { - let pid_path = PathBuf::from(format!("/run/eclipta/{}.pid", self.id)); - - let pid = match fs::read_to_string(&pid_path) { - Ok(content) => match content.trim().parse::() { - Ok(n) => n, - Err(_) => return "offline", - }, - Err(_) => return "offline", - }; - - let proc_path = PathBuf::from(format!("/proc/{}", pid)); - if proc_path.exists() { - "online" - } else { - "offline" - } - } -} diff --git a/eclipta-cli/src/commands/agents_inspect.rs b/eclipta-cli/src/commands/agents_inspect.rs deleted file mode 100644 index aa0b127..0000000 --- a/eclipta-cli/src/commands/agents_inspect.rs +++ /dev/null @@ -1,57 +0,0 @@ -use clap::Args; -use std::{fs, path::PathBuf}; -use serde::{Deserialize, Serialize}; -use crate::utils::logger::{error, success, info}; - -#[derive(Args, Debug)] -pub struct InspectAgentOptions { - /// Agent ID (like agent-001) - pub id: String, - - /// Output JSON - #[arg(long)] - pub json: bool, -} - -#[derive(Debug, Deserialize, Serialize)] -struct AgentStatus { - id: String, - hostname: String, - kernel: String, - version: String, - last_seen: String, - uptime_secs: u64, -} - -pub fn handle_inspect_agent(opts: InspectAgentOptions) { - let path = PathBuf::from(format!("/run/eclipta/{}.json", opts.id)); - - let content = match fs::read_to_string(&path) { - Ok(c) => c, - Err(e) => { - error(&format!("Failed to read agent file: {}", e)); - return; - } - }; - - let agent: AgentStatus = match serde_json::from_str(&content) { - Ok(a) => a, - Err(e) => { - error(&format!("Invalid JSON in agent file: {}", e)); - return; - } - }; - - if opts.json { - let output = serde_json::to_string_pretty(&agent).unwrap(); - println!("{}", output); - return; - } - - success(&format!("Agent: {}", agent.id)); - info(&format!("Hostname: {}", agent.hostname)); - info(&format!("Kernel: {}", agent.kernel)); - info(&format!("Uptime: {}s", agent.uptime_secs)); - info(&format!("Last Seen: {}", agent.last_seen)); - info(&format!("Version: {}", agent.version)); -} diff --git a/eclipta-cli/src/commands/check_db.rs b/eclipta-cli/src/commands/check_db.rs new file mode 100644 index 0000000..ab5119f --- /dev/null +++ b/eclipta-cli/src/commands/check_db.rs @@ -0,0 +1,18 @@ +use crate::utils::db::init_db; +use crate::utils::logger::{success, error}; + +#[derive(clap::Args)] +pub struct CheckDbOptions {} + +pub async fn handle_check_db(_opts: CheckDbOptions) -> Result<(), Box> { + match init_db().await { + Ok(_) => { + success("Database connection successful & migrations applied!"); + Ok(()) + } + Err(e) => { + error(&format!("Database connection failed: {}", e)); + Err(Box::new(e)) + } + } +} diff --git a/eclipta-cli/src/commands/inspect.rs b/eclipta-cli/src/commands/inspect.rs index 3572d54..03c8018 100644 --- a/eclipta-cli/src/commands/inspect.rs +++ b/eclipta-cli/src/commands/inspect.rs @@ -1,14 +1,15 @@ -use aya::{Ebpf}; +use aya::Ebpf; use clap::Args; use std::path::PathBuf; use crate::utils::logger::{info, error}; use serde_json; - +use crate::utils::paths::default_bin_object; #[derive(Args, Debug)] pub struct InspectOptions { - #[arg(short, long, default_value = "target/trace_execve.o")] - pub program: PathBuf, + /// Path to eBPF ELF (defaults to $ECLIPTA_BIN or ./bin/ebpf.so) + #[arg(short, long)] + pub program: Option, #[arg(long)] pub json: bool, @@ -18,12 +19,13 @@ pub struct InspectOptions { } pub fn handle_inspect(opts: InspectOptions) { - if !opts.program.exists() { + let program_path = opts.program.unwrap_or_else(default_bin_object); + if !program_path.exists() { error("Missing compiled eBPF program."); return; } - let bpf = match Ebpf::load_file(&opts.program) { + let bpf = match Ebpf::load_file(&program_path) { Ok(b) => b, Err(e) => { error(&format!("Failed to parse ELF: {}", e)); @@ -36,21 +38,16 @@ pub fn handle_inspect(opts: InspectOptions) { if opts.json { let output = serde_json::json!({ - "elf": opts.program.display().to_string(), + "elf": program_path.display().to_string(), "programs": programs, "maps": maps, }); println!("{}", serde_json::to_string_pretty(&output).unwrap()); } else { - info(&format!("✅ ELF: {}", opts.program.display())); + info(&format!("✅ ELF: {}", program_path.display())); println!("Programs:"); - for name in &programs { - println!(" - {}", name); - } - + for name in &programs { println!(" - {}", name); } println!("Maps:"); - for name in &maps { - println!(" - {}", name); - } + for name in &maps { println!(" - {}", name); } } } diff --git a/eclipta-cli/src/commands/kill_agent.rs b/eclipta-cli/src/commands/kill_agent.rs deleted file mode 100644 index d8a5e21..0000000 --- a/eclipta-cli/src/commands/kill_agent.rs +++ /dev/null @@ -1,31 +0,0 @@ -use clap::Args; -use std::{fs, path::PathBuf}; -use anyhow::{Result, Context}; - -#[derive(Args)] -pub struct KillAgentOptions { - #[arg(long)] - pub agent: String, -} - -pub fn handle_kill_agent(opts: KillAgentOptions) -> Result<()> { - let pid_path = PathBuf::from(format!("/run/eclipta/{}.pid", opts.agent)); - - let pid_str = fs::read_to_string(&pid_path) - .with_context(|| format!("Failed to read PID file for agent '{}'", opts.agent))?; - - let pid: i32 = pid_str.trim().parse() - .with_context(|| format!("Invalid PID found in {}", pid_path.display()))?; - - println!("Sending SIGTERM to agent '{}' (PID {})", opts.agent, pid); - - // Send SIGTERM to the process - nix::sys::signal::kill( - nix::unistd::Pid::from_raw(pid), - nix::sys::signal::Signal::SIGTERM, - ).with_context(|| format!("Failed to send SIGTERM to PID {}", pid))?; - - println!("Kill signal sent successfully."); - - Ok(()) -} diff --git a/eclipta-cli/src/commands/live.rs b/eclipta-cli/src/commands/live.rs deleted file mode 100644 index 29b7789..0000000 --- a/eclipta-cli/src/commands/live.rs +++ /dev/null @@ -1,121 +0,0 @@ -use std::{fs, path::PathBuf, time::Duration}; -use serde::Deserialize; -use tokio::time::sleep; - -use tui::{ - backend::CrosstermBackend, - layout::{Constraint, Direction, Layout}, - style::{Color, Modifier, Style}, - text::{Span}, - widgets::{Block, Borders, Cell, Row, Table}, - Terminal, -}; - -use crossterm::{ - event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, - execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, -}; - -#[derive(Deserialize, Debug)] -pub struct AgentLiveStatus { - pub id: String, - pub hostname: String, - pub uptime_secs: u64, - pub cpu: Option, - pub mem: Option, -} - -pub async fn handle_live() { - let mut stdout = std::io::stdout(); - enable_raw_mode().unwrap(); - execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap(); - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend).unwrap(); - - loop { - // Check if user pressed 'q' to quit - if event::poll(Duration::from_millis(100)).unwrap() { - if let Event::Key(key) = event::read().unwrap() { - if key.code == KeyCode::Char('q') { - break; - } - } - } - - // Read agent JSON files - let dir = PathBuf::from("/run/eclipta"); - let mut agents = Vec::new(); - - if let Ok(files) = fs::read_dir(&dir) { - for entry in files.flatten() { - if entry.file_name().to_string_lossy().starts_with("agent-") { - if let Ok(contents) = fs::read_to_string(entry.path()) { - if let Ok(agent) = serde_json::from_str::(&contents) { - agents.push(agent); - } - } - } - } - } - - // Draw UI - terminal.draw(|f| { - let chunks = Layout::default() - .direction(Direction::Vertical) - .margin(1) - .constraints([Constraint::Length(3), Constraint::Min(1)].as_ref()) - .split(f.size()); - - let title = Block::default() - .title(Span::styled( - "ECLIPTA LIVE – Press 'q' to quit", - Style::default() - .fg(Color::Magenta) - .add_modifier(Modifier::BOLD), - )) - .borders(Borders::ALL); - f.render_widget(title, chunks[0]); - - let header = ["ID", "Hostname", "Uptime", "CPU%", "Memory"]; - let rows: Vec = agents - .iter() - .map(|a| { - Row::new(vec![ - Cell::from(a.id.clone()), - Cell::from(a.hostname.clone()), - Cell::from(format!("{}s", a.uptime_secs)), - Cell::from( - a.cpu - .map(|v| format!("{:.1}", v)) - .unwrap_or_else(|| "--".to_string()), - ), - Cell::from(a.mem.clone().unwrap_or_else(|| "--".to_string())), - ]) - }) - .collect(); - - let table = Table::new(rows) - .header( - Row::new(header) - .style(Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)), - ) - .block(Block::default().title("Agents").borders(Borders::ALL)) - .widths(&[ - Constraint::Length(12), - Constraint::Length(18), - Constraint::Length(10), - Constraint::Length(10), - Constraint::Length(10), - ]) - .column_spacing(2); - f.render_widget(table, chunks[1]); - }).unwrap(); - - sleep(Duration::from_secs(2)).await; - } - - disable_raw_mode().unwrap(); - execute!(terminal.backend_mut(), LeaveAlternateScreen, DisableMouseCapture).unwrap(); - terminal.show_cursor().unwrap(); -} diff --git a/eclipta-cli/src/commands/load.rs b/eclipta-cli/src/commands/load.rs index a6b8a24..60e298a 100644 --- a/eclipta-cli/src/commands/load.rs +++ b/eclipta-cli/src/commands/load.rs @@ -2,14 +2,18 @@ use aya::{programs::TracePoint, EbpfLoader}; use clap::Args; use std::path::PathBuf; use crate::utils::logger::{info, success, error}; +use crate::utils::paths::{default_bin_object, default_pin_prefix, default_state_path}; +use crate::utils::state::{AttachmentRecord, load_state, save_state}; use nix::sys::resource::{setrlimit, Resource, RLIM_INFINITY}; use nix::unistd::Uid; #[derive(Args, Debug)] pub struct LoadOptions { - #[arg(short, long, default_value = "/etc/eclipta/bin/ebpf.so")] - pub program: PathBuf, + /// Path to eBPF ELF (defaults to $ECLIPTA_BIN or ./bin/ebpf.so) + #[arg(short, long)] + pub program: Option, + /// Program name inside ELF #[arg(short, long, default_value = "cpu_usage")] pub name: String, @@ -17,22 +21,32 @@ pub struct LoadOptions { #[arg(short = 't', long, default_value = "sched:sched_switch")] pub tracepoint: String, + /// Pin the program and maps under a prefix in bpffs + #[arg(long, default_value_t = true)] + pub pin: bool, + + /// Pin prefix in bpffs (default $ECLIPTA_PIN_PATH or /sys/fs/bpf/eclipta) + #[arg(long)] + pub pin_prefix: Option, + + /// Persist loader state to this file (default XDG local data dir) + #[arg(long)] + pub state_file: Option, + #[arg(long)] pub dry_run: bool, #[arg(short, long)] pub verbose: bool, - #[arg(long)] - pub force: bool, - #[arg(long)] pub json: bool, } pub fn handle_load(opts: LoadOptions) { - if !opts.program.exists() { - error(&format!("eBPF ELF file not found at: {}", opts.program.display())); + let program_path = opts.program.unwrap_or_else(default_bin_object); + if !program_path.exists() { + error(&format!("eBPF ELF file not found at: {}", program_path.display())); return; } @@ -61,11 +75,12 @@ pub fn handle_load(opts: LoadOptions) { success("✓ Dry run mode - ELF validated and options parsed."); if opts.verbose { info(&format!( - "Program '{}' from '{}' would attach to '{}:{}'", + "Program '{}' from '{}' would attach to '{}:{}' (pin: {})", opts.name, - opts.program.display(), + program_path.display(), tp_category, - tp_name + tp_name, + opts.pin )); } return; @@ -73,11 +88,14 @@ pub fn handle_load(opts: LoadOptions) { if opts.verbose { info(&format!("Loading program: {}", opts.name)); - info(&format!("From ELF file: {}", opts.program.display())); + info(&format!("From ELF file: {}", program_path.display())); info(&format!("Target tracepoint: {}:{}", tp_category, tp_name)); } - match EbpfLoader::new().load_file(&opts.program) { + let pin_prefix = opts.pin_prefix.unwrap_or_else(default_pin_prefix); + let state_file = opts.state_file.unwrap_or_else(default_state_path); + + match EbpfLoader::new().load_file(&program_path) { Ok(mut bpf) => { match bpf.program_mut(&opts.name) { Some(prog) => { @@ -93,13 +111,50 @@ pub fn handle_load(opts: LoadOptions) { return; } + let mut pinned_prog = None; + let mut pinned_maps = Vec::new(); + if opts.pin { + let _ = std::fs::create_dir_all(&pin_prefix); + // Pin program + let prog_pin = pin_prefix.join(&opts.name); + if let Err(e) = tp.pin(&prog_pin) { + error(&format!("Failed to pin program: {}", e)); + } else { + pinned_prog = Some(prog_pin); + } + // Pin maps + for (map_name, m) in bpf.maps_mut() { + let path = pin_prefix.join(map_name); + if let Err(e) = m.pin(&path) { + if opts.verbose { info(&format!("Map '{}' pin failed: {}", map_name, e)); } + } else { + pinned_maps.push(path); + } + } + } + + // Save state + let mut st = load_state(&state_file); + st.attachments.push(AttachmentRecord { + name: opts.name.clone(), + kind: "tracepoint".to_string(), + trace_category: Some(tp_category.clone()), + trace_name: Some(tp_name.clone()), + pinned_prog, + pinned_maps, + pid: std::process::id(), + created_at: chrono::Utc::now().timestamp(), + }); + let _ = save_state(&state_file, st); + if opts.json { println!( - "{{ \"status\": \"ok\", \"program\": \"{}\", \"tracepoint\": \"{}:{}\" }}", - opts.name, tp_category, tp_name + "{{ \"status\": \"ok\", \"program\": \"{}\", \"tracepoint\": \"{}:{}\", \"pinned\": {} }}", + opts.name, tp_category, tp_name, opts.pin ); } else { success(&format!("✓ Program '{}' attached to '{}:{}'", opts.name, tp_category, tp_name)); + if opts.pin { info(&format!("Pinned under {}", pin_prefix.display())); } } } else { error("Could not convert program to TracePoint"); diff --git a/eclipta-cli/src/commands/mod.rs b/eclipta-cli/src/commands/mod.rs index c550483..ff4d73e 100644 --- a/eclipta-cli/src/commands/mod.rs +++ b/eclipta-cli/src/commands/mod.rs @@ -4,19 +4,13 @@ pub mod load; pub mod logs; pub mod unload; pub mod inspect; -pub mod agents; -pub mod agents_inspect; -pub mod restart_agent; -pub mod agent_logs; -pub mod live; pub mod daemon; pub mod monitor; pub mod ping_all; pub mod watch_cpu; pub mod config; pub mod alerts; -pub mod kill_agent; -pub mod update_agent; pub mod version; -pub mod sync_agents; pub mod run; +pub mod check_db; +pub mod upload; diff --git a/eclipta-cli/src/commands/monitor.rs b/eclipta-cli/src/commands/monitor.rs index 8c31ea4..89991c6 100644 --- a/eclipta-cli/src/commands/monitor.rs +++ b/eclipta-cli/src/commands/monitor.rs @@ -4,8 +4,7 @@ use crossterm::{ execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; -use serde::Deserialize; -use std::{fs, io, time::Duration}; +use std::{io, time::Duration, path::PathBuf}; use tui::{ backend::CrosstermBackend, layout::{Constraint, Direction, Layout}, @@ -15,22 +14,22 @@ use tui::{ Terminal, }; -#[derive(Deserialize)] -struct Agent { - id: String, - hostname: String, - last_seen: String, - uptime_secs: u64, - cpu_load: [f32; 3], - mem_used_mb: u64, - mem_total_mb: u64, - process_count: u32, - disk_used_mb: u64, - disk_total_mb: u64, - net_rx_kb: u64, - net_tx_kb: u64, - tcp_connections: u32, - alert: bool, +use crate::utils::paths::default_state_path; +use crate::utils::state::load_state; + +struct AttachmentRow { + name: String, + kind: String, + hook: String, + pid: String, + status: String, + pinned: String, + created: String, +} + +fn proc_alive(pid: u32) -> bool { + let p = PathBuf::from(format!("/proc/{}", pid)); + p.exists() } pub async fn handle_monitor() -> io::Result<()> { @@ -49,16 +48,37 @@ pub async fn handle_monitor() -> io::Result<()> { } } - let mut agents = Vec::new(); - if let Ok(files) = fs::read_dir("/run/eclipta") { - for entry in files.flatten() { - if let Ok(txt) = fs::read_to_string(entry.path()) { - if let Ok(agent) = serde_json::from_str::(&txt) { - agents.push(agent); - } + let st = load_state(&default_state_path()); + let rows_data: Vec = st + .attachments + .iter() + .map(|r| { + let hook = match (&r.trace_category, &r.trace_name) { + (Some(c), Some(n)) => format!("{}:{}", c, n), + _ => r.kind.clone(), + }; + let pinned = r + .pinned_prog + .as_ref() + .map(|p| if p.exists() { "yes" } else { "missing" }) + .unwrap_or("no"); + let status = if proc_alive(r.pid) { "online" } else { "offline" }; + let created = DateTime::from_timestamp(r.created_at, 0) + .unwrap_or(DateTime::::UNIX_EPOCH) + .with_timezone(&Utc) + .format("%Y-%m-%d %H:%M:%S") + .to_string(); + AttachmentRow { + name: r.name.clone(), + kind: r.kind.clone(), + hook, + pid: r.pid.to_string(), + status: status.to_string(), + pinned: pinned.to_string(), + created, } - } - } + }) + .collect(); terminal.draw(|f| { let size = f.size(); @@ -69,52 +89,39 @@ pub async fn handle_monitor() -> io::Result<()> { let header = Row::new( vec![ - "ID", "Host", "Uptime", "CPU", "Mem", "Disk", "Net", "TCP", "Proc", "Alert", - "Seen", + "Program", "Kind", "Hook", "PID", "Status", "Pinned", "Created", ] .into_iter() .map(|h| Cell::from(Span::styled(h, Style::default().fg(Color::Yellow)))) .collect::>(), ); - let rows: Vec = agents + let rows: Vec = rows_data .iter() .map(|a| { - let cpu = format!( - "{:.1}/{:.1}/{:.1}", - a.cpu_load[0], a.cpu_load[1], a.cpu_load[2] - ); - let mem = format!("{} / {}", a.mem_used_mb, a.mem_total_mb); - let disk = format!("{} / {}", a.disk_used_mb, a.disk_total_mb); - let net = format!( - "{:.1}↓ / {:.1}↑", - a.net_rx_kb as f64 / 1024.0, - a.net_tx_kb as f64 / 1024.0 - ); - let seen = DateTime::parse_from_rfc3339(&a.last_seen) - .map(|dt| dt.with_timezone(&Utc).format("%H:%M:%S").to_string()) - .unwrap_or_else(|_| "-".into()); - let alert = if a.alert { - Span::styled( - "⚠️", + let status_span = match a.status.as_str() { + "online" => Span::styled( + "online", + Style::default().fg(Color::Green).add_modifier(Modifier::BOLD), + ), + _ => Span::styled("offline", Style::default().fg(Color::Red)), + }; + let pinned_span = match a.pinned.as_str() { + "yes" => Span::styled("yes", Style::default().fg(Color::Green)), + "missing" => Span::styled( + "missing", Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), - ) - } else { - Span::raw("✅") + ), + _ => Span::raw("no"), }; - Row::new(vec![ - Cell::from(a.id.clone()), - Cell::from(a.hostname.clone()), - Cell::from(format!("{}s", a.uptime_secs)), - Cell::from(cpu), - Cell::from(mem), - Cell::from(disk), - Cell::from(net), - Cell::from(format!("{}", a.tcp_connections)), - Cell::from(format!("{}", a.process_count)), - Cell::from(alert), - Cell::from(seen), + Cell::from(a.name.clone()), + Cell::from(a.kind.clone()), + Cell::from(a.hook.clone()), + Cell::from(a.pid.clone()), + Cell::from(status_span), + Cell::from(pinned_span), + Cell::from(a.created.clone()), ]) }) .collect(); @@ -127,17 +134,13 @@ pub async fn handle_monitor() -> io::Result<()> { .borders(Borders::ALL), ) .widths(&[ - Constraint::Length(10), // ID - Constraint::Length(15), // Hostname - Constraint::Length(8), // Uptime - Constraint::Length(12), // CPU - Constraint::Length(14), // Mem - Constraint::Length(14), // Disk - Constraint::Length(14), // Net - Constraint::Length(5), // TCP - Constraint::Length(6), // Proc - Constraint::Length(6), // Alert - Constraint::Length(8), // Last seen + Constraint::Length(18), // Program + Constraint::Length(12), // Kind + Constraint::Length(24), // Hook + Constraint::Length(8), // PID + Constraint::Length(10), // Status + Constraint::Length(8), // Pinned + Constraint::Length(20), // Created ]) .column_spacing(1); diff --git a/eclipta-cli/src/commands/restart_agent.rs b/eclipta-cli/src/commands/restart_agent.rs deleted file mode 100644 index b41b8a4..0000000 --- a/eclipta-cli/src/commands/restart_agent.rs +++ /dev/null @@ -1,22 +0,0 @@ -use clap::Args; -use std::{fs, path::PathBuf}; -use crate::utils::logger::{error, success}; - -#[derive(Args, Debug)] -pub struct RestartAgentOptions { - /// Agent ID like agent-001 - pub id: String, -} - -pub fn handle_restart_agent(opts: RestartAgentOptions) { - let trigger_file = PathBuf::from(format!("/run/eclipta/restart-{}.trigger", opts.id)); - - match fs::write(&trigger_file, b"restart") { - Ok(_) => { - success(&format!("Restart signal sent to agent `{}`", opts.id)); - } - Err(e) => { - error(&format!("Failed to create trigger file: {}", e)); - } - } -} diff --git a/eclipta-cli/src/commands/run.rs b/eclipta-cli/src/commands/run.rs index f8babea..0a82281 100644 --- a/eclipta-cli/src/commands/run.rs +++ b/eclipta-cli/src/commands/run.rs @@ -3,6 +3,9 @@ use bytes::BytesMut; use clap::Args; use std::{convert::TryInto, path::PathBuf, time::Duration, mem}; use tokio::{signal, time}; +use crate::utils::paths::default_bin_object; +use nix::sys::resource::{setrlimit, Resource, RLIM_INFINITY}; +use nix::unistd::Uid; #[repr(C)] #[derive(Clone, Copy)] @@ -13,17 +16,21 @@ pub struct ExecEvent { #[derive(Args, Debug)] pub struct RunOptions { - #[arg(short, long, default_value = "ebpf-demo/target/bpfel-unknown-none/release/libebpf.so")] - pub program: PathBuf, + /// Path to eBPF ELF (defaults to $ECLIPTA_BIN or ./bin/ebpf.so) + #[arg(short, long)] + pub program: Option, - #[arg(short, long, default_value = "trace_execve")] + /// Program name inside ELF + #[arg(short, long, default_value = "cpu_usage")] pub name: String, - #[arg(short, long, default_value = "syscalls/sys_enter_execve")] + /// Tracepoint in the form "category:name" or "category/name" (e.g., "sched:sched_switch") + #[arg(short, long, default_value = "sched:sched_switch")] pub tracepoint: String, - #[arg(short = 'm', long, default_value = "trace_execve_events")] - pub map: String, + /// PerfEventArray map name to stream (optional) + #[arg(short = 'm', long)] + pub map: Option, #[arg(long)] pub execve_format: bool, @@ -33,12 +40,21 @@ pub struct RunOptions { } pub async fn handle_run(opts: RunOptions) { - if !opts.program.exists() { - eprintln!("Missing compiled eBPF program at {}", opts.program.display()); + if !Uid::effective().is_root() { + eprintln!("This command must be run as root. Try: sudo eclipta run ..."); return; } - let mut bpf = match EbpfLoader::new().load_file(&opts.program) { + // Bump memlock to avoid failures on older kernels + let _ = setrlimit(Resource::RLIMIT_MEMLOCK, RLIM_INFINITY, RLIM_INFINITY); + + let program_path = opts.program.clone().unwrap_or_else(default_bin_object); + if !program_path.exists() { + eprintln!("Missing compiled eBPF program at {}", program_path.display()); + return; + } + + let mut bpf = match EbpfLoader::new().load_file(&program_path) { Ok(b) => b, Err(e) => { eprintln!("Failed to load ELF: {}", e); @@ -55,21 +71,25 @@ pub async fn handle_run(opts: RunOptions) { return; } }; - let parts: Vec<&str> = opts.tracepoint.split('/').collect(); - if parts.len() != 2 { - eprintln!("Tracepoint must be 'category/name', got '{}'", opts.tracepoint); + // Support both category:name and category/name + let s = opts.tracepoint.replace('/', ":"); + let mut parts = s.splitn(2, ':'); + let cat = parts.next().unwrap_or(""); + let nam = parts.next().unwrap_or(""); + if cat.is_empty() || nam.is_empty() { + eprintln!("Tracepoint must be 'category:name', got '{}'", opts.tracepoint); return; } if let Err(e) = tp.load() { eprintln!("Failed to load program: {}", e); return; } - if let Err(e) = tp.attach(parts[0], parts[1]) { + if let Err(e) = tp.attach(cat, nam) { eprintln!("Failed to attach to tracepoint '{}': {}", opts.tracepoint, e); return; } if opts.verbose { - println!("✓ Attached '{}' to '{}'", opts.name, opts.tracepoint); + println!("✓ Attached '{}' to '{}:{}'", opts.name, cat, nam); } } None => { @@ -78,68 +98,73 @@ pub async fn handle_run(opts: RunOptions) { } } - let map = match bpf.take_map(&opts.map) { - Some(m) => m, - None => { - eprintln!("Map '{}' not found in ELF", opts.map); - return; - } - }; - let mut perf_array: PerfEventArray<_> = match map.try_into() { - Ok(pa) => pa, - Err(_) => { - eprintln!("Map '{}' is not a PerfEventArray", opts.map); - return; - } - }; - - println!("Streaming events from '{}' (Ctrl+C to exit)", opts.map); - - for cpu_id in online_cpus().unwrap_or_default() { - let mut buf = match perf_array.open(cpu_id, None) { - Ok(b) => b, - Err(e) => { - eprintln!("Failed to open perf buffer on CPU {}: {}", cpu_id, e); - continue; + // If a map was provided, stream events; otherwise just wait until Ctrl+C + if let Some(map_name) = opts.map.as_ref() { + let map = match bpf.take_map(map_name) { + Some(m) => m, + None => { + eprintln!("Map '{}' not found in ELF", map_name); + return; + } + }; + let mut perf_array: PerfEventArray<_> = match map.try_into() { + Ok(pa) => pa, + Err(_) => { + eprintln!("Map '{}' is not a PerfEventArray", map_name); + return; } }; - let execve_fmt = opts.execve_format; - tokio::spawn(async move { - let mut bufs = vec![BytesMut::with_capacity(1024)]; - loop { - match buf.read_events(&mut bufs) { - Ok(events) => { - for rec in &bufs[..events.read] { - if execve_fmt && rec.len() >= mem::size_of::() { - let ptr = rec.as_ptr() as *const ExecEvent; - let ev = unsafe { *ptr }; - let comm = std::str::from_utf8(&ev.comm) - .unwrap_or("") - .trim_end_matches(char::from(0)) - .to_string(); - println!("exec pid={} comm={}", ev.pid, comm); - } else if let Ok(s) = std::str::from_utf8(&rec) { - println!("{}", s.trim_end_matches(char::from(0))); - } else { - println!( - "{}", - rec.iter() - .map(|b| format!("{:02x}", b)) - .collect::>() - .join("") - ); + println!("Streaming events from '{}' (Ctrl+C to exit)", map_name); + + for cpu_id in online_cpus().unwrap_or_default() { + let mut buf = match perf_array.open(cpu_id, None) { + Ok(b) => b, + Err(e) => { + eprintln!("Failed to open perf buffer on CPU {}: {}", cpu_id, e); + continue; + } + }; + + let execve_fmt = opts.execve_format; + tokio::spawn(async move { + let mut bufs = vec![BytesMut::with_capacity(1024)]; + loop { + match buf.read_events(&mut bufs) { + Ok(events) => { + for rec in &bufs[..events.read] { + if execve_fmt && rec.len() >= mem::size_of::() { + let ptr = rec.as_ptr() as *const ExecEvent; + let ev = unsafe { *ptr }; + let comm = std::str::from_utf8(&ev.comm) + .unwrap_or("") + .trim_end_matches(char::from(0)) + .to_string(); + println!("exec pid={} comm={}", ev.pid, comm); + } else if let Ok(s) = std::str::from_utf8(&rec) { + println!("{}", s.trim_end_matches(char::from(0))); + } else { + println!( + "{}", + rec.iter() + .map(|b| format!("{:02x}", b)) + .collect::>() + .join("") + ); + } + } + if events.lost > 0 { + eprintln!("Lost {} events (perf buffer overflow)", events.lost); } } - if events.lost > 0 { - eprintln!("Lost {} events (perf buffer overflow)", events.lost); - } + Err(e) => eprintln!("read_events error: {}", e), } - Err(e) => eprintln!("read_events error: {}", e), + time::sleep(Duration::from_millis(100)).await; } - time::sleep(Duration::from_millis(100)).await; - } - }); + }); + } + } else { + println!("Attached. No map provided for streaming. Waiting (Ctrl+C to exit)..."); } if let Err(e) = signal::ctrl_c().await { diff --git a/eclipta-cli/src/commands/sync_agents.rs b/eclipta-cli/src/commands/sync_agents.rs deleted file mode 100644 index 2d4a2b1..0000000 --- a/eclipta-cli/src/commands/sync_agents.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::{fs, path::PathBuf}; -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use clap::Args; -use anyhow::Result; - -#[derive(Args)] -pub struct SyncAgentsOptions {} - -#[derive(Serialize, Deserialize, Debug)] -pub struct AgentSnapshot { - pub id: String, - pub hostname: String, - pub kernel: String, - pub version: String, - pub last_seen: DateTime, - pub uptime_secs: u64, - pub cpu_load: [f32; 3], - pub mem_used_mb: u64, - pub mem_total_mb: u64, - pub process_count: u64, - pub disk_used_mb: u64, - pub disk_total_mb: u64, - pub net_rx_kb: u64, - pub net_tx_kb: u64, - pub tcp_connections: u64, - pub alert: bool -} - -pub async fn handle_sync_agents(_: SyncAgentsOptions) -> Result<()> { - let mut snapshots = Vec::new(); - let dir = PathBuf::from("/run/eclipta"); - - if dir.exists() { - for entry in fs::read_dir(&dir)? { - let entry = entry?; - let path = entry.path(); - if path.extension().map(|e| e == "json").unwrap_or(false) { - let raw = fs::read_to_string(&path)?; - match serde_json::from_str::(&raw) { - Ok(snapshot) => snapshots.push(snapshot), - Err(e) => eprintln!("⚠️ Failed to parse {}: {}", path.display(), e), - } - } - } - } - - let out_path = PathBuf::from("/etc/eclipta/agents/snapshot.json"); - fs::create_dir_all(out_path.parent().unwrap())?; - fs::write(&out_path, serde_json::to_string_pretty(&snapshots)?)?; - - println!("✅ Synced {} agents to {}", snapshots.len(), out_path.display()); - - Ok(()) -} diff --git a/eclipta-cli/src/commands/unload.rs b/eclipta-cli/src/commands/unload.rs index 28633d7..81cd26e 100644 --- a/eclipta-cli/src/commands/unload.rs +++ b/eclipta-cli/src/commands/unload.rs @@ -1,18 +1,31 @@ -use crate::utils::logger::{success, error}; +use crate::utils::logger::{success, error, info}; +use crate::utils::paths::{default_bin_object, default_state_path}; +use crate::utils::state::{load_state, save_state}; use aya::Ebpf; use clap::Args; use std::path::PathBuf; #[derive(Args, Debug)] pub struct UnloadOptions { - #[arg(short, long, default_value = "target/trace_execve.o")] - pub program: PathBuf, + /// Path to eBPF ELF (defaults to $ECLIPTA_BIN or ./bin/ebpf.so) + #[arg(short, long)] + pub program: Option, - #[arg(short, long, default_value = "trace_execve")] - pub name: String, + /// Program name inside ELF + #[arg(short, long)] + pub name: Option, - #[arg(short, long, default_value = "syscalls/sys_enter_execve")] - pub tracepoint: String, + /// Tracepoint in the form "category:name" or "category/name" + #[arg(short = 't', long)] + pub tracepoint: Option, + + /// State file to update (default XDG local data dir) + #[arg(long)] + pub state_file: Option, + + /// Unpin pinned objects from bpffs + #[arg(long)] + pub unpin: bool, #[arg(long)] pub json: bool, @@ -22,17 +35,28 @@ pub struct UnloadOptions { } pub fn handle_unload(opts: UnloadOptions) { - if !opts.program.exists() { - error("Missing compiled eBPF program. Run `eclipta load` first."); + let program_path = opts.program.unwrap_or_else(default_bin_object); + let state_file = opts.state_file.unwrap_or_else(default_state_path); + + if !program_path.exists() { + error("Missing compiled eBPF program."); return; } - if opts.verbose { - println!("Attempting to unload program: {}", opts.name); - println!("ELF path: {:?}", opts.program); - } + let name = if let Some(n) = opts.name.clone() { n } else { + // fallback to last record from state + let st = load_state(&state_file); + if let Some(last) = st.attachments.last() { + last.name.clone() + } else { + error("No program name provided and no state available."); + return; + } + }; - let mut bpf = match Ebpf::load_file(&opts.program) { + if opts.verbose { info(&format!("Attempting to unload program: {}", name)); } + + let bpf = match Ebpf::load_file(&program_path) { Ok(bpf) => bpf, Err(e) => { error(&format!("Failed to load ELF: {}", e)); @@ -40,24 +64,27 @@ pub fn handle_unload(opts: UnloadOptions) { } }; - if bpf.program_mut(&opts.name).is_none() { + if bpf.program(&name).is_none() { error("Program not found in ELF"); return; } - // Dropping bpf unloads all programs - drop(bpf); + // Update state: remove records matching name + let mut st = load_state(&state_file); + let removed: Vec<_> = st.attachments.iter().filter(|r| r.name == name).cloned().collect(); + st.attachments.retain(|r| r.name != name); + let _ = save_state(&state_file, st); - if opts.verbose { - println!("Program '{}' unloaded by dropping Ebpf struct", opts.name); + if opts.unpin { + for rec in removed { + if let Some(pp) = rec.pinned_prog { let _ = std::fs::remove_file(pp); } + for m in rec.pinned_maps { let _ = std::fs::remove_file(m); } + } } if opts.json { - println!( - "{{ \"status\": \"ok\", \"unloaded\": true, \"program\": \"{}\" }}", - opts.name - ); + println!("{{ \"status\": \"ok\", \"unloaded\": true, \"program\": \"{}\" }}", name); } else { - success(&format!("✓ Unloaded program '{}'", opts.name)); + success(&format!("✓ Unloaded program '{}'", name)); } } \ No newline at end of file diff --git a/eclipta-cli/src/commands/update_agent.rs b/eclipta-cli/src/commands/update_agent.rs deleted file mode 100644 index 3a3df42..0000000 --- a/eclipta-cli/src/commands/update_agent.rs +++ /dev/null @@ -1,51 +0,0 @@ -use clap::Args; -use anyhow::{Result, Context}; -use std::{fs, path::Path, process::Command}; - -#[derive(Args)] -pub struct UpdateAgentOptions { - #[arg(long)] - pub agent: String, - - #[arg(long)] - pub version: Option, - - #[arg(long)] - pub force: bool, - - #[arg(long)] - pub restart: bool, -} - -pub async fn handle_update_agent(opts: UpdateAgentOptions) -> Result<()> { - let agent_bin_path = format!("/opt/eclipta/agents/{}/eclipta-agent", opts.agent); - let new_bin_path = match &opts.version { - Some(v) => format!("/opt/eclipta/releases/{}/eclipta-agent", v), - None => "/opt/eclipta/releases/latest/eclipta-agent".into(), - }; - - println!("Updating agent '{}'...", opts.agent); - println!(" - Current path: {}", agent_bin_path); - println!(" - New version: {}", new_bin_path); - - if !Path::new(&new_bin_path).exists() { - anyhow::bail!("New agent binary not found at: {}", new_bin_path); - } - - // Replace binary - fs::copy(&new_bin_path, &agent_bin_path) - .with_context(|| format!("Failed to copy new agent binary to {}", agent_bin_path))?; - - println!(" Agent '{}' binary updated.", opts.agent); - - // Optional restart - if opts.restart { - println!(" Restarting agent '{}'", opts.agent); - Command::new("systemctl") - .args(&["restart", &format!("eclipta-agent@{}", opts.agent)]) - .status() - .context("Failed to restart agent service")?; - } - - Ok(()) -} diff --git a/eclipta-cli/src/commands/upload.rs b/eclipta-cli/src/commands/upload.rs new file mode 100644 index 0000000..1bc6bb5 --- /dev/null +++ b/eclipta-cli/src/commands/upload.rs @@ -0,0 +1,96 @@ +use crate::db::programs::insert_program; +use crate::utils::db::init_db; +use crate::utils::logger::{success, error, info, warn}; +use aya::Ebpf; +use clap::Args; +use std::fs; +use std::path::{Path, PathBuf}; +use uuid::Uuid; + +#[derive(Args, Debug)] +pub struct UploadOptions { + /// Path to eBPF ELF file (.o) + #[arg(short, long)] + pub program: PathBuf, + + /// Program title + #[arg(long)] + pub title: String, + + /// Program description + #[arg(long)] + pub description: String, + + /// Version (default: v1.0.0) + #[arg(long, default_value = "v1.0.0")] + pub version: String, +} + +pub async fn handle_upload(opts: UploadOptions) -> Result<(), Box> { + info("Starting upload process..."); + + // Step 1: Validate file exists + if !opts.program.exists() { + error("Provided eBPF program file does not exist."); + return Ok(()); + } + if !opts.program.is_file() { + error("Provided path is not a valid file."); + return Ok(()); + } + + // Step 2: Validate ELF with aya + match Ebpf::load_file(&opts.program) { + Ok(_) => info("ELF validation successful."), + Err(e) => { + error(&format!("Invalid eBPF program: {}", e)); + return Ok(()); + } + } + + // Step 3: Copy program into storage directory with unique name + let storage_dir = Path::new("/var/lib/eclipta/programs"); + if !storage_dir.exists() { + warn("Storage directory missing. Creating..."); + fs::create_dir_all(storage_dir)?; + } + + let unique_name = format!( + "{}-{}.o", + opts.title.replace(' ', "_"), + Uuid::new_v4() + ); + let dest_path = storage_dir.join(unique_name); + + match fs::copy(&opts.program, &dest_path) { + Ok(_) => info(&format!("Program stored at {}", dest_path.display())), + Err(e) => { + error(&format!("Failed to copy program: {}", e)); + return Ok(()); + } + } + + // Step 4: Insert metadata into Postgres + let pool = match init_db().await { + Ok(p) => p, + Err(e) => { + error(&format!("Failed to connect to database: {}", e)); + return Ok(()); + } + }; + + match insert_program( + &pool, + &opts.title, + &opts.description, + &opts.version, + &dest_path.to_string_lossy(), + ) + .await + { + Ok(_) => success("Upload complete! Metadata stored in database."), + Err(e) => error(&format!("Database insert failed: {}", e)), + } + + Ok(()) +} diff --git a/eclipta-cli/src/commands/watch_cpu.rs b/eclipta-cli/src/commands/watch_cpu.rs index a2d6ad4..3fcd2a6 100644 --- a/eclipta-cli/src/commands/watch_cpu.rs +++ b/eclipta-cli/src/commands/watch_cpu.rs @@ -1,57 +1,36 @@ -use std::{fs, time::Duration}; +use std::time::Duration; use tokio::time::sleep; -use serde::Deserialize; -use chrono::Local; use clap::Args; - -#[derive(Deserialize)] -struct AgentCpuLoad { - id: String, - cpu_load: Option<[f32; 3]>, - last_seen: String, -} +use sysinfo::{System, CpuRefreshKind, RefreshKind}; #[derive(Args)] pub struct WatchCpuOptions { - #[arg(long)] - pub agent_id: Option, + #[arg(long, default_value_t = 1)] + pub interval_secs: u64, } pub async fn handle_watch_cpu(opts: WatchCpuOptions) -> anyhow::Result<()> { - let agent_filter = opts.agent_id; + println!("Watching system CPU/memory (Ctrl+C to quit)..."); - println!("Watching CPU load{} (Ctrl+C to quit)...", - agent_filter - .as_ref() - .map(|id| format!(" for agent '{}'", id)) - .unwrap_or_default() + let mut sys = System::new_with_specifics( + RefreshKind::new().with_memory(sysinfo::MemoryRefreshKind::everything()).with_cpu(CpuRefreshKind::everything()), ); loop { - let paths = fs::read_dir("/run/eclipta")?; - println!("\n{}", Local::now().format("%H:%M:%S")); + sys.refresh_memory(); + sys.refresh_cpu(); - for entry in paths.flatten() { - if let Ok(content) = fs::read_to_string(entry.path()) { - if let Ok(agent) = serde_json::from_str::(&content) { - if let Some(ref filter) = agent_filter { - if agent.id != *filter { - continue; - } - } + let total_mem = sys.total_memory(); + let used_mem = sys.used_memory(); + let global_cpu = sys.global_cpu_info().cpu_usage(); - if let Some(load) = agent.cpu_load { - println!( - "{} | CPU Load: {:.1} / {:.1} / {:.1} | Seen: {}", - agent.id, - load[0], load[1], load[2], - agent.last_seen - ); - } - } - } - } + println!( + "CPU: {:.1}% | Mem: {}/{} MiB", + global_cpu, + used_mem / 1024 / 1024, + total_mem / 1024 / 1024 + ); - sleep(Duration::from_secs(3)).await; + sleep(Duration::from_secs(opts.interval_secs)).await; } } diff --git a/eclipta-cli/src/db/mod.rs b/eclipta-cli/src/db/mod.rs new file mode 100644 index 0000000..1e3309b --- /dev/null +++ b/eclipta-cli/src/db/mod.rs @@ -0,0 +1 @@ +pub mod programs; diff --git a/eclipta-cli/src/db/programs.rs b/eclipta-cli/src/db/programs.rs new file mode 100644 index 0000000..be3b04e --- /dev/null +++ b/eclipta-cli/src/db/programs.rs @@ -0,0 +1,26 @@ + +use sqlx::{Pool, Postgres}; + + +pub async fn insert_program( + pool: &Pool, + title: &str, + description: &str, + version: &str, + path: &str, +) -> Result<(), sqlx::Error> { + sqlx::query( + r#" + INSERT INTO ebpf_programs (title, description, version, status, path) + VALUES ($1, $2, $3, 'deactive', $4) + "#, + ) + .bind(title) + .bind(description) + .bind(version) + .bind(path) + .execute(pool) + .await?; + + Ok(()) +} \ No newline at end of file diff --git a/eclipta-cli/src/main.rs b/eclipta-cli/src/main.rs index 3db5fe4..1d69f59 100644 --- a/eclipta-cli/src/main.rs +++ b/eclipta-cli/src/main.rs @@ -1,29 +1,24 @@ mod commands; mod utils; +mod db; use crate::commands::logs::LogOptions; -use clap::{ Parser, Subcommand }; -use commands::agent_logs::{ handle_agent_logs, AgentLogsOptions }; -use commands::agents::{ handle_agents, AgentOptions }; -use commands::agents_inspect::{ handle_inspect_agent, InspectAgentOptions }; +use clap::{Parser, Subcommand}; +use commands::alerts::handle_alerts; +use commands::check_db::{handle_check_db, CheckDbOptions}; +use commands::config::{handle_config, ConfigOptions}; use commands::daemon::handle_daemon; -use commands::inspect::{ handle_inspect, InspectOptions }; -use commands::live::handle_live; +use commands::inspect::{handle_inspect, InspectOptions}; use commands::monitor::handle_monitor; use commands::ping_all; -use commands::alerts::handle_alerts; -use commands::restart_agent::{ handle_restart_agent, RestartAgentOptions }; -use commands::config::{ handle_config, ConfigOptions }; -use commands::watch_cpu::{ handle_watch_cpu, WatchCpuOptions }; -use commands::kill_agent::{ handle_kill_agent, KillAgentOptions }; -use commands::update_agent::{ handle_update_agent, UpdateAgentOptions }; -use commands::version::{ handle_version, VersionOptions }; -use commands::sync_agents::{ handle_sync_agents, SyncAgentsOptions }; +use commands::upload::{handle_upload, UploadOptions}; +use commands::version::{handle_version, VersionOptions}; +use commands::watch_cpu::{handle_watch_cpu, WatchCpuOptions}; use commands::{ load::handle_load, logs::handle_logs, status::run_status, - unload::{ handle_unload, UnloadOptions }, + unload::{handle_unload, UnloadOptions}, welcome::run_welcome, }; @@ -43,24 +38,20 @@ enum Commands { Logs(LogOptions), Unload(UnloadOptions), Inspect(InspectOptions), - InspectAgent(InspectAgentOptions), - RestartAgent(RestartAgentOptions), - Agents(AgentOptions), - AgentLogs(AgentLogsOptions), - Live, Daemon, Monitor, PingAll, WatchCpu(WatchCpuOptions), Config(ConfigOptions), Alerts, - KillAgent(KillAgentOptions), - UpdateAgent(UpdateAgentOptions), Version(VersionOptions), - SyncAgents(SyncAgentsOptions), Run(commands::run::RunOptions), + CheckDb(CheckDbOptions), + Upload(UploadOptions), } -fn main() { + +#[tokio::main] +async fn main() { let cli = Cli::parse(); match cli.command { @@ -69,61 +60,20 @@ fn main() { Commands::Load(opts) => handle_load(opts), Commands::Unload(opts) => handle_unload(opts), Commands::Inspect(opts) => handle_inspect(opts), - Commands::Logs(opts) => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_logs(opts)); - } - Commands::Agents(opts) => handle_agents(opts), - Commands::AgentLogs(opts) => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_agent_logs(opts)); - } - Commands::InspectAgent(opts) => handle_inspect_agent(opts), - Commands::RestartAgent(opts) => handle_restart_agent(opts), - Commands::Live => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_live()); - } - Commands::Daemon => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_daemon()); - } - Commands::Monitor => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_monitor()).unwrap(); - } - Commands::PingAll => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(ping_all::handle_ping_all()); - } - Commands::WatchCpu(opts) => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_watch_cpu(opts)).unwrap(); - } - Commands::Config(opts) => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_config(opts)).unwrap(); - } - Commands::Alerts => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_alerts()).unwrap(); - } - Commands::KillAgent(opts) => handle_kill_agent(opts).unwrap(), - Commands::UpdateAgent(opts) => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_update_agent(opts)).unwrap(); - } - Commands::Version(opts) => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_version(opts)).unwrap(); - } - Commands::SyncAgents(opts) => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(handle_sync_agents(opts)).unwrap(); - } - Commands::Run(opts) => { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(commands::run::handle_run(opts)); + Commands::Logs(opts) => handle_logs(opts).await, + Commands::Daemon => handle_daemon().await, + Commands::Monitor => handle_monitor().await.unwrap(), + Commands::PingAll => ping_all::handle_ping_all().await, + Commands::WatchCpu(opts) => handle_watch_cpu(opts).await.unwrap(), + Commands::Config(opts) => handle_config(opts).await.unwrap(), + Commands::Alerts => handle_alerts().await.unwrap(), + Commands::Version(opts) => handle_version(opts).await.unwrap(), + Commands::Run(opts) => commands::run::handle_run(opts).await, + Commands::CheckDb(opts) => handle_check_db(opts).await.unwrap(), + Commands::Upload(opts) => { + if let Err(e) = handle_upload(opts).await { + eprintln!("Upload failed: {}", e); + } } } } diff --git a/eclipta-cli/src/utils/db.rs b/eclipta-cli/src/utils/db.rs new file mode 100644 index 0000000..eed1f03 --- /dev/null +++ b/eclipta-cli/src/utils/db.rs @@ -0,0 +1,39 @@ +use sqlx::{PgPool, Pool, Postgres}; +use dotenvy::dotenv; +use std::env; +use crate::utils::logger::success; + + +pub type DbPool = Pool; + +pub async fn init_db() -> Result { + dotenv().ok(); + let db_url = env::var("DATABASE_URL") + .expect("DATABASE_URL must be set in .env"); + + let pool = PgPool::connect(&db_url).await?; + + run_migrations(&pool).await?; + Ok(pool) +} + +async fn run_migrations(pool: &Pool) -> Result<(), sqlx::Error> { + sqlx::query( + r#" + CREATE TABLE IF NOT EXISTS ebpf_programs ( + id SERIAL PRIMARY KEY, + title TEXT NOT NULL, + description TEXT, + version TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'deactive', + path TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + "# + ) + .execute(pool) + .await?; + + success("Database migration successful!"); + Ok(()) +} \ No newline at end of file diff --git a/eclipta-cli/src/utils/logger.rs b/eclipta-cli/src/utils/logger.rs index 073c145..327c101 100644 --- a/eclipta-cli/src/utils/logger.rs +++ b/eclipta-cli/src/utils/logger.rs @@ -1,15 +1,17 @@ -pub fn success(msg: &str) { - println!("\x1b[1;32m✔\x1b[0m {}", msg); -} +use colored::*; pub fn info(msg: &str) { - println!("\x1b[1;34mℹ\x1b[0m {}", msg); + println!("{} {}", "[INFO]".blue().bold(), msg); +} + +pub fn warn(msg: &str) { + println!("{} {}", "[WARN]".yellow().bold(), msg); } -// pub fn warn(msg: &str) { -// println!("\x1b[1;33m⚠\x1b[0m {}", msg); -// } +pub fn success(msg: &str) { + println!("{} {}", "[OK]".green().bold(), msg); +} pub fn error(msg: &str) { - eprintln!("\x1b[1;31m✖\x1b[0m {}", msg); + eprintln!("{} {}", "[ERROR]".red().bold(), msg); } diff --git a/eclipta-cli/src/utils/mod.rs b/eclipta-cli/src/utils/mod.rs index 7204c8c..cb2cc50 100644 --- a/eclipta-cli/src/utils/mod.rs +++ b/eclipta-cli/src/utils/mod.rs @@ -1 +1,4 @@ -pub mod logger; \ No newline at end of file +pub mod logger; +pub mod paths; +pub mod state; +pub mod db; diff --git a/eclipta-cli/src/utils/paths.rs b/eclipta-cli/src/utils/paths.rs new file mode 100644 index 0000000..78d4876 --- /dev/null +++ b/eclipta-cli/src/utils/paths.rs @@ -0,0 +1,42 @@ +use std::path::{PathBuf}; +use std::env; + +pub fn default_bin_object() -> PathBuf { + if let Ok(custom) = env::var("ECLIPTA_BIN") { + let p = PathBuf::from(custom); + if p.exists() { return p; } + } + if let Ok(home) = env::var("ECLIPTA_HOME") { + let p = PathBuf::from(home).join("bin").join("ebpf.so"); + if p.exists() { return p; } + } + let cwd = env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + let p = cwd.join("bin").join("ebpf.so"); + if p.exists() { return p; } + + // Fallback to executable dir/../bin/ebpf.so + if let Ok(exe) = env::current_exe() { + if let Some(parent) = exe.parent() { + let p = parent.join("..").join("bin").join("ebpf.so"); + if p.exists() { return p; } + } + } + + // Last resort: return cwd/bin/ebpf.so even if it doesn't exist + cwd.join("bin").join("ebpf.so") +} + +pub fn default_pin_prefix() -> PathBuf { + if let Ok(p) = env::var("ECLIPTA_PIN_PATH") { + return PathBuf::from(p); + } + PathBuf::from("/sys/fs/bpf/eclipta") +} + +pub fn default_state_path() -> PathBuf { + if let Ok(p) = env::var("ECLIPTA_STATE") { return PathBuf::from(p); } + if let Some(dir) = dirs::data_local_dir() { + return dir.join("eclipta").join("state.json"); + } + PathBuf::from(".eclipta_state.json") +} \ No newline at end of file diff --git a/eclipta-cli/src/utils/state.rs b/eclipta-cli/src/utils/state.rs new file mode 100644 index 0000000..c669756 --- /dev/null +++ b/eclipta-cli/src/utils/state.rs @@ -0,0 +1,36 @@ +use serde::{Serialize, Deserialize}; +use std::{fs, path::PathBuf}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct AttachmentRecord { + pub name: String, + pub kind: String, + pub trace_category: Option, + pub trace_name: Option, + pub pinned_prog: Option, + pub pinned_maps: Vec, + pub pid: u32, + pub created_at: i64, +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct State { + pub attachments: Vec, +} + +pub fn load_state(path: &PathBuf) -> State { + if let Ok(bytes) = fs::read(path) { + if let Ok(s) = serde_json::from_slice::(&bytes) { + return s; + } + } + State::default() +} + +pub fn save_state(path: &PathBuf, mut st: State) -> std::io::Result<()> { + if let Some(dir) = path.parent() { fs::create_dir_all(dir)?; } + // Dedup by (name, kind, pinned_prog) + st.attachments.sort_by(|a,b| a.name.cmp(&b.name)); + let bytes = serde_json::to_vec_pretty(&st).unwrap(); + fs::write(path, bytes) +} \ No newline at end of file