#![deny(missing_docs)]
#![deny(clippy::arithmetic_side_effects)]
#![deny(clippy::as_ptr_cast_mut)]
#![deny(clippy::as_underscore)]
#![deny(clippy::assertions_on_result_states)]
#![deny(clippy::borrow_as_ptr)]
#![deny(clippy::branches_sharing_code)]
#![deny(clippy::case_sensitive_file_extension_comparisons)]
#![deny(clippy::cast_lossless)]
#![deny(clippy::cast_possible_truncation)]
#![deny(clippy::cast_possible_wrap)]
#![deny(clippy::cast_precision_loss)]
#![deny(clippy::cast_ptr_alignment)]
#![deny(clippy::cast_sign_loss)]
#![deny(clippy::checked_conversions)]
#![deny(clippy::clear_with_drain)]
#![deny(clippy::clone_on_ref_ptr)]
#![deny(clippy::cloned_instead_of_copied)]
#![deny(clippy::cognitive_complexity)]
#![deny(clippy::collection_is_never_read)]
#![deny(clippy::copy_iterator)]
#![deny(clippy::create_dir)]
#![deny(clippy::dbg_macro)]
#![deny(clippy::debug_assert_with_mut_call)]
#![deny(clippy::decimal_literal_representation)]
#![deny(clippy::default_trait_access)]
#![deny(clippy::default_union_representation)]
#![deny(clippy::derive_partial_eq_without_eq)]
#![deny(clippy::doc_link_with_quotes)]
#![deny(clippy::explicit_into_iter_loop)]
#![deny(clippy::explicit_iter_loop)]
#![deny(clippy::fallible_impl_from)]
#![deny(clippy::missing_safety_doc)]
#![deny(clippy::undocumented_unsafe_blocks)]
pub mod api;
#[cfg(feature = "asm")]
pub mod asm;
pub mod cookie;
pub(crate) mod mask;
#[macro_use]
pub(crate) mod kernel;
pub(crate) mod workers;
pub mod cache;
#[expect(missing_docs)]
pub mod compat;
pub mod config;
pub mod confine;
pub mod dns;
pub mod elf;
pub mod err;
pub mod fs;
pub mod hash;
pub mod hook;
pub mod ioctl;
pub mod landlock_policy;
pub mod log;
pub mod magic;
pub mod parsers;
pub mod path;
pub mod proc;
pub mod ptrace;
pub mod rng;
pub mod sandbox;
pub mod seal;
#[expect(clippy::disallowed_types)]
pub mod sealbox;
pub mod sigset;
pub mod spec;
pub mod sysinfo;
pub mod syslog;
pub mod timer;
pub mod uts;
#[expect(clippy::arithmetic_side_effects)]
pub mod wildmatch;
pub mod wordexp;
#[expect(missing_docs)]
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::undocumented_unsafe_blocks)]
pub mod caps;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
#[expect(missing_docs)]
#[expect(unused_imports)]
#[expect(clippy::as_underscore)]
#[expect(clippy::borrow_as_ptr)]
#[expect(clippy::cast_lossless)]
#[expect(clippy::cast_possible_truncation)]
#[expect(clippy::decimal_literal_representation)]
#[expect(clippy::default_trait_access)]
#[expect(clippy::disallowed_methods)]
#[expect(clippy::init_numbered_fields)]
#[expect(clippy::undocumented_unsafe_blocks)]
pub mod landlock;
pub(crate) mod pool;
pub mod unshare;
use std::{ffi::OsStr, os::fd::AsRawFd};
use lexis::ToName;
use libseccomp::ScmpVersion;
use nix::{
errno::Errno,
sched::CloneFlags,
sys::{
personality::Persona,
resource::{getrlimit, Resource},
signal::{sigaction, signal, SaFlags, SigAction, SigHandler, SigSet, Signal},
socket::{socket, AddressFamily, SockFlag, SockType},
utsname::uname,
},
unistd::{Gid, Group, Uid, User},
};
use serde::{Serialize, Serializer};
use crate::{
compat::{fstatx, lsm_list_modules},
confine::{
check_cross_memory_attach, check_unix_diag, is_coredump, lock_enabled, ns_enabled,
seccomp_arch_native_name, vdso_list_calls, SydPersona,
},
err::err2no,
hash::{aes_ctr_info, check_setsockopt_serial_support, hmac_sha256_info, key_ring_validate},
landlock::ABI,
path::{XPath, XPathBuf},
proc::{proc_fs_file_max, proc_fs_nr_open, proc_kernel_randomize_va_space, proc_kernel_taint},
sealbox::check_mseal_support,
spec::{speculation_get, SpeculationFeature},
};
#[macro_export]
macro_rules! main {
{ $($body:tt)* } => {
fn main() -> std::process::ExitCode {
#[cfg(all(not(feature = "prof"), target_pointer_width = "64"))]
syd::config_mimalloc();
match (|| -> syd::err::SydResult<std::process::ExitCode> { $($body)* })() {
Ok(code) => code,
Err(err) => {
use std::io::Write;
let desc = format!("Error: {err}\n");
let _ = std::io::stderr().write_all(desc.as_bytes());
u8::try_from(
err.errno()
.map(|e| e as i32)
.unwrap_or(128)
)
.map(std::process::ExitCode::from)
.unwrap_or(std::process::ExitCode::FAILURE)
}
}
}
};
}
#[expect(clippy::cognitive_complexity)]
pub fn syd_info(verbose: bool) -> Result<(), Errno> {
use crate::config::*;
printfln!("syd {} ({})", *crate::config::VERSION, syd_code_name())?;
printfln!("Rock solid application kernel")?;
printfln!("Author: Ali Polatel <alip@chesswob.org>")?;
printfln!("License: GPL-3.0-only")?;
let feat = [
#[cfg(debug_assertions)]
"+debug",
#[cfg(not(debug_assertions))]
"-debug",
#[cfg(feature = "log")]
"+log",
#[cfg(not(feature = "log"))]
"-log",
#[cfg(feature = "oci")]
"+oci",
#[cfg(not(feature = "oci"))]
"-oci",
#[cfg(feature = "prof")]
"+prof",
#[cfg(not(feature = "prof"))]
"-prof",
];
printfln!("Features: {}", feat.join(", "))?;
if !verbose {
return Ok(());
}
let libapi = libseccomp::get_api();
match ScmpVersion::current() {
Ok(libver) => {
printfln!(
"LibSeccomp: v{}.{}.{} api:{}",
libver.major,
libver.minor,
libver.micro,
libapi
)?;
}
Err(error) => {
printfln!("LibSeccomp: ? (error: {error})")?;
}
}
match proc_kernel_taint() {
Ok(tflags) => printfln!("{tflags}"),
Err(errno) => printfln!("Kernel may be tainted (error: {errno})."),
}?;
let aslr = match proc_kernel_randomize_va_space() {
Ok(0) => "disabled".to_string(),
Ok(1) => "enabled (stack, mmap, VDSO; PIE text randomized)".to_string(),
Ok(2) => "enabled (heap + stack, mmap, VDSO; PIE text randomized)".to_string(),
Ok(n) => format!("{n} (error: {})", Errno::EINVAL),
Err(errno) => format!("? (error: {errno})"),
};
printfln!("ASLR is {aslr}.")?;
#[expect(clippy::disallowed_methods)]
let bpf_jit = match std::fs::read_to_string("/proc/sys/net/core/bpf_jit_enable") {
Ok(val) => match val.trim() {
"0" => "disabled".to_string(),
"1" => "enabled".to_string(),
"2" => "enabled in debug mode".to_string(),
n => format!("{n} (error: {})", Errno::EINVAL),
},
Err(err) => format!("? (error: {})", err2no(&err)),
};
printfln!("BPF JIT compiler is {bpf_jit}.")?;
let abi = ABI::new_current();
if abi == ABI::Unsupported {
printfln!("Landlock is not supported.")?;
} else {
let state = lock_enabled(abi);
let state_verb = match state {
0 => "fully enforced",
1 => "partially enforced",
2 => "not enforced",
_ => "unsupported",
};
printfln!("Landlock ABI {} is {state_verb}.", abi as i32)?;
}
printfln!(
"User namespaces are {}supported.",
if ns_enabled(CloneFlags::CLONE_NEWUSER).unwrap_or(false) {
""
} else {
"not "
}
)?;
let cfg_cma = check_cross_memory_attach();
printfln!(
"Cross memory attach is {}supported{}",
if cfg_cma { "" } else { "not " },
if cfg_cma {
"."
} else {
" (\x1b[91minsecure\x1b[0m)."
},
)?;
printfln!(
"Memory sealing is {}supported.",
if check_mseal_support() { "" } else { "not " }
)?;
let unix_diag = match check_unix_diag() {
Ok(true) => "supported".to_string(),
Ok(false) => "not supported".to_string(),
Err(errno) => format!("unknown (error: {errno})"),
};
printfln!("UNIX socket diagnostics are {unix_diag}.")?;
printfln!(
"Algorithm sockets {} keyrings(7) support.",
if check_setsockopt_serial_support() {
"have"
} else {
"doesn't have"
}
)?;
match key_ring_validate() {
Ok(()) => {
printfln!("Session keyring is attached to the user keyring.")?;
}
Err(errno) => {
printfln!("Session keyring isn't attached to the user keyring: {errno}!")?;
}
}
printfln!("{}", aes_ctr_info())?;
printfln!("{}", hmac_sha256_info())?;
let lsms = match lsm_list_modules() {
Ok(lsms) => lsms
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>()
.join(", "),
Err(Errno::ENOENT) => "none loaded".to_string(),
Err(errno) => format!("? (error: {errno})"),
};
printfln!("LSMs: {lsms}.")?;
match vdso_list_calls() {
Ok(names) if names.is_empty() => printfln!("No vDSO calls found.")?,
Ok(names) => {
let names = names
.iter()
.map(|s| s.to_string_lossy())
.collect::<Vec<_>>()
.join(", ");
printfln!("List of vDSO calls: {names}.")?;
}
Err(error) => printfln!("List of vDSO calls: ? (error: {error}")?,
}
let (nofile_soft, nofile_hard) = getrlimit(Resource::RLIMIT_NOFILE).unwrap_or((0, 0));
printf!("Open file limits: {nofile_soft} soft, {nofile_hard} hard, ")?;
let file_max = proc_fs_file_max().unwrap_or(0);
let nr_open = proc_fs_nr_open().unwrap_or(0);
printfln!("{nr_open} nr_open, {file_max} file-max")?;
let uname = match uname() {
Ok(info) => OsStr::to_str(info.release()).unwrap_or("?").to_string(),
Err(_) => "?".to_string(),
};
printfln!("Host (build): {}", env!("SYD_BUILDHOST"))?;
printfln!(
"Host (target): {uname} {}",
seccomp_arch_native_name().unwrap_or("?")
)?;
printf!("Host Linux: {}.{} with", KERNEL_VERSION.0, KERNEL_VERSION.1)?;
printf!(" mmap_min_addr={}", *MMAP_MIN_ADDR)?;
printf!(
", {}at_execve_check",
if *HAVE_AT_EXECVE_CHECK { "+" } else { "-" }
)?;
printf!(
", {}madv_guard_install",
if *HAVE_MADV_GUARD { "+" } else { "-" }
)?;
printf!(
", {}namespaced_pid_max",
if *HAVE_NAMESPACED_PID_MAX { "+" } else { "-" }
)?;
printf!(
", {}pidfd_thread",
if *HAVE_PIDFD_THREAD { "+" } else { "-" }
)?;
printf!(
", {}procmap_query",
if *HAVE_PROCMAP_QUERY { "+" } else { "-" }
)?;
printf!(
", {}proc_pid_fd_stat_size",
if *HAVE_PROC_PID_FD_STAT_SIZE {
"+"
} else {
"-"
}
)?;
printf!(
", {}pwritev2_rwf_noappend",
if *HAVE_RWF_NOAPPEND { "+" } else { "-" }
)?;
printf!(
", {}seccomp_user_notif_fd_sync_wake_up",
if *HAVE_SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP {
"+"
} else {
"-"
}
)?;
printfln!(
", {}statx_mnt_id_unique",
if *HAVE_STATX_MNT_ID_UNIQUE { "+" } else { "-" }
)?;
let pers = match Errno::result(unsafe { libc::personality(0xFFFFFFFF) }) {
Ok(n) => SydPersona(Persona::from_bits_retain(n)).to_string(),
Err(errno) => format!("? (error: {errno})"),
};
printfln!(
"Environment: {}-{pers}-{}",
env!("SYD_TARGET_ENV"),
env!("SYD_TARGET_POINTER_WIDTH")
)?;
printfln!(
"CPU: {} ({} cores), {}-endian",
num_cpus::get(),
num_cpus::get_physical(),
env!("SYD_TARGET_ENDIAN")
)?;
printfln!("CPUFLAGS: {}", env!("SYD_TARGET_FEATURE"))?;
for spec_feat in [
SpeculationFeature::StoreBypass,
SpeculationFeature::IndirectBranch,
SpeculationFeature::L1DFlush,
] {
printfln!(
"{}",
match speculation_get(spec_feat) {
Ok(status) => status.to_string(),
Err(errno) => format!("{spec_feat} status: ? (error: {errno})"),
}
)?;
}
Ok(())
}
pub fn syd_code_name() -> String {
#[expect(clippy::disallowed_methods)]
let major = env!("CARGO_PKG_VERSION_MAJOR")
.parse::<u64>()
.expect("CARGO_PKG_VERSION_MAJOR");
#[expect(clippy::disallowed_methods)]
let minor = env!("CARGO_PKG_VERSION_MINOR")
.parse::<u64>()
.expect("CARGO_PKG_VERSION_MINOR");
#[expect(clippy::disallowed_methods)]
let patch = env!("CARGO_PKG_VERSION_PATCH")
.parse::<u64>()
.expect("CARGO_PKG_VERSION_PATCH");
let hex_version = (major << 16) | (minor << 8) | patch;
hex_version
.to_name()
.split('_')
.map(|word| {
let mut c = word.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
}
})
.collect::<Vec<String>>()
.join(" ")
}
pub fn get_user_name(uid: Uid) -> String {
match User::from_uid(uid) {
Ok(Some(user)) => user.name,
_ => "nobody".to_string(),
}
}
pub fn get_user_home(username: &str) -> XPathBuf {
match User::from_name(username) {
Ok(Some(user)) => user.dir.into(),
_ => "/proc/self/fdinfo".into(),
}
}
pub fn ignore_signal(signal: Signal) -> Result<(), Errno> {
let sig_action = SigAction::new(
SigHandler::SigIgn, SaFlags::empty(),
SigSet::empty(),
);
unsafe { sigaction(signal, &sig_action) }.map(drop)
}
pub fn reset_signal(signal: Signal) -> Result<(), Errno> {
let sig_action = SigAction::new(
SigHandler::SigDfl, SaFlags::empty(),
SigSet::empty(),
);
unsafe { sigaction(signal, &sig_action) }.map(drop)
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct IgnoreSignalOpts: u8 {
const SkipIgnoreAlarm = 1 << 0;
const SkipIgnoreCoreDump = 1 << 1;
}
}
impl Serialize for IgnoreSignalOpts {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut opts: Vec<&str> = vec![];
if self.is_empty() {
return serializer.collect_seq(opts);
}
if self.contains(Self::SkipIgnoreAlarm) {
opts.push("skip_ignore_alarm");
}
if self.contains(Self::SkipIgnoreCoreDump) {
opts.push("skip_ignore_core_dump");
}
opts.sort();
serializer.collect_seq(opts)
}
}
pub fn ignore_signals(opts: IgnoreSignalOpts) -> Result<(), Errno> {
for signal in Signal::iterator() {
match signal {
Signal::SIGCHLD | Signal::SIGKILL | Signal::SIGSTOP => {}
#[cfg(feature = "prof")]
Signal::SIGPROF => {}
Signal::SIGALRM if opts.contains(IgnoreSignalOpts::SkipIgnoreAlarm) => {}
signal
if opts.contains(IgnoreSignalOpts::SkipIgnoreCoreDump)
&& is_coredump(signal as i32) => {}
signal => ignore_signal(signal)?,
}
}
for signum in libc::SIGRTMIN()..libc::SIGRTMAX() {
Errno::result(unsafe { libc::signal(signum, libc::SIG_IGN as libc::sighandler_t) })?;
}
Ok(())
}
pub fn reset_signals() -> Result<(), Errno> {
for signal in Signal::iterator() {
if !matches!(signal, Signal::SIGKILL | Signal::SIGSTOP) {
reset_signal(signal)?;
}
}
for signum in libc::SIGRTMIN()..libc::SIGRTMAX() {
Errno::result(unsafe { libc::signal(signum, libc::SIG_DFL as libc::sighandler_t) })?;
}
Ok(())
}
const IOPRIO_CLASS_IDLE: i32 = 3;
const IOPRIO_WHO_PROCESS: i32 = 1;
pub(crate) fn set_io_priority_idle() -> Result<(), Errno> {
let ioprio = IOPRIO_CLASS_IDLE << 13;
Errno::result(unsafe { libc::syscall(libc::SYS_ioprio_set, IOPRIO_WHO_PROCESS, 0, ioprio) })
.map(drop)
}
pub(crate) fn set_cpu_priority_idle() -> Result<(), Errno> {
let param: libc::sched_param = unsafe { std::mem::zeroed() };
Errno::result(unsafe {
libc::sched_setscheduler(0, libc::SCHED_IDLE, std::ptr::addr_of!(param))
})
.map(drop)
}
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_precision_loss)]
pub fn human_size(bytes: usize) -> String {
const SIZES: &[char] = &['B', 'K', 'M', 'G', 'T', 'P', 'E'];
let factor = 1024usize;
let mut size = bytes as f64;
let mut i = 0;
while size > factor as f64 && i < SIZES.len() - 1 {
size /= factor as f64;
i += 1;
}
format!("{:.2}{}", size, SIZES[i])
}
#[expect(clippy::unnecessary_cast)]
const SIOCGIFINDEX: u64 = libc::SIOCGIFINDEX as u64;
#[expect(clippy::unnecessary_cast)]
const SIOCGIFFLAGS: u64 = libc::SIOCGIFFLAGS as u64;
#[expect(clippy::unnecessary_cast)]
const SIOCSIFFLAGS: u64 = libc::SIOCSIFFLAGS as u64;
pub fn loopback_set_up() -> Result<i32, Errno> {
let sock = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
None,
)?;
let mut ifreq = libc::ifreq {
#[expect(clippy::cast_possible_wrap)]
ifr_name: [
b'l' as libc::c_char,
b'o' as libc::c_char,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
],
ifr_ifru: unsafe { std::mem::zeroed() },
};
let loindex: i32 = unsafe {
let mut ifr_index: libc::ifreq = std::mem::zeroed();
ifr_index.ifr_name = ifreq.ifr_name;
Errno::result(libc::syscall(
libc::SYS_ioctl,
sock.as_raw_fd(),
SIOCGIFINDEX as libc::c_ulong,
&mut ifr_index,
))?;
ifr_index.ifr_ifru.ifru_mtu
};
use crate::config::LOOPBACK_BIGTCP_MAX;
match loopback_set_bigtcp(loindex, LOOPBACK_BIGTCP_MAX) {
Ok(_) => {
info!("ctx": "loopback_set_bigtcp",
"msg": "loopback network device has BIGTCP set",
"max": LOOPBACK_BIGTCP_MAX);
}
Err(errno) => {
info!("ctx": "loopback_set_bigtcp",
"msg": format!("set BIGTCP for loopback network device error: {errno}"),
"err": errno as i32);
}
};
Errno::result(unsafe {
libc::syscall(
libc::SYS_ioctl,
sock.as_raw_fd(),
SIOCGIFFLAGS as libc::c_ulong,
&mut ifreq,
)
})?;
#[expect(clippy::cast_possible_truncation)]
unsafe {
ifreq.ifr_ifru.ifru_flags |= (libc::IFF_UP | libc::IFF_RUNNING) as libc::c_short
};
Errno::result(unsafe {
libc::syscall(
libc::SYS_ioctl,
sock.as_raw_fd(),
SIOCSIFFLAGS as libc::c_ulong,
&mut ifreq,
)
})?;
Ok(loindex)
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct nlattr {
nla_len: u16,
nla_type: u16,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct nlmsg {
hdr: libc::nlmsghdr,
info: ifinfomsg,
attrs: [u8; 64],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct ifinfomsg {
family: u8,
pad: u8,
ifi_type: u16, index: i32, flags: u32, change: u32, }
const IFLA_GRO_IPV4_MAX_SIZE: libc::c_ushort = 0x40;
const IFLA_GRO_MAX_SIZE: libc::c_ushort = 0x3a;
const IFLA_GSO_IPV4_MAX_SIZE: libc::c_ushort = 0x3f;
const IFLA_GSO_MAX_SIZE: libc::c_ushort = 0x29;
pub fn loopback_set_bigtcp(ifindex: i32, max_size: u32) -> Result<(), Errno> {
use netlink_sys::{constants::*, Socket, SocketAddr};
let mut sock = Socket::new(NETLINK_ROUTE)
.map_err(|e| Errno::from_raw(e.raw_os_error().unwrap_or(libc::ENOSYS)))?;
sock.bind(&SocketAddr::new(0, 0))
.map_err(|e| Errno::from_raw(e.raw_os_error().unwrap_or(libc::ENOSYS)))?;
let mut msg: nlmsg = unsafe { std::mem::zeroed() };
let nl_hdr = &mut msg.hdr;
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_possible_truncation)]
{
nl_hdr.nlmsg_len =
(std::mem::size_of::<libc::nlmsghdr>() + std::mem::size_of::<ifinfomsg>()) as u32;
nl_hdr.nlmsg_type = libc::RTM_NEWLINK;
nl_hdr.nlmsg_flags = (libc::NLM_F_REQUEST | libc::NLM_F_ACK) as u16;
nl_hdr.nlmsg_seq = 1;
nl_hdr.nlmsg_pid = 0;
}
let info = &mut msg.info;
#[expect(clippy::cast_possible_truncation)]
{
info.family = libc::AF_UNSPEC as u8;
info.index = ifindex;
info.change = u32::MAX;
}
let mut offset = 0;
for &kind in &[
IFLA_GRO_IPV4_MAX_SIZE,
IFLA_GRO_MAX_SIZE,
IFLA_GSO_IPV4_MAX_SIZE,
IFLA_GSO_MAX_SIZE,
] {
#[expect(clippy::cast_ptr_alignment)]
let attr_ptr = unsafe { msg.attrs.as_mut_ptr().add(offset) as *mut nlattr };
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_possible_truncation)]
unsafe {
(*attr_ptr).nla_type = kind;
(*attr_ptr).nla_len =
(std::mem::size_of::<nlattr>() + std::mem::size_of::<u32>()) as u16;
}
#[expect(clippy::cast_ptr_alignment)]
unsafe {
let ptr = (attr_ptr as *mut u8).add(std::mem::size_of::<nlattr>()) as *mut u32;
*ptr = max_size;
}
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_lossless)]
#[expect(clippy::cast_sign_loss)]
{
offset += unsafe { libc::NLA_ALIGN((*attr_ptr).nla_len as libc::c_int) } as usize;
}
}
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_possible_truncation)]
{
msg.hdr.nlmsg_len += offset as u32;
}
let buf = unsafe {
std::slice::from_raw_parts(
std::ptr::addr_of!(msg) as *const u8,
msg.hdr.nlmsg_len as usize,
)
};
sock.send(buf, 0)
.map_err(|e| Errno::from_raw(e.raw_os_error().unwrap_or(libc::ENOSYS)))?;
let (buf, _) = sock
.recv_from_full()
.map_err(|e| Errno::from_raw(e.raw_os_error().unwrap_or(libc::ENOSYS)))?;
let mut offset = 0;
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_ptr_alignment)]
while offset + std::mem::size_of::<libc::nlmsghdr>() <= buf.len() {
let hdr = unsafe { &*(buf.as_ptr().add(offset) as *const libc::nlmsghdr) };
let len = hdr.nlmsg_len as usize;
if len < std::mem::size_of::<libc::nlmsghdr>() || offset + len > buf.len() {
return Err(Errno::EINVAL);
}
#[expect(clippy::cast_possible_truncation)]
if hdr.nlmsg_type == libc::NLMSG_ERROR as libc::c_ushort
&& len >= std::mem::size_of::<libc::nlmsghdr>() + std::mem::size_of::<libc::nlmsgerr>()
{
let err = unsafe {
&*(buf
.as_ptr()
.add(offset + std::mem::size_of::<libc::nlmsghdr>())
as *const libc::nlmsgerr)
};
if err.error != 0 {
return Err(Errno::from_raw(-err.error));
}
}
#[expect(clippy::cast_possible_truncation)]
#[expect(clippy::cast_possible_wrap)]
#[expect(clippy::cast_sign_loss)]
{
offset += unsafe { libc::NLA_ALIGN(len as i32) as usize };
}
}
Ok(())
}
pub(crate) fn parse_user(name: &str) -> Result<Uid, Errno> {
if name.chars().all(|c| c.is_ascii_digit()) {
Ok(Uid::from_raw(
name.parse::<libc::uid_t>().or(Err(Errno::EINVAL))?,
))
} else if let Some(user) = User::from_name(name)? {
Ok(user.uid)
} else {
Err(Errno::ENOENT)
}
}
pub(crate) fn parse_group(name: &str) -> Result<Gid, Errno> {
if name.chars().all(|c| c.is_ascii_digit()) {
Ok(Gid::from_raw(
name.parse::<libc::gid_t>().or(Err(Errno::EINVAL))?,
))
} else if let Some(group) = Group::from_name(name)? {
Ok(group.gid)
} else {
Err(Errno::ENOENT)
}
}
pub fn set_sigpipe_dfl() -> Result<(), Errno> {
unsafe { signal(Signal::SIGPIPE, SigHandler::SigDfl) }.map(drop)
}
#[cfg(all(not(feature = "prof"), target_pointer_width = "64"))]
#[allow(non_upper_case_globals)]
pub fn config_mimalloc() {
#[allow(non_camel_case_types)]
type mi_option_t = libc::c_int;
#[allow(non_camel_case_types)]
type mi_output_fun =
Option<unsafe extern "C" fn(msg: *const libc::c_char, arg: *mut libc::c_void)>;
#[link(name = "mimalloc")]
extern "C" {
fn mi_option_set_enabled(option: mi_option_t, enable: bool);
fn mi_register_output(out: mi_output_fun, arg: *mut libc::c_void);
}
extern "C" fn syd_noop_output(_msg: *const libc::c_char, _arg: *mut libc::c_void) {}
const mi_option_show_errors: mi_option_t = 0;
const mi_option_show_stats: mi_option_t = 1;
const mi_option_verbose: mi_option_t = 2;
unsafe {
mi_option_set_enabled(mi_option_show_errors, false);
mi_option_set_enabled(mi_option_show_stats, false);
mi_option_set_enabled(mi_option_verbose, false);
mi_register_output(Some(syd_noop_output), std::ptr::null_mut());
}
}
#[inline]
#[cold]
fn cold() {}
#[expect(dead_code)]
#[inline]
pub(crate) fn likely(b: bool) -> bool {
if !b {
cold()
}
b
}
#[expect(dead_code)]
#[inline]
pub(crate) fn unlikely(b: bool) -> bool {
if b {
cold()
}
b
}
pub fn t(msg: &str) {
let buf = msg.as_bytes();
let len = buf.len() as libc::size_t;
unsafe { libc::syscall(libc::SYS_write, -31415, buf.as_ptr(), len) };
}
#[macro_export]
macro_rules! t {
($($arg:tt)*) => {{
syd::t(&format!($($arg)*));
}}
}
#[macro_export]
macro_rules! T {
($($arg:tt)*) => {{
$crate::t(&format!($($arg)*));
}}
}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn start_cpu_profile(name: &str) {
gperftools::profiler::PROFILER
.lock()
.expect("lock profiler")
.start(format!("./syd-cpu-{name}.pprof"))
.expect("start profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn start_cpu_profile(_name: &str) {}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn stop_cpu_profile() {
gperftools::profiler::PROFILER
.lock()
.expect("lock profiler")
.stop()
.expect("stop profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn stop_cpu_profile() {}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn start_mem_profile(name: &str) {
gperftools::heap_profiler::HEAP_PROFILER
.lock()
.expect("lock profiler")
.start(format!("./syd-mem-{name}"))
.expect("start profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn start_mem_profile(_name: &str) {}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn dump_mem_profile(name: &str) {
gperftools::heap_profiler::HEAP_PROFILER
.lock()
.expect("lock profiler")
.dump(format!("./syd-mem-{name}"))
.expect("dump profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn dump_mem_profile(_name: &str) {}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn stop_mem_profile() {
gperftools::heap_profiler::HEAP_PROFILER
.lock()
.expect("lock profiler")
.stop()
.expect("stop profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn stop_mem_profile() {}