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