Skip to content

Commit a4c6d58

Browse files
committed
Adds option --bypass-root-check to install and remove command
And escalate sudo by default in both commands.
1 parent 68023f2 commit a4c6d58

File tree

2 files changed

+69
-32
lines changed

2 files changed

+69
-32
lines changed

crates/cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ dialoguer = "0.11"
1919
libloading = "0.8"
2020
cargo_metadata = "0.20"
2121
semver = "1.0"
22-
sudo = "0.6.0"
22+
elevate = "0.6.1"
2323

2424
[lints.rust]
2525
missing_docs = "warn"

crates/cli/src/lib.rs

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ struct Install {
121121
/// Whether to bypass the install prompt.
122122
#[clap(long)]
123123
yes: bool,
124+
/// Whether to bypass the root check
125+
#[clap(long)]
126+
bypass_root_check: bool,
124127
}
125128

126129
#[derive(Parser)]
@@ -140,6 +143,9 @@ struct Remove {
140143
/// Whether to bypass the remove prompt.
141144
#[clap(long)]
142145
yes: bool,
146+
/// Whether to bypass the root check
147+
#[clap(long)]
148+
bypass_root_check: bool,
143149
}
144150

145151
#[cfg(not(windows))]
@@ -193,6 +199,13 @@ impl Install {
193199
self.no_default_features,
194200
)?;
195201

202+
if !self.bypass_root_check {
203+
anyhow::ensure!(
204+
elevate::check() == elevate::RunningAs::User,
205+
"Running as root is not recommended. Use --bypass-root-check to override."
206+
);
207+
}
208+
196209
let (mut ext_dir, mut php_ini) = if let Some(install_dir) = self.install_dir {
197210
(install_dir, None)
198211
} else {
@@ -221,50 +234,74 @@ impl Install {
221234
ext_dir.push(ext_name);
222235
}
223236

224-
// We copying of file fails, escalate the privilege and try again.
225-
if let Err(_) = std::fs::copy(&ext_path, &ext_dir) {
226-
// failed to copy. escalate the privileges and try again.
227-
#[cfg(unix)]
228-
let _ = sudo::escalate_if_needed().ok();
237+
copy_extension(&ext_path, &ext_dir).with_context(|| {
238+
"Failed to copy extension from target directory to extension directory"
239+
})?;
229240

230-
std::fs::copy(&ext_path, &ext_dir).with_context(|| {
231-
"Failed to copy extension from target directory to extension directory"
232-
})?;
241+
if let Some(php_ini) = php_ini {
242+
copy_ini_file(&php_ini, ext_name, self.disable)
243+
.with_context(|| "Failed to update `php.ini`")?;
233244
}
234245

235-
if let Some(php_ini) = php_ini {
236-
let mut file = OpenOptions::new()
246+
Ok(())
247+
}
248+
}
249+
250+
// Copy ini file, if fails, try with sudo again.
251+
fn copy_ini_file(php_ini: &PathBuf, ext_name: &str, disable: bool) -> anyhow::Result<()> {
252+
let mut file = match OpenOptions::new().read(true).write(true).open(php_ini) {
253+
Ok(x) => x,
254+
Err(_e) => {
255+
#[cfg(unix)]
256+
{
257+
elevate::escalate_if_needed().expect("sudo failed");
258+
}
259+
OpenOptions::new()
237260
.read(true)
238261
.write(true)
239262
.open(php_ini)
240-
.with_context(|| "Failed to open `php.ini`")?;
263+
.with_context(|| "Failed to open `php.ini`")?
264+
}
265+
};
241266

242-
let mut ext_line = format!("extension={ext_name}");
267+
let mut ext_line = format!("extension={ext_name}");
243268

244-
let mut new_lines = vec![];
245-
for line in BufReader::new(&file).lines() {
246-
let line = line.with_context(|| "Failed to read line from `php.ini`")?;
247-
if line.contains(&ext_line) {
248-
bail!("Extension already enabled.");
249-
}
269+
let mut new_lines = vec![];
270+
for line in BufReader::new(&file).lines() {
271+
let line = line.with_context(|| "Failed to read line from `php.ini`")?;
272+
if line.contains(&ext_line) {
273+
bail!("Extension already enabled.");
274+
}
250275

251-
new_lines.push(line);
252-
}
276+
new_lines.push(line);
277+
}
253278

254-
// Comment out extension if user specifies disable flag
255-
if self.disable {
256-
ext_line.insert(0, ';');
257-
}
279+
// Comment out extension if user specifies disable flag
280+
if disable {
281+
ext_line.insert(0, ';');
282+
}
258283

259-
new_lines.push(ext_line);
260-
file.rewind()?;
261-
file.set_len(0)?;
262-
file.write(new_lines.join("\n").as_bytes())
263-
.with_context(|| "Failed to update `php.ini`")?;
264-
}
284+
new_lines.push(ext_line);
285+
file.rewind()?;
286+
file.set_len(0)?;
287+
let _ = file.write(new_lines.join("\n").as_bytes())?;
288+
Ok(())
289+
}
265290

266-
Ok(())
291+
// Copy extension, if fails, try with sudo again.
292+
//
293+
// We can check if we have write permission for ext_dir but due to ACL, group
294+
// list and and other nuances, it may not be reliable. See
295+
// https://doc.rust-lang.org/std/fs/struct.Permissions.html#method.readonly
296+
fn copy_extension(ext_path: &Utf8PathBuf, ext_dir: &PathBuf) -> anyhow::Result<()> {
297+
if let Err(_e) = std::fs::copy(ext_path, ext_dir) {
298+
#[cfg(unix)]
299+
{
300+
elevate::escalate_if_needed().expect("sudo failed");
301+
}
302+
std::fs::copy(ext_path, ext_dir)?;
267303
}
304+
Ok(())
268305
}
269306

270307
/// Returns the path to the extension directory utilised by the PHP interpreter,

0 commit comments

Comments
 (0)