-
Notifications
You must be signed in to change notification settings - Fork 921
/
Copy pathunix.rs
190 lines (163 loc) · 6.74 KB
/
unix.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::{Context, Result, bail};
use tracing::{error, warn};
use super::install_bins;
use super::shell;
use crate::process::Process;
use crate::utils::{self, Notification};
// If the user is trying to install with sudo, on some systems this will
// result in writing root-owned files to the user's home directory, because
// sudo is configured not to change $HOME. Don't let that bogosity happen.
pub(crate) fn do_anti_sudo_check(no_prompt: bool, process: &Process) -> Result<utils::ExitCode> {
pub(crate) fn home_mismatch(process: &Process) -> (bool, PathBuf, PathBuf) {
let fallback = || (false, PathBuf::new(), PathBuf::new());
// test runner should set this, nothing else
if process
.var_os("RUSTUP_INIT_SKIP_SUDO_CHECK")
.is_some_and(|s| s == "yes")
{
return fallback();
}
match (utils::home_dir_from_passwd(), process.var_os("HOME")) {
(Some(pw), Some(eh)) if eh != pw => return (true, PathBuf::from(eh), pw),
(None, _) => warn!("getpwuid_r: couldn't get user data"),
_ => {}
}
fallback()
}
match home_mismatch(process) {
(false, _, _) => {}
(true, env_home, euid_home) => {
error!("$HOME differs from euid-obtained home directory: you may be using sudo");
error!("$HOME directory: {}", env_home.display());
error!("euid-obtained home directory: {}", euid_home.display());
if !no_prompt {
error!("if this is what you want, restart the installation with `-y'");
return Ok(utils::ExitCode(1));
}
}
}
Ok(utils::ExitCode(0))
}
pub(crate) fn delete_rustup_and_cargo_home(process: &Process) -> Result<()> {
let cargo_home = process.cargo_home()?;
utils::remove_dir("cargo_home", &cargo_home, &|_: Notification<'_>| ())
}
pub(crate) fn do_remove_from_path(process: &Process) -> Result<()> {
for sh in shell::get_available_shells(process) {
let source_bytes = format!("{}\n", sh.source_string(process)?).into_bytes();
// Check more files for cleanup than normally are updated.
for rc in sh.rcfiles(process).iter().filter(|rc| rc.is_file()) {
let file = utils::read_file("rcfile", rc)?;
let file_bytes = file.into_bytes();
// FIXME: This is whitespace sensitive where it should not be.
if let Some(idx) = file_bytes
.windows(source_bytes.len())
.position(|w| w == source_bytes.as_slice())
{
// Here we rewrite the file without the offending line.
let mut new_bytes = file_bytes[..idx].to_vec();
new_bytes.extend(&file_bytes[idx + source_bytes.len()..]);
let new_file = String::from_utf8(new_bytes).unwrap();
utils::write_file("rcfile", rc, &new_file)?;
}
}
}
remove_legacy_paths(process)?;
Ok(())
}
pub(crate) fn do_add_to_path(process: &Process) -> Result<()> {
for sh in shell::get_available_shells(process) {
let source_cmd = sh.source_string(process)?;
let source_cmd_with_newline = format!("\n{}", &source_cmd);
for rc in sh.update_rcs(process) {
let cmd_to_write = match utils::read_file("rcfile", &rc) {
Ok(contents) if contents.contains(&source_cmd) => continue,
Ok(contents) if !contents.ends_with('\n') => &source_cmd_with_newline,
_ => &source_cmd,
};
let rc_dir = rc.parent().with_context(|| {
format!(
"parent directory doesn't exist for rcfile path: `{}`",
rc.display()
)
})?;
utils::ensure_dir_exists("rcfile dir", rc_dir, &|_: Notification<'_>| ())?;
utils::append_file("rcfile", &rc, cmd_to_write)
.with_context(|| format!("could not amend shell profile: '{}'", rc.display()))?;
}
}
remove_legacy_paths(process)?;
Ok(())
}
pub(crate) fn do_write_env_files(process: &Process) -> Result<()> {
let mut written = vec![];
for sh in shell::get_available_shells(process) {
let script = sh.env_script();
// Only write each possible script once.
if !written.contains(&script) {
script.write(process)?;
written.push(script);
}
}
Ok(())
}
/// Tell the upgrader to replace the rustup bins, then delete
/// itself.
pub(crate) fn run_update(setup_path: &Path) -> Result<utils::ExitCode> {
let status = Command::new(setup_path)
.arg("--self-replace")
.status()
.context("unable to run updater")?;
if !status.success() {
bail!("self-updated failed to replace rustup executable");
}
Ok(utils::ExitCode(0))
}
/// This function is as the final step of a self-upgrade. It replaces
/// `CARGO_HOME`/bin/rustup with the running exe, and updates the
/// links to it.
pub(crate) fn self_replace(process: &Process) -> Result<utils::ExitCode> {
install_bins(process)?;
Ok(utils::ExitCode(0))
}
fn remove_legacy_source_command(source_cmd: String, process: &Process) -> Result<()> {
let cmd_bytes = source_cmd.into_bytes();
for rc in shell::legacy_paths(process).filter(|rc| rc.is_file()) {
let file = utils::read_file("rcfile", &rc)?;
let file_bytes = file.into_bytes();
// FIXME: This is whitespace sensitive where it should not be.
if let Some(idx) = file_bytes
.windows(cmd_bytes.len())
.position(|w| w == cmd_bytes.as_slice())
{
// Here we rewrite the file without the offending line.
let mut new_bytes = file_bytes[..idx].to_vec();
new_bytes.extend(&file_bytes[idx + cmd_bytes.len()..]);
let new_file = String::from_utf8(new_bytes).unwrap();
utils::write_file("rcfile", &rc, &new_file)?;
}
}
Ok(())
}
fn remove_legacy_paths(process: &Process) -> Result<()> {
// Before the work to support more kinds of shells, which was released in
// version 1.23.0 of Rustup, we always inserted this line instead, which is
// now considered legacy
remove_legacy_source_command(
format!(
"export PATH=\"{}/bin:$PATH\"\n",
shell::cargo_home_str(process)?
),
process,
)?;
// Unfortunately in 1.23, we accidentally used `source` rather than `.`
// which, while widely supported, isn't actually POSIX, so we also
// clean that up here. This issue was filed as #2623.
remove_legacy_source_command(
format!("source \"{}/env\"\n", shell::cargo_home_str(process)?),
process,
)?;
Ok(())
}