#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ProcessConfiguration
{
#[serde(default ="ProcessConfiguration::minimum_linux_kernel_version_default" )] pub minimum_linux_kernel_version: LinuxKernelVersionNumber,
#[serde(default)] pub global: Option<GlobalConfiguration>,
#[serde(default)] pub mandatory_cpu_feature_checks_to_suppress: HashSet<MandatoryCpuFeatureCheck>,
#[serde(default = "ProcessConfiguration::optional_cpu_feature_checks_to_suppress_default")] pub optional_cpu_feature_checks_to_suppress: HashSet<OptionalCpuFeatureCheck>,
#[serde(default)] pub name: ProcessName,
#[serde(default)] pub locale: LocaleName,
#[serde(default)] pub umask: AccessPermissions,
#[serde(default)] pub initial_capabilities: Option<ThreadCapabilitiesConfiguration>,
#[serde(default = "ProcessConfiguration::resource_limits_default")] pub resource_limits: ResourceLimitsSet,
#[serde(default)] pub out_of_memory_adjustment: Option<OutOfMemoryAdjustment>,
#[serde(default)] pub out_of_memory_score_adjustment: Option<OutOfMemoryScoreAdjustment>,
#[serde(default)] pub synchronize_and_drop_caches: bool,
#[serde(default)] pub compact_memory: bool,
#[serde(default)] pub lock_all_memory: Option<LockAllMemory>,
#[serde(default)] pub process_nice_configuration: Option<ProcessNiceConfiguration>,
#[serde(default)] pub process_io_priority_configuration: Option<ProcessIoPriorityConfiguration>,
#[serde(default)] pub enable_or_disable_io_flusher: Option<bool>,
#[allow(missing_docs)]
#[serde(default)] pub machine_check_exception_kill_policy: Option<Option<MachineCheckExceptionKillPolicy>>,
#[allow(missing_docs)]
#[serde(default)] pub timestamp_counter_setting: Option<TimestampCounterSetting>,
#[allow(missing_docs)]
#[serde(default)] pub enable_or_disable_process_performance_counters: Option<bool>,
#[serde(default)] pub logging_configuration: ProcessLoggingConfiguration,
#[serde(default = "ProcessConfiguration::enable_full_rust_stack_back_traces_default")] pub enable_full_rust_stack_back_traces: bool,
#[serde(default = "ProcessConfiguration::binary_paths_default")] pub binary_paths: BTreeSet<PathBuf>,
#[serde(default = "ProcessConfiguration::working_directory_default")] pub working_directory: PathBuf,
#[serde(default = "ProcessConfiguration::core_dump_filter_default")] pub core_dump_filter: CoreDumpFilterFlags,
}
impl Default for ProcessConfiguration
{
#[inline(always)]
fn default() -> Self
{
Self
{
minimum_linux_kernel_version: Self::minimum_linux_kernel_version_default(),
global: None,
mandatory_cpu_feature_checks_to_suppress: Default::default(),
optional_cpu_feature_checks_to_suppress: Self::optional_cpu_feature_checks_to_suppress_default(),
name: Default::default(),
locale: Default::default(),
umask: Default::default(),
initial_capabilities: None,
resource_limits: Self::resource_limits_default(),
out_of_memory_adjustment: None,
out_of_memory_score_adjustment: None,
synchronize_and_drop_caches: false,
compact_memory: false,
lock_all_memory: None,
process_nice_configuration: None,
process_io_priority_configuration: None,
enable_or_disable_io_flusher: None,
machine_check_exception_kill_policy: None,
timestamp_counter_setting: None,
enable_or_disable_process_performance_counters: None,
logging_configuration: Default::default(),
enable_full_rust_stack_back_traces: Self::enable_full_rust_stack_back_traces_default(),
binary_paths: Self::binary_paths_default(),
working_directory: Self::working_directory_default(),
core_dump_filter: Self::core_dump_filter_default()
}
}
}
impl ProcessConfiguration
{
#[inline(always)]
pub fn configure(&self, run_as_daemon: bool, file_system_layout: &FileSystemLayout, defaults: &DefaultHugePageSizes, additional_logging_configuration: &mut impl AdditionalLoggingConfiguration, global_computed_scheduling_affinity: Option<&GlobalComputedSchedulingConfiguration>, process_affinity: Option<&HyperThreads>) -> Result<Arc<impl Terminate>, ProcessConfigurationError>
{
use self::ProcessConfigurationError::*;
let (sys_path, proc_path, dev_path, etc_path) = file_system_layout.paths();
Self::block_all_signals_on_current_thread();
reset_all_signal_handlers_to_default();
Self::validate_not_running_setuid_or_setgid()?;
Self::protect_access_to_proc_self_and_disable_core_dumps()?;
self.cpu_feature_checks()?;
self.validate_linux_kernel_version_is_recent_enough(proc_path)?;
self.set_global_configuration(sys_path, proc_path, defaults)?;
Self::set_global_computed_configuration(sys_path, proc_path, global_computed_scheduling_affinity)?;
self.reduce_initial_capabilities_to_minimum_set()?;
self.apply_core_dump_filter(proc_path)?;
Self::validate_current_personality(proc_path)?;
close_all_open_file_descriptors_apart_from_standard(proc_path).map_err(CouldNotCloseAllOpenFileDescriptorsApartFromStandard)?;
self.umask.set_umask();
set_current_dir(&self.working_directory).map_err(CouldNotChangeWorkingDirectory)?;
self.set_locale()?;
self.set_process_name(proc_path)?;
self.resource_limits.change().map_err(CouldNotChangeResourceLimit)?;
self.set_out_of_memory_adjustment(proc_path)?;
self.synchronize_and_drop_caches(proc_path)?;
self.compact_memory(proc_path)?;
self.set_environment_variables_to_minimum_required_and_force_time_zone_to_utc(etc_path)?;
if run_as_daemon
{
daemonize(dev_path)
}
self.configure_logging(dev_path, proc_path, run_as_daemon, additional_logging_configuration)?;
let terminate = SimpleTerminate::new(ParsedPanicErrorLoggerProcessLoggingConfiguration);
configure_global_panic_hook(&terminate);
self.lock_all_memory();
self.configure_process_affinity(proc_path, process_affinity)?;
set_value(proc_path, |proc_path, process_nice_configuration| process_nice_configuration.configure(proc_path),self.process_nice_configuration.as_ref(), ProcessNiceConfiguration)?;
set_value(proc_path, |_, process_io_priority_configuration| process_io_priority_configuration.configure(), self.process_io_priority_configuration.as_ref(), ProcessIoPriorityConfiguration)?;
self.enable_or_disable_io_flusher()?;
self.change_machine_check_exception_kill_policy().map_err(CouldNotChangeMachineCheckExceptionKillPolicy)?;
self.set_timestamp_counter_setting()?;
self.enable_or_disable_process_performance_counters()?;
#[cfg(any(target_arch = "mips64", target_arch = "powerpc64", target_arch = "x86_64"))] Self::secure_io_ports();
change_no_new_privileges(true).map_err(CouldNotPreventTheGrantingOfNoNewPrivileges)?;
Ok(terminate)
}
#[inline(always)]
fn set_out_of_memory_adjustment(&self, proc_path: &ProcPath) -> Result<(), ProcessConfigurationError>
{
if let Some(out_of_memory_adjustment) = self.out_of_memory_adjustment
{
out_of_memory_adjustment.set(proc_path, ProcessIdentifierChoice::Current).map_err(ProcessConfigurationError::CouldNotChangeOutOfMemoryAdjustment)
}
else
{
Ok(())
}
}
#[inline(always)]
fn synchronize_and_drop_caches(&self, proc_path: &ProcPath) -> Result<(), ProcessConfigurationError>
{
if self.synchronize_and_drop_caches
{
File::synchronize_everything();
assert_effective_user_id_is_root("write /proc/sys/vm/drop_caches");
const DropPageCaches: u8 = 1;
const DropSlabObjects: u8 = 2;
const DisableInformationalLogging: u8 = 4;
set_proc_sys_vm_value(proc_path, "drop_caches", Some(UnpaddedDecimalInteger(DropPageCaches | DropSlabObjects | DisableInformationalLogging)), ProcessConfigurationError::CouldNotDropCaches)
}
else
{
Ok(())
}
}
#[inline(always)]
fn compact_memory(&self, proc_path: &ProcPath) -> Result<(), ProcessConfigurationError>
{
if self.compact_memory
{
assert_effective_user_id_is_root("write /proc/sys/vm/compact_memory");
set_proc_sys_vm_value(proc_path, "compact_memory", Some(true), ProcessConfigurationError::CouldNotCompactMemory)
}
else
{
Ok(())
}
}
fn configure_logging(&self, dev_path: &DevPath, proc_path: &ProcPath, run_as_daemon: bool, additional_logging_configuration: &mut impl AdditionalLoggingConfiguration) -> Result<(), ProcessConfigurationError>
{
use self::ProcessConfigurationError::*;
let internet_protocol_addresses = Self::get_internet_protocol_addresses_using_netlink().map_err(|cause| CouldNotGetInternetProtocolAddressesUsingNetlink(cause))?;
let host_name = LinuxKernelHostName::new(proc_path).map_err(CouldNotParseLinuxKernelHostName)?;
let domain_name = LinuxKernelDomainName::new(proc_path).map_err(CouldNotParseLinuxKernelDomainName)?;
let boot_identifier = BootIdentifierUniversallyUniqueIdentifier::new(proc_path).map_err(CouldNotParseBootIdentifier)?;
self.logging_configuration.configure_logging(additional_logging_configuration, dev_path, !run_as_daemon, &internet_protocol_addresses, host_name.as_ref(), domain_name.as_ref(), &self.name, &boot_identifier)?;
if run_as_daemon
{
self.logging_configuration.redirect_FILE_standard_out_and_file_standard_error_to_log(host_name.as_ref(), &self.name);
}
Ok(())
}
fn get_internet_protocol_addresses_using_netlink() -> Result<Vec<IpAddr>, String>
{
let mut internet_protocol_addresses = Vec::new();
let mut netlink_socket_file_descriptor = NetlinkSocketFileDescriptor::open().map_err(|cause| cause.to_string())?;
for entry in RouteNetlinkProtocol::get_internet_protocol_version_4_addresses(&mut netlink_socket_file_descriptor, None)?
{
match entry.unicast_common.source_address_and_point_to_point_peer_destination_address
{
None => (),
Some((source_address, _)) => internet_protocol_addresses.push(IpAddr::V4(source_address.into())),
}
}
for entry in RouteNetlinkProtocol::get_internet_protocol_version_6_addresses(&mut netlink_socket_file_descriptor, None)?
{
match entry.unicast_common.source_address_and_point_to_point_peer_destination_address
{
None => (),
Some((source_address, _)) => internet_protocol_addresses.push(IpAddr::V6(source_address.into())),
}
}
Ok(internet_protocol_addresses)
}
#[inline(always)]
fn reduce_initial_capabilities_to_minimum_set(&self) -> Result<(), ProcessConfigurationError>
{
if let Some(ref initial_capabilities) = self.initial_capabilities.as_ref()
{
initial_capabilities.configure_just_capabilities().map_err(ProcessConfigurationError::CouldNotChangeInitialCapabilities)
}
else
{
Ok(())
}
}
#[inline(always)]
fn apply_core_dump_filter(&self, proc_path: &ProcPath) -> Result<(), ProcessConfigurationError>
{
self.core_dump_filter.change_core_dump_filter(proc_path, ProcessIdentifierChoice::Current).map_err(ProcessConfigurationError::CouldNotChangeCoredumpFilter)
}
#[cfg(any(target_arch = "mips64", target_arch = "powerpc64", target_arch = "x86_64"))]
#[inline(always)]
fn secure_io_ports()
{
remove_ioport_permissions();
remove_ioport_privileges();
}
#[inline(always)]
fn set_environment_variables_to_minimum_required_and_force_time_zone_to_utc(&self, etc_path: &EtcPath) -> Result<(), ProcessConfigurationError>
{
let utc_file_path = etc_path.zoneinfo("UTC");
utc_file_path.read_raw().map_err(ProcessConfigurationError::UtcFilePathDoesNotExistOrIsNotReadable)?;
populate_clean_environment(self.enable_full_rust_stack_back_traces, &self.binary_paths, UserIdentifier::current_real().user_name_home_directory_and_shell(etc_path), utc_file_path)?;
unsafe { tzset() };
Ok(())
}
#[inline(always)]
fn protect_access_to_proc_self_and_disable_core_dumps() -> Result<(), ProcessConfigurationError>
{
change_dumpable(false).map_err(ProcessConfigurationError::CouldNotDisableDumpable)
}
#[inline(always)]
fn set_process_name(&self, proc_path: &ProcPath) -> Result<(), ProcessConfigurationError>
{
self.name.set_process_name(ProcessIdentifierChoice::Current, proc_path).map_err(ProcessConfigurationError::CouldNotSetProcessName)
}
#[inline(always)]
fn configure_process_affinity(&self, proc_path: &ProcPath, process_affinity: Option<&HyperThreads>) -> Result<(), ProcessConfigurationError>
{
set_value(proc_path, |_proc_path, value| value.set_current_process_affinity(), process_affinity, ProcessConfigurationError::CouldNotChangeProcessAffinity)
}
#[inline(always)]
fn set_locale(&self) -> Result<LocaleName, ProcessConfigurationError>
{
self.locale.set_all().map_err(|_: ()| ProcessConfigurationError::CouldNotSetLocale(self.locale.clone()))
}
#[inline(always)]
fn lock_all_memory(&self)
{
if let Some(lock_all_memory) = self.lock_all_memory
{
lock_all_memory.lock()
}
}
#[inline(always)]
fn set_global_configuration(&self, sys_path: &SysPath, proc_path: &ProcPath, defaults: &DefaultHugePageSizes) -> Result<(), ProcessConfigurationError>
{
set_value(proc_path, |proc_path, global| global.configure(sys_path, proc_path, defaults), self.global.as_ref(), ProcessConfigurationError::CouldNotChangeGlobalConfiguration)
}
#[inline(always)]
fn set_global_computed_configuration(sys_path: &SysPath, proc_path: &ProcPath, global_computed_scheduling_affinity: Option<&GlobalComputedSchedulingConfiguration>) -> Result<(), ProcessConfigurationError>
{
set_value(proc_path, |proc_path, global_computed_scheduling_affinity| global_computed_scheduling_affinity.configure(sys_path, proc_path), global_computed_scheduling_affinity, ProcessConfigurationError::CouldNotChangeGlobalComputedSchedulingConfiguration)
}
#[inline(always)]
fn enable_or_disable_io_flusher(&self) -> Result<(), ProcessConfigurationError>
{
if let Some(enable_or_disable_io_flusher) = self.enable_or_disable_io_flusher
{
change_io_flusher(enable_or_disable_io_flusher).map_err(ProcessConfigurationError::CouldNotEnableOrDisableIoFlusher)
}
else
{
Ok(())
}
}
#[inline(always)]
fn change_machine_check_exception_kill_policy(&self) -> io::Result<()>
{
if let Some(machine_check_exception_kill_policy) = self.machine_check_exception_kill_policy
{
match machine_check_exception_kill_policy
{
None => MachineCheckExceptionKillPolicy::clear_for_current_thread(),
Some(machine_check_exception_kill_policy) => machine_check_exception_kill_policy.set_for_current_thread(),
}
}
else
{
Ok(())
}
}
#[inline(always)]
fn set_timestamp_counter_setting(&self) -> Result<(), ProcessConfigurationError>
{
if let Some(timestamp_counter_setting) = self.timestamp_counter_setting
{
timestamp_counter_setting.set().map_err(ProcessConfigurationError::CouldNotSetTimestampCounterSetting)
}
else
{
Ok(())
}
}
#[inline(always)]
fn enable_or_disable_process_performance_counters(&self) -> Result<(), ProcessConfigurationError>
{
if let Some(enable_or_disable_process_performance_counters) = self.enable_or_disable_process_performance_counters
{
change_process_performance_counters(enable_or_disable_process_performance_counters).map_err(ProcessConfigurationError::CouldNotEnableOrDisableProcessPerformanceCounters)
}
else
{
Ok(())
}
}
#[inline(always)]
fn block_all_signals_on_current_thread()
{
Signals::block_all_signals_on_current_thread();
}
#[inline(always)]
fn validate_not_running_setuid_or_setgid() -> Result<(), ProcessConfigurationError>
{
use self::ProcessConfigurationError::*;
if unlikely!(UserIdentifier::running_setuid())
{
return Err(RunningSetUid)
}
if unlikely!(GroupIdentifier::running_setuid())
{
return Err(RunningSetGid)
}
Ok(())
}
#[inline(always)]
fn validate_current_personality(proc_path: &ProcPath) -> Result<(), ProcessConfigurationError>
{
use self::ProcessConfigurationError::*;
PersonalityFlags::validate_only_one_execution_domain(proc_path);
let current_personality = PersonalityFlags::current().map_err(|_ :()| CouldNotObtainPersonality)?;
if likely!(current_personality.is_standard_linux())
{
Ok(())
}
else
{
Err(CurrentPersonalityIsNotLinux(current_personality))
}
}
#[inline(always)]
fn validate_linux_kernel_version_is_recent_enough(&self, proc_path: &ProcPath) -> Result<(), ProcessConfigurationError>
{
use self::ProcessConfigurationError::*;
if LinuxKernelVersion::parse(proc_path).map_err(CouldNotParseLinuxKernelVersion)?.major_minor_revision() < self.minimum_linux_kernel_version
{
Err(LinuxKernelVersionIsTooOld)
}
else
{
Ok(())
}
}
#[inline(always)]
fn cpu_feature_checks(&self) -> Result<(), ProcessConfigurationError>
{
let check_arguments;
#[cfg(target_arch = "x86_64")]
{
let cpu_id = CpuId::new();
check_arguments =
(
cpu_id.get_feature_info().unwrap(),
cpu_id.get_extended_function_info().unwrap(),
cpu_id.get_extended_feature_info().unwrap(),
);
}
#[cfg(not(target_arch = "x86_64"))]
{
check_arguments = ();
}
use self::ProcessConfigurationError::*;
let empty: HashSet<CompiledCpuFeatureCheck> = HashSet::new();
Self::cpu_feature_check(&empty, &check_arguments, CompiledCpuFeatureChecksFailed)?;
Self::cpu_feature_check(&self.mandatory_cpu_feature_checks_to_suppress, &check_arguments, MandatoryCpuFeatureChecksFailed)?;
Self::cpu_feature_check(&self.optional_cpu_feature_checks_to_suppress, &check_arguments, OptionalCpuFeatureChecksFailed)
}
#[inline(always)]
fn cpu_feature_check<C: Check>(cpu_feature_checks_to_suppress: &HashSet<C>, check_arguments: &C::CheckArguments, error: impl FnOnce(FailedChecks<C>) -> ProcessConfigurationError) -> Result<(), ProcessConfigurationError>
{
C::run_all_checks(cpu_feature_checks_to_suppress, check_arguments).map_err(error)
}
#[inline(always)]
const fn minimum_linux_kernel_version_default() -> LinuxKernelVersionNumber
{
LinuxKernelVersionNumber::MinimumForIoUringSupport
}
#[inline(always)]
fn optional_cpu_feature_checks_to_suppress_default() -> HashSet<OptionalCpuFeatureCheck>
{
#[cfg(target_arch = "x86_64")] use self::OptionalCpuFeatureCheck::*;
#[cfg(target_arch = "x86_64")] return fast_secure_hash_set!
{
has_rep_movsb_stosb,
has_prefetchw,
has_ss,
has_working_xsave,
has_tsc_adjust_msr,
has_invpcid,
has_smap,
};
#[cfg(not(target_arch = "x86_64"))] return fast_secure_hash_set!
{
};
}
#[inline(always)]
fn resource_limits_default() -> ResourceLimitsSet
{
ResourceLimitsSet::defaultish(ResourceLimit::maximum_number_of_open_file_descriptors(&ProcPath::default()).expect("Could not read maximum number of file descriptors"))
}
#[inline(always)]
const fn enable_full_rust_stack_back_traces_default() -> bool
{
true
}
#[inline(always)]
fn binary_paths_default() -> BTreeSet<PathBuf>
{
btreeset!
{
PathBuf::from("/usr/bin"),
PathBuf::from("/bin"),
}
}
#[inline(always)]
fn working_directory_default() -> PathBuf
{
PathBuf::from("/")
}
#[inline(always)]
fn core_dump_filter_default() -> CoreDumpFilterFlags
{
CoreDumpFilterFlags::empty()
}
}