#![allow(unsafe_code)]
use core::mem::MaybeUninit;
use core::num::NonZeroU64;
use core::ptr;
use core::ptr::NonNull;
use core::sync::atomic::AtomicU8;
use bitflags::bitflags;
use crate::backend::prctl::syscalls;
#[cfg(feature = "alloc")]
use crate::ffi::CString;
use crate::ffi::{c_int, c_uint, c_void, CStr};
use crate::io;
use crate::io::Errno;
use crate::pid::Pid;
use crate::prctl::{
prctl_1arg, prctl_2args, prctl_3args, prctl_get_at_arg2_optional, PointerAuthenticationKeys,
};
use crate::utils::as_ptr;
use super::CapabilitySet;
const PR_GET_KEEPCAPS: c_int = 7;
#[inline]
pub fn get_keep_capabilities() -> io::Result<bool> {
unsafe { prctl_1arg(PR_GET_KEEPCAPS) }.map(|r| r != 0)
}
const PR_SET_KEEPCAPS: c_int = 8;
#[inline]
pub fn set_keep_capabilities(enable: bool) -> io::Result<()> {
unsafe { prctl_2args(PR_SET_KEEPCAPS, usize::from(enable) as *mut _) }.map(|_r| ())
}
#[cfg(feature = "alloc")]
const PR_GET_NAME: c_int = 16;
#[inline]
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn name() -> io::Result<CString> {
let mut buffer = [0_u8; 16];
unsafe { prctl_2args(PR_GET_NAME, buffer.as_mut_ptr().cast())? };
let len = buffer.iter().position(|&x| x == 0_u8).unwrap_or(0);
CString::new(&buffer[..len]).map_err(|_r| io::Errno::ILSEQ)
}
const PR_SET_NAME: c_int = 15;
#[inline]
pub fn set_name(name: &CStr) -> io::Result<()> {
unsafe { prctl_2args(PR_SET_NAME, name.as_ptr() as *mut _) }.map(|_r| ())
}
const PR_GET_SECCOMP: c_int = 21;
const SECCOMP_MODE_DISABLED: i32 = 0;
const SECCOMP_MODE_STRICT: i32 = 1;
const SECCOMP_MODE_FILTER: i32 = 2;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum SecureComputingMode {
Disabled = SECCOMP_MODE_DISABLED,
Strict = SECCOMP_MODE_STRICT,
Filter = SECCOMP_MODE_FILTER,
}
impl TryFrom<i32> for SecureComputingMode {
type Error = io::Errno;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
SECCOMP_MODE_DISABLED => Ok(Self::Disabled),
SECCOMP_MODE_STRICT => Ok(Self::Strict),
SECCOMP_MODE_FILTER => Ok(Self::Filter),
_ => Err(io::Errno::RANGE),
}
}
}
#[inline]
pub fn secure_computing_mode() -> io::Result<SecureComputingMode> {
unsafe { prctl_1arg(PR_GET_SECCOMP) }.and_then(TryInto::try_into)
}
const PR_SET_SECCOMP: c_int = 22;
#[inline]
pub fn set_secure_computing_mode(mode: SecureComputingMode) -> io::Result<()> {
unsafe { prctl_2args(PR_SET_SECCOMP, mode as usize as *mut _) }.map(|_r| ())
}
const PR_CAPBSET_READ: c_int = 23;
#[deprecated(since = "1.1.0", note = "Use CapabilitySet with a single bit instead")]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u32)]
#[non_exhaustive]
pub enum Capability {
ChangeOwnership = linux_raw_sys::general::CAP_CHOWN,
DACOverride = linux_raw_sys::general::CAP_DAC_OVERRIDE,
DACReadSearch = linux_raw_sys::general::CAP_DAC_READ_SEARCH,
FileOwner = linux_raw_sys::general::CAP_FOWNER,
FileSetID = linux_raw_sys::general::CAP_FSETID,
Kill = linux_raw_sys::general::CAP_KILL,
SetGroupID = linux_raw_sys::general::CAP_SETGID,
SetUserID = linux_raw_sys::general::CAP_SETUID,
SetPermittedCapabilities = linux_raw_sys::general::CAP_SETPCAP,
LinuxImmutable = linux_raw_sys::general::CAP_LINUX_IMMUTABLE,
NetBindService = linux_raw_sys::general::CAP_NET_BIND_SERVICE,
NetBroadcast = linux_raw_sys::general::CAP_NET_BROADCAST,
NetAdmin = linux_raw_sys::general::CAP_NET_ADMIN,
NetRaw = linux_raw_sys::general::CAP_NET_RAW,
IPCLock = linux_raw_sys::general::CAP_IPC_LOCK,
IPCOwner = linux_raw_sys::general::CAP_IPC_OWNER,
SystemModule = linux_raw_sys::general::CAP_SYS_MODULE,
SystemRawIO = linux_raw_sys::general::CAP_SYS_RAWIO,
SystemChangeRoot = linux_raw_sys::general::CAP_SYS_CHROOT,
SystemProcessTrace = linux_raw_sys::general::CAP_SYS_PTRACE,
SystemProcessAccounting = linux_raw_sys::general::CAP_SYS_PACCT,
SystemAdmin = linux_raw_sys::general::CAP_SYS_ADMIN,
SystemBoot = linux_raw_sys::general::CAP_SYS_BOOT,
SystemNice = linux_raw_sys::general::CAP_SYS_NICE,
SystemResource = linux_raw_sys::general::CAP_SYS_RESOURCE,
SystemTime = linux_raw_sys::general::CAP_SYS_TIME,
SystemTTYConfig = linux_raw_sys::general::CAP_SYS_TTY_CONFIG,
MakeNode = linux_raw_sys::general::CAP_MKNOD,
Lease = linux_raw_sys::general::CAP_LEASE,
AuditWrite = linux_raw_sys::general::CAP_AUDIT_WRITE,
AuditControl = linux_raw_sys::general::CAP_AUDIT_CONTROL,
SetFileCapabilities = linux_raw_sys::general::CAP_SETFCAP,
MACOverride = linux_raw_sys::general::CAP_MAC_OVERRIDE,
MACAdmin = linux_raw_sys::general::CAP_MAC_ADMIN,
SystemLog = linux_raw_sys::general::CAP_SYSLOG,
WakeAlarm = linux_raw_sys::general::CAP_WAKE_ALARM,
BlockSuspend = linux_raw_sys::general::CAP_BLOCK_SUSPEND,
AuditRead = linux_raw_sys::general::CAP_AUDIT_READ,
PerformanceMonitoring = linux_raw_sys::general::CAP_PERFMON,
BerkeleyPacketFilters = linux_raw_sys::general::CAP_BPF,
CheckpointRestore = linux_raw_sys::general::CAP_CHECKPOINT_RESTORE,
}
mod private {
pub trait Sealed {}
pub struct Token;
#[allow(deprecated)]
impl Sealed for crate::thread::Capability {}
impl Sealed for crate::thread::CapabilitySet {}
}
pub trait CompatCapability: private::Sealed + Copy {
#[doc(hidden)]
fn as_capability_set(self, _: private::Token) -> CapabilitySet;
}
#[allow(deprecated)]
impl CompatCapability for Capability {
fn as_capability_set(self, _: private::Token) -> CapabilitySet {
match self {
Self::ChangeOwnership => CapabilitySet::CHOWN,
Self::DACOverride => CapabilitySet::DAC_OVERRIDE,
Self::DACReadSearch => CapabilitySet::DAC_READ_SEARCH,
Self::FileOwner => CapabilitySet::FOWNER,
Self::FileSetID => CapabilitySet::FSETID,
Self::Kill => CapabilitySet::KILL,
Self::SetGroupID => CapabilitySet::SETGID,
Self::SetUserID => CapabilitySet::SETUID,
Self::SetPermittedCapabilities => CapabilitySet::SETPCAP,
Self::LinuxImmutable => CapabilitySet::LINUX_IMMUTABLE,
Self::NetBindService => CapabilitySet::NET_BIND_SERVICE,
Self::NetBroadcast => CapabilitySet::NET_BROADCAST,
Self::NetAdmin => CapabilitySet::NET_ADMIN,
Self::NetRaw => CapabilitySet::NET_RAW,
Self::IPCLock => CapabilitySet::IPC_LOCK,
Self::IPCOwner => CapabilitySet::IPC_OWNER,
Self::SystemModule => CapabilitySet::SYS_MODULE,
Self::SystemRawIO => CapabilitySet::SYS_RAWIO,
Self::SystemChangeRoot => CapabilitySet::SYS_CHROOT,
Self::SystemProcessTrace => CapabilitySet::SYS_PTRACE,
Self::SystemProcessAccounting => CapabilitySet::SYS_PACCT,
Self::SystemAdmin => CapabilitySet::SYS_ADMIN,
Self::SystemBoot => CapabilitySet::SYS_BOOT,
Self::SystemNice => CapabilitySet::SYS_NICE,
Self::SystemResource => CapabilitySet::SYS_RESOURCE,
Self::SystemTime => CapabilitySet::SYS_TIME,
Self::SystemTTYConfig => CapabilitySet::SYS_TTY_CONFIG,
Self::MakeNode => CapabilitySet::MKNOD,
Self::Lease => CapabilitySet::LEASE,
Self::AuditWrite => CapabilitySet::AUDIT_WRITE,
Self::AuditControl => CapabilitySet::AUDIT_CONTROL,
Self::SetFileCapabilities => CapabilitySet::SETFCAP,
Self::MACOverride => CapabilitySet::MAC_OVERRIDE,
Self::MACAdmin => CapabilitySet::MAC_ADMIN,
Self::SystemLog => CapabilitySet::SYSLOG,
Self::WakeAlarm => CapabilitySet::WAKE_ALARM,
Self::BlockSuspend => CapabilitySet::BLOCK_SUSPEND,
Self::AuditRead => CapabilitySet::AUDIT_READ,
Self::PerformanceMonitoring => CapabilitySet::PERFMON,
Self::BerkeleyPacketFilters => CapabilitySet::BPF,
Self::CheckpointRestore => CapabilitySet::CHECKPOINT_RESTORE,
}
}
}
impl CompatCapability for CapabilitySet {
fn as_capability_set(self, _: private::Token) -> CapabilitySet {
self
}
}
#[inline]
pub fn capability_is_in_bounding_set(capability: impl CompatCapability) -> io::Result<bool> {
let capset = capability.as_capability_set(private::Token).bits();
if capset.count_ones() != 1 {
return Err(Errno::INVAL);
}
let cap = capset.trailing_zeros();
unsafe { prctl_2args(PR_CAPBSET_READ, cap as usize as *mut _) }.map(|r| r != 0)
}
const PR_CAPBSET_DROP: c_int = 24;
#[inline]
pub fn remove_capability_from_bounding_set(capability: impl CompatCapability) -> io::Result<()> {
let capset = capability.as_capability_set(private::Token).bits();
if capset.count_ones() != 1 {
return Err(Errno::INVAL);
}
let cap = capset.trailing_zeros();
unsafe { prctl_2args(PR_CAPBSET_DROP, cap as usize as *mut _) }.map(|_r| ())
}
const PR_GET_SECUREBITS: c_int = 27;
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct CapabilitiesSecureBits: u32 {
const NO_ROOT = 1_u32 << 0;
const NO_ROOT_LOCKED = 1_u32 << 1;
const NO_SETUID_FIXUP = 1_u32 << 2;
const NO_SETUID_FIXUP_LOCKED = 1_u32 << 3;
const KEEP_CAPS = 1_u32 << 4;
const KEEP_CAPS_LOCKED = 1_u32 << 5;
const NO_CAP_AMBIENT_RAISE = 1_u32 << 6;
const NO_CAP_AMBIENT_RAISE_LOCKED = 1_u32 << 7;
const _ = !0;
}
}
#[inline]
pub fn capabilities_secure_bits() -> io::Result<CapabilitiesSecureBits> {
let r = unsafe { prctl_1arg(PR_GET_SECUREBITS)? } as c_uint;
CapabilitiesSecureBits::from_bits(r).ok_or(io::Errno::RANGE)
}
const PR_SET_SECUREBITS: c_int = 28;
#[inline]
pub fn set_capabilities_secure_bits(bits: CapabilitiesSecureBits) -> io::Result<()> {
unsafe { prctl_2args(PR_SET_SECUREBITS, bits.bits() as usize as *mut _) }.map(|_r| ())
}
const PR_GET_TIMERSLACK: c_int = 30;
#[inline]
pub fn current_timer_slack() -> io::Result<u64> {
unsafe { prctl_1arg(PR_GET_TIMERSLACK) }.map(|r| r as u64)
}
const PR_SET_TIMERSLACK: c_int = 29;
#[inline]
pub fn set_current_timer_slack(value: Option<NonZeroU64>) -> io::Result<()> {
let value = usize::try_from(value.map_or(0, NonZeroU64::get)).map_err(|_r| io::Errno::RANGE)?;
unsafe { prctl_2args(PR_SET_TIMERSLACK, value as *mut _) }.map(|_r| ())
}
const PR_GET_NO_NEW_PRIVS: c_int = 39;
#[inline]
pub fn no_new_privs() -> io::Result<bool> {
unsafe { prctl_1arg(PR_GET_NO_NEW_PRIVS) }.map(|r| r != 0)
}
const PR_SET_NO_NEW_PRIVS: c_int = 38;
#[inline]
pub fn set_no_new_privs(no_new_privs: bool) -> io::Result<()> {
unsafe { prctl_2args(PR_SET_NO_NEW_PRIVS, usize::from(no_new_privs) as *mut _) }.map(|_r| ())
}
const PR_GET_TID_ADDRESS: c_int = 40;
#[inline]
pub fn get_clear_child_tid_address() -> io::Result<Option<NonNull<c_void>>> {
unsafe { prctl_get_at_arg2_optional::<*mut c_void>(PR_GET_TID_ADDRESS) }.map(NonNull::new)
}
const PR_GET_THP_DISABLE: c_int = 42;
#[inline]
pub fn transparent_huge_pages_are_disabled() -> io::Result<bool> {
unsafe { prctl_1arg(PR_GET_THP_DISABLE) }.map(|r| r != 0)
}
const PR_SET_THP_DISABLE: c_int = 41;
#[inline]
pub fn disable_transparent_huge_pages(thp_disable: bool) -> io::Result<()> {
unsafe { prctl_2args(PR_SET_THP_DISABLE, usize::from(thp_disable) as *mut _) }.map(|_r| ())
}
const PR_CAP_AMBIENT: c_int = 47;
const PR_CAP_AMBIENT_IS_SET: usize = 1;
#[inline]
pub fn capability_is_in_ambient_set(capability: impl CompatCapability) -> io::Result<bool> {
let capset = capability.as_capability_set(private::Token).bits();
if capset.count_ones() != 1 {
return Err(Errno::INVAL);
}
let cap = capset.trailing_zeros();
unsafe {
prctl_3args(
PR_CAP_AMBIENT,
PR_CAP_AMBIENT_IS_SET as *mut _,
cap as usize as *mut _,
)
}
.map(|r| r != 0)
}
const PR_CAP_AMBIENT_CLEAR_ALL: usize = 4;
#[inline]
pub fn clear_ambient_capability_set() -> io::Result<()> {
unsafe { prctl_2args(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL as *mut _) }.map(|_r| ())
}
const PR_CAP_AMBIENT_RAISE: usize = 2;
const PR_CAP_AMBIENT_LOWER: usize = 3;
#[inline]
pub fn configure_capability_in_ambient_set(
capability: impl CompatCapability,
enable: bool,
) -> io::Result<()> {
let sub_operation = if enable {
PR_CAP_AMBIENT_RAISE
} else {
PR_CAP_AMBIENT_LOWER
};
let capset = capability.as_capability_set(private::Token).bits();
if capset.count_ones() != 1 {
return Err(Errno::INVAL);
}
let cap = capset.trailing_zeros();
unsafe {
prctl_3args(
PR_CAP_AMBIENT,
sub_operation as *mut _,
cap as usize as *mut _,
)
}
.map(|_r| ())
}
const PR_SVE_GET_VL: c_int = 51;
const PR_SVE_VL_LEN_MASK: u32 = 0xffff;
const PR_SVE_VL_INHERIT: u32 = 1_u32 << 17;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct SVEVectorLengthConfig {
pub vector_length_in_bytes: u32,
pub vector_length_inherited_across_execve: bool,
}
#[inline]
pub fn sve_vector_length_configuration() -> io::Result<SVEVectorLengthConfig> {
let bits = unsafe { prctl_1arg(PR_SVE_GET_VL)? } as c_uint;
Ok(SVEVectorLengthConfig {
vector_length_in_bytes: bits & PR_SVE_VL_LEN_MASK,
vector_length_inherited_across_execve: (bits & PR_SVE_VL_INHERIT) != 0,
})
}
const PR_SVE_SET_VL: c_int = 50;
const PR_SVE_SET_VL_ONEXEC: u32 = 1_u32 << 18;
#[inline]
pub unsafe fn set_sve_vector_length_configuration(
vector_length_in_bytes: usize,
vector_length_inherited_across_execve: bool,
defer_change_to_next_execve: bool,
) -> io::Result<()> {
let vector_length_in_bytes =
u32::try_from(vector_length_in_bytes).map_err(|_r| io::Errno::RANGE)?;
let mut bits = vector_length_in_bytes & PR_SVE_VL_LEN_MASK;
if vector_length_inherited_across_execve {
bits |= PR_SVE_VL_INHERIT;
}
if defer_change_to_next_execve {
bits |= PR_SVE_SET_VL_ONEXEC;
}
prctl_2args(PR_SVE_SET_VL, bits as usize as *mut _).map(|_r| ())
}
const PR_PAC_RESET_KEYS: c_int = 54;
#[inline]
pub unsafe fn reset_pointer_authentication_keys(
keys: Option<PointerAuthenticationKeys>,
) -> io::Result<()> {
let keys = keys.as_ref().map_or(0_u32, PointerAuthenticationKeys::bits);
prctl_2args(PR_PAC_RESET_KEYS, keys as usize as *mut _).map(|_r| ())
}
const PR_GET_TAGGED_ADDR_CTRL: c_int = 56;
const PR_MTE_TAG_SHIFT: u32 = 3;
const PR_MTE_TAG_MASK: u32 = 0xffff_u32 << PR_MTE_TAG_SHIFT;
bitflags! {
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct TaggedAddressMode: u32 {
const ENABLED = 1_u32 << 0;
const TCF_SYNC = 1_u32 << 1;
const TCF_ASYNC = 1_u32 << 2;
const _ = !0;
}
}
#[inline]
pub fn current_tagged_address_mode() -> io::Result<(Option<TaggedAddressMode>, u32)> {
let r = unsafe { prctl_1arg(PR_GET_TAGGED_ADDR_CTRL)? } as c_uint;
let mode = r & 0b111_u32;
let mte_tag = (r & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT;
Ok((TaggedAddressMode::from_bits(mode), mte_tag))
}
const PR_SET_TAGGED_ADDR_CTRL: c_int = 55;
#[inline]
pub unsafe fn set_current_tagged_address_mode(
mode: Option<TaggedAddressMode>,
mte_tag: u32,
) -> io::Result<()> {
let config = mode.as_ref().map_or(0_u32, TaggedAddressMode::bits)
| ((mte_tag << PR_MTE_TAG_SHIFT) & PR_MTE_TAG_MASK);
prctl_2args(PR_SET_TAGGED_ADDR_CTRL, config as usize as *mut _).map(|_r| ())
}
const PR_SET_SYSCALL_USER_DISPATCH: c_int = 59;
const PR_SYS_DISPATCH_OFF: usize = 0;
#[inline]
pub unsafe fn disable_syscall_user_dispatch() -> io::Result<()> {
prctl_2args(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_OFF as *mut _).map(|_r| ())
}
const PR_SYS_DISPATCH_ON: usize = 1;
const SYSCALL_DISPATCH_FILTER_ALLOW: u8 = 0;
const SYSCALL_DISPATCH_FILTER_BLOCK: u8 = 1;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum SysCallUserDispatchFastSwitch {
Allow = SYSCALL_DISPATCH_FILTER_ALLOW,
Block = SYSCALL_DISPATCH_FILTER_BLOCK,
}
impl TryFrom<u8> for SysCallUserDispatchFastSwitch {
type Error = io::Errno;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
SYSCALL_DISPATCH_FILTER_ALLOW => Ok(Self::Allow),
SYSCALL_DISPATCH_FILTER_BLOCK => Ok(Self::Block),
_ => Err(io::Errno::RANGE),
}
}
}
#[inline]
pub unsafe fn enable_syscall_user_dispatch(
always_allowed_region: &[u8],
fast_switch_flag: &AtomicU8,
) -> io::Result<()> {
syscalls::prctl(
PR_SET_SYSCALL_USER_DISPATCH,
PR_SYS_DISPATCH_ON as *mut _,
always_allowed_region.as_ptr() as *mut _,
always_allowed_region.len() as *mut _,
as_ptr(fast_switch_flag) as *mut _,
)
.map(|_r| ())
}
const PR_SCHED_CORE: c_int = 62;
const PR_SCHED_CORE_GET: usize = 0;
const PR_SCHED_CORE_SCOPE_THREAD: u32 = 0;
const PR_SCHED_CORE_SCOPE_THREAD_GROUP: u32 = 1;
const PR_SCHED_CORE_SCOPE_PROCESS_GROUP: u32 = 2;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum CoreSchedulingScope {
Thread = PR_SCHED_CORE_SCOPE_THREAD,
ThreadGroup = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
ProcessGroup = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
}
impl TryFrom<u32> for CoreSchedulingScope {
type Error = io::Errno;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
PR_SCHED_CORE_SCOPE_THREAD => Ok(Self::Thread),
PR_SCHED_CORE_SCOPE_THREAD_GROUP => Ok(Self::ThreadGroup),
PR_SCHED_CORE_SCOPE_PROCESS_GROUP => Ok(Self::ProcessGroup),
_ => Err(io::Errno::RANGE),
}
}
}
#[inline]
pub fn core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<u64> {
let mut value: MaybeUninit<u64> = MaybeUninit::uninit();
unsafe {
syscalls::prctl(
PR_SCHED_CORE,
PR_SCHED_CORE_GET as *mut _,
pid.as_raw_nonzero().get() as usize as *mut _,
scope as usize as *mut _,
value.as_mut_ptr().cast(),
)?;
Ok(value.assume_init())
}
}
const PR_SCHED_CORE_CREATE: usize = 1;
#[inline]
pub fn create_core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<()> {
unsafe {
syscalls::prctl(
PR_SCHED_CORE,
PR_SCHED_CORE_CREATE as *mut _,
pid.as_raw_nonzero().get() as usize as *mut _,
scope as usize as *mut _,
ptr::null_mut(),
)
.map(|_r| ())
}
}
const PR_SCHED_CORE_SHARE_TO: usize = 2;
#[inline]
pub fn push_core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<()> {
unsafe {
syscalls::prctl(
PR_SCHED_CORE,
PR_SCHED_CORE_SHARE_TO as *mut _,
pid.as_raw_nonzero().get() as usize as *mut _,
scope as usize as *mut _,
ptr::null_mut(),
)
.map(|_r| ())
}
}
const PR_SCHED_CORE_SHARE_FROM: usize = 3;
#[inline]
pub fn pull_core_scheduling_cookie(pid: Pid, scope: CoreSchedulingScope) -> io::Result<()> {
unsafe {
syscalls::prctl(
PR_SCHED_CORE,
PR_SCHED_CORE_SHARE_FROM as *mut _,
pid.as_raw_nonzero().get() as usize as *mut _,
scope as usize as *mut _,
ptr::null_mut(),
)
.map(|_r| ())
}
}