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)
|
return Ok(ExitCode::FAILURE)
|
||||||
} ,
|
} ,
|
||||||
None => {
|
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},
|
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
||||||
StatefulAction,
|
StatefulAction,
|
||||||
},
|
},
|
||||||
|
error::HasExpectedErrors,
|
||||||
planner::{Planner, PlannerError},
|
planner::{Planner, PlannerError},
|
||||||
settings::CommonSettings,
|
settings::CommonSettings,
|
||||||
settings::{InitSettings, InstallSettingsError},
|
settings::{InitSettings, InitSystem, InstallSettingsError},
|
||||||
Action, BuiltinPlanner,
|
Action, BuiltinPlanner,
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, path::Path};
|
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> {
|
async fn plan(&self) -> Result<Vec<StatefulAction<Box<dyn Action>>>, PlannerError> {
|
||||||
// If on NixOS, running `nix_installer` is pointless
|
check_not_nixos()?;
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if std::env::var("WSL_DISTRO_NAME").is_ok() && std::env::var("WSL_INTEROP").is_err() {
|
check_nix_not_already_installed().await?;
|
||||||
return Err(PlannerError::Wsl1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We currently do not support SELinux
|
check_not_wsl1()?;
|
||||||
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`")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now, we don't try to repair the user's Nix install or anything special.
|
check_not_selinux().await?;
|
||||||
if let Ok(_) = Command::new("nix-env")
|
|
||||||
.arg("--version")
|
if self.init.init == InitSystem::Systemd && self.init.start_daemon {
|
||||||
.stdin(std::process::Stdio::null())
|
check_systemd_active()?;
|
||||||
.status()
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
return Err(PlannerError::NixExists);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(vec![
|
Ok(vec![
|
||||||
|
@ -146,3 +121,108 @@ impl Into<BuiltinPlanner> for Linux {
|
||||||
BuiltinPlanner::Linux(self)
|
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)")]
|
#[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,
|
RosettaDetected,
|
||||||
/// A Linux SELinux related error
|
/// 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,
|
SelinuxEnforcing,
|
||||||
/// A UTF-8 related error
|
/// A UTF-8 related error
|
||||||
#[error("UTF-8 error")]
|
#[error("UTF-8 error")]
|
||||||
|
@ -397,7 +402,13 @@ impl HasExpectedErrors for PlannerError {
|
||||||
this @ PlannerError::RosettaDetected => Some(Box::new(this)),
|
this @ PlannerError::RosettaDetected => Some(Box::new(this)),
|
||||||
PlannerError::Utf8(_) => None,
|
PlannerError::Utf8(_) => None,
|
||||||
PlannerError::SelinuxEnforcing => Some(Box::new(self)),
|
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::NixOs => Some(Box::new(this)),
|
||||||
this @ PlannerError::NixExists => Some(Box::new(this)),
|
this @ PlannerError::NixExists => Some(Box::new(this)),
|
||||||
this @ PlannerError::Wsl1 => 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 =
|
pub const NIX_AARCH64_DARWIN_URL: &str =
|
||||||
"https://releases.nixos.org/nix/nix-2.13.3/nix-2.13.3-aarch64-darwin.tar.xz";
|
"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))]
|
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
|
||||||
pub enum InitSystem {
|
pub enum InitSystem {
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
|
Loading…
Reference in a new issue