Skip to main content

mz_environmentd/environmentd/
sys.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! System support functions.
11
12use anyhow::Context;
13use nix::sys::signal;
14use tracing::trace;
15
16#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "ios")))]
17pub fn adjust_rlimits() {
18    trace!("rlimit crate does not support this OS; not adjusting nofile limit");
19}
20
21/// Attempts to increase the soft nofile rlimit to the maximum possible value.
22#[cfg(any(target_os = "macos", target_os = "linux", target_os = "ios"))]
23pub fn adjust_rlimits() {
24    use rlimit::Resource;
25    use tracing::warn;
26
27    // getrlimit/setrlimit can have surprisingly different behavior across
28    // platforms, even with the rlimit wrapper crate that we use. This function
29    // is chattier than normal at the trace log level in an attempt to ease
30    // debugging of such differences.
31
32    let (soft, hard) = match Resource::NOFILE.get() {
33        Ok(limits) => limits,
34        Err(e) => {
35            trace!("unable to read initial nofile rlimit: {}", e);
36            return;
37        }
38    };
39    trace!("initial nofile rlimit: ({}, {})", soft, hard);
40
41    #[cfg(target_os = "macos")]
42    let hard = {
43        use std::cmp;
44
45        use mz_ore::result::ResultExt;
46        use sysctl::Sysctl;
47
48        // On macOS, getrlimit by default reports that the hard limit is
49        // unlimited, but there is usually a stricter hard limit discoverable
50        // via sysctl. Failing to discover this secret stricter hard limit will
51        // cause the call to setrlimit below to fail.
52        let res = sysctl::Ctl::new("kern.maxfilesperproc")
53            .and_then(|ctl| ctl.value())
54            .map_err_to_string_with_causes()
55            .and_then(|v| match v {
56                sysctl::CtlValue::Int(v) => u64::try_from(v)
57                    .map_err(|_| format!("kern.maxfilesperproc unexpectedly negative: {}", v)),
58                o => Err(format!("unexpected sysctl value type: {:?}", o)),
59            });
60        match res {
61            Ok(v) => {
62                trace!("sysctl kern.maxfilesperproc hard limit: {}", v);
63                cmp::min(v, hard)
64            }
65            Err(e) => {
66                trace!("error while reading sysctl: {}", e);
67                hard
68            }
69        }
70    };
71
72    trace!("attempting to adjust nofile rlimit to ({0}, {0})", hard);
73    if let Err(e) = Resource::NOFILE.set(hard, hard) {
74        trace!("error adjusting nofile rlimit: {}", e);
75        return;
76    }
77
78    // Check whether getrlimit reflects the limit we installed with setrlimit.
79    // Some platforms will silently ignore invalid values in setrlimit.
80    let (soft, hard) = match Resource::NOFILE.get() {
81        Ok(limits) => limits,
82        Err(e) => {
83            trace!("unable to read adjusted nofile rlimit: {}", e);
84            return;
85        }
86    };
87    trace!("adjusted nofile rlimit: ({}, {})", soft, hard);
88
89    const RECOMMENDED_SOFT: u64 = 1024;
90    if soft < RECOMMENDED_SOFT {
91        warn!(
92            "soft nofile rlimit ({}) is dangerously low; at least {} is recommended",
93            soft, RECOMMENDED_SOFT
94        )
95    }
96}
97
98pub fn enable_sigusr2_coverage_dump() -> Result<(), anyhow::Error> {
99    let action = signal::SigAction::new(
100        signal::SigHandler::Handler(handle_sigusr2_signal),
101        signal::SaFlags::SA_NODEFER | signal::SaFlags::SA_ONSTACK,
102        signal::SigSet::empty(),
103    );
104
105    unsafe { signal::sigaction(signal::SIGUSR2, &action) }
106        .context("failed to install SIGUSR2 handler")?;
107
108    Ok(())
109}
110
111pub fn enable_termination_signal_cleanup() -> Result<(), anyhow::Error> {
112    let action = signal::SigAction::new(
113        signal::SigHandler::Handler(handle_termination_signal),
114        signal::SaFlags::SA_NODEFER | signal::SaFlags::SA_ONSTACK,
115        signal::SigSet::empty(),
116    );
117
118    for signum in &[
119        signal::SIGHUP,
120        signal::SIGINT,
121        signal::SIGALRM,
122        signal::SIGTERM,
123        signal::SIGUSR1,
124    ] {
125        unsafe { signal::sigaction(*signum, &action) }
126            .with_context(|| format!("failed to install handler for {}", signum))?;
127    }
128
129    Ok(())
130}
131
132unsafe extern "C" {
133    fn __llvm_profile_write_file() -> std::ffi::c_int;
134}
135
136extern "C" fn handle_sigusr2_signal(_: i32) {
137    let _ = unsafe { __llvm_profile_write_file() };
138}
139
140extern "C" fn handle_termination_signal(signum: i32) {
141    let _ = unsafe { __llvm_profile_write_file() };
142
143    let action = signal::SigAction::new(
144        signal::SigHandler::SigDfl,
145        signal::SaFlags::SA_NODEFER | signal::SaFlags::SA_ONSTACK,
146        signal::SigSet::empty(),
147    );
148    unsafe { signal::sigaction(signum.try_into().unwrap(), &action) }
149        .unwrap_or_else(|_| panic!("failed to uninstall handler for {}", signum));
150
151    signal::raise(signum.try_into().unwrap())
152        .unwrap_or_else(|e| panic!("failed to re-raise signal {}: {}", signum, e));
153}