diff --git a/.gitignore b/.gitignore index e34f02a..d5783a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,43 +1,96 @@ -# === Rust build & cargo files === +# Rust build artifacts /target/ -**/target/ +**/*.rs.bk +Cargo.lock -# === IDE/editor folders === -.idea/ +# IDE and editor files .vscode/ +.idea/ *.swp *.swo +*~ -# === OS generated files === +# OS generated files .DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db Thumbs.db -# === Logs, temp, runtime === +# Logs *.log +logs/ +/var/log/eclipta/ + +# Runtime data /run/eclipta/ -*.json +/var/lib/eclipta/ +/tmp/eclipta/ + +# Configuration files with sensitive data +config.yaml +*.key +*.pem +*.crt +*.p12 + +# eBPF compiled objects (keep source, ignore compiled) +*.o +*.so +*.skel.h + +# Keep sample eBPF objects for testing +!bin/simple_trace.o +!bin/simple_xdp.o + +# Database files +*.db +*.sqlite +*.sqlite3 -# === Byproducts === +# Backup files *.bak +*.backup *.old -# === Environment files === -.env -.env.* +# Temporary files +*.tmp +*.temp + +# Coverage reports +tarpaulin-report.html +lcov.info + +# Profiling data +*.prof +perf.data* -# === Rust/Cargo specific === -Cargo.lock # optional: keep it if you're building a binary (e.g., CLI), not a library -/.cargo/ +# Documentation build +/book/ +/docs/_build/ + +# Package files +*.tar.gz +*.zip +*.deb +*.rpm + +# Local development +.env +.env.local +.env.*.local -# === Coverage/test tools === -htmlcov/ +# Test artifacts +test-results/ coverage/ -*.lcov -# === Debug/profiler === -*.profraw -*.profdata -flamegraph.svg +# eBPF development +vmlinux.h +*.ll +*.bc -/tests/* -/bin/* \ No newline at end of file +# System files +/var/cache/eclipta/ +/var/run/eclipta.pid \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..da3dd78 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog + +## [0.1.0] - 2025-01-27 + +### Added +- Complete project restructure for open source release +- Comprehensive documentation in `eclipta.yaml` +- Sample eBPF programs for testing +- Proper `.gitignore` configuration +- MIT License +- Updated installation script +- Clean project structure + +### Changed +- Removed unnecessary directories (`bin/`, `ebpf-demo/`, `tests/`, `target/`) +- Updated `Cargo.toml` workspace configuration +- Streamlined README.md +- Reorganized project layout + +### Removed +- Old binary files and demo programs +- Unnecessary test files +- Build artifacts and temporary files + +### Project Structure +``` +eclipta/ +├── eclipta-cli/ # Main CLI application +├── examples/ebpf/ # Sample eBPF programs +├── bin/ # Compiled eBPF programs for testing +├── eclipta.yaml # Comprehensive documentation +├── README.md # Project overview +├── LICENSE # MIT License +├── install.sh # Installation script +└── .gitignore # Git ignore rules +``` + +### Sample eBPF Programs +- `simple_trace.c` - Basic tracepoint program +- `simple_xdp.c` - Basic XDP program +- Compiled versions available in `bin/` directory + +### Documentation +- Complete command reference in `eclipta.yaml` +- Installation instructions +- Usage examples +- Development guidelines +- Troubleshooting guide diff --git a/Cargo.lock b/Cargo.lock index d927a08..f426006 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,22 +128,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "aya" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758d57288601ecc9d149e3413a5f23d6b72c0373febc97044d4f4aa149033b5e" -dependencies = [ - "bitflags 1.3.2", - "bytes", - "lazy_static", - "libc", - "log", - "object 0.28.4", - "parking_lot", - "thiserror", -] - [[package]] name = "aya" version = "0.13.1" @@ -638,7 +622,7 @@ name = "eclipta-cli" version = "1.0.0" dependencies = [ "anyhow", - "aya 0.13.1", + "aya", "byteorder", "bytes", "chrono", @@ -1377,25 +1361,6 @@ dependencies = [ "libm", ] -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.28.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" -dependencies = [ - "memchr", -] - [[package]] name = "object" version = "0.32.2" @@ -2312,7 +2277,6 @@ dependencies = [ "bytes", "libc", "mio 1.0.4", - "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -2513,16 +2477,6 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" -[[package]] -name = "user" -version = "0.1.0" -dependencies = [ - "anyhow", - "aya 0.11.0", - "num_cpus", - "tokio", -] - [[package]] name = "utf8_iter" version = "1.0.4" diff --git a/Cargo.toml b/Cargo.toml index a18ab6d..183e450 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [workspace] members = [ - "eclipta-cli", - "ebpf-demo/user" + "eclipta-cli" ] resolver = "2" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f30b677 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Mahesh Bhatiya + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 84ea1a8..95f80ae 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,182 @@ -# eclipta CLI — Self-Hosted Observability Platform +# Eclipta CLI -**eclipta** is a lightweight, modular CLI tool for managing, monitoring, and observing Linux agents across your infrastructure. Built in Rust, eclipta enables DevOps, sysadmins, and SREs to inspect system health, manage agent lifecycles, and capture real-time performance metrics — entirely self-hosted. +A lightweight, modular CLI tool for managing and monitoring eBPF programs on Linux systems. Built in Rust for performance and reliability. ## Features -- Agent lifecycle control (`load`, `unload`, `restart`, `kill`, `update`) -- Live system metrics (`cpu`, `memory`, `disk`, `network`, `processes`) -- Dynamic agent discovery (`/run/eclipta/*.json`) -- Snapshot syncing to `/etc/eclipta/agents/snapshot.json` -- Configuration management with safe JSON storage -- Realtime monitoring terminal UI (`monitor`, `live`) -- Alert handling and health summaries -- Zero external dependencies — Rust-native +- **eBPF Program Management**: Load, unload, and monitor eBPF programs +- **System Monitoring**: Real-time system metrics and process monitoring +- **Interactive TUI**: Terminal-based user interface for monitoring +- **Configuration Management**: Flexible configuration system +- **Network Monitoring**: Network interface and traffic monitoring +- **Database Integration**: SQLite-based data storage and management -## Installation +## Quick Start + +### Prerequisites + +- Rust 1.70+ +- Linux kernel 4.18+ with eBPF support +- libbpf development libraries +- clang and llvm for eBPF compilation + +### Installation -### Build from source: ```bash +# Clone the repository git clone https://github.com/stackroost/eclipta.git -cd eclipta/cli +cd eclipta + +# Build from source cargo build --release + +# Install (requires sudo for eBPF operations) sudo cp target/release/eclipta /usr/local/bin/eclipta ``` -## Usage +### Basic Usage ```bash -eclipta [options] -``` +# Show welcome message and help +eclipta welcome -### Common Commands: - -| Command | Description | -|-------------------|----------------------------------------------| -| `load` | Load/start an agent binary | -| `unload` | Gracefully unload agent | -| `restart-agent` | Restart agent process | -| `kill-agent` | Forcefully kill agent | -| `update-agent` | Replace agent binary with updated version | -| `monitor` | Interactive terminal UI of all agents | -| `live` | Stream real-time agent logs + stats | -| `logs` | View system or agent logs | -| `agent-logs` | Tail logs from a specific agent | -| `watch-cpu` | Monitor CPU usage of an agent | -| `alerts` | List all agents currently in alert state | -| `agents` | Show all detected agents | -| `inspect-agent` | Print detailed stats of a specific agent | -| `inspect` | Inspect eclipta CLI environment | -| `ping-all` | Check if all agents are alive/responding | -| `sync-agents` | Scan `/run/eclipta` and sync active agents | -| `config` | Get/set/list CLI configuration options | -| `version` | Show current CLI version | -| `welcome` | Show welcome message and setup hint | -| `status` | Show CLI runtime status | - -## Agent Snapshot Format - -Synced to: `/etc/eclipta/agents/snapshot.json` - -```json -[ - { - "id": "agent-001", - "hostname": "host1", - "version": "0.2.1", - "cpu_load": [0.39, 1.09, 1.38], - "mem_used_mb": 4865, - "disk_used_mb": 79177, - "net_rx_kb": 247187, - "alert": false, - "last_seen": "2025-07-01T16:38:29Z" - } -] +# Check system status +eclipta status + +# Load a sample eBPF program +eclipta load --program bin/simple_trace.o --name my-tracer + +# List loaded programs +eclipta list + +# Start interactive monitoring +eclipta monitor + +# Unload program +eclipta unload --program my-tracer ``` -## Development +## Sample eBPF Programs -To run locally: +The project includes sample eBPF programs for testing: + +- `bin/simple_trace.o` - Basic tracepoint program +- `bin/simple_xdp.o` - Basic XDP program + +To build your own eBPF programs: ```bash -cargo run -- monitor +cd examples/ebpf +make ``` -## Roadmap +## Commands -- [ ] `install-agent` from GitHub Releases -- [ ] Remote API mode (multi-node) -- [ ] TUI dashboard for snapshot view -- [ ] Plugin architecture for collectors +### System Commands +- `welcome` - Show welcome message and setup help +- `status` - Show CLI runtime status +- `monitor` - Interactive terminal UI for monitoring +- `logs` - View system or agent logs +- `watch-cpu` - Monitor CPU usage -## License +### eBPF Commands +- `load` - Load eBPF program +- `unload` - Unload eBPF program +- `list` - List loaded programs +- `inspect` - Inspect program details +- `upload` - Upload program to storage +- `remove` - Remove program from storage -MIT © 2025 Mahesh Bhatiya +### Network Commands +- `ping-all` - Check agent connectivity +- `alerts` - List alerting agents + +### Configuration Commands +- `config` - Manage configuration +- `daemon` - Start daemon process + +### Database Commands +- `check-db` - Check database status +- `migrate` - Run database migrations + +## Configuration + +Configuration is stored in `/etc/eclipta/config.yaml`. Key settings: + +```yaml +log_level: "info" +daemon_enabled: false +auto_start_programs: false +monitoring_interval: 5 +``` ## Project Structure ``` -/cli/ - src/ - commands/ - utils/ - main.rs # CLI parser & dispatch -/run/eclipta/ # Live agent metrics (.json) -/etc/eclipta/ # Persistent config + snapshot.json +eclipta/ +├── eclipta-cli/ # Main CLI application +│ ├── src/ +│ │ ├── commands/ # Command implementations +│ │ ├── utils/ # Utility functions +│ │ └── main.rs # Entry point +│ └── Cargo.toml +├── examples/ebpf/ # Sample eBPF programs +│ ├── simple_trace.c +│ ├── simple_xdp.c +│ └── Makefile +├── bin/ # Compiled eBPF programs +│ ├── simple_trace.o +│ └── simple_xdp.o +├── eclipta.yaml # Comprehensive documentation +└── README.md ``` +## Development + +### Building + +```bash +# Development build +cargo build + +# Release build +cargo build --release + +# Run tests +cargo test + +# Format code +cargo fmt + +# Lint code +cargo clippy +``` + +### Adding New Commands + +1. Create command module in `eclipta-cli/src/commands/` +2. Add command to `main.rs` command enum +3. Implement command handler +4. Update documentation in `eclipta.yaml` + +## Documentation + +For comprehensive documentation including all commands, options, and examples, see `eclipta.yaml`. + ## Contributing -PRs welcome! If you're building tooling for observability or Linux system automation, open an issue or suggest improvements. +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Add tests if applicable +5. Submit a pull request + +## License + +MIT © 2025 Mahesh Bhatiya + +## Support + +- Issues: [GitHub Issues](https://github.com/stackroost/eclipta/issues) +- Documentation: [GitHub Wiki](https://github.com/stackroost/eclipta/wiki) +- Email: support@eclipta.dev \ No newline at end of file diff --git a/bin/simple_trace.o b/bin/simple_trace.o new file mode 100644 index 0000000..c8f5a3d Binary files /dev/null and b/bin/simple_trace.o differ diff --git a/bin/simple_xdp.o b/bin/simple_xdp.o new file mode 100644 index 0000000..7608715 Binary files /dev/null and b/bin/simple_xdp.o differ diff --git a/ebpf-demo/Cargo.lock b/ebpf-demo/Cargo.lock deleted file mode 100644 index 227823c..0000000 --- a/ebpf-demo/Cargo.lock +++ /dev/null @@ -1,119 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aya-ebpf" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8dbaf5409a1a0982e5c9bdc0f499a55fe5ead39fe9c846012053faf0d404f73" -dependencies = [ - "aya-ebpf-bindings", - "aya-ebpf-cty", - "aya-ebpf-macros", - "rustversion", -] - -[[package]] -name = "aya-ebpf-bindings" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783dc1a82a3d71d83286165381dcc1b1d41643f4b110733d135547527c000a9a" -dependencies = [ - "aya-ebpf-cty", -] - -[[package]] -name = "aya-ebpf-cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cce099aaf3abb89f9a1f8594ffe07fa53738ebc2882fac624d10d9ba31a1b10" - -[[package]] -name = "aya-ebpf-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f47f7b4a75eb5f1d7ba0fb5628d247b1cf20388658899177875dabdda66865" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "simple-ebpf" -version = "0.1.0" -dependencies = [ - "aya-ebpf", -] - -[[package]] -name = "syn" -version = "2.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" diff --git a/ebpf-demo/Cargo.toml b/ebpf-demo/Cargo.toml deleted file mode 100644 index 82e0bfb..0000000 --- a/ebpf-demo/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[workspace] -members = ["user", - "xdp_drop" -] -resolver = "2" diff --git a/ebpf-demo/user/Cargo.toml b/ebpf-demo/user/Cargo.toml deleted file mode 100644 index 52ad8dc..0000000 --- a/ebpf-demo/user/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "user" -version = "0.1.0" -edition = "2024" - -[dependencies] -aya = "0.11" -anyhow = "1" -tokio = { version = "1", features = ["full"] } -num_cpus = "1" \ No newline at end of file diff --git a/ebpf-demo/user/src/main.rs b/ebpf-demo/user/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/ebpf-demo/user/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/ebpf-demo/xdp_drop/.cargo/config.toml b/ebpf-demo/xdp_drop/.cargo/config.toml deleted file mode 100644 index c3ccdee..0000000 --- a/ebpf-demo/xdp_drop/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[build] -target-dir = "target" - -[target.bpfel-unknown-none] -linker = "bpf-linker" \ No newline at end of file diff --git a/ebpf-demo/xdp_drop/Cargo.toml b/ebpf-demo/xdp_drop/Cargo.toml deleted file mode 100644 index 45e47da..0000000 --- a/ebpf-demo/xdp_drop/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "simple-ebpf" -version = "0.1.0" -edition = "2021" - -[dependencies] -aya-ebpf = "0.1" - -[lib] -name = "simple_ebpf" -path = "src/lib.rs" -crate-type = ["cdylib"] diff --git a/ebpf-demo/xdp_drop/src/lib.rs b/ebpf-demo/xdp_drop/src/lib.rs deleted file mode 100644 index f91c453..0000000 --- a/ebpf-demo/xdp_drop/src/lib.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![no_std] -#![no_main] - -use aya_ebpf::{macros::xdp, programs::XdpContext}; -use aya_ebpf::bindings::xdp_action; - -#[xdp(name = "xdp_drop")] -pub fn xdp_drop(_ctx: XdpContext) -> u32 { - xdp_action::XDP_DROP -} - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop {} -} diff --git a/ebpf-demo/xdp_drop/src/main.rs b/ebpf-demo/xdp_drop/src/main.rs deleted file mode 100644 index fb47aa8..0000000 --- a/ebpf-demo/xdp_drop/src/main.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_std] -#![no_main] - -use aya_ebpf::{ - bindings::xdp_action, - macros::xdp, - programs::XdpContext, -}; - -#[xdp] -pub fn simple_ebpf_program(ctx: XdpContext) -> u32 { - match try_simple_ebpf_program(ctx) { - Ok(ret) => ret, - Err(_) => xdp_action::XDP_ABORTED, - } -} - -fn try_simple_ebpf_program(_ctx: XdpContext) -> Result { - // Simple success - no logging to avoid macro issues - Ok(xdp_action::XDP_PASS) // Return 2 (XDP_PASS - allows packet through) -} - -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - unsafe { core::hint::unreachable_unchecked() } -} \ No newline at end of file diff --git a/ebpf-demo/xdp_pass_kern.c b/ebpf-demo/xdp_pass_kern.c deleted file mode 100644 index 6446b16..0000000 --- a/ebpf-demo/xdp_pass_kern.c +++ /dev/null @@ -1,12 +0,0 @@ -// xdp_pass_kern.c -#include -#include - -// XDP program: just passes all packets without dropping -SEC("xdp") -int xdp_pass(struct xdp_md *ctx) { - return XDP_PASS; // let packets continue -} - -// Required license declaration -char LICENSE[] SEC("license") = "GPL"; diff --git a/ebpf-demo/xdp_pass_kern.o b/ebpf-demo/xdp_pass_kern.o deleted file mode 100644 index 0a95580..0000000 Binary files a/ebpf-demo/xdp_pass_kern.o and /dev/null differ diff --git a/eclipta-cli/src/commands/ebpf/load.rs b/eclipta-cli/src/commands/ebpf/load.rs index d77b1c6..e9366b1 100644 --- a/eclipta-cli/src/commands/ebpf/load.rs +++ b/eclipta-cli/src/commands/ebpf/load.rs @@ -49,6 +49,8 @@ pub struct ProgramRequirements { pub requires_interface: bool, pub requires_socket_fd: bool, pub program_type: String, + pub tracepoint_category: Option, + pub tracepoint_name: Option, } pub async fn handle_load(opts: LoadOptions) -> Result<()> { @@ -137,6 +139,8 @@ pub fn validate_ebpf_file(path: &PathBuf) -> Result { let mut requires_interface = false; let mut requires_socket_fd = false; let mut program_type = String::new(); + let mut tracepoint_category: Option = None; + let mut tracepoint_name: Option = None; for section in obj.sections() { println!("Section: {}", section.name().unwrap()); @@ -144,6 +148,13 @@ pub fn validate_ebpf_file(path: &PathBuf) -> Result { if name.starts_with("tracepoint/") { found_sections.insert("Tracepoint".to_string()); program_type = "Tracepoint".to_string(); + + // Extract category and name from tracepoint section + let parts: Vec<&str> = name.split('/').collect(); + if parts.len() >= 3 { + tracepoint_category = Some(parts[1].to_string()); + tracepoint_name = Some(parts[2].to_string()); + } } else { match name { XDP_SECTION | XDP_DROP_SECTION => { @@ -196,6 +207,8 @@ pub fn validate_ebpf_file(path: &PathBuf) -> Result { requires_interface, requires_socket_fd, program_type, + tracepoint_category, + tracepoint_name, }) } @@ -318,6 +331,30 @@ async fn attach_program_to_kernel( Err(anyhow!("No SocketFilter program found in eBPF object")) } + "Tracepoint" => { + let category = requirements.tracepoint_category.as_ref() + .ok_or_else(|| anyhow!("Tracepoint category not found in ELF sections"))?; + let name = requirements.tracepoint_name.as_ref() + .ok_or_else(|| anyhow!("Tracepoint name not found in ELF sections"))?; + + let mut ebpf = Ebpf::load_file(path) + .context("Failed to load eBPF for Tracepoint attachment")?; + + for (prog_name, program) in ebpf.programs_mut() { + if let Program::TracePoint(tp_prog) = program { + tp_prog.load() + .context("Failed to load Tracepoint program")?; + + tp_prog.attach(category, name) + .context(format!("Failed to attach Tracepoint program to '{}:{}'", category, name))?; + + println!("Tracepoint program '{}' attached to '{}:{}'", prog_name, category, name); + return Ok(format!("Tracepoint program attached to {}:{}", category, name)); + } + } + Err(anyhow!("No Tracepoint program found in eBPF object")) + } + _ => { println!("Program type '{}' not yet implemented for kernel attachment", requirements.program_type); Ok(format!("Program type {} loaded but not attached", requirements.program_type)) @@ -378,6 +415,47 @@ async fn verify_kernel_attachment(requirements: &ProgramRequirements, opts: &Loa println!("SocketFilter verification requires manual inspection of socket state"); } + "Tracepoint" => { + let category = requirements.tracepoint_category.as_ref() + .ok_or_else(|| anyhow!("Tracepoint category not found for verification"))?; + let name = requirements.tracepoint_name.as_ref() + .ok_or_else(|| anyhow!("Tracepoint name not found for verification"))?; + + // Use bpftool to verify tracepoint attachment + let output = Command::new("bpftool") + .args(["link", "list"]) + .output() + .await + .context("Failed to execute bpftool command")?; + + let output_str = String::from_utf8_lossy(&output.stdout); + + // Look for tracepoint programs in the output + let mut found = false; + for line in output_str.lines() { + if line.contains("tracepoint") { + println!(" Found tracepoint link: {}", line.trim()); + // Check if this line contains our specific tracepoint + if line.contains(category) && line.contains(name) { + found = true; + break; + } + } + } + + if found { + println!("Tracepoint program verified as attached to '{}:{}'", category, name); + } else { + println!("Tracepoint program not found attached to '{}:{}'", category, name); + println!("Available tracepoint links:"); + for line in output_str.lines() { + if line.contains("tracepoint") { + println!(" {}", line.trim()); + } + } + } + } + _ => { println!("Verification not implemented for program type '{}'", requirements.program_type); } @@ -404,6 +482,13 @@ fn print_program_summary( println!(" Socket FD: {}", socket_fd); } + if let Some(ref category) = requirements.tracepoint_category { + println!(" Tracepoint Category: {}", category); + } + if let Some(ref name) = requirements.tracepoint_name { + println!(" Tracepoint Name: {}", name); + } + println!(" Interface Required: {}", requirements.requires_interface); println!(" Socket FD Required: {}", requirements.requires_socket_fd); println!(" Kernel Attachment: {}", attach_result); @@ -477,6 +562,11 @@ pub fn get_program_requirements(sections: &HashSet) -> ProgramRequiremen requires_socket_fd = true; program_type = "SocketFilter".to_string(); } + "Tracepoint" => { + program_type = "Tracepoint".to_string(); + // Note: For this function, we can't extract category/name from section names + // as we only have the processed section names, not the raw ELF section names + } _ => {} } } @@ -486,6 +576,8 @@ pub fn get_program_requirements(sections: &HashSet) -> ProgramRequiremen requires_interface, requires_socket_fd, program_type, + tracepoint_category: None, + tracepoint_name: None, } } diff --git a/eclipta.yaml b/eclipta.yaml new file mode 100644 index 0000000..bbd49ae --- /dev/null +++ b/eclipta.yaml @@ -0,0 +1,353 @@ +# Eclipta CLI - Self-Hosted Observability Platform +# Comprehensive project documentation and configuration + +project: + name: "eclipta" + version: "0.1.0" + description: "Lightweight, modular CLI tool for managing, monitoring, and observing Linux agents" + author: "Mahesh Bhatiya" + license: "MIT" + repository: "https://github.com/stackroost/eclipta" + homepage: "https://github.com/stackroost/eclipta" + +# Build and Development Configuration +build: + rust_version: "1.70+" + target: "x86_64-unknown-linux-gnu" + features: + - "default" + - "release" + profile: + release: + strip: true + opt-level: "z" + lto: true + codegen-units: 1 + +# Dependencies +dependencies: + runtime: + - "libbpf" + - "clang" + - "llvm" + development: + - "cargo" + - "rustc" + - "clippy" + - "rustfmt" + +# CLI Commands Documentation +commands: + system: + welcome: + description: "Show welcome message and setup hint" + usage: "eclipta welcome" + examples: + - "eclipta welcome" + + status: + description: "Show CLI runtime status" + usage: "eclipta status [options]" + options: + - "--verbose, -v: Show detailed status information" + - "--json: Output in JSON format" + examples: + - "eclipta status" + - "eclipta status --verbose" + - "eclipta status --json" + + monitor: + description: "Interactive terminal UI of all agents" + usage: "eclipta monitor" + examples: + - "eclipta monitor" + + logs: + description: "View system or agent logs" + usage: "eclipta logs [options]" + options: + - "--agent, -a: Show logs for specific agent" + - "--follow, -f: Follow log output" + - "--lines, -n: Number of lines to show" + examples: + - "eclipta logs" + - "eclipta logs --agent my-agent" + - "eclipta logs --follow --lines 100" + + watch_cpu: + description: "Monitor CPU usage of an agent" + usage: "eclipta watch-cpu [options]" + options: + - "--agent, -a: Agent to monitor (required)" + - "--interval, -i: Update interval in seconds" + examples: + - "eclipta watch-cpu --agent my-agent" + - "eclipta watch-cpu --agent my-agent --interval 2" + + ebpf: + load: + description: "Load/start an eBPF program" + usage: "eclipta load [options]" + options: + - "--program, -p: Path to eBPF program file (required)" + - "--name, -n: Name for the loaded program" + - "--interface, -i: Network interface (for XDP programs)" + examples: + - "eclipta load --program bin/simple_trace.o" + - "eclipta load --program bin/simple_xdp.o --interface eth0" + - "eclipta load --program bin/simple_trace.o --name my-tracer" + + unload: + description: "Gracefully unload eBPF program" + usage: "eclipta unload [options]" + options: + - "--program, -p: Program name or ID to unload (required)" + - "--force, -f: Force unload without graceful shutdown" + examples: + - "eclipta unload --program my-tracer" + - "eclipta unload --program 12345 --force" + + list: + description: "List all loaded eBPF programs" + usage: "eclipta list" + examples: + - "eclipta list" + + inspect: + description: "Inspect eBPF program details" + usage: "eclipta inspect [options]" + options: + - "--program, -p: Program name or ID to inspect (required)" + - "--maps: Show program maps" + - "--functions: Show program functions" + examples: + - "eclipta inspect --program my-tracer" + - "eclipta inspect --program my-tracer --maps" + + upload: + description: "Upload eBPF program to remote storage" + usage: "eclipta upload [options]" + options: + - "--program, -p: Path to eBPF program file (required)" + - "--name, -n: Name for the uploaded program" + - "--description, -d: Description of the program" + examples: + - "eclipta upload --program bin/simple_trace.o --name trace-open" + + remove: + description: "Remove eBPF program from storage" + usage: "eclipta remove [options]" + options: + - "--program, -p: Program name or ID to remove (required)" + - "--force, -f: Force removal without confirmation" + examples: + - "eclipta remove --program trace-open" + - "eclipta remove --program 12345 --force" + + network: + ping_all: + description: "Check if all agents are alive/responding" + usage: "eclipta ping-all" + examples: + - "eclipta ping-all" + + alerts: + description: "List all agents currently in alert state" + usage: "eclipta alerts" + examples: + - "eclipta alerts" + + config: + config: + description: "Get/set/list CLI configuration options" + usage: "eclipta config [options]" + options: + - "--get, -g: Get configuration value" + - "--set, -s: Set configuration value" + - "--list, -l: List all configuration options" + - "--key, -k: Configuration key" + - "--value, -v: Configuration value" + examples: + - "eclipta config --list" + - "eclipta config --get --key log_level" + - "eclipta config --set --key log_level --value debug" + + daemon: + description: "Start eclipta daemon process" + usage: "eclipta daemon" + examples: + - "eclipta daemon" + + store: + check_db: + description: "Check database connectivity and status" + usage: "eclipta check-db [options]" + options: + - "--verbose, -v: Show detailed database information" + examples: + - "eclipta check-db" + - "eclipta check-db --verbose" + + migrate: + description: "Run database migrations" + usage: "eclipta migrate [options]" + options: + - "--up: Run pending migrations" + - "--down: Rollback last migration" + - "--status: Show migration status" + examples: + - "eclipta migrate --up" + - "eclipta migrate --status" + + other: + version: + description: "Show current CLI version" + usage: "eclipta version [options]" + options: + - "--verbose, -v: Show detailed version information" + examples: + - "eclipta version" + - "eclipta version --verbose" + + run: + description: "Run a command or script" + usage: "eclipta run [options]" + options: + - "--command, -c: Command to run (required)" + - "--timeout, -t: Command timeout in seconds" + examples: + - "eclipta run --command 'ls -la'" + - "eclipta run --command 'ping google.com' --timeout 10" + +# File Structure +file_structure: + root: + - "Cargo.toml: Workspace configuration" + - "README.md: Project documentation" + - "eclipta.yaml: This configuration file" + - "install.sh: Installation script" + + eclipta_cli: + - "src/main.rs: CLI entry point and command dispatch" + - "src/commands/: Command implementations" + - "system/: System monitoring commands" + - "ebpf/: eBPF program management commands" + - "network/: Network-related commands" + - "config/: Configuration management commands" + - "store/: Database and storage commands" + - "src/utils/: Utility functions" + - "src/db/: Database management" + + examples: + - "ebpf/: Sample eBPF programs" + - "simple_trace.c: Basic tracepoint program" + - "simple_xdp.c: Basic XDP program" + - "Makefile: Build configuration" + + bin: + - "simple_trace.o: Compiled tracepoint program" + - "simple_xdp.o: Compiled XDP program" + +# Installation Instructions +installation: + prerequisites: + - "Rust 1.70 or later" + - "libbpf development libraries" + - "clang and llvm for eBPF compilation" + - "Linux kernel 4.18+ with eBPF support" + + build_from_source: + - "git clone https://github.com/stackroost/eclipta.git" + - "cd eclipta" + - "cargo build --release" + - "sudo cp target/release/eclipta /usr/local/bin/eclipta" + + install_dependencies: + ubuntu_debian: + - "sudo apt update" + - "sudo apt install libbpf-dev clang llvm" + arch_linux: + - "sudo pacman -S libbpf clang llvm" + fedora: + - "sudo dnf install libbpf-devel clang llvm" + +# Usage Examples +usage_examples: + basic_monitoring: + - "eclipta welcome" + - "eclipta status" + - "eclipta monitor" + + ebpf_program_management: + - "eclipta load --program bin/simple_trace.o --name my-tracer" + - "eclipta list" + - "eclipta inspect --program my-tracer" + - "eclipta unload --program my-tracer" + + system_monitoring: + - "eclipta logs --follow" + - "eclipta watch-cpu --agent my-agent" + - "eclipta alerts" + - "eclipta ping-all" + +# Configuration +configuration: + config_file: "/etc/eclipta/config.yaml" + data_directory: "/var/lib/eclipta" + log_directory: "/var/log/eclipta" + runtime_directory: "/run/eclipta" + + default_settings: + log_level: "info" + daemon_enabled: false + auto_start_programs: false + monitoring_interval: 5 + +# Troubleshooting +troubleshooting: + common_issues: + - "Permission denied: Run with sudo for eBPF operations" + - "eBPF program load failed: Check kernel version and eBPF support" + - "libbpf not found: Install libbpf development package" + - "Compilation errors: Ensure clang and llvm are installed" + + debugging: + - "Use --verbose flag for detailed output" + - "Check logs in /var/log/eclipta/" + - "Verify eBPF program with: eclipta inspect --program " + - "Test with simple programs first" + +# Contributing +contributing: + development_setup: + - "Fork the repository" + - "Clone your fork" + - "Create a feature branch" + - "Make your changes" + - "Add tests if applicable" + - "Submit a pull request" + + coding_standards: + - "Follow Rust formatting with rustfmt" + - "Run clippy for linting" + - "Add documentation for public APIs" + - "Write meaningful commit messages" + + testing: + - "Unit tests: cargo test" + - "Integration tests: cargo test --test integration" + - "eBPF programs: Test with sample programs in examples/" + +# License and Legal +license: + type: "MIT" + year: "2025" + holder: "Mahesh Bhatiya" + text: "See LICENSE file for full license text" + +# Support and Community +support: + issues: "https://github.com/stackroost/eclipta/issues" + discussions: "https://github.com/stackroost/eclipta/discussions" + documentation: "https://github.com/stackroost/eclipta/wiki" + email: "support@eclipta.dev" diff --git a/examples/ebpf/Makefile b/examples/ebpf/Makefile new file mode 100644 index 0000000..82a0a26 --- /dev/null +++ b/examples/ebpf/Makefile @@ -0,0 +1,33 @@ +# Makefile for eBPF examples +# Requires: clang, llvm, libbpf + +CLANG ?= clang +LLC ?= llc +ARCH ?= x86 + +# Common flags +CFLAGS := -O2 -g -Wall -Wno-unused-value -Wno-pointer-sign \ + -Wno-compare-distinct-pointer-types \ + -Werror -Wno-unused-function -Wno-unused-variable + +# Include paths +INCLUDES := -I/usr/include/bpf -I/usr/include/linux + +# Target files +TARGETS := simple_trace.o simple_xdp.o + +.PHONY: all clean + +all: $(TARGETS) + +%.o: %.c + $(CLANG) $(CFLAGS) $(INCLUDES) -target bpf -c $< -o $@ + +clean: + rm -f *.o + +install: all + mkdir -p ../../bin + cp *.o ../../bin/ + +.PHONY: install diff --git a/examples/ebpf/simple_trace.c b/examples/ebpf/simple_trace.c new file mode 100644 index 0000000..c3cbbe5 --- /dev/null +++ b/examples/ebpf/simple_trace.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +// Simple eBPF program for testing + +#include +#include + +// Simple program that always returns 0 +SEC("tracepoint/syscalls/sys_enter_open") +int trace_open(void *ctx) +{ + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/examples/ebpf/simple_xdp.c b/examples/ebpf/simple_xdp.c new file mode 100644 index 0000000..43549a5 --- /dev/null +++ b/examples/ebpf/simple_xdp.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +// Simple XDP eBPF program for testing + +#include +#include + +// Simple XDP program that passes all packets +SEC("xdp") +int xdp_pass(struct xdp_md *ctx) +{ + return XDP_PASS; +} + +char _license[] SEC("license") = "GPL"; diff --git a/install.sh b/install.sh index 2856d6e..34c74db 100755 --- a/install.sh +++ b/install.sh @@ -2,37 +2,91 @@ set -euo pipefail -echo "Building Eclipta..." - -# Step 1: Build Frontend (Vite) -echo "Building frontend..." -cd frontend -npm install -npm run build -cd .. - -# Step 2: Build Backend -echo "Building backend..." -cargo build --release --package backend - -# Step 3: Package -DIST_DIR="dist" -echo "Packaging into $DIST_DIR/" -rm -rf "$DIST_DIR" -mkdir -p "$DIST_DIR" - -cp target/release/backend "$DIST_DIR/eclipta-backend" -cp -r frontend/dist/* "$DIST_DIR/" - -# Step 4: Optional CLI build -if [ -f "eclipta-cli/Cargo.toml" ]; then - echo "🛠 Building CLI..." - cargo build --release --package eclipta-cli - cp target/release/eclipta-cli "$DIST_DIR/eclipta-cli" +echo "Installing Eclipta CLI..." + +# Check if Rust is installed +if ! command -v cargo &> /dev/null; then + echo "Error: Rust/Cargo is not installed. Please install Rust first." + echo "Visit: https://rustup.rs/" + exit 1 +fi + +# Check if required dependencies are available +echo "Checking dependencies..." + +# Check for libbpf +if ! pkg-config --exists libbpf; then + echo "Warning: libbpf not found. You may need to install libbpf development libraries." + echo "Ubuntu/Debian: sudo apt install libbpf-dev" + echo "Arch Linux: sudo pacman -S libbpf" + echo "Fedora: sudo dnf install libbpf-devel" +fi + +# Check for clang +if ! command -v clang &> /dev/null; then + echo "Warning: clang not found. You may need clang for eBPF compilation." + echo "Ubuntu/Debian: sudo apt install clang llvm" + echo "Arch Linux: sudo pacman -S clang llvm" + echo "Fedora: sudo dnf install clang llvm" +fi + +# Build the CLI +echo "Building Eclipta CLI..." +cargo build --release + +# Create installation directory +INSTALL_DIR="/usr/local/bin" +echo "Installing to $INSTALL_DIR..." + +# Copy binary (requires sudo) +if [ -w "$INSTALL_DIR" ]; then + cp target/release/eclipta-cli "$INSTALL_DIR/eclipta" + chmod +x "$INSTALL_DIR/eclipta" else - echo "CLI not found, skipping." + echo "Installing with sudo..." + sudo cp target/release/eclipta-cli "$INSTALL_DIR/eclipta" + sudo chmod +x "$INSTALL_DIR/eclipta" +fi + +# Create configuration directory +CONFIG_DIR="/etc/eclipta" +if [ ! -d "$CONFIG_DIR" ]; then + echo "Creating configuration directory..." + if [ -w "/etc" ]; then + mkdir -p "$CONFIG_DIR" + else + sudo mkdir -p "$CONFIG_DIR" + fi +fi + +# Create runtime directory +RUNTIME_DIR="/run/eclipta" +if [ ! -d "$RUNTIME_DIR" ]; then + echo "Creating runtime directory..." + if [ -w "/run" ]; then + mkdir -p "$RUNTIME_DIR" + else + sudo mkdir -p "$RUNTIME_DIR" + fi fi +# Build sample eBPF programs +echo "Building sample eBPF programs..." +cd examples/ebpf +make install +cd ../.. + +echo "" +echo " Installation complete!" +echo "" +echo "Usage:" +echo " eclipta welcome # Show welcome message" +echo " eclipta status # Check system status" +echo " eclipta load --help # Load eBPF program" +echo " eclipta monitor # Start monitoring" +echo "" +echo "Sample eBPF programs are available in bin/:" +echo " bin/simple_trace.o # Basic tracepoint program" +echo " bin/simple_xdp.o # Basic XDP program" echo "" -echo "Build complete!" -echo "Run backend with: ./dist/eclipta-backend" +echo "For full documentation, see eclipta.yaml"