use std::env;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::sync::{Mutex, Once};
use tempfile::{Builder, TempDir};
#[cfg(unix)]
use libc::{c_char, c_int, c_void};
#[cfg(unix)]
unsafe extern "C" {
fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
fn dlerror() -> *const c_char;
}
static INIT: Once = Once::new();
static EXTRACTED_LIB_PATH: Mutex<Option<PathBuf>> = Mutex::new(None);
static TEMP_DIR: Mutex<Option<TempDir>> = Mutex::new(None);
pub(crate) fn ensure_library_available() -> Result<PathBuf, String> {
INIT.call_once(|| {
if let Ok(path) = extract_embedded_library() {
if let Ok(mut guard) = EXTRACTED_LIB_PATH.lock() {
*guard = path;
}
}
});
EXTRACTED_LIB_PATH
.lock()
.map_err(|e| format!("Mutex poison: {}", e))?
.as_ref()
.ok_or_else(|| "Library extraction failed".to_string())
.map(|p| p.clone())
}
fn extract_embedded_library() -> Result<Option<PathBuf>, Box<dyn std::error::Error + Send + Sync>> {
if let Some(path) = find_library_in_standard_paths() {
return Ok(Some(path));
}
let embedded_data = include_bytes!(concat!(env!("OUT_DIR"), "/libeloqstore_combine.so"));
let temp_dir = Builder::new()
.prefix("eloqstore_libs_")
.tempdir()
.map_err(|e| format!("Failed to create temp directory: {}", e))?;
let lib_path = temp_dir.path().join("libeloqstore_combine.so");
let mut file = fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(&lib_path)
.map_err(|e| format!("Failed to create library file atomically: {}", e))?;
file.write_all(embedded_data)
.map_err(|e| format!("Failed to write library data: {}", e))?;
file.sync_all()
.map_err(|e| format!("Failed to sync library file: {}", e))?;
drop(file);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&lib_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&lib_path, perms)?;
}
if let Ok(mut guard) = TEMP_DIR.lock() {
*guard = Some(temp_dir);
}
#[cfg(unix)]
{
use std::ffi::CString;
let lib_path_cstr = CString::new(lib_path.to_string_lossy().as_ref())
.map_err(|e| format!("Invalid library path: {}", e))?;
unsafe {
let handle = dlopen(lib_path_cstr.as_ptr(), libc::RTLD_LAZY | libc::RTLD_GLOBAL);
if handle.is_null() {
let err = dlerror();
let error_msg = if err.is_null() {
"Unknown error".to_string()
} else {
std::ffi::CStr::from_ptr(err).to_string_lossy().into_owned()
};
return Err(format!(
"Failed to dlopen library {}: {}",
lib_path.display(),
error_msg
)
.into());
}
}
}
Ok(Some(lib_path))
}
fn find_library_in_standard_paths() -> Option<PathBuf> {
if let Ok(exe) = env::current_exe() {
if let Some(exe_dir) = exe.parent() {
let lib = exe_dir.join("libeloqstore_combine.so");
if lib.exists() {
return Some(lib);
}
let lib = exe_dir.parent().map(|p| p.join("libeloqstore_combine.so"));
if let Some(ref lib) = lib {
if lib.exists() {
return Some(lib.clone());
}
}
}
}
for path in ["/usr/local/lib", "/usr/lib"] {
let lib = PathBuf::from(path).join("libeloqstore_combine.so");
if lib.exists() {
return Some(lib);
}
}
None
}