More fleshing out

This commit is contained in:
Ana Hobden 2022-10-19 13:32:15 -07:00
parent da0219deb0
commit fcc200b9d0
10 changed files with 366 additions and 256 deletions

View file

@ -61,9 +61,36 @@ impl Actionable for CreateGroup {
} }
tracing::debug!("Creating group"); tracing::debug!("Creating group");
execute_command(Command::new("groupadd").args(["-g", &gid.to_string(), "--system", &name])) use target_lexicon::OperatingSystem;
.await match target_lexicon::HOST.operating_system {
.map_err(CreateGroupError::Command)?; OperatingSystem::MacOSX {
major: _,
minor: _,
patch: _,
} => {
execute_command(Command::new("/usr/sbin/dseditgroup").args([
"-o",
"create",
"-r",
"Nix build group for nix-daemon",
"-i",
&format!("{gid}"),
name.as_str(),
]))
.await
.map_err(Self::Error::Command)?;
},
_ => {
execute_command(Command::new("groupadd").args([
"-g",
&gid.to_string(),
"--system",
&name,
]))
.await
.map_err(CreateGroupError::Command)?;
},
};
tracing::trace!("Created group"); tracing::trace!("Created group");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;

View file

@ -67,27 +67,49 @@ impl Actionable for CreateUser {
} }
tracing::debug!("Creating user"); tracing::debug!("Creating user");
execute_command(Command::new("useradd").args([ use target_lexicon::OperatingSystem;
"--home-dir", match target_lexicon::HOST.operating_system {
"/var/empty", OperatingSystem::MacOSX {
"--comment", major: _,
&format!("\"Nix build user\""), minor: _,
"--gid", patch: _,
&gid.to_string(), } => {
"--groups", execute_command(Command::new("/usr/bin/dscl").args([
&gid.to_string(), ".",
"--no-user-group", "create",
"--system", &format!("/Users/{name}"),
"--shell", "UniqueId",
"/sbin/nologin", &format!("{uid}"),
"--uid", "PrimaryGroupID",
&uid.to_string(), &format!("{gid}"),
"--password", ]))
"\"!\"", .await
&name.to_string(), .map_err(Self::Error::Command)?;
])) },
.await _ => {
.map_err(Self::Error::Command)?; execute_command(Command::new("useradd").args([
"--home-dir",
"/var/empty",
"--comment",
&format!("\"Nix build user\""),
"--gid",
&gid.to_string(),
"--groups",
&gid.to_string(),
"--no-user-group",
"--system",
"--shell",
"/sbin/nologin",
"--uid",
&uid.to_string(),
"--password",
"\"!\"",
&name.to_string(),
]))
.await
.map_err(Self::Error::Command)?;
},
}
tracing::trace!("Created user"); tracing::trace!("Created user");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
@ -132,9 +154,21 @@ impl Actionable for CreateUser {
} }
tracing::debug!("Deleting user"); tracing::debug!("Deleting user");
execute_command(Command::new("userdel").args([&name.to_string()])) use target_lexicon::OperatingSystem;
.await match target_lexicon::HOST.operating_system {
.map_err(Self::Error::Command)?; OperatingSystem::MacOSX {
major: _,
minor: _,
patch: _,
} => {
todo!()
},
_ => {
execute_command(Command::new("userdel").args([&name.to_string()]))
.await
.map_err(Self::Error::Command)?;
},
};
tracing::trace!("Deleted user"); tracing::trace!("Deleted user");
*action_state = ActionState::Uncompleted; *action_state = ActionState::Uncompleted;

View file

@ -1,3 +1,5 @@
use std::path::{Path, PathBuf};
use serde::Serialize; use serde::Serialize;
use tokio::process::Command; use tokio::process::Command;
@ -7,15 +9,15 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct BootstrapVolume { pub struct BootstrapVolume {
unit: String, path: PathBuf,
action_state: ActionState, action_state: ActionState,
} }
impl BootstrapVolume { impl BootstrapVolume {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn plan(unit: String) -> Result<Self, BootstrapVolumeError> { pub async fn plan(path: impl AsRef<Path>) -> Result<Self, BootstrapVolumeError> {
Ok(Self { Ok(Self {
unit, path: path.as_ref().to_path_buf(),
action_state: ActionState::Uncompleted, action_state: ActionState::Uncompleted,
}) })
} }
@ -30,36 +32,35 @@ impl Actionable for BootstrapVolume {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![ActionDescription::new(
"Start the systemd Nix service and socket".to_string(), format!("Bootstrap and kickstart `{}`", self.path.display()),
vec![ vec![],
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
]
)] )]
} }
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields(
unit = %self.unit, path = %self.path.display(),
))] ))]
async fn execute(&mut self) -> Result<(), Self::Error> { async fn execute(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self { path, action_state } = self;
if *action_state == ActionState::Completed { if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Starting systemd unit"); tracing::trace!("Already completed: Bootstrapping volume");
return Ok(()); return Ok(());
} }
tracing::debug!("Starting systemd unit"); tracing::debug!("Bootstrapping volume");
// TODO(@Hoverbear): Handle proxy vars
execute_command( execute_command(
Command::new("systemctl") Command::new("launchctl")
.arg("enable") .args(["bootstrap", "system"])
.arg("--now") .arg(path),
.arg(format!("{unit}")),
) )
.await .await
.map_err(BootstrapVolumeError::Command)?; .map_err(Self::Error::Command)?;
execute_command(Command::new("launchctl").args(["-k", "system/org.nixos.darwin-store"]))
.await
.map_err(Self::Error::Command)?;
tracing::trace!("Started systemd unit"); tracing::trace!("Bootstrapped volume");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }
@ -69,31 +70,32 @@ impl Actionable for BootstrapVolume {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![ActionDescription::new(
"Stop the systemd Nix service and socket".to_string(), format!("Stop `{}`", self.path.display()),
vec![ vec![],
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
]
)] )]
} }
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields(
unit = %self.unit, path = %self.path.display(),
))] ))]
async fn revert(&mut self) -> Result<(), Self::Error> { async fn revert(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self { path, action_state } = self;
if *action_state == ActionState::Uncompleted { if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Stopping systemd unit"); tracing::trace!("Already reverted: Stop volume");
return Ok(()); return Ok(());
} }
tracing::debug!("Stopping systemd unit"); tracing::debug!("Stop volume");
// TODO(@Hoverbear): Handle proxy vars execute_command(
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) Command::new("launchctl")
.await .args(["bootout", "system"])
.map_err(BootstrapVolumeError::Command)?; .arg(path),
)
.await
.map_err(Self::Error::Command)?;
tracing::trace!("Stopped systemd unit"); tracing::trace!("Stopped volume");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }

View file

@ -7,15 +7,13 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateSyntheticObjects { pub struct CreateSyntheticObjects {
unit: String,
action_state: ActionState, action_state: ActionState,
} }
impl CreateSyntheticObjects { impl CreateSyntheticObjects {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn plan(unit: String) -> Result<Self, CreateSyntheticObjectsError> { pub async fn plan() -> Result<Self, CreateSyntheticObjectsError> {
Ok(Self { Ok(Self {
unit,
action_state: ActionState::Uncompleted, action_state: ActionState::Uncompleted,
}) })
} }
@ -30,36 +28,36 @@ impl Actionable for CreateSyntheticObjects {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![ActionDescription::new(
"Start the systemd Nix service and socket".to_string(), "Create objects defined in `/etc/synthetic.conf`".to_string(),
vec![ vec!["Populates the `/nix` path".to_string()],
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
]
)] )]
} }
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields())]
unit = %self.unit,
))]
async fn execute(&mut self) -> Result<(), Self::Error> { async fn execute(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self { action_state } = self;
if *action_state == ActionState::Completed { if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Starting systemd unit"); tracing::trace!("Already completed: Creating synthetic objects");
return Ok(()); return Ok(());
} }
tracing::debug!("Starting systemd unit"); tracing::debug!("Creating synthetic objects");
// TODO(@Hoverbear): Handle proxy vars // Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
execute_command( execute_command(
Command::new("systemctl") Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
.arg("enable") .arg("-t"),
.arg("--now")
.arg(format!("{unit}")),
) )
.await .await
.map_err(CreateSyntheticObjectsError::Command)?; .ok(); // Deliberate
execute_command(
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
.arg("-B"),
)
.await
.ok(); // Deliberate
tracing::trace!("Started systemd unit"); tracing::trace!("Created synthetic objects");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }
@ -69,31 +67,36 @@ impl Actionable for CreateSyntheticObjects {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![ActionDescription::new(
"Stop the systemd Nix service and socket".to_string(), "Refresh the objects defined in `/etc/synthetic.conf`".to_string(),
vec![ vec!["Will remove the `/nix` path".to_string()],
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
]
)] )]
} }
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields())]
unit = %self.unit,
))]
async fn revert(&mut self) -> Result<(), Self::Error> { async fn revert(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self { action_state } = self;
if *action_state == ActionState::Uncompleted { if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Stopping systemd unit"); tracing::trace!("Already reverted: Refreshing synthetic objects");
return Ok(()); return Ok(());
} }
tracing::debug!("Stopping systemd unit"); tracing::debug!("Refreshing synthetic objects");
// TODO(@Hoverbear): Handle proxy vars // Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) execute_command(
.await Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
.map_err(CreateSyntheticObjectsError::Command)?; .arg("-t"),
)
.await
.ok(); // Deliberate
execute_command(
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
.arg("-B"),
)
.await
.ok(); // Deliberate
tracing::trace!("Stopped systemd unit"); tracing::trace!("Refreshed synthetic objects");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }

View file

@ -1,3 +1,5 @@
use std::path::{Path, PathBuf};
use serde::Serialize; use serde::Serialize;
use tokio::process::Command; use tokio::process::Command;
@ -7,15 +9,23 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateVolume { pub struct CreateVolume {
unit: String, disk: PathBuf,
name: String,
case_sensitive: bool,
action_state: ActionState, action_state: ActionState,
} }
impl CreateVolume { impl CreateVolume {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn plan(unit: String) -> Result<Self, CreateVolumeError> { pub async fn plan(
disk: impl AsRef<Path>,
name: String,
case_sensitive: bool,
) -> Result<Self, CreateVolumeError> {
Ok(Self { Ok(Self {
unit, disk: disk.as_ref().to_path_buf(),
name,
case_sensitive,
action_state: ActionState::Uncompleted, action_state: ActionState::Uncompleted,
}) })
} }
@ -30,36 +40,50 @@ impl Actionable for CreateVolume {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![ActionDescription::new(
"Start the systemd Nix service and socket".to_string(), format!(
vec![ "Create a volumne on `{}` named `{}`",
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() self.disk.display(),
] self.name
),
vec![],
)] )]
} }
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields(
unit = %self.unit, disk = %self.disk.display(),
name = %self.name,
case_sensitive = %self.case_sensitive,
))] ))]
async fn execute(&mut self) -> Result<(), Self::Error> { async fn execute(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self {
disk,
name,
case_sensitive,
action_state,
} = self;
if *action_state == ActionState::Completed { if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Starting systemd unit"); tracing::trace!("Already completed: Creating volume");
return Ok(()); return Ok(());
} }
tracing::debug!("Starting systemd unit"); tracing::debug!("Creating volume");
// TODO(@Hoverbear): Handle proxy vars execute_command(Command::new("/usr/sbin/diskutil").args([
execute_command( "apfs",
Command::new("systemctl") "addVolume",
.arg("enable") &format!("{}", disk.display()),
.arg("--now") if !*case_sensitive {
.arg(format!("{unit}")), "APFS"
) } else {
"Case-sensitive APFS"
},
name,
"-nomount",
]))
.await .await
.map_err(CreateVolumeError::Command)?; .map_err(Self::Error::Command)?;
tracing::trace!("Started systemd unit"); tracing::trace!("Created volume");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }
@ -69,31 +93,44 @@ impl Actionable for CreateVolume {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![ActionDescription::new(
"Stop the systemd Nix service and socket".to_string(), format!(
vec![ "Remove the volume on `{}` named `{}`",
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string() self.disk.display(),
] self.name
),
vec![],
)] )]
} }
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields(
unit = %self.unit, disk = %self.disk.display(),
name = %self.name,
case_sensitive = %self.case_sensitive,
))] ))]
async fn revert(&mut self) -> Result<(), Self::Error> { async fn revert(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self {
disk,
name,
case_sensitive: _,
action_state,
} = self;
if *action_state == ActionState::Uncompleted { if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Stopping systemd unit"); tracing::trace!("Already reverted: Deleting volume");
return Ok(()); return Ok(());
} }
tracing::debug!("Stopping systemd unit"); tracing::debug!("Deleting volume");
// TODO(@Hoverbear): Handle proxy vars execute_command(Command::new("/usr/sbin/diskutil").args([
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) "apfs",
.await "deleteVolume",
.map_err(CreateVolumeError::Command)?; &format!("{}", disk.display()),
name,
]))
.await
.map_err(Self::Error::Command)?;
tracing::trace!("Stopped systemd unit"); tracing::trace!("Deleted volume");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }

View file

@ -1,3 +1,5 @@
use std::path::{Path, PathBuf};
use serde::Serialize; use serde::Serialize;
use tokio::process::Command; use tokio::process::Command;
@ -7,15 +9,15 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct EnableOwnership { pub struct EnableOwnership {
unit: String, path: PathBuf,
action_state: ActionState, action_state: ActionState,
} }
impl EnableOwnership { impl EnableOwnership {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn plan(unit: String) -> Result<Self, EnableOwnershipError> { pub async fn plan(path: impl AsRef<Path>) -> Result<Self, EnableOwnershipError> {
Ok(Self { Ok(Self {
unit, path: path.as_ref().to_path_buf(),
action_state: ActionState::Uncompleted, action_state: ActionState::Uncompleted,
}) })
} }
@ -30,36 +32,56 @@ impl Actionable for EnableOwnership {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![ActionDescription::new(
"Start the systemd Nix service and socket".to_string(), format!("Enable ownership on {}", self.path.display()),
vec![ vec![],
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
]
)] )]
} }
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields(
unit = %self.unit, path = %self.path.display(),
))] ))]
async fn execute(&mut self) -> Result<(), Self::Error> { async fn execute(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self { path, action_state } = self;
if *action_state == ActionState::Completed { if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Starting systemd unit"); tracing::trace!("Already completed: Enabling ownership");
return Ok(()); return Ok(());
} }
tracing::debug!("Starting systemd unit"); tracing::debug!("Enabling ownership");
// TODO(@Hoverbear): Handle proxy vars let should_enable_ownership = {
execute_command( let buf = execute_command(
Command::new("systemctl") Command::new("/usr/sbin/diskutil")
.arg("enable") .args(["info", "-plist"])
.arg("--now") .arg(&path),
.arg(format!("{unit}")), )
) .await
.await .unwrap()
.map_err(EnableOwnershipError::Command)?; .stdout;
let package = sxd_document::parser::parse(&String::from_utf8(buf).unwrap()).unwrap();
tracing::trace!("Started systemd unit"); match sxd_xpath::evaluate_xpath(
&package.as_document(),
"(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]",
)
.unwrap()
{
sxd_xpath::Value::Boolean(bool) => bool,
_ => panic!("At the other disk i/o!!!"),
}
};
if should_enable_ownership {
execute_command(
Command::new("/usr/sbin/diskutil")
.arg("enableOwnership")
.arg(path),
)
.await
.map_err(Self::Error::Command)?;
}
tracing::trace!("Enabled ownership");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }
@ -68,32 +90,25 @@ impl Actionable for EnableOwnership {
if self.action_state == ActionState::Uncompleted { if self.action_state == ActionState::Uncompleted {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![]
"Stop the systemd Nix service and socket".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( #[tracing::instrument(skip_all, fields(
unit = %self.unit, path = %self.path.display(),
))] ))]
async fn revert(&mut self) -> Result<(), Self::Error> { async fn revert(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self {
path: _,
action_state,
} = self;
if *action_state == ActionState::Uncompleted { if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Stopping systemd unit"); tracing::trace!("Already reverted: Unenabling ownership (noop)");
return Ok(()); return Ok(());
} }
tracing::debug!("Stopping systemd unit"); tracing::debug!("Unenabling ownership (noop)");
// TODO(@Hoverbear): Handle proxy vars tracing::trace!("Unenabled ownership (noop)");
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}")))
.await
.map_err(EnableOwnershipError::Command)?;
tracing::trace!("Stopped systemd unit");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }

View file

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use std::path::{Path, PathBuf};
use tokio::process::Command; use tokio::process::Command;
use crate::execute_command; use crate::execute_command;
@ -7,15 +8,20 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct EncryptVolume { pub struct EncryptVolume {
unit: String, disk: PathBuf,
password: String,
action_state: ActionState, action_state: ActionState,
} }
impl EncryptVolume { impl EncryptVolume {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn plan(unit: String) -> Result<Self, EncryptVolumeError> { pub async fn plan(
disk: impl AsRef<Path>,
password: String,
) -> Result<Self, EncryptVolumeError> {
Ok(Self { Ok(Self {
unit, disk: disk.as_ref().to_path_buf(),
password,
action_state: ActionState::Uncompleted, action_state: ActionState::Uncompleted,
}) })
} }
@ -30,36 +36,30 @@ impl Actionable for EncryptVolume {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![ActionDescription::new(
"Start the systemd Nix service and socket".to_string(), format!("Encrypt volume `{}`", self.disk.display()),
vec![ vec![],
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
]
)] )]
} }
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields(
unit = %self.unit, disk = %self.disk.display(),
))] ))]
async fn execute(&mut self) -> Result<(), Self::Error> { async fn execute(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self {
disk,
password,
action_state,
} = self;
if *action_state == ActionState::Completed { if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Starting systemd unit"); tracing::trace!("Already completed: Encrypting volume");
return Ok(()); return Ok(());
} }
tracing::debug!("Starting systemd unit"); tracing::debug!("Encrypting volume");
// TODO(@Hoverbear): Handle proxy vars todo!();
execute_command(
Command::new("systemctl")
.arg("enable")
.arg("--now")
.arg(format!("{unit}")),
)
.await
.map_err(EncryptVolumeError::Command)?;
tracing::trace!("Started systemd unit"); tracing::trace!("Encrypted volume");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }
@ -68,32 +68,26 @@ impl Actionable for EncryptVolume {
if self.action_state == ActionState::Uncompleted { if self.action_state == ActionState::Uncompleted {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![]
"Stop the systemd Nix service and socket".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( #[tracing::instrument(skip_all, fields(
unit = %self.unit, disk = %self.disk.display(),
))] ))]
async fn revert(&mut self) -> Result<(), Self::Error> { async fn revert(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self {
disk,
password,
action_state,
} = self;
if *action_state == ActionState::Uncompleted { if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Stopping systemd unit"); tracing::trace!("Already reverted: Unencrypted volume (noop)");
return Ok(()); return Ok(());
} }
tracing::debug!("Stopping systemd unit"); tracing::debug!("Unencrypted volume (noop)");
// TODO(@Hoverbear): Handle proxy vars tracing::trace!("Unencrypted volume (noop)");
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}")))
.await
.map_err(EncryptVolumeError::Command)?;
tracing::trace!("Stopped systemd unit");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }

View file

@ -1,3 +1,5 @@
use std::path::{Path, PathBuf};
use serde::Serialize; use serde::Serialize;
use tokio::process::Command; use tokio::process::Command;
@ -7,15 +9,18 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct UnmountVolume { pub struct UnmountVolume {
unit: String, disk: PathBuf,
name: String,
action_state: ActionState, action_state: ActionState,
} }
impl UnmountVolume { impl UnmountVolume {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn plan(unit: String) -> Result<Self, UnmountVolumeError> { pub async fn plan(disk: impl AsRef<Path>, name: String) -> Result<Self, UnmountVolumeError> {
let disk = disk.as_ref().to_owned();
Ok(Self { Ok(Self {
unit, disk,
name,
action_state: ActionState::Uncompleted, action_state: ActionState::Uncompleted,
}) })
} }
@ -39,27 +44,30 @@ impl Actionable for UnmountVolume {
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields(
unit = %self.unit, disk = %self.disk.display(),
name = %self.name,
))] ))]
async fn execute(&mut self) -> Result<(), Self::Error> { async fn execute(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self {
disk,
name,
action_state,
} = self;
if *action_state == ActionState::Completed { if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Starting systemd unit"); tracing::trace!("Already completed: Unmounting volume");
return Ok(()); return Ok(());
} }
tracing::debug!("Starting systemd unit"); tracing::debug!("Unmounting volume");
// TODO(@Hoverbear): Handle proxy vars
execute_command( execute_command(
Command::new("systemctl") Command::new(" /usr/sbin/diskutil")
.arg("enable") .args(["unmount", "force"])
.arg("--now") .arg(name),
.arg(format!("{unit}")),
) )
.await .await
.map_err(UnmountVolumeError::Command)?; .map_err(Self::Error::Command)?;
tracing::trace!("Started systemd unit"); tracing::trace!("Unmounted volume");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
Ok(()) Ok(())
} }
@ -78,20 +86,28 @@ impl Actionable for UnmountVolume {
} }
#[tracing::instrument(skip_all, fields( #[tracing::instrument(skip_all, fields(
unit = %self.unit, disk = %self.disk.display(),
name = %self.name,
))] ))]
async fn revert(&mut self) -> Result<(), Self::Error> { async fn revert(&mut self) -> Result<(), Self::Error> {
let Self { unit, action_state } = self; let Self {
disk,
name,
action_state,
} = self;
if *action_state == ActionState::Uncompleted { if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Stopping systemd unit"); tracing::trace!("Already reverted: Stopping systemd unit");
return Ok(()); return Ok(());
} }
tracing::debug!("Stopping systemd unit"); tracing::debug!("Stopping systemd unit");
// TODO(@Hoverbear): Handle proxy vars execute_command(
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) Command::new(" /usr/sbin/diskutil")
.await .args(["unmount", "force"])
.map_err(UnmountVolumeError::Command)?; .arg(name),
)
.await
.map_err(Self::Error::Command)?;
tracing::trace!("Stopped systemd unit"); tracing::trace!("Stopped systemd unit");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;

View file

@ -58,7 +58,7 @@ impl Actionable for SystemdSysextMerge {
execute_command(Command::new("systemd-sysext").arg("merge").arg(device)) execute_command(Command::new("systemd-sysext").arg("merge").arg(device))
.await .await
.map_err(SystemdSysextMergeError::Command)?; .map_err(Self::Error::Command)?;
tracing::trace!("Merged systemd-sysext"); tracing::trace!("Merged systemd-sysext");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;

View file

@ -7,8 +7,7 @@ use crate::actions::base::{
CreateVolume, CreateVolumeError, EnableOwnership, EnableOwnershipError, EncryptVolume, CreateVolume, CreateVolumeError, EnableOwnership, EnableOwnershipError, EncryptVolume,
EncryptVolumeError, UnmountVolume, UnmountVolumeError, EncryptVolumeError, UnmountVolume, UnmountVolumeError,
}, },
CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFile, CreateOrAppendFileError,
CreateOrAppendFileError,
}; };
use crate::actions::{base::darwin, Action, ActionDescription, ActionState, Actionable}; use crate::actions::{base::darwin, Action, ActionDescription, ActionState, Actionable};
@ -51,10 +50,9 @@ impl CreateApfsVolume {
let create_synthetic_objects = CreateSyntheticObjects::plan().await?; let create_synthetic_objects = CreateSyntheticObjects::plan().await?;
let unmount_volume = let unmount_volume = UnmountVolume::plan(disk, name.clone()).await?;
UnmountVolume::plan(disk, name.clone(), case_sensitive, encrypt).await?;
let create_volume = CreateVolume::plan(disk, name.clone(), case_sensitive, encrypt).await?; let create_volume = CreateVolume::plan(disk, name.clone(), case_sensitive).await?;
let create_or_append_fstab = CreateOrAppendFile::plan( let create_or_append_fstab = CreateOrAppendFile::plan(
"/etc/fstab", "/etc/fstab",
@ -65,13 +63,13 @@ impl CreateApfsVolume {
) )
.await?; .await?;
let encrypt_volume = if let Some(password) = encrypt { let encrypt_volume = if let Some(password) = encrypt.as_ref() {
Some(EncryptVolume::plan(disk, password).await) Some(EncryptVolume::plan(disk, password.to_string()).await?)
} else { } else {
None None
}; };
let bootstrap_volume = BootstrapVolume::plan(NIX_VOLUME_MOUNTD_DEST, disk, name).await?; let bootstrap_volume = BootstrapVolume::plan(NIX_VOLUME_MOUNTD_DEST).await?;
let enable_ownership = EnableOwnership::plan("/nix").await?; let enable_ownership = EnableOwnership::plan("/nix").await?;
Ok(Self { Ok(Self {
@ -100,17 +98,8 @@ impl Actionable for CreateApfsVolume {
let Self { let Self {
disk, disk,
name, name,
case_sensitive,
encrypt,
create_or_append_synthetic_conf,
create_synthetic_objects,
unmount_volume,
create_volume,
create_or_append_fstab,
encrypt_volume,
bootstrap_volume,
enable_ownership,
action_state: _, action_state: _,
..
} = &self; } = &self;
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {
vec![] vec![]
@ -127,10 +116,10 @@ impl Actionable for CreateApfsVolume {
#[tracing::instrument(skip_all, fields(destination,))] #[tracing::instrument(skip_all, fields(destination,))]
async fn execute(&mut self) -> Result<(), Self::Error> { async fn execute(&mut self) -> Result<(), Self::Error> {
let Self { let Self {
disk, disk: _,
name, name: _,
case_sensitive, case_sensitive: _,
encrypt, encrypt: _,
create_or_append_synthetic_conf, create_or_append_synthetic_conf,
create_synthetic_objects, create_synthetic_objects,
unmount_volume, unmount_volume,
@ -152,7 +141,9 @@ impl Actionable for CreateApfsVolume {
unmount_volume.execute().await?; unmount_volume.execute().await?;
create_volume.execute().await?; create_volume.execute().await?;
create_or_append_fstab.execute().await?; create_or_append_fstab.execute().await?;
encrypt_volume.execute().await?; if let Some(encrypt_volume) = encrypt_volume {
encrypt_volume.execute().await?;
}
bootstrap_volume.execute().await?; bootstrap_volume.execute().await?;
enable_ownership.execute().await?; enable_ownership.execute().await?;
@ -165,19 +156,10 @@ impl Actionable for CreateApfsVolume {
let Self { let Self {
disk, disk,
name, name,
case_sensitive, action_state,
encrypt, ..
create_or_append_synthetic_conf,
create_synthetic_objects,
unmount_volume,
create_volume,
create_or_append_fstab,
encrypt_volume,
bootstrap_volume,
enable_ownership,
action_state: _,
} = &self; } = &self;
if self.action_state == ActionState::Uncompleted { if *action_state == ActionState::Uncompleted {
vec![] vec![]
} else { } else {
vec![ActionDescription::new( vec![ActionDescription::new(