forked from lix-project/lix-installer
Cure existing systemd units (#313)
* Cure existing systemd units * ActionError::Exists -> ActionError::DifferentContent To more accurately reflect its associated error message. * create_directory: use PathWasNotDir error instead * check if the service files and an override dir exists in plan and execute * fixup: target_os guarding * fixup: check if existing file is a symlink and see if they link to the same place * abstract systemd unit checking to function * fixup: logic error if the link_dest and unit_src were the same, we'd still error that the file exists
This commit is contained in:
parent
3f4480a1d2
commit
f9ab680840
|
@ -42,7 +42,7 @@ impl CreateDirectory {
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ActionError::GettingMetadata(path.clone(), e))?;
|
.map_err(|e| ActionError::GettingMetadata(path.clone(), e))?;
|
||||||
if !metadata.is_dir() {
|
if !metadata.is_dir() {
|
||||||
return Err(ActionError::Exists(path.to_owned()));
|
return Err(ActionError::PathWasNotDirectory(path.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does it have the right user/group?
|
// Does it have the right user/group?
|
||||||
|
|
|
@ -120,7 +120,7 @@ impl CreateFile {
|
||||||
.map_err(|e| ActionError::Read(this.path.clone(), e))?;
|
.map_err(|e| ActionError::Read(this.path.clone(), e))?;
|
||||||
|
|
||||||
if discovered_buf != this.buf {
|
if discovered_buf != this.buf {
|
||||||
return Err(ActionError::Exists(this.path.clone()));
|
return Err(ActionError::DifferentContent(this.path.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!("Creating file `{}` already complete", this.path.display());
|
tracing::debug!("Creating file `{}` already complete", this.path.display());
|
||||||
|
@ -346,7 +346,7 @@ mod test {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(ActionError::Exists(path)) => assert_eq!(path, test_file.as_path()),
|
Err(ActionError::DifferentContent(path)) => assert_eq!(path, test_file.as_path()),
|
||||||
_ => return Err(eyre!("Should have returned an ActionError::Exists error")),
|
_ => return Err(eyre!("Should have returned an ActionError::Exists error")),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,12 @@ use crate::settings::InitSystem;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
|
const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
const SERVICE_DEST: &str = "/etc/systemd/system/nix-daemon.service";
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
|
const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
const SOCKET_DEST: &str = "/etc/systemd/system/nix-daemon.socket";
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
const TMPFILES_SRC: &str = "/nix/var/nix/profiles/default/lib/tmpfiles.d/nix-daemon.conf";
|
const TMPFILES_SRC: &str = "/nix/var/nix/profiles/default/lib/tmpfiles.d/nix-daemon.conf";
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf";
|
const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf";
|
||||||
|
@ -32,11 +36,54 @@ pub struct ConfigureInitService {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigureInitService {
|
impl ConfigureInitService {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
async fn check_if_systemd_unit_exists(src: &str, dest: &str) -> Result<(), ActionError> {
|
||||||
|
// TODO: once we have a way to communicate interaction between the library and the cli,
|
||||||
|
// interactively ask for permission to remove the file
|
||||||
|
|
||||||
|
let unit_src = PathBuf::from(src);
|
||||||
|
// NOTE: Check if the unit file already exists...
|
||||||
|
let unit_dest = PathBuf::from(dest);
|
||||||
|
if unit_dest.exists() {
|
||||||
|
if unit_dest.is_symlink() {
|
||||||
|
let link_dest = tokio::fs::read_link(&unit_dest)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ActionError::ReadSymlink(unit_dest.clone(), e))?;
|
||||||
|
if link_dest != unit_src {
|
||||||
|
return Err(ActionError::SymlinkExists(unit_dest));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(ActionError::FileExists(unit_dest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NOTE: ...and if there are any overrides in the most well-known places for systemd
|
||||||
|
if Path::new(&format!("{dest}.d")).exists() {
|
||||||
|
return Err(ActionError::DirExists(PathBuf::from(format!("{dest}.d"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(
|
||||||
init: InitSystem,
|
init: InitSystem,
|
||||||
start_daemon: bool,
|
start_daemon: bool,
|
||||||
) -> Result<StatefulAction<Self>, ActionError> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
|
match init {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
InitSystem::Launchd => {
|
||||||
|
// No plan checks, yet
|
||||||
|
},
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
InitSystem::Systemd => {
|
||||||
|
Self::check_if_systemd_unit_exists(SERVICE_SRC, SERVICE_DEST).await?;
|
||||||
|
Self::check_if_systemd_unit_exists(SOCKET_SRC, SOCKET_DEST).await?;
|
||||||
|
},
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
InitSystem::None => {
|
||||||
|
// Nothing here, no init system
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self { init, start_daemon }.into())
|
Ok(Self { init, start_daemon }.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,8 +118,8 @@ impl Action for ConfigureInitService {
|
||||||
InitSystem::Systemd => {
|
InitSystem::Systemd => {
|
||||||
let mut explanation = vec![
|
let mut explanation = vec![
|
||||||
"Run `systemd-tempfiles --create --prefix=/nix/var/nix`".to_string(),
|
"Run `systemd-tempfiles --create --prefix=/nix/var/nix`".to_string(),
|
||||||
format!("Run `systemctl link {SERVICE_SRC}`"),
|
format!("Symlink `{SERVICE_SRC}` to `{SERVICE_DEST}`"),
|
||||||
format!("Run `systemctl link {SOCKET_SRC}`"),
|
format!("Symlink `{SOCKET_SRC}` to `{SOCKET_DEST}`"),
|
||||||
"Run `systemctl daemon-reload`".to_string(),
|
"Run `systemctl daemon-reload`".to_string(),
|
||||||
];
|
];
|
||||||
if self.start_daemon {
|
if self.start_daemon {
|
||||||
|
@ -159,23 +206,30 @@ impl Action for ConfigureInitService {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
execute_command(
|
// TODO: once we have a way to communicate interaction between the library and the
|
||||||
Command::new("systemctl")
|
// cli, interactively ask for permission to remove the file
|
||||||
.process_group(0)
|
|
||||||
.arg("link")
|
|
||||||
.arg(SERVICE_SRC)
|
|
||||||
.stdin(std::process::Stdio::null()),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
execute_command(
|
Self::check_if_systemd_unit_exists(SERVICE_SRC, SERVICE_DEST).await?;
|
||||||
Command::new("systemctl")
|
tokio::fs::symlink(SERVICE_SRC, SERVICE_DEST)
|
||||||
.process_group(0)
|
.await
|
||||||
.arg("link")
|
.map_err(|e| {
|
||||||
.arg(SOCKET_SRC)
|
ActionError::Symlink(
|
||||||
.stdin(std::process::Stdio::null()),
|
PathBuf::from(SERVICE_SRC),
|
||||||
)
|
PathBuf::from(SERVICE_DEST),
|
||||||
.await?;
|
e,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Self::check_if_systemd_unit_exists(SOCKET_SRC, SOCKET_DEST).await?;
|
||||||
|
tokio::fs::symlink(SOCKET_SRC, SOCKET_DEST)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
ActionError::Symlink(
|
||||||
|
PathBuf::from(SOCKET_SRC),
|
||||||
|
PathBuf::from(SOCKET_DEST),
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
if *start_daemon {
|
if *start_daemon {
|
||||||
execute_command(
|
execute_command(
|
||||||
|
|
|
@ -298,11 +298,20 @@ pub enum ActionError {
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>().join(" & "))]
|
}).collect::<Vec<_>>().join(" & "))]
|
||||||
Children(Vec<Box<ActionError>>),
|
Children(Vec<Box<ActionError>>),
|
||||||
/// The path already exists
|
/// The path already exists with different content that expected
|
||||||
#[error(
|
#[error(
|
||||||
"`{0}` exists with different content than planned, consider removing it with `rm {0}`"
|
"`{0}` exists with different content than planned, consider removing it with `rm {0}`"
|
||||||
)]
|
)]
|
||||||
Exists(std::path::PathBuf),
|
DifferentContent(std::path::PathBuf),
|
||||||
|
/// The file already exists
|
||||||
|
#[error("`{0}` already exists, consider removing it with `rm {0}`")]
|
||||||
|
FileExists(std::path::PathBuf),
|
||||||
|
/// The directory already exists
|
||||||
|
#[error("`{0}` already exists, consider removing it with `rm -r {0}`")]
|
||||||
|
DirExists(std::path::PathBuf),
|
||||||
|
/// The symlink already exists
|
||||||
|
#[error("`{0}` already exists, consider removing it with `rm {0}`")]
|
||||||
|
SymlinkExists(std::path::PathBuf),
|
||||||
#[error("`{0}` exists with a different uid ({1}) than planned ({2}), consider updating it with `chown {2} {0}`")]
|
#[error("`{0}` exists with a different uid ({1}) than planned ({2}), consider updating it with `chown {2} {0}`")]
|
||||||
PathUserMismatch(std::path::PathBuf, u32, u32),
|
PathUserMismatch(std::path::PathBuf, u32, u32),
|
||||||
#[error("`{0}` exists with a different gid ({1}) than planned ({2}), consider updating it with `chgrp {2} {0}`")]
|
#[error("`{0}` exists with a different gid ({1}) than planned ({2}), consider updating it with `chgrp {2} {0}`")]
|
||||||
|
@ -345,6 +354,8 @@ pub enum ActionError {
|
||||||
Read(std::path::PathBuf, #[source] std::io::Error),
|
Read(std::path::PathBuf, #[source] std::io::Error),
|
||||||
#[error("Reading directory `{0}`")]
|
#[error("Reading directory `{0}`")]
|
||||||
ReadDir(std::path::PathBuf, #[source] std::io::Error),
|
ReadDir(std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Reading symbolic link `{0}`")]
|
||||||
|
ReadSymlink(std::path::PathBuf, #[source] std::io::Error),
|
||||||
#[error("Open path `{0}`")]
|
#[error("Open path `{0}`")]
|
||||||
Open(std::path::PathBuf, #[source] std::io::Error),
|
Open(std::path::PathBuf, #[source] std::io::Error),
|
||||||
#[error("Write path `{0}`")]
|
#[error("Write path `{0}`")]
|
||||||
|
|
Loading…
Reference in a new issue