linux-support 0.0.25

Comprehensive Linux support for namespaces, cgroups, processes, scheduling, parsing /proc, parsing /sys, signals, hyper threads, CPUS, NUMA nodes, unusual file descriptors, PCI devices and much, much more
// This file is part of file-descriptors. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/file-descriptors/master/COPYRIGHT. No part of file-descriptors, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
// Copyright © 2018-2019 The developers of file-descriptors. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/file-descriptors/master/COPYRIGHT.


/// Received signal information.
///
/// Values are set or not as per the code in Linux's <https://github.com/torvalds/linux/blob/master/fs/signalfd.c>.
///
/// Use a combination of `ssi_signo and ssi_code` to work out which fields have been set; Linux calls these permutations `SIL_`*.
#[repr(C)]
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct signalfd_siginfo
{
	/// Signal number.
	///
	/// *Always* set by the Kernel.
	pub ssi_signo: u32,

	/// Error number (unused except for some events generated by the USB URBS device subsystem).
	///
	/// *Always* set by the Kernel.
	pub ssi_errno: i32,

	/// Signal code.
	///
	/// *Always* set by the Kernel.
	pub ssi_code: i32,

	/// PID of sender (for signals `SIGCHLD` and `sigqueue()`).
	///
	/// Only set for the `SIL_KILL`, `SIL_CHLD` and `SIL_RT` cases.
	pub ssi_pid: i32,

	/// Real UID of sender (for signals `SIGCHLD` and `sigqueue()`).
	///
	///
	/// Only set for the `SIL_KILL`, `SIL_CHLD` and `SIL_RT` cases.
	pub ssi_uid: u32,

	/// File descriptor (for signal `SIGIO` (for `poll()`)).
	///
	/// Only set for the `SIL_POLL` case.
	pub ssi_fd: i32,

	/// Kernel timer ID (for a POSIX timer signal).
	///
	/// Only set for the `SIL_TIMER` case.
	pub ssi_tid: u32,

	/// Band event (for signal `SIGIO` (for `poll()`)).
	///
	/// Only set for the `SIL_POLL` case.
	pub ssi_band: u32,

	/// POSIX timer overrun count.
	///
	/// Only set for the `SIL_TIMER` case.
	pub ssi_overrun: u32,

	/// Trap number that caused signal.
	///
	/// Only set for the `SIL_FAULT_BNDERR`, `SIL_FAULT_PKUERR`, `SIL_FAULT` and `SIL_FAULT_MCEERR` cases when the Kernel is compiled with `__ARCH_SI_TRAPNO` defined.
	///
	/// We do not differentiate `SIL_FAULT_BNDERR` and `SIL_FAULT_PKUERR` at this time, but in the future, they may have additional information available.
	pub ssi_trapno: u32,

	/// Exit status or signal (for signal `SIGCHLD`).
	///
	/// Only set for the `SIL_CHLD` case.
	pub ssi_status: i32,

	/// Integer sent by `sigqueue()` and POSIX timers.
	///
	/// Only set for the `SIL_TIMER` and `SIL_RT` cases.
	pub ssi_int: i32,

	/// Pointer sent by `sigqueue()` and POSIX timers.
	///
	/// Only set for the `SIL_TIMER` and `SIL_RT` cases.
	pub ssi_ptr: u64,

	/// User CPU time consumed (for signal `SIGCHLD`).
	///
	/// Only set for the `SIL_CHLD` case.
	pub ssi_utime: u64,

	/// System CPU time consumed (for signal `SIGCHLD`).
	///
	/// Only set for the `SIL_CHLD` case.
	pub ssi_stime: u64,

	/// Address that generated signal (for hardware-generated signals).
	///
	/// Only set for the `SIL_FAULT_BNDERR`, `SIL_FAULT_PKUERR`, `SIL_FAULT` and `SIL_FAULT_MCEERR` cases.
	pub ssi_addr: u64,

	/// Least significant bit of address (for signal SIGBUS).
	///
	/// Since Linux 2.6.37.
	///
	/// Only set for the `SIL_FAULT_MCEERR` case.
	pub ssi_addr_lsb: u16,

	_pad2: u16,

	/// System call number.
	///
	/// Only set for the `SIL_SYS` case.
	pub ssi_syscall: i32,

	/// System call address.
	///
	/// Only set for the `SIL_SYS` case.
	pub ssi_call_addr: u64,

	/// System call architecture.
	///
	/// Only set for the `SIL_SYS` case.
	pub ssi_arch: u32,

	_pad: [u8; 28],
}

impl signalfd_siginfo
{
	#[inline(always)]
	pub(crate) fn parse_signal(&self) -> ParsedSignal
	{
		use self::ParsedSignal::*;

		if self.is_kernel_generated()
		{
			if likely!(self.ssi_signo < u8::MAX as u32)
			{
				match self.ssi_signo as u8
				{
					Signal::SIGILL_ => self.kernel_generated_Fault::<IllegalInstructionCode, _>(FaultCode::SIGILL),

					Signal::SIGFPE_ => self.kernel_generated_Fault::<ArithmeticErrorCode, _>(FaultCode::SIGFPE),

					Signal::SIGSEGV_ => self.kernel_generated_Fault::<SegmentationFaultCode, _>(FaultCode::SIGSEGV),

					Signal::SIGTRAP_ => self.kernel_generated_Fault::<TrapCode, _>(FaultCode::SIGTRAP),

					#[cfg(any(target_arch = "mips64", target_arch = "sparc64"))] Signal::SIGEMT_ => self.kernel_generated_Fault::<EmulatorTrapCode, _>(FaultCode::SIGEMT),

					Signal::SIGCHLD_ => self.kernel_generated::<ChildCode, _>(|child_code|
					{
						debug_assert_eq!(self.ssi_errno, 0);
						Child
						{
							process_identifier: ProcessIdentifier::try_from(self.ssi_pid).expect("Zero or negative process identifier from kernel"),
							user_identifier: UserIdentifier::from(self.ssi_uid),
							status: ChildStatus::parse(self.ssi_status).expect("Invalid status from kernel"),
							user_cpu_time_consumed_in_clock_ticks: ClockTicks::from(self.ssi_utime),
							system_cpu_time_consumed_in_clock_ticks: ClockTicks::from(self.ssi_stime),
							child_code,
						}
					}),

					Signal::SIGIO_ => self.kernel_generated::<PollCode, _>(|poll_code|
					{
						debug_assert_eq!(self.ssi_errno, 0);
						Poll
						{
							signal: Signal::SIGIO,
							band_event: self.ssi_band,
							file_descriptor: self.ssi_fd,
							poll_code,
						}
					}),

					Signal::SIGSYS_ => self.kernel_generated::<SystemCallCode, _>(|system_call_code| SeccompSystemCall
					{
						seccomp_ret_data: self.ssi_errno,
						system_call_number: UnconstrainedSystemCallNumber::from(self.ssi_syscall),
						address: VirtualAddress::from(self.ssi_call_addr),
						audit_architecture: AuditArchitecture::optional_architecture(self.ssi_arch),
						system_call_code,
					}),

					Signal::SIGBUS_ => if self.ssi_code <= BusCode::InclusiveMaximum.into()
					{
						Fault
						{
							address: VirtualAddress::from(self.ssi_addr),
							#[cfg(any(target_arch = "mips64", target_arch = "sparc64"))] trap_number: self.ssi_trapno,
							#[cfg(any(target_arch = "aarch64", target_arch = "powerpc64"))] trap_number: self.ssi_errno,
							fault_code: FaultCode::SIGBUS(unsafe { transmute(self.ssi_code) })
						}
					}
					else if (self.ssi_code >= HardwareErrorMachineCheckBusCode::ActionRequired.into()) && (self.ssi_code <= HardwareErrorMachineCheckBusCode::ActionOptional.into())
					{
						debug_assert_eq!(self.ssi_errno, 0);

						FaultMachineCheck
						{
							address: VirtualAddress::from(self.ssi_addr),
							#[cfg(any(target_arch = "mips64", target_arch = "sparc64"))] trap_number: self.ssi_trapno,
							#[cfg(any(target_arch = "aarch64", target_arch = "powerpc64"))] trap_number: self.ssi_errno,
							hardware_error_machine_check_bus_code: unsafe { transmute(self.ssi_code) },
							address_least_significant_bit: self.ssi_addr_lsb,
						}
					}
					else
					{
						self.kernel_generated_fallthrough()
					},

					_ => self.kernel_generated_fallthrough()
				}
			}
			else
			{
				self.kernel_generated_fallthrough()
			}
		}
		else
		{
			let signal = Signal::parse_raw_signal_number_u32(self.ssi_signo);

			match self.ssi_code
			{
				SI_TIMER => UserspaceTimer
				{
					signal,
					error_number: self.ssi_errno,
					timer_identifier: self.ssi_tid,
					overrun_count: self.ssi_overrun,
					pointer: VirtualAddress::from(self.ssi_ptr),
					int: self.ssi_int,
				},

				SI_SIGIO => UserspacePoll
				{
					signal,
					error_number: self.ssi_errno,
					band_event: self.ssi_band,
					file_descriptor: self.ssi_fd,
				},

				_ =>
				{
					let process_identifier = ProcessIdentifier::try_from(self.ssi_pid);
					let user_identifier = UserIdentifier::from(self.ssi_uid);

					if self.ssi_code < SI_USER
					{
						UserspaceRealTime
						{
							signal,
							error_number: self.ssi_errno,
							process_identifier,
							user_identifier,
							pointer: VirtualAddress::from(self.ssi_ptr),
							int: self.ssi_int,
							userspace_signal_code: UserspaceSignalCode::from_si_code(self.ssi_code),
						}
					}
					// NOTE: This covers `si_code == 0` and `si_code >= SI_KERNEL`, both real oddities and only possible for the kernel.
					else
					{
						#[cfg(target_arch = "sparc64")]
						assert_ne!(si_code, SI_NOINFO, "SI_NOINFO on sparc64 is not supported");

						Kill
						{
							signal: signal.expect("Linux kernel specified an invalid signal number"),
							process_identifier: process_identifier.expect("Zero or negative process identifier from kernel"),
							user_identifier,
						}
					}
				}
			}
		}
	}

	#[inline(always)]
	fn is_kernel_generated(&self) -> bool
	{
		(self.ssi_code > SI_USER) && (self.ssi_code < SI_KERNEL)
	}

	#[inline(always)]
	fn kernel_generated<C: Code, Constructor: FnOnce(C) -> ParsedSignal>(&self, constructor: Constructor) -> ParsedSignal
	{
		let inclusive_maximum = C::InclusiveMaximum.into();
		if self.ssi_code <= inclusive_maximum
		{
			constructor(C::rehydrate(self.ssi_code))
		}
		else
		{
			self.kernel_generated_fallthrough()
		}
	}

	#[inline(always)]
	fn kernel_generated_Fault<C: Code, Constructor: FnOnce(C) -> FaultCode>(&self, fault_code: Constructor) -> ParsedSignal
	{
		self.kernel_generated::<C, _>(|code| ParsedSignal::Fault
		{
			address: VirtualAddress::from(self.ssi_addr),
			#[cfg(any(target_arch = "mips64", target_arch = "sparc64"))] trap_number: self.ssi_trapno,
			#[cfg(any(target_arch = "arrch64", target_arch = "powerpc64"))] trap_number: self.ssi_errno,
			fault_code: fault_code(code)
		})
	}

	#[inline(always)]
	fn kernel_generated_fallthrough(&self) -> ParsedSignal
	{
		use self::ParsedSignal::*;

		let signal = Signal::parse_raw_signal_number_u32(self.ssi_signo).expect("Kernel specified an invalid signal number");

		debug_assert_eq!(self.ssi_errno, 0);

		if self.ssi_code <= PollCode::InclusiveMaximum.into()
		{
			Poll
			{
				signal,
				band_event: self.ssi_band,
				file_descriptor: self.ssi_fd,
				poll_code: unsafe { transmute(self.ssi_code) },
			}
		}
		else
		{
			Kill
			{
				signal,
				process_identifier: ProcessIdentifier::try_from(self.ssi_pid).expect("Zero or negative process identifier from kernel"),
				user_identifier: UserIdentifier::from(self.ssi_uid),
			}
		}
	}
}