forked from lix-project/lix-installer
Even more mac progress, can plan and start runs
This commit is contained in:
parent
c75a4ed511
commit
86a518f2fe
43
Cargo.lock
generated
43
Cargo.lock
generated
|
@ -498,6 +498,26 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "4.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"redox_users",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.31"
|
version = "0.8.31"
|
||||||
|
@ -684,6 +704,17 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ghost"
|
name = "ghost"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -749,6 +780,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"dirs",
|
||||||
"eyre",
|
"eyre",
|
||||||
"futures 0.3.24",
|
"futures 0.3.24",
|
||||||
"glob",
|
"glob",
|
||||||
|
@ -1380,6 +1412,17 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"redox_syscall",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
|
|
|
@ -39,3 +39,4 @@ sxd-xpath = "0.4.2"
|
||||||
xz2 = { version = "0.1.7", features = ["static", "tokio"] }
|
xz2 = { version = "0.1.7", features = ["static", "tokio"] }
|
||||||
sxd-document = "0.3.2"
|
sxd-document = "0.3.2"
|
||||||
plist = "1.3.1"
|
plist = "1.3.1"
|
||||||
|
dirs = "4.0.0"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use target_lexicon::OperatingSystem;
|
||||||
use tokio::fs::remove_file;
|
use tokio::fs::remove_file;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
@ -21,9 +22,20 @@ pub struct ConfigureNixDaemonService {
|
||||||
impl ConfigureNixDaemonService {
|
impl ConfigureNixDaemonService {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan() -> Result<Self, ConfigureNixDaemonServiceError> {
|
pub async fn plan() -> Result<Self, ConfigureNixDaemonServiceError> {
|
||||||
if !Path::new("/run/systemd/system").exists() {
|
match OperatingSystem::host() {
|
||||||
return Err(ConfigureNixDaemonServiceError::InitNotSupported);
|
OperatingSystem::MacOSX {
|
||||||
}
|
major: _,
|
||||||
|
minor: _,
|
||||||
|
patch: _,
|
||||||
|
}
|
||||||
|
| OperatingSystem::Darwin => (),
|
||||||
|
_ => {
|
||||||
|
if !Path::new("/run/systemd/system").exists() {
|
||||||
|
return Err(ConfigureNixDaemonServiceError::InitNotSupported);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
action_state: ActionState::Uncompleted,
|
action_state: ActionState::Uncompleted,
|
||||||
})
|
})
|
||||||
|
@ -59,32 +71,68 @@ impl Actionable for ConfigureNixDaemonService {
|
||||||
}
|
}
|
||||||
tracing::debug!("Configuring nix daemon service");
|
tracing::debug!("Configuring nix daemon service");
|
||||||
|
|
||||||
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
|
match OperatingSystem::host() {
|
||||||
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
|
OperatingSystem::MacOSX {
|
||||||
.await
|
major: _,
|
||||||
.map_err(|e| {
|
minor: _,
|
||||||
Self::Error::Symlink(PathBuf::from(TMPFILES_SRC), PathBuf::from(TMPFILES_DEST), e)
|
patch: _,
|
||||||
})?;
|
}
|
||||||
|
| OperatingSystem::Darwin => {
|
||||||
|
const DARWIN_NIX_DAEMON_DEST: &str =
|
||||||
|
"/Library/LaunchDaemons/org.nixos.nix-daemon.plist";
|
||||||
|
|
||||||
execute_command(
|
let src = Path::new("/nix/var/nix/profiles/default").join(DARWIN_NIX_DAEMON_DEST);
|
||||||
Command::new("systemd-tmpfiles")
|
tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST)
|
||||||
.arg("--create")
|
.await
|
||||||
.arg("--prefix=/nix/var/nix"),
|
.map_err(|e| {
|
||||||
)
|
Self::Error::Copy(
|
||||||
.await
|
src.to_path_buf(),
|
||||||
.map_err(Self::Error::CommandFailed)?;
|
PathBuf::from(DARWIN_NIX_DAEMON_DEST),
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
execute_command(Command::new("systemctl").arg("link").arg(SERVICE_SRC))
|
execute_command(
|
||||||
.await
|
Command::new("launchctl")
|
||||||
.map_err(Self::Error::CommandFailed)?;
|
.arg("load")
|
||||||
|
.arg(DARWIN_NIX_DAEMON_DEST),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(Self::Error::CommandFailed)?;
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
|
||||||
|
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
Self::Error::Symlink(
|
||||||
|
PathBuf::from(TMPFILES_SRC),
|
||||||
|
PathBuf::from(TMPFILES_DEST),
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC))
|
execute_command(
|
||||||
.await
|
Command::new("systemd-tmpfiles")
|
||||||
.map_err(Self::Error::CommandFailed)?;
|
.arg("--create")
|
||||||
|
.arg("--prefix=/nix/var/nix"),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(Self::Error::CommandFailed)?;
|
||||||
|
|
||||||
execute_command(Command::new("systemctl").arg("daemon-reload"))
|
execute_command(Command::new("systemctl").arg("link").arg(SERVICE_SRC))
|
||||||
.await
|
.await
|
||||||
.map_err(Self::Error::CommandFailed)?;
|
.map_err(Self::Error::CommandFailed)?;
|
||||||
|
|
||||||
|
execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC))
|
||||||
|
.await
|
||||||
|
.map_err(Self::Error::CommandFailed)?;
|
||||||
|
|
||||||
|
execute_command(Command::new("systemctl").arg("daemon-reload"))
|
||||||
|
.await
|
||||||
|
.map_err(Self::Error::CommandFailed)?;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
tracing::trace!("Configured nix daemon service");
|
tracing::trace!("Configured nix daemon service");
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
|
@ -176,6 +224,14 @@ pub enum ConfigureNixDaemonServiceError {
|
||||||
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
std::io::Error,
|
std::io::Error,
|
||||||
),
|
),
|
||||||
|
#[error("Copying file `{0}` to `{1}`")]
|
||||||
|
Copy(
|
||||||
|
std::path::PathBuf,
|
||||||
|
std::path::PathBuf,
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
#[error("No supported init system found")]
|
#[error("No supported init system found")]
|
||||||
InitNotSupported,
|
InitNotSupported,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,9 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable};
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateDirectory {
|
pub struct CreateDirectory {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
user: String,
|
user: Option<String>,
|
||||||
group: String,
|
group: Option<String>,
|
||||||
mode: u32,
|
mode: Option<u32>,
|
||||||
action_state: ActionState,
|
action_state: ActionState,
|
||||||
force_prune_on_revert: bool,
|
force_prune_on_revert: bool,
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,15 @@ impl CreateDirectory {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
user: String,
|
user: impl Into<Option<String>>,
|
||||||
group: String,
|
group: impl Into<Option<String>>,
|
||||||
mode: u32,
|
mode: impl Into<Option<u32>>,
|
||||||
force_prune_on_revert: bool,
|
force_prune_on_revert: bool,
|
||||||
) -> Result<Self, CreateDirectoryError> {
|
) -> Result<Self, CreateDirectoryError> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
let user = user.into();
|
||||||
|
let group = group.into();
|
||||||
|
let mode = mode.into();
|
||||||
|
|
||||||
let action_state = if path.exists() {
|
let action_state = if path.exists() {
|
||||||
let metadata = tokio::fs::metadata(path)
|
let metadata = tokio::fs::metadata(path)
|
||||||
|
@ -77,10 +80,7 @@ impl Actionable for CreateDirectory {
|
||||||
} else {
|
} else {
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Create the directory `{}`", path.display()),
|
format!("Create the directory `{}`", path.display()),
|
||||||
vec![format!(
|
vec![],
|
||||||
"Creating directory `{}` owned by `{user}:{group}` with mode `{mode:#o}`",
|
|
||||||
path.display()
|
|
||||||
)],
|
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ impl Actionable for CreateDirectory {
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
user = self.user,
|
user = self.user,
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = format!("{:#o}", self.mode),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Self::Error> {
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
let Self {
|
let Self {
|
||||||
|
@ -106,23 +106,37 @@ impl Actionable for CreateDirectory {
|
||||||
}
|
}
|
||||||
tracing::debug!("Creating directory");
|
tracing::debug!("Creating directory");
|
||||||
|
|
||||||
let gid = Group::from_name(group.as_str())
|
let gid = if let Some(group) = group {
|
||||||
.map_err(|e| Self::Error::GroupId(group.clone(), e))?
|
Some(
|
||||||
.ok_or(Self::Error::NoGroup(group.clone()))?
|
Group::from_name(group.as_str())
|
||||||
.gid;
|
.map_err(|e| Self::Error::GroupId(group.clone(), e))?
|
||||||
let uid = User::from_name(user.as_str())
|
.ok_or(Self::Error::NoGroup(group.clone()))?
|
||||||
.map_err(|e| Self::Error::UserId(user.clone(), e))?
|
.gid,
|
||||||
.ok_or(Self::Error::NoUser(user.clone()))?
|
)
|
||||||
.uid;
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let uid = if let Some(user) = user {
|
||||||
|
Some(
|
||||||
|
User::from_name(user.as_str())
|
||||||
|
.map_err(|e| Self::Error::UserId(user.clone(), e))?
|
||||||
|
.ok_or(Self::Error::NoUser(user.clone()))?
|
||||||
|
.uid,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
create_dir(path.clone())
|
create_dir(path.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Self::Error::Creating(path.clone(), e))?;
|
.map_err(|e| Self::Error::Creating(path.clone(), e))?;
|
||||||
chown(path, Some(uid), Some(gid)).map_err(|e| Self::Error::Chown(path.clone(), e))?;
|
chown(path, uid, gid).map_err(|e| Self::Error::Chown(path.clone(), e))?;
|
||||||
|
|
||||||
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
if let Some(mode) = mode {
|
||||||
.await
|
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
||||||
.map_err(|e| Self::Error::SetPermissions(*mode, path.to_owned(), e))?;
|
.await
|
||||||
|
.map_err(|e| Self::Error::SetPermissions(*mode, path.to_owned(), e))?;
|
||||||
|
}
|
||||||
|
|
||||||
tracing::trace!("Created directory");
|
tracing::trace!("Created directory");
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
|
@ -160,7 +174,7 @@ impl Actionable for CreateDirectory {
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
user = self.user,
|
user = self.user,
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = format!("{:#o}", self.mode),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Self::Error> {
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
let Self {
|
let Self {
|
||||||
|
|
|
@ -12,10 +12,10 @@ use crate::actions::{ActionDescription, Actionable};
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateFile {
|
pub struct CreateFile {
|
||||||
path: PathBuf,
|
pub(crate) path: PathBuf,
|
||||||
user: String,
|
user: Option<String>,
|
||||||
group: String,
|
group: Option<String>,
|
||||||
mode: u32,
|
mode: Option<u32>,
|
||||||
buf: String,
|
buf: String,
|
||||||
force: bool,
|
force: bool,
|
||||||
action_state: ActionState,
|
action_state: ActionState,
|
||||||
|
@ -25,9 +25,9 @@ impl CreateFile {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
user: String,
|
user: impl Into<Option<String>>,
|
||||||
group: String,
|
group: impl Into<Option<String>>,
|
||||||
mode: u32,
|
mode: impl Into<Option<u32>>,
|
||||||
buf: String,
|
buf: String,
|
||||||
force: bool,
|
force: bool,
|
||||||
) -> Result<Self, CreateFileError> {
|
) -> Result<Self, CreateFileError> {
|
||||||
|
@ -39,9 +39,9 @@ impl CreateFile {
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path,
|
path,
|
||||||
user,
|
user: user.into(),
|
||||||
group,
|
group: group.into(),
|
||||||
mode,
|
mode: mode.into(),
|
||||||
buf,
|
buf,
|
||||||
force,
|
force,
|
||||||
action_state: ActionState::Uncompleted,
|
action_state: ActionState::Uncompleted,
|
||||||
|
@ -68,9 +68,7 @@ impl Actionable for CreateFile {
|
||||||
} else {
|
} else {
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Create or overwrite file `{}`", path.display()),
|
format!("Create or overwrite file `{}`", path.display()),
|
||||||
vec![format!(
|
vec![],
|
||||||
"Create or overwrite `{}` owned by `{user}:{group}` with mode `{mode:#o}` with `{buf}`", path.display()
|
|
||||||
)],
|
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +77,7 @@ impl Actionable for CreateFile {
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
user = self.user,
|
user = self.user,
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = format!("{:#o}", self.mode),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Self::Error> {
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
let Self {
|
let Self {
|
||||||
|
@ -97,11 +95,14 @@ impl Actionable for CreateFile {
|
||||||
}
|
}
|
||||||
tracing::debug!("Creating file");
|
tracing::debug!("Creating file");
|
||||||
|
|
||||||
let mut file = OpenOptions::new()
|
let mut options = OpenOptions::new();
|
||||||
.create_new(true)
|
options.create_new(true).write(true).read(true);
|
||||||
.mode(*mode)
|
|
||||||
.write(true)
|
if let Some(mode) = mode {
|
||||||
.read(true)
|
options.mode(*mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = options
|
||||||
.open(&path)
|
.open(&path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Self::Error::OpenFile(path.to_owned(), e))?;
|
.map_err(|e| Self::Error::OpenFile(path.to_owned(), e))?;
|
||||||
|
@ -110,16 +111,27 @@ impl Actionable for CreateFile {
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?;
|
.map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?;
|
||||||
|
|
||||||
let gid = Group::from_name(group.as_str())
|
let gid = if let Some(group) = group {
|
||||||
.map_err(|e| Self::Error::GroupId(group.clone(), e))?
|
Some(
|
||||||
.ok_or(Self::Error::NoGroup(group.clone()))?
|
Group::from_name(group.as_str())
|
||||||
.gid;
|
.map_err(|e| Self::Error::GroupId(group.clone(), e))?
|
||||||
let uid = User::from_name(user.as_str())
|
.ok_or(Self::Error::NoGroup(group.clone()))?
|
||||||
.map_err(|e| Self::Error::UserId(user.clone(), e))?
|
.gid,
|
||||||
.ok_or(Self::Error::NoUser(user.clone()))?
|
)
|
||||||
.uid;
|
} else {
|
||||||
|
None
|
||||||
chown(path, Some(uid), Some(gid)).map_err(|e| Self::Error::Chown(path.clone(), e))?;
|
};
|
||||||
|
let uid = if let Some(user) = user {
|
||||||
|
Some(
|
||||||
|
User::from_name(user.as_str())
|
||||||
|
.map_err(|e| Self::Error::UserId(user.clone(), e))?
|
||||||
|
.ok_or(Self::Error::NoUser(user.clone()))?
|
||||||
|
.uid,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
chown(path, uid, gid).map_err(|e| Self::Error::Chown(path.clone(), e))?;
|
||||||
|
|
||||||
tracing::trace!("Created file");
|
tracing::trace!("Created file");
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
|
@ -150,7 +162,7 @@ impl Actionable for CreateFile {
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
user = self.user,
|
user = self.user,
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = format!("{:#o}", self.mode),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Self::Error> {
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
let Self {
|
let Self {
|
||||||
|
|
|
@ -17,9 +17,9 @@ use crate::actions::{ActionDescription, Actionable};
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateOrAppendFile {
|
pub struct CreateOrAppendFile {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
user: String,
|
user: Option<String>,
|
||||||
group: String,
|
group: Option<String>,
|
||||||
mode: u32,
|
mode: Option<u32>,
|
||||||
buf: String,
|
buf: String,
|
||||||
action_state: ActionState,
|
action_state: ActionState,
|
||||||
}
|
}
|
||||||
|
@ -28,18 +28,18 @@ impl CreateOrAppendFile {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
user: String,
|
user: impl Into<Option<String>>,
|
||||||
group: String,
|
group: impl Into<Option<String>>,
|
||||||
mode: u32,
|
mode: impl Into<Option<u32>>,
|
||||||
buf: String,
|
buf: String,
|
||||||
) -> Result<Self, CreateOrAppendFileError> {
|
) -> Result<Self, CreateOrAppendFileError> {
|
||||||
let path = path.as_ref().to_path_buf();
|
let path = path.as_ref().to_path_buf();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path,
|
path,
|
||||||
user,
|
user: user.into(),
|
||||||
group,
|
group: group.into(),
|
||||||
mode,
|
mode: mode.into(),
|
||||||
buf,
|
buf,
|
||||||
action_state: ActionState::Uncompleted,
|
action_state: ActionState::Uncompleted,
|
||||||
})
|
})
|
||||||
|
@ -64,9 +64,7 @@ impl Actionable for CreateOrAppendFile {
|
||||||
} else {
|
} else {
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Create or append file `{}`", path.display()),
|
format!("Create or append file `{}`", path.display()),
|
||||||
vec![format!(
|
vec![],
|
||||||
"Create or append `{}` owned by `{user}:{group}` with mode `{mode:#o}` with `{buf}`", path.display()
|
|
||||||
)],
|
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +73,7 @@ impl Actionable for CreateOrAppendFile {
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
user = self.user,
|
user = self.user,
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = format!("{:#o}", self.mode),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Self::Error> {
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
let Self {
|
let Self {
|
||||||
|
@ -108,20 +106,34 @@ impl Actionable for CreateOrAppendFile {
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?;
|
.map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?;
|
||||||
|
|
||||||
let gid = Group::from_name(group.as_str())
|
let gid = if let Some(group) = group {
|
||||||
.map_err(|e| Self::Error::GroupId(group.clone(), e))?
|
Some(
|
||||||
.ok_or(Self::Error::NoGroup(group.clone()))?
|
Group::from_name(group.as_str())
|
||||||
.gid;
|
.map_err(|e| Self::Error::GroupId(group.clone(), e))?
|
||||||
let uid = User::from_name(user.as_str())
|
.ok_or(Self::Error::NoGroup(group.clone()))?
|
||||||
.map_err(|e| Self::Error::UserId(user.clone(), e))?
|
.gid,
|
||||||
.ok_or(Self::Error::NoUser(user.clone()))?
|
)
|
||||||
.uid;
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let uid = if let Some(user) = user {
|
||||||
|
Some(
|
||||||
|
User::from_name(user.as_str())
|
||||||
|
.map_err(|e| Self::Error::UserId(user.clone(), e))?
|
||||||
|
.ok_or(Self::Error::NoUser(user.clone()))?
|
||||||
|
.uid,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
if let Some(mode) = mode {
|
||||||
.await
|
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
||||||
.map_err(|e| Self::Error::SetPermissions(*mode, path.to_owned(), e))?;
|
.await
|
||||||
|
.map_err(|e| Self::Error::SetPermissions(*mode, path.to_owned(), e))?;
|
||||||
|
}
|
||||||
|
|
||||||
chown(path, Some(uid), Some(gid)).map_err(|e| Self::Error::Chown(path.clone(), e))?;
|
chown(path, uid, gid).map_err(|e| Self::Error::Chown(path.clone(), e))?;
|
||||||
|
|
||||||
tracing::trace!("Created or appended fragment to file");
|
tracing::trace!("Created or appended fragment to file");
|
||||||
*action_state = ActionState::Completed;
|
*action_state = ActionState::Completed;
|
||||||
|
@ -154,7 +166,7 @@ impl Actionable for CreateOrAppendFile {
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
user = self.user,
|
user = self.user,
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = format!("{:#o}", self.mode),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Self::Error> {
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
let Self {
|
let Self {
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl Actionable for CreateUser {
|
||||||
tracing::debug!("Creating user");
|
tracing::debug!("Creating user");
|
||||||
|
|
||||||
use target_lexicon::OperatingSystem;
|
use target_lexicon::OperatingSystem;
|
||||||
match target_lexicon::OperatingSystem::host() {
|
match OperatingSystem::host() {
|
||||||
OperatingSystem::MacOSX {
|
OperatingSystem::MacOSX {
|
||||||
major: _,
|
major: _,
|
||||||
minor: _,
|
minor: _,
|
||||||
|
|
115
src/actions/base/darwin/kickstart_launchctl_service.rs
Normal file
115
src/actions/base/darwin/kickstart_launchctl_service.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::execute_command;
|
||||||
|
|
||||||
|
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct KickstartLaunchctlService {
|
||||||
|
unit: String,
|
||||||
|
action_state: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KickstartLaunchctlService {
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub async fn plan(unit: String) -> Result<Self, KickstartLaunchctlServiceError> {
|
||||||
|
Ok(Self {
|
||||||
|
unit,
|
||||||
|
action_state: ActionState::Uncompleted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Actionable for KickstartLaunchctlService {
|
||||||
|
type Error = KickstartLaunchctlServiceError;
|
||||||
|
|
||||||
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
format!("Kickstart the launchctl unit `{unit}`"),
|
||||||
|
vec![
|
||||||
|
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
||||||
|
]
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn execute(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Completed {
|
||||||
|
tracing::trace!("Already completed: Kickstarting launchctl unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Kickstarting launchctl unit");
|
||||||
|
|
||||||
|
execute_command(
|
||||||
|
Command::new("launchctl")
|
||||||
|
.arg("kickstart")
|
||||||
|
.arg("-k")
|
||||||
|
.arg(unit),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(KickstartLaunchctlServiceError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Kickstarted launchctl unit");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn describe_revert(&self) -> Vec<ActionDescription> {
|
||||||
|
if self.action_state == ActionState::Uncompleted {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
"Kick".to_string(),
|
||||||
|
vec![
|
||||||
|
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
||||||
|
]
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(
|
||||||
|
unit = %self.unit,
|
||||||
|
))]
|
||||||
|
async fn revert(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let Self { unit, action_state } = self;
|
||||||
|
if *action_state == ActionState::Uncompleted {
|
||||||
|
tracing::trace!("Already reverted: Stopping launchctl unit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
tracing::debug!("Stopping launchctl unit");
|
||||||
|
|
||||||
|
execute_command(Command::new("launchctl").arg("stop").arg(unit))
|
||||||
|
.await
|
||||||
|
.map_err(KickstartLaunchctlServiceError::Command)?;
|
||||||
|
|
||||||
|
tracing::trace!("Stopped launchctl unit");
|
||||||
|
*action_state = ActionState::Completed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<KickstartLaunchctlService> for Action {
|
||||||
|
fn from(v: KickstartLaunchctlService) -> Self {
|
||||||
|
Action::DarwinKickStartLaunchctlService(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, Serialize)]
|
||||||
|
pub enum KickstartLaunchctlServiceError {
|
||||||
|
#[error("Failed to execute command")]
|
||||||
|
Command(
|
||||||
|
#[source]
|
||||||
|
#[serde(serialize_with = "crate::serialize_error_to_display")]
|
||||||
|
std::io::Error,
|
||||||
|
),
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ mod create_synthetic_objects;
|
||||||
mod create_volume;
|
mod create_volume;
|
||||||
mod enable_ownership;
|
mod enable_ownership;
|
||||||
mod encrypt_volume;
|
mod encrypt_volume;
|
||||||
|
mod kickstart_launchctl_service;
|
||||||
mod unmount_volume;
|
mod unmount_volume;
|
||||||
|
|
||||||
pub use bootstrap_volume::{BootstrapVolume, BootstrapVolumeError};
|
pub use bootstrap_volume::{BootstrapVolume, BootstrapVolumeError};
|
||||||
|
@ -10,4 +11,5 @@ pub use create_synthetic_objects::{CreateSyntheticObjects, CreateSyntheticObject
|
||||||
pub use create_volume::{CreateVolume, CreateVolumeError};
|
pub use create_volume::{CreateVolume, CreateVolumeError};
|
||||||
pub use enable_ownership::{EnableOwnership, EnableOwnershipError};
|
pub use enable_ownership::{EnableOwnership, EnableOwnershipError};
|
||||||
pub use encrypt_volume::{EncryptVolume, EncryptVolumeError};
|
pub use encrypt_volume::{EncryptVolume, EncryptVolumeError};
|
||||||
|
pub use kickstart_launchctl_service::{KickstartLaunchctlService, KickstartLaunchctlServiceError};
|
||||||
pub use unmount_volume::{UnmountVolume, UnmountVolumeError};
|
pub use unmount_volume::{UnmountVolume, UnmountVolumeError};
|
||||||
|
|
|
@ -60,7 +60,7 @@ impl Actionable for UnmountVolume {
|
||||||
tracing::debug!("Unmounting volume");
|
tracing::debug!("Unmounting volume");
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new(" /usr/sbin/diskutil")
|
Command::new("/usr/sbin/diskutil")
|
||||||
.args(["unmount", "force"])
|
.args(["unmount", "force"])
|
||||||
.arg(name),
|
.arg(name),
|
||||||
)
|
)
|
||||||
|
|
|
@ -41,10 +41,8 @@ impl ConfigureShellProfile {
|
||||||
# End Nix\n
|
# End Nix\n
|
||||||
\n",
|
\n",
|
||||||
);
|
);
|
||||||
create_or_append_files.push(
|
create_or_append_files
|
||||||
CreateOrAppendFile::plan(path, "root".to_string(), "root".to_string(), 0o0644, buf)
|
.push(CreateOrAppendFile::plan(path, None, None, 0o0644, buf).await?);
|
||||||
.await?,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
|
@ -31,9 +31,7 @@ impl CreateNixTree {
|
||||||
let mut create_directories = Vec::default();
|
let mut create_directories = Vec::default();
|
||||||
for path in PATHS {
|
for path in PATHS {
|
||||||
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
|
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
|
||||||
create_directories.push(
|
create_directories.push(CreateDirectory::plan(path, None, None, 0o0755, false).await?)
|
||||||
CreateDirectory::plan(path, "root".into(), "root".into(), 0o0755, false).await?,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
|
@ -26,19 +26,11 @@ impl CreateSystemdSysext {
|
||||||
pub async fn plan(destination: impl AsRef<Path>) -> Result<Self, CreateSystemdSysextError> {
|
pub async fn plan(destination: impl AsRef<Path>) -> Result<Self, CreateSystemdSysextError> {
|
||||||
let destination = destination.as_ref();
|
let destination = destination.as_ref();
|
||||||
|
|
||||||
let mut create_directories = vec![
|
let mut create_directories =
|
||||||
CreateDirectory::plan(destination, "root".into(), "root".into(), 0o0755, true).await?,
|
vec![CreateDirectory::plan(destination, None, None, 0o0755, true).await?];
|
||||||
];
|
|
||||||
for path in PATHS {
|
for path in PATHS {
|
||||||
create_directories.push(
|
create_directories.push(
|
||||||
CreateDirectory::plan(
|
CreateDirectory::plan(destination.join(path), None, None, 0o0755, false).await?,
|
||||||
destination.join(path),
|
|
||||||
"root".into(),
|
|
||||||
"root".into(),
|
|
||||||
0o0755,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,8 +49,8 @@ impl CreateSystemdSysext {
|
||||||
format!("SYSEXT_LEVEL=1.0\nID=steamos\nVERSION_ID={version_id}");
|
format!("SYSEXT_LEVEL=1.0\nID=steamos\nVERSION_ID={version_id}");
|
||||||
let create_extension_release = CreateFile::plan(
|
let create_extension_release = CreateFile::plan(
|
||||||
destination.join("usr/lib/extension-release.d/extension-release.nix"),
|
destination.join("usr/lib/extension-release.d/extension-release.nix"),
|
||||||
"root".into(),
|
None,
|
||||||
"root".into(),
|
None,
|
||||||
0o0755,
|
0o0755,
|
||||||
extension_release_buf,
|
extension_release_buf,
|
||||||
false,
|
false,
|
||||||
|
@ -77,8 +69,8 @@ impl CreateSystemdSysext {
|
||||||
);
|
);
|
||||||
let create_bind_mount_unit = CreateFile::plan(
|
let create_bind_mount_unit = CreateFile::plan(
|
||||||
destination.join("usr/lib/systemd/system/nix.mount"),
|
destination.join("usr/lib/systemd/system/nix.mount"),
|
||||||
"root".into(),
|
None,
|
||||||
"root".into(),
|
None,
|
||||||
0o0755,
|
0o0755,
|
||||||
create_bind_mount_buf,
|
create_bind_mount_buf,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -39,14 +39,9 @@ impl CreateApfsVolume {
|
||||||
encrypt: Option<String>,
|
encrypt: Option<String>,
|
||||||
) -> Result<Self, CreateApfsVolumeError> {
|
) -> Result<Self, CreateApfsVolumeError> {
|
||||||
let disk = disk.as_ref();
|
let disk = disk.as_ref();
|
||||||
let create_or_append_synthetic_conf = CreateOrAppendFile::plan(
|
let create_or_append_synthetic_conf =
|
||||||
"/etc/synthetic.conf",
|
CreateOrAppendFile::plan("/etc/synthetic.conf", None, None, 0o0655, "nix".into())
|
||||||
"root".into(),
|
.await?;
|
||||||
"wheel".into(),
|
|
||||||
0o0655,
|
|
||||||
"nix".into(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let create_synthetic_objects = CreateSyntheticObjects::plan().await?;
|
let create_synthetic_objects = CreateSyntheticObjects::plan().await?;
|
||||||
|
|
||||||
|
@ -56,10 +51,10 @@ impl CreateApfsVolume {
|
||||||
|
|
||||||
let create_or_append_fstab = CreateOrAppendFile::plan(
|
let create_or_append_fstab = CreateOrAppendFile::plan(
|
||||||
"/etc/fstab",
|
"/etc/fstab",
|
||||||
"root".into(),
|
None,
|
||||||
"root".into(),
|
None,
|
||||||
0o0655,
|
0o0655,
|
||||||
"NAME={name} /nix apfs rw,noauto,nobrowse,suid,owners".into(),
|
format!("NAME=\"{name}\" /nix apfs rw,noauto,nobrowse,suid,owners"),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -138,7 +133,7 @@ impl Actionable for CreateApfsVolume {
|
||||||
|
|
||||||
create_or_append_synthetic_conf.execute().await?;
|
create_or_append_synthetic_conf.execute().await?;
|
||||||
create_synthetic_objects.execute().await?;
|
create_synthetic_objects.execute().await?;
|
||||||
unmount_volume.execute().await?;
|
unmount_volume.execute().await.ok(); // We actually expect this may fail.
|
||||||
create_volume.execute().await?;
|
create_volume.execute().await?;
|
||||||
create_or_append_fstab.execute().await?;
|
create_or_append_fstab.execute().await?;
|
||||||
if let Some(encrypt_volume) = encrypt_volume {
|
if let Some(encrypt_volume) = encrypt_volume {
|
||||||
|
|
|
@ -9,7 +9,6 @@ pub mod darwin;
|
||||||
mod place_channel_configuration;
|
mod place_channel_configuration;
|
||||||
mod place_nix_configuration;
|
mod place_nix_configuration;
|
||||||
mod provision_nix;
|
mod provision_nix;
|
||||||
mod start_nix_daemon;
|
|
||||||
|
|
||||||
pub use configure_nix::{ConfigureNix, ConfigureNixError};
|
pub use configure_nix::{ConfigureNix, ConfigureNixError};
|
||||||
pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileError};
|
pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileError};
|
||||||
|
@ -19,4 +18,3 @@ pub use create_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupError};
|
||||||
pub use place_channel_configuration::{PlaceChannelConfiguration, PlaceChannelConfigurationError};
|
pub use place_channel_configuration::{PlaceChannelConfiguration, PlaceChannelConfigurationError};
|
||||||
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationError};
|
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationError};
|
||||||
pub use provision_nix::{ProvisionNix, ProvisionNixError};
|
pub use provision_nix::{ProvisionNix, ProvisionNixError};
|
||||||
pub use start_nix_daemon::{StartNixDaemon, StartNixDaemonError};
|
|
||||||
|
|
|
@ -5,8 +5,6 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable};
|
||||||
|
|
||||||
use crate::actions::base::{CreateFile, CreateFileError};
|
use crate::actions::base::{CreateFile, CreateFileError};
|
||||||
|
|
||||||
const NIX_CHANNELS_PATH: &str = "/root/.nix-channels";
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct PlaceChannelConfiguration {
|
pub struct PlaceChannelConfiguration {
|
||||||
channels: Vec<(String, Url)>,
|
channels: Vec<(String, Url)>,
|
||||||
|
@ -26,9 +24,11 @@ impl PlaceChannelConfiguration {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
let create_file = CreateFile::plan(
|
let create_file = CreateFile::plan(
|
||||||
NIX_CHANNELS_PATH,
|
dirs::home_dir()
|
||||||
"root".into(),
|
.ok_or(PlaceChannelConfigurationError::NoRootHome)?
|
||||||
"root".into(),
|
.join("/.nix-channels"),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
0o0664,
|
0o0664,
|
||||||
buf,
|
buf,
|
||||||
force,
|
force,
|
||||||
|
@ -49,17 +49,18 @@ impl Actionable for PlaceChannelConfiguration {
|
||||||
fn describe_execute(&self) -> Vec<ActionDescription> {
|
fn describe_execute(&self) -> Vec<ActionDescription> {
|
||||||
let Self {
|
let Self {
|
||||||
channels: _,
|
channels: _,
|
||||||
create_file: _,
|
create_file,
|
||||||
action_state: _,
|
action_state: _,
|
||||||
} = self;
|
} = self;
|
||||||
if self.action_state == ActionState::Completed {
|
if self.action_state == ActionState::Completed {
|
||||||
vec![]
|
vec![]
|
||||||
} else {
|
} else {
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Place channel configuration at `{NIX_CHANNELS_PATH}`"),
|
format!(
|
||||||
vec![format!(
|
"Place channel configuration at `{}`",
|
||||||
"Place channel configuration at `{NIX_CHANNELS_PATH}`"
|
create_file.path.display()
|
||||||
)],
|
),
|
||||||
|
vec![],
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,17 +91,18 @@ impl Actionable for PlaceChannelConfiguration {
|
||||||
fn describe_revert(&self) -> Vec<ActionDescription> {
|
fn describe_revert(&self) -> Vec<ActionDescription> {
|
||||||
let Self {
|
let Self {
|
||||||
channels: _,
|
channels: _,
|
||||||
create_file: _,
|
create_file,
|
||||||
action_state: _,
|
action_state: _,
|
||||||
} = self;
|
} = self;
|
||||||
if self.action_state == ActionState::Uncompleted {
|
if self.action_state == ActionState::Uncompleted {
|
||||||
vec![]
|
vec![]
|
||||||
} else {
|
} else {
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Remove channel configuration at `{NIX_CHANNELS_PATH}`"),
|
format!(
|
||||||
vec![format!(
|
"Remove channel configuration at `{}`",
|
||||||
"Remove channel configuration at `{NIX_CHANNELS_PATH}`"
|
create_file.path.display()
|
||||||
)],
|
),
|
||||||
|
vec![],
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,4 +145,6 @@ pub enum PlaceChannelConfigurationError {
|
||||||
#[from]
|
#[from]
|
||||||
CreateFileError,
|
CreateFileError,
|
||||||
),
|
),
|
||||||
|
#[error("No root home found to place channel configuration in")]
|
||||||
|
NoRootHome,
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,10 +33,8 @@ impl PlaceNixConfiguration {
|
||||||
extra_conf = extra_conf.unwrap_or_else(|| "".into()),
|
extra_conf = extra_conf.unwrap_or_else(|| "".into()),
|
||||||
);
|
);
|
||||||
let create_directory =
|
let create_directory =
|
||||||
CreateDirectory::plan(NIX_CONF_FOLDER, "root".into(), "root".into(), 0o0755, force)
|
CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force).await?;
|
||||||
.await?;
|
let create_file = CreateFile::plan(NIX_CONF, None, None, 0o0664, buf, force).await?;
|
||||||
let create_file =
|
|
||||||
CreateFile::plan(NIX_CONF, "root".into(), "root".into(), 0o0664, buf, force).await?;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
create_directory,
|
create_directory,
|
||||||
create_file,
|
create_file,
|
||||||
|
|
|
@ -26,8 +26,7 @@ pub struct ProvisionNix {
|
||||||
impl ProvisionNix {
|
impl ProvisionNix {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(settings: InstallSettings) -> Result<Self, ProvisionNixError> {
|
pub async fn plan(settings: InstallSettings) -> Result<Self, ProvisionNixError> {
|
||||||
let create_nix_dir =
|
let create_nix_dir = CreateDirectory::plan("/nix", None, None, 0o0755, true).await?;
|
||||||
CreateDirectory::plan("/nix", "root".into(), "root".into(), 0o0755, true).await?;
|
|
||||||
|
|
||||||
let fetch_nix = FetchNix::plan(
|
let fetch_nix = FetchNix::plan(
|
||||||
settings.nix_package_url.clone(),
|
settings.nix_package_url.clone(),
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use crate::actions::base::{StartSystemdUnit, StartSystemdUnitError};
|
|
||||||
|
|
||||||
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
|
|
||||||
|
|
||||||
/// This is mostly indirection for supporting non-systemd
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
|
||||||
pub struct StartNixDaemon {
|
|
||||||
start_systemd_socket: StartSystemdUnit,
|
|
||||||
action_state: ActionState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StartNixDaemon {
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
pub async fn plan() -> Result<Self, StartNixDaemonError> {
|
|
||||||
let start_systemd_socket = StartSystemdUnit::plan("nix-daemon.socket".into()).await?;
|
|
||||||
Ok(Self {
|
|
||||||
start_systemd_socket,
|
|
||||||
action_state: ActionState::Uncompleted,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl Actionable for StartNixDaemon {
|
|
||||||
type Error = StartNixDaemonError;
|
|
||||||
|
|
||||||
fn describe_execute(&self) -> Vec<ActionDescription> {
|
|
||||||
if self.action_state == ActionState::Completed {
|
|
||||||
vec![]
|
|
||||||
} else {
|
|
||||||
self.start_systemd_socket.describe_execute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
async fn execute(&mut self) -> Result<(), Self::Error> {
|
|
||||||
let Self {
|
|
||||||
start_systemd_socket,
|
|
||||||
action_state,
|
|
||||||
} = self;
|
|
||||||
if *action_state == ActionState::Completed {
|
|
||||||
tracing::trace!("Already completed: Starting the nix daemon");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
*action_state = ActionState::Progress;
|
|
||||||
tracing::debug!("Starting the nix daemon");
|
|
||||||
|
|
||||||
start_systemd_socket.execute().await?;
|
|
||||||
|
|
||||||
tracing::trace!("Started the nix daemon");
|
|
||||||
*action_state = ActionState::Completed;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn describe_revert(&self) -> Vec<ActionDescription> {
|
|
||||||
if self.action_state == ActionState::Uncompleted {
|
|
||||||
vec![]
|
|
||||||
} else {
|
|
||||||
self.start_systemd_socket.describe_revert()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
async fn revert(&mut self) -> Result<(), Self::Error> {
|
|
||||||
let Self {
|
|
||||||
start_systemd_socket,
|
|
||||||
action_state,
|
|
||||||
..
|
|
||||||
} = self;
|
|
||||||
if *action_state == ActionState::Uncompleted {
|
|
||||||
tracing::trace!("Already reverted: Stop the nix daemon");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
*action_state = ActionState::Progress;
|
|
||||||
tracing::debug!("Stop the nix daemon");
|
|
||||||
|
|
||||||
start_systemd_socket.revert().await?;
|
|
||||||
|
|
||||||
tracing::trace!("Stopped the nix daemon");
|
|
||||||
*action_state = ActionState::Uncompleted;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<StartNixDaemon> for Action {
|
|
||||||
fn from(v: StartNixDaemon) -> Self {
|
|
||||||
Action::StartNixDaemon(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, Serialize)]
|
|
||||||
pub enum StartNixDaemonError {
|
|
||||||
#[error("Starting systemd unit")]
|
|
||||||
StartSystemdUnit(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
StartSystemdUnitError,
|
|
||||||
),
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@ use meta::{
|
||||||
CreateNixTree, CreateNixTreeError, CreateSystemdSysext, CreateSystemdSysextError,
|
CreateNixTree, CreateNixTreeError, CreateSystemdSysext, CreateSystemdSysextError,
|
||||||
CreateUsersAndGroup, CreateUsersAndGroupError, PlaceChannelConfiguration,
|
CreateUsersAndGroup, CreateUsersAndGroupError, PlaceChannelConfiguration,
|
||||||
PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError,
|
PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError,
|
||||||
ProvisionNix, ProvisionNixError, StartNixDaemon, StartNixDaemonError,
|
ProvisionNix, ProvisionNixError,
|
||||||
};
|
};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ pub enum Action {
|
||||||
DarwinCreateVolume(base::darwin::CreateVolume),
|
DarwinCreateVolume(base::darwin::CreateVolume),
|
||||||
DarwinEnableOwnership(base::darwin::EnableOwnership),
|
DarwinEnableOwnership(base::darwin::EnableOwnership),
|
||||||
DarwinEncryptVolume(base::darwin::EncryptVolume),
|
DarwinEncryptVolume(base::darwin::EncryptVolume),
|
||||||
|
DarwinKickStartLaunchctlService(base::darwin::KickstartLaunchctlService),
|
||||||
DarwinUnmountVolume(base::darwin::UnmountVolume),
|
DarwinUnmountVolume(base::darwin::UnmountVolume),
|
||||||
ConfigureNix(ConfigureNix),
|
ConfigureNix(ConfigureNix),
|
||||||
ConfigureNixDaemonService(ConfigureNixDaemonService),
|
ConfigureNixDaemonService(ConfigureNixDaemonService),
|
||||||
|
@ -81,7 +82,6 @@ pub enum Action {
|
||||||
PlaceNixConfiguration(PlaceNixConfiguration),
|
PlaceNixConfiguration(PlaceNixConfiguration),
|
||||||
ProvisionNix(ProvisionNix),
|
ProvisionNix(ProvisionNix),
|
||||||
SetupDefaultProfile(SetupDefaultProfile),
|
SetupDefaultProfile(SetupDefaultProfile),
|
||||||
StartNixDaemon(StartNixDaemon),
|
|
||||||
StartSystemdUnit(StartSystemdUnit),
|
StartSystemdUnit(StartSystemdUnit),
|
||||||
SystemdSysextMerge(SystemdSysextMerge),
|
SystemdSysextMerge(SystemdSysextMerge),
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,8 @@ pub enum ActionError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
DarwinEncryptVolume(#[from] base::darwin::EncryptVolumeError),
|
DarwinEncryptVolume(#[from] base::darwin::EncryptVolumeError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
DarwinKickStartLaunchctlService(#[from] base::darwin::KickstartLaunchctlServiceError),
|
||||||
|
#[error(transparent)]
|
||||||
DarwinUnmountVolume(#[from] base::darwin::UnmountVolumeError),
|
DarwinUnmountVolume(#[from] base::darwin::UnmountVolumeError),
|
||||||
#[error("Attempted to revert an unexecuted action")]
|
#[error("Attempted to revert an unexecuted action")]
|
||||||
NotExecuted(Action),
|
NotExecuted(Action),
|
||||||
|
@ -141,8 +143,6 @@ pub enum ActionError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
SetupDefaultProfile(#[from] SetupDefaultProfileError),
|
SetupDefaultProfile(#[from] SetupDefaultProfileError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
StartNixDaemon(#[from] StartNixDaemonError),
|
|
||||||
#[error(transparent)]
|
|
||||||
StartSystemdUnit(#[from] StartSystemdUnitError),
|
StartSystemdUnit(#[from] StartSystemdUnitError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
SystemdSysExtMerge(#[from] SystemdSysextMergeError),
|
SystemdSysExtMerge(#[from] SystemdSysextMergeError),
|
||||||
|
@ -179,7 +179,7 @@ impl Actionable for Action {
|
||||||
Action::PlaceNixConfiguration(i) => i.describe_execute(),
|
Action::PlaceNixConfiguration(i) => i.describe_execute(),
|
||||||
Action::ProvisionNix(i) => i.describe_execute(),
|
Action::ProvisionNix(i) => i.describe_execute(),
|
||||||
Action::SetupDefaultProfile(i) => i.describe_execute(),
|
Action::SetupDefaultProfile(i) => i.describe_execute(),
|
||||||
Action::StartNixDaemon(i) => i.describe_execute(),
|
Action::DarwinKickStartLaunchctlService(i) => i.describe_execute(),
|
||||||
Action::StartSystemdUnit(i) => i.describe_execute(),
|
Action::StartSystemdUnit(i) => i.describe_execute(),
|
||||||
Action::SystemdSysextMerge(i) => i.describe_execute(),
|
Action::SystemdSysextMerge(i) => i.describe_execute(),
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ impl Actionable for Action {
|
||||||
Action::PlaceNixConfiguration(i) => i.execute().await?,
|
Action::PlaceNixConfiguration(i) => i.execute().await?,
|
||||||
Action::ProvisionNix(i) => i.execute().await?,
|
Action::ProvisionNix(i) => i.execute().await?,
|
||||||
Action::SetupDefaultProfile(i) => i.execute().await?,
|
Action::SetupDefaultProfile(i) => i.execute().await?,
|
||||||
Action::StartNixDaemon(i) => i.execute().await?,
|
Action::DarwinKickStartLaunchctlService(i) => i.execute().await?,
|
||||||
Action::StartSystemdUnit(i) => i.execute().await?,
|
Action::StartSystemdUnit(i) => i.execute().await?,
|
||||||
Action::SystemdSysextMerge(i) => i.execute().await?,
|
Action::SystemdSysextMerge(i) => i.execute().await?,
|
||||||
};
|
};
|
||||||
|
@ -244,7 +244,7 @@ impl Actionable for Action {
|
||||||
Action::PlaceNixConfiguration(i) => i.describe_revert(),
|
Action::PlaceNixConfiguration(i) => i.describe_revert(),
|
||||||
Action::ProvisionNix(i) => i.describe_revert(),
|
Action::ProvisionNix(i) => i.describe_revert(),
|
||||||
Action::SetupDefaultProfile(i) => i.describe_revert(),
|
Action::SetupDefaultProfile(i) => i.describe_revert(),
|
||||||
Action::StartNixDaemon(i) => i.describe_revert(),
|
Action::DarwinKickStartLaunchctlService(i) => i.describe_revert(),
|
||||||
Action::StartSystemdUnit(i) => i.describe_revert(),
|
Action::StartSystemdUnit(i) => i.describe_revert(),
|
||||||
Action::SystemdSysextMerge(i) => i.describe_revert(),
|
Action::SystemdSysextMerge(i) => i.describe_revert(),
|
||||||
}
|
}
|
||||||
|
@ -276,7 +276,7 @@ impl Actionable for Action {
|
||||||
Action::PlaceNixConfiguration(i) => i.revert().await?,
|
Action::PlaceNixConfiguration(i) => i.revert().await?,
|
||||||
Action::ProvisionNix(i) => i.revert().await?,
|
Action::ProvisionNix(i) => i.revert().await?,
|
||||||
Action::SetupDefaultProfile(i) => i.revert().await?,
|
Action::SetupDefaultProfile(i) => i.revert().await?,
|
||||||
Action::StartNixDaemon(i) => i.revert().await?,
|
Action::DarwinKickStartLaunchctlService(i) => i.revert().await?,
|
||||||
Action::StartSystemdUnit(i) => i.revert().await?,
|
Action::StartSystemdUnit(i) => i.revert().await?,
|
||||||
Action::SystemdSysextMerge(i) => i.revert().await?,
|
Action::SystemdSysextMerge(i) => i.revert().await?,
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,9 @@ impl InstallPlan {
|
||||||
// The plan itself represents the concept of the sequence of stages.
|
// The plan itself represents the concept of the sequence of stages.
|
||||||
for action in actions {
|
for action in actions {
|
||||||
if let Err(err) = action.execute().await {
|
if let Err(err) = action.execute().await {
|
||||||
write_receipt(self.clone()).await?;
|
if let Err(err) = write_receipt(self.clone()).await {
|
||||||
|
tracing::error!("Error saving receipt: {:?}", err);
|
||||||
|
}
|
||||||
return Err(ActionError::from(err).into());
|
return Err(ActionError::from(err).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +159,9 @@ impl InstallPlan {
|
||||||
// The plan itself represents the concept of the sequence of stages.
|
// The plan itself represents the concept of the sequence of stages.
|
||||||
for action in actions {
|
for action in actions {
|
||||||
if let Err(err) = action.revert().await {
|
if let Err(err) = action.revert().await {
|
||||||
write_receipt(self.clone()).await?;
|
if let Err(err) = write_receipt(self.clone()).await {
|
||||||
|
tracing::error!("Error saving receipt: {:?}", err);
|
||||||
|
}
|
||||||
return Err(ActionError::from(err).into());
|
return Err(ActionError::from(err).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@ use tokio::process::Command;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actions::{
|
actions::{
|
||||||
meta::{darwin::CreateApfsVolume, ConfigureNix, ProvisionNix, StartNixDaemon},
|
base::darwin::KickstartLaunchctlService,
|
||||||
|
meta::{darwin::CreateApfsVolume, ConfigureNix, ProvisionNix},
|
||||||
Action, ActionError,
|
Action, ActionError,
|
||||||
},
|
},
|
||||||
execute_command,
|
execute_command,
|
||||||
|
@ -57,7 +58,7 @@ impl Plannable for DarwinMultiUser {
|
||||||
.await
|
.await
|
||||||
.map(Action::from)
|
.map(Action::from)
|
||||||
.map_err(ActionError::from)?,
|
.map_err(ActionError::from)?,
|
||||||
StartNixDaemon::plan()
|
KickstartLaunchctlService::plan("system/org.nixos.nix-daemon".into())
|
||||||
.await
|
.await
|
||||||
.map(Action::from)
|
.map(Action::from)
|
||||||
.map_err(ActionError::from)?,
|
.map_err(ActionError::from)?,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
actions::{
|
actions::{
|
||||||
meta::{ConfigureNix, ProvisionNix, StartNixDaemon},
|
base::StartSystemdUnit,
|
||||||
|
meta::{ConfigureNix, ProvisionNix},
|
||||||
Action, ActionError,
|
Action, ActionError,
|
||||||
},
|
},
|
||||||
planner::{Plannable, PlannerError},
|
planner::{Plannable, PlannerError},
|
||||||
|
@ -28,7 +29,7 @@ impl Plannable for LinuxMultiUser {
|
||||||
.await
|
.await
|
||||||
.map(Action::from)
|
.map(Action::from)
|
||||||
.map_err(ActionError::from)?,
|
.map_err(ActionError::from)?,
|
||||||
StartNixDaemon::plan()
|
StartSystemdUnit::plan("nix-daemon.socket".into())
|
||||||
.await
|
.await
|
||||||
.map(Action::from)
|
.map(Action::from)
|
||||||
.map_err(ActionError::from)?,
|
.map_err(ActionError::from)?,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
actions::{
|
actions::{
|
||||||
meta::{CreateSystemdSysext, ProvisionNix, StartNixDaemon},
|
base::StartSystemdUnit,
|
||||||
|
meta::{CreateSystemdSysext, ProvisionNix},
|
||||||
Action, ActionError,
|
Action, ActionError,
|
||||||
},
|
},
|
||||||
planner::Plannable,
|
planner::Plannable,
|
||||||
|
@ -30,7 +31,7 @@ impl Plannable for SteamDeck {
|
||||||
.await
|
.await
|
||||||
.map(Action::from)
|
.map(Action::from)
|
||||||
.map_err(ActionError::from)?,
|
.map_err(ActionError::from)?,
|
||||||
StartNixDaemon::plan()
|
StartSystemdUnit::plan("nix-daemon.socket".into())
|
||||||
.await
|
.await
|
||||||
.map(Action::from)
|
.map(Action::from)
|
||||||
.map_err(ActionError::from)?,
|
.map_err(ActionError::from)?,
|
||||||
|
|
Loading…
Reference in a new issue