Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/bin/sccache-dist/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ fn run(command: Command) -> Result<i32> {
}
};

daemonize()?;
daemonize(&[])?;
let scheduler = Scheduler::new();
let http_scheduler = dist::http::Scheduler::new(
public_addr,
Expand Down
12 changes: 10 additions & 2 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ use log::Level::Trace;
use std::env;
use std::ffi::{OsStr, OsString};
use std::io::{self, IsTerminal, Write};
#[cfg(not(windows))]
use std::os::fd::AsRawFd;
#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;
#[cfg(windows)]
use std::os::windows::io::AsRawHandle;
use std::path::Path;
use std::process;
use std::sync::Arc;
Expand Down Expand Up @@ -758,12 +762,16 @@ pub fn run_command(cmd: Command) -> Result<i32> {
trace!("Command::InternalStartServer");
if env::var("SCCACHE_ERROR_LOG").is_ok() {
let f = create_error_log()?;
#[cfg(not(windows))]
let preserve = [f.as_raw_fd()];
#[cfg(windows)]
let preserve = [f.as_raw_handle()];
// Can't report failure here, we're already daemonized.
daemonize()?;
daemonize(&preserve)?;
redirect_error_log(f)?;
} else {
// We aren't asking for a log file
daemonize()?;
daemonize(&[])?;
}
server::start_server(config, &get_addr())?;
}
Expand Down
41 changes: 3 additions & 38 deletions src/jobserver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,44 +29,9 @@ use crate::errors::*;
// What we do instead is to arbitrary use our own jobserver.
// Unfortunately, that doesn't absolve us from having to deal with the original
// jobserver, because make may give us file descriptors to its pipes, and the
// simple fact of keeping them open can block it.
// So if it does give us those file descriptors, close the preemptively.
//
// unsafe because it can use the wrong fds.
#[cfg(not(windows))]
pub unsafe fn discard_inherited_jobserver() {
if let Some(value) = ["CARGO_MAKEFLAGS", "MAKEFLAGS", "MFLAGS"]
.into_iter()
.find_map(|env| std::env::var(env).ok())
&& let Some(auth) = value.rsplit(' ').find_map(|arg| {
arg.strip_prefix("--jobserver-auth=")
.or_else(|| arg.strip_prefix("--jobserver-fds="))
})
&& !auth.starts_with("fifo:")
{
let mut parts = auth.splitn(2, ',');
let read = parts.next().unwrap();
let write = match parts.next() {
Some(w) => w,
None => return,
};
let read = read.parse().unwrap();
let write = write.parse().unwrap();
if read < 0 || write < 0 {
return;
}
unsafe {
if libc::fcntl(read, libc::F_GETFD) == -1 {
return;
}
if libc::fcntl(write, libc::F_GETFD) == -1 {
return;
}
libc::close(read);
libc::close(write);
}
}
}
// simple fact of keeping them open can block it. That is handled by closing
// every inherited descriptor when the server detaches; see
// `util::close_inherited_fds`.

#[derive(Clone)]
pub struct Client {
Expand Down
55 changes: 49 additions & 6 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -897,10 +897,55 @@ impl Hasher for HashToDigest<'_> {
}
}

/// Close every file descriptor we inherited from whoever spawned us, keeping
/// stdin/out/err and anything in `preserve`.
#[cfg(not(windows))]
fn close_inherited_fds(preserve: &[std::os::unix::io::RawFd]) {
use std::os::unix::io::RawFd;

let keep = |fd: RawFd| fd <= libc::STDERR_FILENO || preserve.contains(&fd);

// macOS/BSD: /dev/fd; Linux: /proc/self/fd
let listing = std::fs::read_dir("/dev/fd").or_else(|_| std::fs::read_dir("/proc/self/fd"));
let victims: Option<Vec<RawFd>> = listing.ok().map(|entries| {
entries
.flatten()
.filter_map(|e| e.file_name().to_str()?.parse::<RawFd>().ok())
.filter(|fd| !keep(*fd))
.collect()
});

match victims {
Some(fds) => {
for fd in fds {
unsafe { libc::close(fd) };
}
}
// No fd directory to enumerate, so fall back to sweeping the whole
// range. Bounded by the soft limit rather than the hard one to keep
// this from turning into a million syscalls.
None => {
let max = unsafe { libc::sysconf(libc::_SC_OPEN_MAX) };
let max = if max < 0 {
4096
} else {
max.min(65536) as RawFd
};
for fd in (libc::STDERR_FILENO + 1)..max {
if !keep(fd) {
unsafe { libc::close(fd) };
}
}
}
}
}

/// Pipe `cmd`'s stdio to `/dev/null`, unless a specific env var is set.
///
/// `preserve_fds` lists descriptors the caller opened before daemonizing and
/// still needs afterwards; everything else inherited is closed.
#[cfg(not(windows))]
pub fn daemonize() -> Result<()> {
use crate::jobserver::discard_inherited_jobserver;
pub fn daemonize(preserve_fds: &[std::os::unix::io::RawFd]) -> Result<()> {
use daemonix::Daemonize;
use std::env;
use std::mem;
Expand All @@ -912,9 +957,7 @@ pub fn daemonize() -> Result<()> {
}
}

unsafe {
discard_inherited_jobserver();
}
close_inherited_fds(preserve_fds);

static mut PREV_SIGSEGV: *mut libc::sigaction = std::ptr::null_mut();
static mut PREV_SIGBUS: *mut libc::sigaction = std::ptr::null_mut();
Expand Down Expand Up @@ -986,7 +1029,7 @@ pub fn daemonize() -> Result<()> {

/// This is a no-op on Windows.
#[cfg(windows)]
pub fn daemonize() -> Result<()> {
pub fn daemonize(_preserve_fds: &[std::os::windows::io::RawHandle]) -> Result<()> {
Ok(())
}

Expand Down
Loading