use anyhow::Context as _;
use libloading::{Library, Symbol};
use std::env;
use std::ffi;
use std::path::Path;
use std::process::exit;
use tracing_subscriber::{
Layer,
filter::{EnvFilter, LevelFilter},
prelude::*,
};
const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
#[cfg(target_os = "linux")]
const CDYLIB_EXT: &str = "so";
#[cfg(target_os = "macos")]
const CDYLIB_EXT: &str = "dylib";
fn main() {
unsafe {
env::remove_var("REDIRECT_FROM");
env::remove_var("REDIRECT_TO");
}
let layer = tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr)
.with_filter(
EnvFilter::builder()
.with_default_directive(LevelFilter::WARN.into())
.from_env_lossy(),
)
.boxed();
let (layer, _reload_handle) = tracing_subscriber::reload::Layer::new(layer);
tracing_subscriber::registry().with(layer).init();
let exe_dir = match env::current_exe().context("failed to get executable path") {
Ok(v) => v.parent().unwrap().to_path_buf(),
Err(e) => {
tracing::error!("{e:?}");
exit(1);
}
};
let cdylib_path = exe_dir.join(format!("lib{PKG_NAME}.{CDYLIB_EXT}"));
tracing::debug!("library path: {}", cdylib_path.display());
if !cdylib_path.is_file() {
tracing::error!(
"library not found; output binary to: {}",
cdylib_path.display()
);
exit(1);
}
let dylib = match DyLib::load(&cdylib_path) {
Ok(lib) => match lib.version() {
Ok(version) => {
tracing::debug!("current library version: {version}");
if version == PKG_VERSION {
Some(lib)
} else {
tracing::error!("library version not matched with executable version!");
None
}
}
Err(e) => {
tracing::error!("{e:?}");
None
}
},
Err(e) => {
tracing::error!("{e:?}");
None
}
};
if let Some(dylib) = dylib {
tracing::debug!("enter main function");
if let Err(e) = dylib.main(&cdylib_path) {
tracing::warn!("{e:?}");
}
}
tracing::info!("error in loading library");
exit(1);
}
struct DyLib {
cdylib: Library,
}
impl DyLib {
fn load(library_path: &Path) -> anyhow::Result<Self> {
let cdylib = unsafe { Library::new(library_path).context("failed to load library") }?;
Ok(Self { cdylib })
}
pub fn version(&self) -> anyhow::Result<String> {
unsafe {
let func: Symbol<unsafe extern "C" fn() -> *mut u8> = self
.cdylib
.get(b"version")
.context("failed to load version function")?;
let ptr = func();
Ok(ffi::CString::from_raw(ptr as *mut ffi::c_char)
.to_string_lossy()
.to_string())
}
}
pub fn main(&self, cdylib_path: &Path) -> anyhow::Result<()> {
let cdylib_str = cdylib_path.to_string_lossy().to_string();
let cdylib_cstr = unsafe { ffi::CStr::from_ptr(cdylib_str.as_ptr() as *const ffi::c_char) };
unsafe {
let func: Symbol<unsafe extern "C" fn(*const ffi::c_char)> =
self.cdylib
.get(b"main")
.context("failed to load main function")?;
func(cdylib_cstr.as_ptr());
exit(0);
}
}
}