Skip to content

Process cleanup #3877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
24 changes: 9 additions & 15 deletions src/bin/rustup-init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,32 @@ use cfg_if::cfg_if;
use rs_tracing::{
close_trace_file, close_trace_file_internal, open_trace_file, trace_to_file_internal,
};
use tokio::runtime::Builder;

use rustup::cli::common;
use rustup::cli::proxy_mode;
use rustup::cli::rustup_mode;
#[cfg(windows)]
use rustup::cli::self_update;
use rustup::cli::setup_mode;
use rustup::currentprocess::{with_runtime, Process};
use rustup::currentprocess::Process;
use rustup::env_var::RUST_RECURSION_COUNT_MAX;
use rustup::errors::RustupError;
use rustup::is_proxyable_tools;
use rustup::utils::utils::{self, ExitCode};

fn main() {
#[tokio::main]
async fn main() {
#[cfg(windows)]
pre_rustup_main_init();

let process = Process::os();
let mut builder = Builder::new_multi_thread();
builder.enable_all();
with_runtime(process.clone(), builder, {
async move {
match maybe_trace_rustup(&process).await {
Err(e) => {
common::report_error(&e, &process);
std::process::exit(1);
}
Ok(utils::ExitCode(c)) => std::process::exit(c),
}
match maybe_trace_rustup(&process).await {
Err(e) => {
common::report_error(&e, &process);
std::process::exit(1);
}
});
Ok(utils::ExitCode(c)) => std::process::exit(c),
}
}

async fn maybe_trace_rustup(process: &Process) -> Result<utils::ExitCode> {
Expand Down
22 changes: 12 additions & 10 deletions src/cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,10 @@ fn update_root(process: &Process) -> String {
/// `CARGO_HOME` suitable for display, possibly with $HOME
/// substituted for the directory prefix
fn canonical_cargo_home(process: &Process) -> Result<Cow<'static, str>> {
let path = utils::cargo_home(process)?;
let path = process.cargo_home()?;

let default_cargo_home = utils::home_dir(process)
let default_cargo_home = process
.home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".cargo");
Ok(if default_cargo_home == path {
Expand Down Expand Up @@ -727,7 +728,7 @@ fn check_existence_of_rustc_or_cargo_in_path(no_prompt: bool, process: &Process)
fn do_pre_install_sanity_checks(no_prompt: bool, process: &Process) -> Result<()> {
let rustc_manifest_path = PathBuf::from("/usr/local/lib/rustlib/manifest-rustc");
let uninstaller_path = PathBuf::from("/usr/local/lib/rustlib/uninstall.sh");
let rustup_sh_path = utils::home_dir(process).unwrap().join(".rustup");
let rustup_sh_path = process.home_dir().unwrap().join(".rustup");
let rustup_sh_version_path = rustup_sh_path.join("rustup-version");

let rustc_exists = rustc_manifest_path.exists() && uninstaller_path.exists();
Expand Down Expand Up @@ -761,7 +762,7 @@ fn do_pre_install_sanity_checks(no_prompt: bool, process: &Process) -> Result<()
}

fn pre_install_msg(no_modify_path: bool, process: &Process) -> Result<String> {
let cargo_home = utils::cargo_home(process)?;
let cargo_home = process.cargo_home()?;
let cargo_home_bin = cargo_home.join("bin");
let rustup_home = home::rustup_home()?;

Expand Down Expand Up @@ -824,7 +825,7 @@ fn current_install_opts(opts: &InstallOpts<'_>, process: &Process) -> String {
}

fn install_bins(process: &Process) -> Result<()> {
let bin_path = utils::cargo_home(process)?.join("bin");
let bin_path = process.cargo_home()?.join("bin");
let this_exe_path = utils::current_exe()?;
let rustup_path = bin_path.join(format!("rustup{EXE_SUFFIX}"));

Expand All @@ -840,7 +841,7 @@ fn install_bins(process: &Process) -> Result<()> {
}

pub(crate) fn install_proxies(process: &Process) -> Result<()> {
let bin_path = utils::cargo_home(process)?.join("bin");
let bin_path = process.cargo_home()?.join("bin");
let rustup_path = bin_path.join(format!("rustup{EXE_SUFFIX}"));

let rustup = Handle::from_path(&rustup_path)?;
Expand Down Expand Up @@ -938,7 +939,8 @@ async fn maybe_install_rust(

// If RUSTUP_HOME is not set, make sure it exists
if process.var_os("RUSTUP_HOME").is_none() {
let home = utils::home_dir(process)
let home = process
.home_dir()
.map(|p| p.join(".rustup"))
.ok_or_else(|| anyhow::anyhow!("could not find home dir to put .rustup in"))?;

Expand Down Expand Up @@ -988,7 +990,7 @@ pub(crate) fn uninstall(no_prompt: bool, process: &Process) -> Result<utils::Exi
return Ok(utils::ExitCode(1));
}

let cargo_home = utils::cargo_home(process)?;
let cargo_home = process.cargo_home()?;

if !cargo_home.join(format!("bin/rustup{EXE_SUFFIX}")).exists() {
return Err(CLIError::NotSelfInstalled { p: cargo_home }.into());
Expand Down Expand Up @@ -1178,7 +1180,7 @@ fn parse_new_rustup_version(version: String) -> String {
}

pub(crate) async fn prepare_update(process: &Process) -> Result<Option<PathBuf>> {
let cargo_home = utils::cargo_home(process)?;
let cargo_home = process.cargo_home()?;
let rustup_path = cargo_home.join(format!("bin{MAIN_SEPARATOR}rustup{EXE_SUFFIX}"));
let setup_path = cargo_home.join(format!("bin{MAIN_SEPARATOR}rustup-init{EXE_SUFFIX}"));

Expand Down Expand Up @@ -1319,7 +1321,7 @@ pub(crate) async fn check_rustup_update(process: &Process) -> Result<()> {

#[cfg_attr(feature = "otel", tracing::instrument)]
pub(crate) fn cleanup_self_updater(process: &Process) -> Result<()> {
let cargo_home = utils::cargo_home(process)?;
let cargo_home = process.cargo_home()?;
let setup = cargo_home.join(format!("bin/rustup-init{EXE_SUFFIX}"));

if setup.exists() {
Expand Down
19 changes: 10 additions & 9 deletions src/cli/self_update/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub(crate) struct ShellScript {

impl ShellScript {
pub(crate) fn write(&self, process: &Process) -> Result<()> {
let home = utils::cargo_home(process)?;
let home = process.cargo_home()?;
let cargo_bin = format!("{}/bin", cargo_home_str(process)?);
let env_name = home.join(self.name);
let env_file = self.content.replace("{cargo_bin}", &cargo_bin);
Expand All @@ -52,9 +52,10 @@ impl ShellScript {

// TODO: Update into a bytestring.
pub(crate) fn cargo_home_str(process: &Process) -> Result<Cow<'static, str>> {
let path = utils::cargo_home(process)?;
let path = process.cargo_home()?;

let default_cargo_home = utils::home_dir(process)
let default_cargo_home = process
.home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".cargo");
Ok(if default_cargo_home == path {
Expand Down Expand Up @@ -117,7 +118,7 @@ impl UnixShell for Posix {
}

fn rcfiles(&self, process: &Process) -> Vec<PathBuf> {
match utils::home_dir(process) {
match process.home_dir() {
Some(dir) => vec![dir.join(".profile")],
_ => vec![],
}
Expand All @@ -142,7 +143,7 @@ impl UnixShell for Bash {
// .profile as part of POSIX and always does setup for POSIX shells.
[".bash_profile", ".bash_login", ".bashrc"]
.iter()
.filter_map(|rc| utils::home_dir(process).map(|dir| dir.join(rc)))
.filter_map(|rc| process.home_dir().map(|dir| dir.join(rc)))
.collect()
}

Expand Down Expand Up @@ -186,7 +187,7 @@ impl UnixShell for Zsh {
}

fn rcfiles(&self, process: &Process) -> Vec<PathBuf> {
[Zsh::zdotdir(process).ok(), utils::home_dir(process)]
[Zsh::zdotdir(process).ok(), process.home_dir()]
.iter()
.filter_map(|dir| dir.as_ref().map(|p| p.join(".zshenv")))
.collect()
Expand Down Expand Up @@ -226,7 +227,7 @@ impl UnixShell for Fish {
path
});

let p1 = utils::home_dir(process).map(|mut path| {
let p1 = process.home_dir().map(|mut path| {
path.push(".config/fish/conf.d/rustup.fish");
path
});
Expand Down Expand Up @@ -257,11 +258,11 @@ impl UnixShell for Fish {
pub(crate) fn legacy_paths(process: &Process) -> impl Iterator<Item = PathBuf> + '_ {
let zprofiles = Zsh::zdotdir(process)
.into_iter()
.chain(utils::home_dir(process))
.chain(process.home_dir())
.map(|d| d.join(".zprofile"));
let profiles = [".bash_profile", ".profile"]
.iter()
.filter_map(|rc| utils::home_dir(process).map(|d| d.join(rc)));
.filter_map(|rc| process.home_dir().map(|d| d.join(rc)));

profiles.chain(zprofiles)
}
2 changes: 1 addition & 1 deletion src/cli/self_update/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub(crate) fn do_anti_sudo_check(no_prompt: bool, process: &Process) -> Result<u
}

pub(crate) fn delete_rustup_and_cargo_home(process: &Process) -> Result<()> {
let cargo_home = utils::cargo_home(process)?;
let cargo_home = process.cargo_home()?;
utils::remove_dir("cargo_home", &cargo_home, &|_: Notification<'_>| ())
}

Expand Down
8 changes: 4 additions & 4 deletions src/cli/self_update/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ pub fn complete_windows_uninstall(process: &Process) -> Result<utils::ExitCode>
wait_for_parent()?;

// Now that the parent has exited there are hopefully no more files open in CARGO_HOME
let cargo_home = utils::cargo_home(process)?;
let cargo_home = process.cargo_home()?;
utils::remove_dir("cargo_home", &cargo_home, &|_: Notification<'_>| ())?;

// Now, run a *system* binary to inherit the DELETE_ON_CLOSE
Expand Down Expand Up @@ -492,7 +492,7 @@ where
F: FnOnce(Vec<u16>, Vec<u16>) -> Option<Vec<u16>>,
{
let windows_path = get_windows_path_var()?;
let mut path_str = utils::cargo_home(process)?;
let mut path_str = process.cargo_home()?;
path_str.push("bin");
Ok(windows_path
.and_then(|old_path| f(old_path, OsString::from(path_str).encode_wide().collect())))
Expand Down Expand Up @@ -535,7 +535,7 @@ pub(crate) fn do_add_to_programs(process: &Process) -> Result<()> {
}
}

let mut path = utils::cargo_home(process)?;
let mut path = process.cargo_home()?;
path.push("bin\\rustup.exe");
let mut uninstall_cmd = OsString::from("\"");
uninstall_cmd.push(path);
Expand Down Expand Up @@ -657,7 +657,7 @@ pub(crate) fn delete_rustup_and_cargo_home(process: &Process) -> Result<()> {
};

// CARGO_HOME, hopefully empty except for bin/rustup.exe
let cargo_home = utils::cargo_home(process)?;
let cargo_home = process.cargo_home()?;
// The rustup.exe bin
let rustup_path = cargo_home.join(format!("bin/rustup{EXE_SUFFIX}"));

Expand Down
2 changes: 1 addition & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ impl<'a> Cfg<'a> {
process: &'a Process,
) -> Result<Self> {
// Set up the rustup home directory
let rustup_dir = utils::rustup_home(process)?;
let rustup_dir = process.rustup_home()?;

utils::ensure_dir_exists("home", &rustup_dir, notify_handler.as_ref())?;

Expand Down
104 changes: 14 additions & 90 deletions src/currentprocess.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use std::env;
use std::ffi::OsString;
use std::fmt::Debug;
use std::future::Future;
use std::io;
use std::panic;
use std::io::IsTerminal;
use std::path::PathBuf;
use std::sync::Once;
use std::{cell::RefCell, io::IsTerminal};
#[cfg(feature = "test")]
use std::{
collections::HashMap,
Expand All @@ -15,6 +12,7 @@ use std::{
sync::{Arc, Mutex},
};

use anyhow::{Context, Result};
#[cfg(feature = "test")]
use tracing::subscriber::DefaultGuard;
use tracing_subscriber::util::SubscriberInitExt;
Expand Down Expand Up @@ -48,6 +46,18 @@ impl Process {
.map(String::from)
}

pub(crate) fn home_dir(&self) -> Option<PathBuf> {
home::env::home_dir_with_env(self)
}

pub(crate) fn cargo_home(&self) -> Result<PathBuf> {
home::env::cargo_home_with_env(self).context("failed to determine cargo home")
}

pub(crate) fn rustup_home(&self) -> Result<PathBuf> {
home::env::rustup_home_with_env(self).context("failed to determine rustup home dir")
}

pub fn var(&self, key: &str) -> Result<String, env::VarError> {
match self {
Process::OSProcess(_) => env::var(key),
Expand Down Expand Up @@ -142,92 +152,6 @@ impl home::env::Env for Process {
}
}

static HOOK_INSTALLED: Once = Once::new();

fn ensure_hook() {
HOOK_INSTALLED.call_once(|| {
let orig_hook = panic::take_hook();
panic::set_hook(Box::new(move |info| {
clear_process();
orig_hook(info);
}));
});
}

/// Run a function in the context of a process definition and a tokio runtime.
///
/// The process state is injected into a thread-local in every work thread of
/// the runtime, but this requires access to the runtime builder, so this
/// function must be the one to create the runtime.
pub fn with_runtime<'a, R>(
process: Process,
mut runtime_builder: tokio::runtime::Builder,
fut: impl Future<Output = R> + 'a,
) -> R {
ensure_hook();

let start_process = process.clone();
let unpark_process = process.clone();
let runtime = runtime_builder
// propagate to blocking threads
.on_thread_start(move || {
// assign the process persistently to the thread local.
PROCESS.with(|p| {
if let Some(old_p) = &*p.borrow() {
panic!("current process already set {old_p:?}");
}
*p.borrow_mut() = Some(start_process.clone());
// Thread exits will clear the process.
});
})
.on_thread_stop(move || {
PROCESS.with(|p| {
*p.borrow_mut() = None;
});
})
// propagate to async worker threads
.on_thread_unpark(move || {
// assign the process persistently to the thread local.
PROCESS.with(|p| {
if let Some(old_p) = &*p.borrow() {
panic!("current process already set {old_p:?}");
}
*p.borrow_mut() = Some(unpark_process.clone());
// Thread exits will clear the process.
});
})
.on_thread_park(move || {
PROCESS.with(|p| {
*p.borrow_mut() = None;
});
})
.build()
.unwrap();

// The current thread doesn't get hooks run on it.
PROCESS.with(move |p| {
if let Some(old_p) = &*p.borrow() {
panic!("current process already set {old_p:?}");
}
*p.borrow_mut() = Some(process.clone());
let result = runtime.block_on(async {
let _guard = crate::cli::log::tracing_subscriber(&process).set_default();
fut.await
});
*p.borrow_mut() = None;
result
})
}

/// Internal - for the panic hook only
fn clear_process() {
PROCESS.with(|p| p.replace(None));
}

thread_local! {
pub(crate) static PROCESS: RefCell<Option<Process>> = const { RefCell::new(None) };
}

// ----------- real process -----------------

#[derive(Clone, Debug)]
Expand Down
Loading