Merge pull request #42 from yonasBSD/yonasBSD

fix: Build on FreeBSD.
This commit is contained in:
S.B
2026-04-22 14:03:40 +02:00
committed by GitHub
6 changed files with 82 additions and 9 deletions
+5 -1
View File
@@ -64,7 +64,7 @@ rlimit = "0.11"
# Bluetooth
btleplug = "0.12"
btleplug = { version = "0.12", optional = true }
# WPair migrated from ratatui+crossterm TUI to rustyline REPL — deps removed.
@@ -163,3 +163,7 @@ strip = true
inherits = "release"
lto = "thin" # Faster than fat LTO
codegen-units = 4 # Parallel codegen
[features]
default = ["bluetooth"]
bluetooth = ["dep:btleplug"]
+55 -2
View File
@@ -16,6 +16,9 @@ fn main() {
return;
}
// Check which features are enabled
let features = Features::detect();
// Discover categories dynamically from subdirectories of src/modules/
let mut categories: Vec<String> = Vec::new();
let entries = match fs::read_dir(modules_root) {
@@ -31,6 +34,11 @@ fn main() {
if path.is_dir() {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if !name.starts_with('.') {
// Skip categories that are entirely disabled
if features.should_skip_category(name) {
println!("cargo:warning=Skipping category '{}' (feature disabled)", name);
continue;
}
categories.push(name.to_string());
}
}
@@ -42,6 +50,9 @@ fn main() {
for cat in &categories {
println!("cargo:rerun-if-changed=src/modules/{}", cat);
}
// Also rerun if features change
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_BLUETOOTH");
// Compile regexes once, reuse across all categories.
let run_re = Regex::new(r"pub\s+async\s+fn\s+run\s*\(\s*[^)]*:\s*&str\s*\)")
@@ -60,7 +71,7 @@ fn main() {
let out_file = format!("{}_dispatch.rs", dispatch_name(cat));
let display_name = capitalize(cat);
match generate_dispatch(&root, &out_file, &mod_prefix, &display_name, &run_re, &info_re, &check_re) {
match generate_dispatch(&root, &out_file, &mod_prefix, &display_name, &run_re, &info_re, &check_re, &features) {
Ok(_module_count) => {
registry_entries.push(RegistryEntry {
category: cat.clone(),
@@ -81,6 +92,40 @@ fn main() {
}
}
/// Feature detection and module filtering
struct Features {
bluetooth: bool,
}
impl Features {
fn detect() -> Self {
Self {
bluetooth: env::var("CARGO_FEATURE_BLUETOOTH").is_ok(),
}
}
/// Check if a category should be entirely skipped
fn should_skip_category(&self, category: &str) -> bool {
match category {
"bluetooth" => !self.bluetooth,
_ => false,
}
}
/// Check if a module path should be skipped (for feature-gated submodules)
fn should_skip_module(&self, module_path: &str) -> bool {
// If bluetooth feature is disabled, skip any module under bluetooth/
if !self.bluetooth && module_path.starts_with("bluetooth/") {
return true;
}
// Add more feature checks here as needed
// Example: if !self.some_feature && module_path.starts_with("some/path/")
false
}
}
struct RegistryEntry {
category: String,
dispatch_name: String,
@@ -118,6 +163,7 @@ fn generate_dispatch(
run_re: &Regex,
info_re: &Regex,
check_re: &Regex,
features: &Features,
) -> Result<usize, Box<dyn std::error::Error>> {
let out_dir = env::var("OUT_DIR").map_err(|_| "OUT_DIR environment variable not set")?;
let dest_path = Path::new(&out_dir).join(out_file);
@@ -127,7 +173,7 @@ fn generate_dispatch(
return Err(format!("Module directory '{}' does not exist", root).into());
}
let mappings = find_modules(root_path, run_re, info_re, check_re)?;
let mappings = find_modules(root_path, run_re, info_re, check_re, features)?;
// Sort for deterministic output
let mut sorted_mappings: Vec<_> = mappings.into_iter().collect();
@@ -411,6 +457,7 @@ fn find_modules(
run_re: &Regex,
info_re: &Regex,
check_re: &Regex,
features: &Features,
) -> Result<HashSet<ModuleMapping>, Box<dyn std::error::Error>> {
let mut mappings = HashSet::new();
@@ -422,6 +469,12 @@ fn find_modules(
if let Ok(relative) = path.strip_prefix(root) {
let rel_str = relative.with_extension("").to_string_lossy().replace("\\", "/");
// Skip modules that are feature-gated out
if features.should_skip_module(&rel_str) {
println!("cargo:warning=Skipping module '{}' (feature disabled)", rel_str);
continue;
}
let mut content = String::new();
if File::open(path).and_then(|mut f| f.read_to_string(&mut content)).is_ok() {
+18 -3
View File
@@ -7,23 +7,38 @@ use super::types::{
ServerInfo, ToolsCapability,
};
// You can place this function in src/mcp/server.rs or a shared utility module
fn errno() -> i32 {
unsafe {
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
{ *libc::__error() }
#[cfg(target_os = "linux")]
{ *libc::__errno_location() }
// Add fallbacks for other OSes if needed
#[cfg(not(any(target_os = "freebsd", target_os = "macos", target_os = "linux")))]
{ 0 } // Or compile_error! to force checking for a new OS
}
}
fn isolate_protocol_stdout() -> anyhow::Result<tokio::fs::File> {
use std::os::fd::FromRawFd;
unsafe {
let saved_fd = libc::dup(1);
if saved_fd < 0 {
anyhow::bail!("dup(1) failed: errno {}", *libc::__errno_location());
anyhow::bail!("dup(1) failed: errno {}", errno());
}
let null_path = b"/dev/null\0";
let null_fd = libc::open(null_path.as_ptr() as *const libc::c_char, libc::O_WRONLY);
if null_fd < 0 {
libc::close(saved_fd);
anyhow::bail!("open(/dev/null) failed: errno {}", *libc::__errno_location());
anyhow::bail!("open(/dev/null) failed: errno {}", errno());
}
if libc::dup2(null_fd, 1) < 0 {
libc::close(null_fd);
libc::close(saved_fd);
anyhow::bail!("dup2(null, 1) failed: errno {}", *libc::__errno_location());
anyhow::bail!("dup2(null, 1) failed: errno {}", errno());
}
libc::close(null_fd);
let std_file = std::fs::File::from_raw_fd(saved_fd);
@@ -318,7 +318,7 @@ fn sendmmsg_batch(
msg
}).collect();
let ret = unsafe { libc::sendmmsg(fd, msgs.as_mut_ptr(), count as u32, 0) };
let ret = unsafe { libc::sendmmsg(fd, msgs.as_mut_ptr(), (count as u32).try_into().unwrap(), 0) };
if ret < 0 {
let errno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
(0, Some(errno))
+3 -1
View File
@@ -1,4 +1,3 @@
pub mod bluetooth;
pub mod cameras;
pub mod cowrie;
pub mod crypto;
@@ -20,3 +19,6 @@ pub mod vnc;
pub mod voip;
pub mod webapps;
pub mod windows;
#[cfg(feature = "bluetooth")]
pub mod bluetooth;
@@ -33,7 +33,6 @@ use reqwest::Client;
use std::{
fs::File,
io::{BufRead, BufReader},
process::Command,
time::Duration,
};
use url::Url;