#[cfg(all(feature = "allocation-profile", feature = "mimalloc"))]
compile_error!("allocation-profile and mimalloc are mutually exclusive global allocators");
#[cfg(all(feature = "allocation-profile", not(feature = "mimalloc")))]
#[global_allocator]
static GLOBAL: keyhog_profile::TrackingAllocator = keyhog_profile::TrackingAllocator::new();
#[cfg(all(feature = "mimalloc", not(feature = "allocation-profile")))]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
#[cfg(all(feature = "mimalloc", not(feature = "allocation-profile")))]
fn configure_allocator_memory_policy() {
const MI_OPTION_EAGER_COMMIT: std::ffi::c_int = 3;
const MI_OPTION_ARENA_EAGER_COMMIT: std::ffi::c_int = 4;
const MI_OPTION_PURGE_DELAY: std::ffi::c_int = 15;
const MI_OPTION_GENERIC_COLLECT: std::ffi::c_int = 36;
unsafe extern "C" {
fn mi_option_set(option: std::ffi::c_int, value: i64);
}
unsafe {
mi_option_set(MI_OPTION_EAGER_COMMIT, 0);
mi_option_set(MI_OPTION_ARENA_EAGER_COMMIT, 0);
mi_option_set(MI_OPTION_PURGE_DELAY, 0);
mi_option_set(MI_OPTION_GENERIC_COLLECT, 1024);
}
}
#[cfg(not(all(feature = "mimalloc", not(feature = "allocation-profile"))))]
fn configure_allocator_memory_policy() {}
use std::process::ExitCode;
#[cfg(unix)]
fn reset_sigpipe() {
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
}
#[cfg(not(unix))]
fn reset_sigpipe() {}
#[cfg(unix)]
mod interrupt {
fn append(buf: &mut [u8; 256], len: &mut usize, src: &[u8]) {
for &byte in src {
if *len < buf.len() {
buf[*len] = byte;
*len += 1;
}
}
}
fn append_usize(buf: &mut [u8; 256], len: &mut usize, mut value: usize) {
if value == 0 {
append(buf, len, b"0");
return;
}
let mut digits = [0u8; 20];
let mut count = 0;
while value > 0 {
digits[count] = b'0' + (value % 10) as u8;
value /= 10;
count += 1;
}
while count > 0 {
count -= 1;
let digit = digits[count];
append(buf, len, &[digit]);
}
}
extern "C" fn handle_sigint(_signum: libc::c_int) {
let (scanned, total, findings) = keyhog::interrupt_counts();
let mut buf = [0u8; 256];
let mut len = 0;
append(&mut buf, &mut len, b"\nScan interrupted. ");
append_usize(&mut buf, &mut len, scanned);
append(&mut buf, &mut len, b"/");
append_usize(&mut buf, &mut len, total);
append(&mut buf, &mut len, b" files scanned. ");
append_usize(&mut buf, &mut len, findings);
append(&mut buf, &mut len, b" findings.\n");
if keyhog::operator_profile_active() {
append(
&mut buf,
&mut len,
b"profile outcome status=failed coverage=cancelled errors=1 exit=130 interruption=sigint\n",
);
}
unsafe {
libc::write(2, buf.as_ptr().cast(), len);
libc::_exit(keyhog::exit_codes::EXIT_INTERRUPTED as libc::c_int);
}
}
pub(super) fn install() {
unsafe {
libc::signal(
libc::SIGINT,
handle_sigint as *const () as libc::sighandler_t,
);
}
}
}
#[cfg(not(unix))]
mod interrupt {
pub(super) fn install() {}
}
fn main() -> ExitCode {
configure_allocator_memory_policy();
let runtime = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
{
Ok(runtime) => runtime,
Err(error) => {
eprintln!(
"error: failed to build the KeyHog async runtime: {error}. \
Fix: verify available process resources and retry."
);
return ExitCode::from(keyhog::exit_codes::EXIT_SYSTEM_ERROR);
}
};
runtime.block_on(async_main())
}
async fn async_main() -> ExitCode {
let _startup_span = keyhog_profile::span(keyhog_profile::Stage::Preprocess);
reset_sigpipe();
interrupt::install();
drop(_startup_span);
keyhog::cli_main().await
}