Provide users a better error message if systemd is not active (#412)
* Provide users a better error message if systemd is not active * Fixups * Fix mac * Fixup
This commit is contained in:
parent
cb48a7261b
commit
75627bcd91
|
@ -106,7 +106,17 @@ impl CommandExecute for Install {
|
|||
return Ok(ExitCode::FAILURE)
|
||||
} ,
|
||||
None => {
|
||||
planner.plan().await.map_err(|e| eyre!(e))?
|
||||
let res = planner.plan().await;
|
||||
match res {
|
||||
Ok(plan) => plan,
|
||||
Err(err) => {
|
||||
if let Some(expected) = err.expected() {
|
||||
eprintln!("{}", expected.red());
|
||||
return Ok(ExitCode::FAILURE);
|
||||
}
|
||||
return Err(err)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
@ -4,9 +4,10 @@ use crate::{
|
|||
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
||||
StatefulAction,
|
||||
},
|
||||
error::HasExpectedErrors,
|
||||
planner::{Planner, PlannerError},
|
||||
settings::CommonSettings,
|
||||
settings::{InitSettings, InstallSettingsError},
|
||||
settings::{InitSettings, InitSystem, InstallSettingsError},
|
||||
Action, BuiltinPlanner,
|
||||
};
|
||||
use std::{collections::HashMap, path::Path};
|
||||
|
@ -35,42 +36,16 @@ impl Planner for Linux {
|
|||
}
|
||||
|
||||
async fn plan(&self) -> Result<Vec<StatefulAction<Box<dyn Action>>>, PlannerError> {
|
||||
// If on NixOS, running `nix_installer` is pointless
|
||||
// NixOS always sets up this file as part of setting up /etc itself: https://github.com/NixOS/nixpkgs/blob/bdd39e5757d858bd6ea58ed65b4a2e52c8ed11ca/nixos/modules/system/etc/setup-etc.pl#L145
|
||||
if Path::new("/etc/NIXOS").exists() {
|
||||
return Err(PlannerError::NixOs);
|
||||
}
|
||||
check_not_nixos()?;
|
||||
|
||||
if std::env::var("WSL_DISTRO_NAME").is_ok() && std::env::var("WSL_INTEROP").is_err() {
|
||||
return Err(PlannerError::Wsl1);
|
||||
}
|
||||
check_nix_not_already_installed().await?;
|
||||
|
||||
// We currently do not support SELinux
|
||||
match Command::new("getenforce").output().await {
|
||||
Ok(output) => {
|
||||
let stdout_string = String::from_utf8(output.stdout).map_err(PlannerError::Utf8)?;
|
||||
tracing::trace!(getenforce_stdout = stdout_string, "SELinux detected");
|
||||
match stdout_string.trim() {
|
||||
"Enforcing" => return Err(PlannerError::SelinuxEnforcing),
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
// The device doesn't have SELinux set up
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => (),
|
||||
// Some unknown error
|
||||
Err(e) => {
|
||||
tracing::warn!(error = ?e, "Got an error checking for SELinux setting, this install may fail if SELinux is set to `Enforcing`")
|
||||
},
|
||||
}
|
||||
check_not_wsl1()?;
|
||||
|
||||
// For now, we don't try to repair the user's Nix install or anything special.
|
||||
if let Ok(_) = Command::new("nix-env")
|
||||
.arg("--version")
|
||||
.stdin(std::process::Stdio::null())
|
||||
.status()
|
||||
.await
|
||||
{
|
||||
return Err(PlannerError::NixExists);
|
||||
check_not_selinux().await?;
|
||||
|
||||
if self.init.init == InitSystem::Systemd && self.init.start_daemon {
|
||||
check_systemd_active()?;
|
||||
}
|
||||
|
||||
Ok(vec![
|
||||
|
@ -146,3 +121,108 @@ impl Into<BuiltinPlanner> for Linux {
|
|||
BuiltinPlanner::Linux(self)
|
||||
}
|
||||
}
|
||||
|
||||
// If on NixOS, running `nix_installer` is pointless
|
||||
fn check_not_nixos() -> Result<(), PlannerError> {
|
||||
// NixOS always sets up this file as part of setting up /etc itself: https://github.com/NixOS/nixpkgs/blob/bdd39e5757d858bd6ea58ed65b4a2e52c8ed11ca/nixos/modules/system/etc/setup-etc.pl#L145
|
||||
if Path::new("/etc/NIXOS").exists() {
|
||||
return Err(PlannerError::NixOs);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_not_wsl1() -> Result<(), PlannerError> {
|
||||
// Detection strategies: https://patrickwu.space/wslconf/
|
||||
if std::env::var("WSL_DISTRO_NAME").is_ok() && std::env::var("WSL_INTEROP").is_err() {
|
||||
return Err(PlannerError::Wsl1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_not_selinux() -> Result<(), PlannerError> {
|
||||
// We currently do not support SELinux
|
||||
match Command::new("getenforce").output().await {
|
||||
Ok(output) => {
|
||||
let stdout_string = String::from_utf8(output.stdout).map_err(PlannerError::Utf8)?;
|
||||
tracing::trace!(getenforce_stdout = stdout_string, "SELinux detected");
|
||||
match stdout_string.trim() {
|
||||
"Enforcing" => return Err(PlannerError::SelinuxEnforcing),
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
// The device doesn't have SELinux set up
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => (),
|
||||
// Some unknown error
|
||||
Err(e) => {
|
||||
tracing::warn!(error = ?e, "Got an error checking for SELinux setting, this install may fail if SELinux is set to `Enforcing`")
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check_nix_not_already_installed() -> Result<(), PlannerError> {
|
||||
// For now, we don't try to repair the user's Nix install or anything special.
|
||||
if let Ok(_) = Command::new("nix-env")
|
||||
.arg("--version")
|
||||
.stdin(std::process::Stdio::null())
|
||||
.status()
|
||||
.await
|
||||
{
|
||||
return Err(PlannerError::NixExists);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_systemd_active() -> Result<(), PlannerError> {
|
||||
if !Path::new("/run/systemd/system").exists() {
|
||||
if std::env::var("WSL_DISTRO_NAME").is_ok() {
|
||||
return Err(LinuxErrorKind::Wsl2SystemdNotActive)?;
|
||||
} else {
|
||||
return Err(LinuxErrorKind::SystemdNotActive)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum LinuxErrorKind {
|
||||
#[error(
|
||||
"\
|
||||
systemd was not active.\n\
|
||||
\n\
|
||||
If it will be started later consider, passing `--no-start-daemon`.\n\
|
||||
\n\
|
||||
To use a `root`-only Nix install, consider passing `--init none`."
|
||||
)]
|
||||
SystemdNotActive,
|
||||
#[error(
|
||||
"\
|
||||
systemd was not active.\n\
|
||||
\n\
|
||||
On WSL2, systemd is not enabled by default. Consider enabling it by adding it to your `/etc/wsl.conf` with `echo -e '[boot]\\nsystemd=true'` then restarting WSL2 with `wsl.exe --shutdown` and re-entering the WSL shell. For more information, see https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/.\n\
|
||||
\n\
|
||||
If it will be started later consider, passing `--no-start-daemon`.\n\
|
||||
\n\
|
||||
To use a `root`-only Nix install, consider passing `--init none`."
|
||||
)]
|
||||
Wsl2SystemdNotActive,
|
||||
}
|
||||
|
||||
impl HasExpectedErrors for LinuxErrorKind {
|
||||
fn expected<'a>(&'a self) -> Option<Box<dyn std::error::Error + 'a>> {
|
||||
match self {
|
||||
LinuxErrorKind::SystemdNotActive => Some(Box::new(self)),
|
||||
LinuxErrorKind::Wsl2SystemdNotActive => Some(Box::new(self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LinuxErrorKind> for PlannerError {
|
||||
fn from(v: LinuxErrorKind) -> PlannerError {
|
||||
PlannerError::Custom(Box::new(v))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -367,7 +367,12 @@ pub enum PlannerError {
|
|||
#[error("Detected that this process is running under Rosetta, using Nix in Rosetta is not supported (Please open an issue with your use case)")]
|
||||
RosettaDetected,
|
||||
/// A Linux SELinux related error
|
||||
#[error("This installer doesn't yet support SELinux in `Enforcing` mode. If SELinux is important to you, please see https://github.com/DeterminateSystems/nix-installer/issues/124. You can also try again after setting SELinux to `Permissive` mode with `setenforce Permissive`")]
|
||||
#[error("\
|
||||
This installer doesn't yet support SELinux in `Enforcing` mode.\n
|
||||
\n\
|
||||
If desirable, consider setting SELinux to `Permissive` mode with `setenforce Permissive`.\n\
|
||||
\n\
|
||||
If SELinux is important to you, please see https://github.com/DeterminateSystems/nix-installer/issues/124.")]
|
||||
SelinuxEnforcing,
|
||||
/// A UTF-8 related error
|
||||
#[error("UTF-8 error")]
|
||||
|
@ -397,7 +402,13 @@ impl HasExpectedErrors for PlannerError {
|
|||
this @ PlannerError::RosettaDetected => Some(Box::new(this)),
|
||||
PlannerError::Utf8(_) => None,
|
||||
PlannerError::SelinuxEnforcing => Some(Box::new(self)),
|
||||
PlannerError::Custom(_) => None,
|
||||
PlannerError::Custom(e) => {
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Some(err) = e.downcast_ref::<linux::LinuxErrorKind>() {
|
||||
return err.expected();
|
||||
}
|
||||
None
|
||||
},
|
||||
this @ PlannerError::NixOs => Some(Box::new(this)),
|
||||
this @ PlannerError::NixExists => Some(Box::new(this)),
|
||||
this @ PlannerError::Wsl1 => Some(Box::new(this)),
|
||||
|
|
|
@ -24,7 +24,7 @@ pub const NIX_X64_64_DARWIN_URL: &str =
|
|||
pub const NIX_AARCH64_DARWIN_URL: &str =
|
||||
"https://releases.nixos.org/nix/nix-2.13.3/nix-2.13.3-aarch64-darwin.tar.xz";
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Copy)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
|
||||
pub enum InitSystem {
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
|
|
Loading…
Reference in a new issue