Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 0 additions & 125 deletions eclipta-cli/src/commands/config/config.rs

This file was deleted.

73 changes: 0 additions & 73 deletions eclipta-cli/src/commands/config/daemon.rs

This file was deleted.

2 changes: 0 additions & 2 deletions eclipta-cli/src/commands/config/mod.rs

This file was deleted.

164 changes: 141 additions & 23 deletions eclipta-cli/src/commands/ebpf/inspect.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,171 @@
use aya::Ebpf;
use clap::Args;
use std::path::PathBuf;
use crate::utils::logger::{info, error};
use crate::utils::logger::success;
use crate::utils::db::ensure_db_ready;
use crate::db::programs::{get_program_by_id, get_program_by_title};
use serde_json;
use crate::utils::paths::default_bin_object;
use anyhow::{Result, anyhow};

#[derive(Args, Debug)]
pub struct InspectOptions {
/// Path to eBPF ELF (defaults to $ECLIPTA_BIN or ./bin/ebpf.so)
#[arg(short, long)]
/// Path to eBPF ELF file (alternative to --id/--title)
#[arg(short, long, conflicts_with_all = ["id", "title"])]
pub program: Option<PathBuf>,

/// Program ID from database (alternative to --program/--title)
#[arg(long, conflicts_with_all = ["program", "title"])]
pub id: Option<i32>,

/// Program title from database (alternative to --program/--id)
#[arg(long, conflicts_with_all = ["program", "id"])]
pub title: Option<String>,

/// Output in JSON format
#[arg(long)]
pub json: bool,

/// Show verbose technical details
#[arg(long)]
pub verbose: bool,
}

pub fn handle_inspect(opts: InspectOptions) {
let program_path = opts.program.unwrap_or_else(default_bin_object);
if !program_path.exists() {
error("Missing compiled eBPF program.");
return;
pub async fn handle_inspect(opts: InspectOptions) -> Result<()> {
if opts.program.is_none() && opts.id.is_none() && opts.title.is_none() {
return Err(anyhow!(
"Please specify a program to inspect using one of:\n\
--program <path> (for file path)\n\
--id <id> (for database ID)\n\
--title <title> (for database title)\n\
\n\
Use --help for more information."
));
}

let (program_path, program_metadata) = if let Some(id) = opts.id {
println!("Looking up program with ID {} in database...", id);
let pool = ensure_db_ready().await
.map_err(|e| anyhow!("Failed to connect to database: {}", e))?;

let program = get_program_by_id(&pool, id).await
.map_err(|e| anyhow!("Database query failed: {}", e))?
.ok_or_else(|| anyhow!("No program found with ID {}", id))?;

println!("Found program: '{}' (v{})", program.title, program.version);

let path = PathBuf::from(&program.path);
if !path.exists() {
return Err(anyhow!("Program file not found at: {}\nThe program may have been moved or deleted.", program.path));
}

(path, Some(program))
} else if let Some(ref title) = opts.title {
println!("Looking up program with title '{}' in database...", title);
let pool = ensure_db_ready().await
.map_err(|e| anyhow!("Failed to connect to database: {}", e))?;

let programs = get_program_by_title(&pool, title).await
.map_err(|e| anyhow!("Database query failed: {}", e))?;

match programs.len() {
1 => {
let program = programs[0].clone();
println!("Found program: '{}' (v{})", program.title, program.version);

let path = PathBuf::from(&program.path);
if !path.exists() {
return Err(anyhow!("Program file not found at: {}\nThe program may have been moved or deleted.", program.path));
}
(path, Some(program))
}
n if n > 1 => {
println!("Multiple programs found with title '{}':", title);
for (i, prog) in programs.iter().enumerate() {
println!(" {}. ID: {}, Version: {}, Status: {}", i + 1, prog.id, prog.version, prog.status);
}
return Err(anyhow!("Multiple programs found with title '{}'. Please use --id to specify which one to inspect.", title));
}
_ => {
return Err(anyhow!("No program found with title '{}'", title));
}
}
} else if let Some(program_path) = opts.program {
println!("Inspecting program file: {}", program_path.display());
if !program_path.exists() {
return Err(anyhow!("Program file not found at: {}", program_path.display()));
}
(program_path, None)
} else {
return Err(anyhow!("No program specified. Use --help for available options."));
};

println!("Parsing eBPF ELF file...");
let bpf = match Ebpf::load_file(&program_path) {
Ok(b) => b,
Ok(b) => {
println!("Successfully parsed ELF file");
b
}
Err(e) => {
error(&format!("Failed to parse ELF: {}", e));
return;
return Err(anyhow!("Failed to parse ELF file: {}\nMake sure the file is a valid compiled eBPF program.", e));
}
};

let programs: Vec<_> = bpf.programs().map(|(name, _)| name.to_string()).collect();
let maps: Vec<_> = bpf.maps().map(|(name, _)| name.to_string()).collect();
let mut output_data = serde_json::json!({
"elf_path": program_path.display().to_string(),
"programs": programs,
"maps": maps,
});
if let Some(ref metadata) = program_metadata {
output_data["metadata"] = serde_json::json!({
"id": metadata.id,
"title": metadata.title,
"version": metadata.version,
"status": metadata.status,
"path": metadata.path
});
}

if opts.json {
let output = serde_json::json!({
"elf": program_path.display().to_string(),
"programs": programs,
"maps": maps,
});
println!("{}", serde_json::to_string_pretty(&output).unwrap());
println!("{}", serde_json::to_string_pretty(&output_data).unwrap());
} else {
info(&format!("✅ ELF: {}", program_path.display()));
println!("Programs:");
for name in &programs { println!(" - {}", name); }
println!("Maps:");
for name in &maps { println!(" - {}", name); }
success(&format!("Inspecting eBPF Program: {}", program_path.display()));

if let Some(ref metadata) = program_metadata {
println!("\nProgram Metadata:");
println!(" ID: {}", metadata.id);
println!(" Title: {}", metadata.title);
println!(" Version: {}", metadata.version);
println!(" Status: {}", metadata.status);
println!(" Path: {}", metadata.path);
}

println!("\neBPF Programs:");
if programs.is_empty() {
println!(" (no programs found)");
} else {
for name in &programs {
println!(" {}", name);
}
}

println!("\neBPF Maps:");
if maps.is_empty() {
println!(" (no maps found)");
} else {
for name in &maps {
println!(" {}", name);
}
}

if opts.verbose {
println!("\nTechnical Details:");
println!(" ELF Path: {}", program_path.display());
println!(" Programs Count: {}", programs.len());
println!(" Maps Count: {}", maps.len());
}
}

Ok(())
}
Loading