#![cfg(unix)]
use super::collector_manager::Collector;
use super::receiver_manager::Receiver;
use super::signal_handler_manager::chain_signal_handler;
use crate::crash_info::Metadata;
use crate::shared::configuration::CrashtrackerConfiguration;
use libc::{c_void, siginfo_t, ucontext_t};
use libdd_common::timeout::TimeoutManager;
use std::ptr;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64};
static METADATA: AtomicPtr<(Metadata, String)> = AtomicPtr::new(ptr::null_mut());
static CONFIG: AtomicPtr<(CrashtrackerConfiguration, String)> = AtomicPtr::new(ptr::null_mut());
#[derive(Debug, thiserror::Error)]
pub enum CrashHandlerError {
#[error("No crashtracking config available")]
NoConfig,
#[error("No crashtracking metadata available")]
NoMetadata,
#[error("Failed to spawn receiver: {0}")]
ReceiverSpawnError(#[from] super::receiver_manager::ReceiverError),
#[error("Failed to spawn collector: {0}")]
CollectorSpawnError(#[from] super::collector_manager::CollectorSpawnError),
}
pub fn update_metadata(metadata: Metadata) -> anyhow::Result<()> {
let metadata_string = serde_json::to_string(&metadata)?;
let box_ptr = Box::into_raw(Box::new((metadata, metadata_string)));
let old = METADATA.swap(box_ptr, SeqCst);
if !old.is_null() {
unsafe {
std::mem::drop(Box::from_raw(old));
}
}
Ok(())
}
pub fn update_config(config: CrashtrackerConfiguration) -> anyhow::Result<()> {
let config_string = serde_json::to_string(&config)?;
let box_ptr = Box::into_raw(Box::new((config, config_string)));
let old = CONFIG.swap(box_ptr, SeqCst);
if !old.is_null() {
unsafe {
std::mem::drop(Box::from_raw(old));
}
}
Ok(())
}
pub(crate) extern "C" fn handle_posix_sigaction(
signum: i32,
sig_info: *mut siginfo_t,
ucontext: *mut c_void,
) {
let _ = handle_posix_signal_impl(sig_info, ucontext as *mut ucontext_t);
unsafe { chain_signal_handler(signum, sig_info, ucontext) };
}
static ENABLED: AtomicBool = AtomicBool::new(true);
pub fn disable() {
ENABLED.store(false, SeqCst);
}
pub fn enable() {
ENABLED.store(true, SeqCst);
}
fn handle_posix_signal_impl(
sig_info: *const siginfo_t,
ucontext: *const ucontext_t,
) -> Result<(), CrashHandlerError> {
if !ENABLED.load(SeqCst) {
return Ok(());
}
static NUM_TIMES_CALLED: AtomicU64 = AtomicU64::new(0);
if NUM_TIMES_CALLED.fetch_add(1, SeqCst) > 0 {
return Ok(());
}
let config_ptr = CONFIG.swap(ptr::null_mut(), SeqCst);
if config_ptr.is_null() {
return Err(CrashHandlerError::NoConfig);
}
let (config, config_str) = unsafe { &*config_ptr };
let metadata_ptr = METADATA.swap(ptr::null_mut(), SeqCst);
if metadata_ptr.is_null() {
return Err(CrashHandlerError::NoMetadata);
}
let (_metadata, metadata_string) = unsafe { &*metadata_ptr };
let timeout_manager = TimeoutManager::new(config.timeout());
let unix_socket_path = config.unix_socket_path().as_deref().unwrap_or_default();
let receiver = if unix_socket_path.is_empty() {
Receiver::spawn_from_stored_config()?
} else {
Receiver::from_socket(unix_socket_path)?
};
let collector = Collector::spawn(
&receiver,
config,
config_str,
metadata_string,
sig_info,
ucontext,
)?;
collector.finish(&timeout_manager);
receiver.finish(&timeout_manager);
Ok(())
}