Skip to content

Commit 2c22e92

Browse files
committed
Close non-stdio file handles when daemonizing
Otherwise, when the compiler wrapper spawns the sccache server, the server may end up with unintended file descriptors, which can lead to unexpected problems. This is particularly problematic if any of those file descriptors that accidentally end up in the server process is a pipe, as the pipe will only be closed when all the processes with that file descriptor close it or exit. This was causing sccache to hang ninja, as ninja uses the EOF of a pipe given to the subprocess to detect when that subprocess has exited: ninja-build/ninja#2444 (comment) This patch adds a dependency on the [close_fds](https://crates.io/crates/close_fds) crate, which automatically chooses an appropriate mechanism to close a range of file descriptors. On Linux 5.9+ that mechanism will be libc::close_range(). Fixes mozilla#2313
1 parent 9ca8beb commit 2c22e92

File tree

5 files changed

+28
-23
lines changed

5 files changed

+28
-23
lines changed

Cargo.lock

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ rouille = { version = "3.6", optional = true, default-features = false, features
121121
] }
122122
syslog = { version = "6", optional = true }
123123
version-compare = { version = "0.1.1", optional = true }
124+
close_fds = "0.3.2"
124125

125126
[dev-dependencies]
126127
assert_cmd = "2.0.13"

src/bin/sccache-dist/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ fn run(command: Command) -> Result<i32> {
209209
}
210210
};
211211

212-
daemonize()?;
212+
daemonize(None)?;
213213
let scheduler = Scheduler::new();
214214
let http_scheduler = dist::http::Scheduler::new(
215215
public_addr,

src/commands.rs

+4-20
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,6 @@ fn run_server_process(startup_timeout: Option<Duration>) -> Result<ServerStartup
126126
})
127127
}
128128

129-
#[cfg(not(windows))]
130-
fn redirect_stderr(f: File) {
131-
use libc::dup2;
132-
use std::os::unix::io::IntoRawFd;
133-
// Ignore errors here.
134-
unsafe {
135-
dup2(f.into_raw_fd(), 2);
136-
}
137-
}
138-
139129
#[cfg(windows)]
140130
fn redirect_stderr(f: File) {
141131
use std::os::windows::io::IntoRawHandle;
@@ -165,13 +155,6 @@ fn create_error_log() -> Result<File> {
165155
Ok(f)
166156
}
167157

168-
/// If `SCCACHE_ERROR_LOG` is set, redirect stderr to it.
169-
fn redirect_error_log(f: File) -> Result<()> {
170-
debug!("redirecting stderr into {:?}", f);
171-
redirect_stderr(f);
172-
Ok(())
173-
}
174-
175158
/// Re-execute the current executable as a background server.
176159
#[cfg(windows)]
177160
fn run_server_process(startup_timeout: Option<Duration>) -> Result<ServerStartup> {
@@ -654,14 +637,15 @@ pub fn run_command(cmd: Command) -> Result<i32> {
654637
}
655638
Command::InternalStartServer => {
656639
trace!("Command::InternalStartServer");
640+
// If `SCCACHE_ERROR_LOG` is set, redirect stderr to it.
657641
if env::var("SCCACHE_ERROR_LOG").is_ok() {
658642
let f = create_error_log()?;
643+
debug!("redirecting stderr into {:?}", f);
659644
// Can't report failure here, we're already daemonized.
660-
daemonize()?;
661-
redirect_error_log(f)?;
645+
daemonize(Some(f))?;
662646
} else {
663647
// We aren't asking for a log file
664-
daemonize()?;
648+
daemonize(None)?;
665649
}
666650
server::start_server(config, &get_addr())?;
667651
}

src/util.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ impl<'a> Hasher for HashToDigest<'a> {
841841

842842
/// Pipe `cmd`'s stdio to `/dev/null`, unless a specific env var is set.
843843
#[cfg(not(windows))]
844-
pub fn daemonize() -> Result<()> {
844+
pub fn daemonize(log_file: Option<File>) -> Result<()> {
845845
use crate::jobserver::discard_inherited_jobserver;
846846
use daemonize::Daemonize;
847847
use std::env;
@@ -850,7 +850,16 @@ pub fn daemonize() -> Result<()> {
850850
match env::var("SCCACHE_NO_DAEMON") {
851851
Ok(ref val) if val == "1" => {}
852852
_ => {
853-
Daemonize::new().start().context("failed to daemonize")?;
853+
Daemonize::new()
854+
.stderr(log_file
855+
.map(|f| f.into_parts().0.into())
856+
.unwrap_or_else(|| daemonize::Stdio::devnull())
857+
)
858+
.start().context("failed to daemonize")?;
859+
// Be extra-zealous and clase all non-stdio file descriptors.
860+
unsafe {
861+
close_fds::close_open_fds(3, &[]);
862+
}
854863
}
855864
}
856865

0 commit comments

Comments
 (0)