From fcc200b9d0ceedd3e40819e869519d490b772891 Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Wed, 19 Oct 2022 13:32:15 -0700 Subject: [PATCH] More fleshing out --- src/actions/base/create_group.rs | 33 +++++- src/actions/base/create_user.rs | 82 ++++++++++---- src/actions/base/darwin/bootstrap_volume.rs | 64 +++++------ .../base/darwin/create_synthetic_objects.rs | 73 +++++++------ src/actions/base/darwin/create_volume.rs | 103 ++++++++++++------ src/actions/base/darwin/enable_ownership.rs | 89 ++++++++------- src/actions/base/darwin/encrypt_volume.rs | 68 ++++++------ src/actions/base/darwin/unmount_volume.rs | 56 ++++++---- src/actions/base/systemd_sysext_merge.rs | 2 +- src/actions/meta/darwin/create_apfs_volume.rs | 52 +++------ 10 files changed, 366 insertions(+), 256 deletions(-) diff --git a/src/actions/base/create_group.rs b/src/actions/base/create_group.rs index cb77d58..6a7a108 100644 --- a/src/actions/base/create_group.rs +++ b/src/actions/base/create_group.rs @@ -61,9 +61,36 @@ impl Actionable for CreateGroup { } tracing::debug!("Creating group"); - execute_command(Command::new("groupadd").args(["-g", &gid.to_string(), "--system", &name])) - .await - .map_err(CreateGroupError::Command)?; + use target_lexicon::OperatingSystem; + match target_lexicon::HOST.operating_system { + 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"); *action_state = ActionState::Completed; diff --git a/src/actions/base/create_user.rs b/src/actions/base/create_user.rs index 9423555..d6654d9 100644 --- a/src/actions/base/create_user.rs +++ b/src/actions/base/create_user.rs @@ -67,27 +67,49 @@ impl Actionable for CreateUser { } tracing::debug!("Creating user"); - 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)?; + use target_lexicon::OperatingSystem; + match target_lexicon::HOST.operating_system { + OperatingSystem::MacOSX { + major: _, + minor: _, + patch: _, + } => { + execute_command(Command::new("/usr/bin/dscl").args([ + ".", + "create", + &format!("/Users/{name}"), + "UniqueId", + &format!("{uid}"), + "PrimaryGroupID", + &format!("{gid}"), + ])) + .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"); *action_state = ActionState::Completed; @@ -132,9 +154,21 @@ impl Actionable for CreateUser { } tracing::debug!("Deleting user"); - execute_command(Command::new("userdel").args([&name.to_string()])) - .await - .map_err(Self::Error::Command)?; + use target_lexicon::OperatingSystem; + match target_lexicon::HOST.operating_system { + 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"); *action_state = ActionState::Uncompleted; diff --git a/src/actions/base/darwin/bootstrap_volume.rs b/src/actions/base/darwin/bootstrap_volume.rs index 0b2de35..e40f31a 100644 --- a/src/actions/base/darwin/bootstrap_volume.rs +++ b/src/actions/base/darwin/bootstrap_volume.rs @@ -1,3 +1,5 @@ +use std::path::{Path, PathBuf}; + use serde::Serialize; use tokio::process::Command; @@ -7,15 +9,15 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct BootstrapVolume { - unit: String, + path: PathBuf, action_state: ActionState, } impl BootstrapVolume { #[tracing::instrument(skip_all)] - pub async fn plan(unit: String) -> Result { + pub async fn plan(path: impl AsRef) -> Result { Ok(Self { - unit, + path: path.as_ref().to_path_buf(), action_state: ActionState::Uncompleted, }) } @@ -30,36 +32,35 @@ impl Actionable for BootstrapVolume { vec![] } else { vec![ActionDescription::new( - "Start 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() - ] + format!("Bootstrap and kickstart `{}`", self.path.display()), + vec![], )] } } #[tracing::instrument(skip_all, fields( - unit = %self.unit, + path = %self.path.display(), ))] 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 { - tracing::trace!("Already completed: Starting systemd unit"); + tracing::trace!("Already completed: Bootstrapping volume"); return Ok(()); } - tracing::debug!("Starting systemd unit"); + tracing::debug!("Bootstrapping volume"); - // TODO(@Hoverbear): Handle proxy vars execute_command( - Command::new("systemctl") - .arg("enable") - .arg("--now") - .arg(format!("{unit}")), + Command::new("launchctl") + .args(["bootstrap", "system"]) + .arg(path), ) .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; Ok(()) } @@ -69,31 +70,32 @@ impl Actionable for BootstrapVolume { vec![] } else { vec![ActionDescription::new( - "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() - ] + format!("Stop `{}`", self.path.display()), + vec![], )] } } #[tracing::instrument(skip_all, fields( - unit = %self.unit, + path = %self.path.display(), ))] 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 { - tracing::trace!("Already reverted: Stopping systemd unit"); + tracing::trace!("Already reverted: Stop volume"); return Ok(()); } - tracing::debug!("Stopping systemd unit"); + tracing::debug!("Stop volume"); - // TODO(@Hoverbear): Handle proxy vars - execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) - .await - .map_err(BootstrapVolumeError::Command)?; + execute_command( + Command::new("launchctl") + .args(["bootout", "system"]) + .arg(path), + ) + .await + .map_err(Self::Error::Command)?; - tracing::trace!("Stopped systemd unit"); + tracing::trace!("Stopped volume"); *action_state = ActionState::Completed; Ok(()) } diff --git a/src/actions/base/darwin/create_synthetic_objects.rs b/src/actions/base/darwin/create_synthetic_objects.rs index d3c04a0..fb6928e 100644 --- a/src/actions/base/darwin/create_synthetic_objects.rs +++ b/src/actions/base/darwin/create_synthetic_objects.rs @@ -7,15 +7,13 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateSyntheticObjects { - unit: String, action_state: ActionState, } impl CreateSyntheticObjects { #[tracing::instrument(skip_all)] - pub async fn plan(unit: String) -> Result { + pub async fn plan() -> Result { Ok(Self { - unit, action_state: ActionState::Uncompleted, }) } @@ -30,36 +28,36 @@ impl Actionable for CreateSyntheticObjects { vec![] } else { vec![ActionDescription::new( - "Start 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() - ] + "Create objects defined in `/etc/synthetic.conf`".to_string(), + vec!["Populates the `/nix` path".to_string()], )] } } - #[tracing::instrument(skip_all, fields( - unit = %self.unit, - ))] + #[tracing::instrument(skip_all, fields())] async fn execute(&mut self) -> Result<(), Self::Error> { - let Self { unit, action_state } = self; + let Self { action_state } = self; if *action_state == ActionState::Completed { - tracing::trace!("Already completed: Starting systemd unit"); + tracing::trace!("Already completed: Creating synthetic objects"); 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( - Command::new("systemctl") - .arg("enable") - .arg("--now") - .arg(format!("{unit}")), + Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util") + .arg("-t"), ) .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; Ok(()) } @@ -69,31 +67,36 @@ impl Actionable for CreateSyntheticObjects { vec![] } else { vec![ActionDescription::new( - "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() - ] + "Refresh the objects defined in `/etc/synthetic.conf`".to_string(), + vec!["Will remove the `/nix` path".to_string()], )] } } - #[tracing::instrument(skip_all, fields( - unit = %self.unit, - ))] + #[tracing::instrument(skip_all, fields())] async fn revert(&mut self) -> Result<(), Self::Error> { - let Self { unit, action_state } = self; + let Self { action_state } = self; if *action_state == ActionState::Uncompleted { - tracing::trace!("Already reverted: Stopping systemd unit"); + tracing::trace!("Already reverted: Refreshing synthetic objects"); return Ok(()); } - tracing::debug!("Stopping systemd unit"); + tracing::debug!("Refreshing synthetic objects"); - // TODO(@Hoverbear): Handle proxy vars - execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) - .await - .map_err(CreateSyntheticObjectsError::Command)?; + // 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("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util") + .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; Ok(()) } diff --git a/src/actions/base/darwin/create_volume.rs b/src/actions/base/darwin/create_volume.rs index eddb6e6..19aaa68 100644 --- a/src/actions/base/darwin/create_volume.rs +++ b/src/actions/base/darwin/create_volume.rs @@ -1,3 +1,5 @@ +use std::path::{Path, PathBuf}; + use serde::Serialize; use tokio::process::Command; @@ -7,15 +9,23 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateVolume { - unit: String, + disk: PathBuf, + name: String, + case_sensitive: bool, action_state: ActionState, } impl CreateVolume { #[tracing::instrument(skip_all)] - pub async fn plan(unit: String) -> Result { + pub async fn plan( + disk: impl AsRef, + name: String, + case_sensitive: bool, + ) -> Result { Ok(Self { - unit, + disk: disk.as_ref().to_path_buf(), + name, + case_sensitive, action_state: ActionState::Uncompleted, }) } @@ -30,36 +40,50 @@ impl Actionable for CreateVolume { vec![] } else { vec![ActionDescription::new( - "Start 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() - ] + format!( + "Create a volumne on `{}` named `{}`", + self.disk.display(), + self.name + ), + vec![], )] } } #[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> { - let Self { unit, action_state } = self; + let Self { + disk, + name, + case_sensitive, + action_state, + } = self; if *action_state == ActionState::Completed { - tracing::trace!("Already completed: Starting systemd unit"); + tracing::trace!("Already completed: Creating volume"); return Ok(()); } - tracing::debug!("Starting systemd unit"); + tracing::debug!("Creating volume"); - // TODO(@Hoverbear): Handle proxy vars - execute_command( - Command::new("systemctl") - .arg("enable") - .arg("--now") - .arg(format!("{unit}")), - ) + execute_command(Command::new("/usr/sbin/diskutil").args([ + "apfs", + "addVolume", + &format!("{}", disk.display()), + if !*case_sensitive { + "APFS" + } else { + "Case-sensitive APFS" + }, + name, + "-nomount", + ])) .await - .map_err(CreateVolumeError::Command)?; + .map_err(Self::Error::Command)?; - tracing::trace!("Started systemd unit"); + tracing::trace!("Created volume"); *action_state = ActionState::Completed; Ok(()) } @@ -69,31 +93,44 @@ impl Actionable for CreateVolume { vec![] } else { vec![ActionDescription::new( - "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() - ] + format!( + "Remove the volume on `{}` named `{}`", + self.disk.display(), + self.name + ), + vec![], )] } } #[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> { - let Self { unit, action_state } = self; + let Self { + disk, + name, + case_sensitive: _, + action_state, + } = self; if *action_state == ActionState::Uncompleted { - tracing::trace!("Already reverted: Stopping systemd unit"); + tracing::trace!("Already reverted: Deleting volume"); return Ok(()); } - tracing::debug!("Stopping systemd unit"); + tracing::debug!("Deleting volume"); - // TODO(@Hoverbear): Handle proxy vars - execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) - .await - .map_err(CreateVolumeError::Command)?; + execute_command(Command::new("/usr/sbin/diskutil").args([ + "apfs", + "deleteVolume", + &format!("{}", disk.display()), + name, + ])) + .await + .map_err(Self::Error::Command)?; - tracing::trace!("Stopped systemd unit"); + tracing::trace!("Deleted volume"); *action_state = ActionState::Completed; Ok(()) } diff --git a/src/actions/base/darwin/enable_ownership.rs b/src/actions/base/darwin/enable_ownership.rs index fbc9ec5..3f80eef 100644 --- a/src/actions/base/darwin/enable_ownership.rs +++ b/src/actions/base/darwin/enable_ownership.rs @@ -1,3 +1,5 @@ +use std::path::{Path, PathBuf}; + use serde::Serialize; use tokio::process::Command; @@ -7,15 +9,15 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct EnableOwnership { - unit: String, + path: PathBuf, action_state: ActionState, } impl EnableOwnership { #[tracing::instrument(skip_all)] - pub async fn plan(unit: String) -> Result { + pub async fn plan(path: impl AsRef) -> Result { Ok(Self { - unit, + path: path.as_ref().to_path_buf(), action_state: ActionState::Uncompleted, }) } @@ -30,36 +32,56 @@ impl Actionable for EnableOwnership { vec![] } else { vec![ActionDescription::new( - "Start 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() - ] + format!("Enable ownership on {}", self.path.display()), + vec![], )] } } #[tracing::instrument(skip_all, fields( - unit = %self.unit, + path = %self.path.display(), ))] 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 { - tracing::trace!("Already completed: Starting systemd unit"); + tracing::trace!("Already completed: Enabling ownership"); return Ok(()); } - tracing::debug!("Starting systemd unit"); + tracing::debug!("Enabling ownership"); - // TODO(@Hoverbear): Handle proxy vars - execute_command( - Command::new("systemctl") - .arg("enable") - .arg("--now") - .arg(format!("{unit}")), - ) - .await - .map_err(EnableOwnershipError::Command)?; + let should_enable_ownership = { + let buf = execute_command( + Command::new("/usr/sbin/diskutil") + .args(["info", "-plist"]) + .arg(&path), + ) + .await + .unwrap() + .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; Ok(()) } @@ -68,32 +90,25 @@ impl Actionable for EnableOwnership { if self.action_state == ActionState::Uncompleted { vec![] } else { - vec![ActionDescription::new( - "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() - ] - )] + vec![] } } #[tracing::instrument(skip_all, fields( - unit = %self.unit, + path = %self.path.display(), ))] 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 { - tracing::trace!("Already reverted: Stopping systemd unit"); + tracing::trace!("Already reverted: Unenabling ownership (noop)"); return Ok(()); } - tracing::debug!("Stopping systemd unit"); + tracing::debug!("Unenabling ownership (noop)"); - // TODO(@Hoverbear): Handle proxy vars - execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) - .await - .map_err(EnableOwnershipError::Command)?; - - tracing::trace!("Stopped systemd unit"); + tracing::trace!("Unenabled ownership (noop)"); *action_state = ActionState::Completed; Ok(()) } diff --git a/src/actions/base/darwin/encrypt_volume.rs b/src/actions/base/darwin/encrypt_volume.rs index 2814d91..9139a24 100644 --- a/src/actions/base/darwin/encrypt_volume.rs +++ b/src/actions/base/darwin/encrypt_volume.rs @@ -1,4 +1,5 @@ use serde::Serialize; +use std::path::{Path, PathBuf}; use tokio::process::Command; use crate::execute_command; @@ -7,15 +8,20 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct EncryptVolume { - unit: String, + disk: PathBuf, + password: String, action_state: ActionState, } impl EncryptVolume { #[tracing::instrument(skip_all)] - pub async fn plan(unit: String) -> Result { + pub async fn plan( + disk: impl AsRef, + password: String, + ) -> Result { Ok(Self { - unit, + disk: disk.as_ref().to_path_buf(), + password, action_state: ActionState::Uncompleted, }) } @@ -30,36 +36,30 @@ impl Actionable for EncryptVolume { vec![] } else { vec![ActionDescription::new( - "Start 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() - ] + format!("Encrypt volume `{}`", self.disk.display()), + vec![], )] } } #[tracing::instrument(skip_all, fields( - unit = %self.unit, + disk = %self.disk.display(), ))] 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 { - tracing::trace!("Already completed: Starting systemd unit"); + tracing::trace!("Already completed: Encrypting volume"); return Ok(()); } - tracing::debug!("Starting systemd unit"); + tracing::debug!("Encrypting volume"); - // TODO(@Hoverbear): Handle proxy vars - execute_command( - Command::new("systemctl") - .arg("enable") - .arg("--now") - .arg(format!("{unit}")), - ) - .await - .map_err(EncryptVolumeError::Command)?; + todo!(); - tracing::trace!("Started systemd unit"); + tracing::trace!("Encrypted volume"); *action_state = ActionState::Completed; Ok(()) } @@ -68,32 +68,26 @@ impl Actionable for EncryptVolume { if self.action_state == ActionState::Uncompleted { vec![] } else { - vec![ActionDescription::new( - "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() - ] - )] + vec![] } } #[tracing::instrument(skip_all, fields( - unit = %self.unit, + disk = %self.disk.display(), ))] 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 { - tracing::trace!("Already reverted: Stopping systemd unit"); + tracing::trace!("Already reverted: Unencrypted volume (noop)"); return Ok(()); } - tracing::debug!("Stopping systemd unit"); + tracing::debug!("Unencrypted volume (noop)"); - // TODO(@Hoverbear): Handle proxy vars - execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) - .await - .map_err(EncryptVolumeError::Command)?; - - tracing::trace!("Stopped systemd unit"); + tracing::trace!("Unencrypted volume (noop)"); *action_state = ActionState::Completed; Ok(()) } diff --git a/src/actions/base/darwin/unmount_volume.rs b/src/actions/base/darwin/unmount_volume.rs index 41cd5fe..1e18868 100644 --- a/src/actions/base/darwin/unmount_volume.rs +++ b/src/actions/base/darwin/unmount_volume.rs @@ -1,3 +1,5 @@ +use std::path::{Path, PathBuf}; + use serde::Serialize; use tokio::process::Command; @@ -7,15 +9,18 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct UnmountVolume { - unit: String, + disk: PathBuf, + name: String, action_state: ActionState, } impl UnmountVolume { #[tracing::instrument(skip_all)] - pub async fn plan(unit: String) -> Result { + pub async fn plan(disk: impl AsRef, name: String) -> Result { + let disk = disk.as_ref().to_owned(); Ok(Self { - unit, + disk, + name, action_state: ActionState::Uncompleted, }) } @@ -39,27 +44,30 @@ impl Actionable for UnmountVolume { } #[tracing::instrument(skip_all, fields( - unit = %self.unit, + disk = %self.disk.display(), + name = %self.name, ))] 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 { - tracing::trace!("Already completed: Starting systemd unit"); + tracing::trace!("Already completed: Unmounting volume"); return Ok(()); } - tracing::debug!("Starting systemd unit"); + tracing::debug!("Unmounting volume"); - // TODO(@Hoverbear): Handle proxy vars execute_command( - Command::new("systemctl") - .arg("enable") - .arg("--now") - .arg(format!("{unit}")), + Command::new(" /usr/sbin/diskutil") + .args(["unmount", "force"]) + .arg(name), ) .await - .map_err(UnmountVolumeError::Command)?; + .map_err(Self::Error::Command)?; - tracing::trace!("Started systemd unit"); + tracing::trace!("Unmounted volume"); *action_state = ActionState::Completed; Ok(()) } @@ -78,20 +86,28 @@ impl Actionable for UnmountVolume { } #[tracing::instrument(skip_all, fields( - unit = %self.unit, + disk = %self.disk.display(), + name = %self.name, ))] 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 { tracing::trace!("Already reverted: Stopping systemd unit"); return Ok(()); } tracing::debug!("Stopping systemd unit"); - // TODO(@Hoverbear): Handle proxy vars - execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) - .await - .map_err(UnmountVolumeError::Command)?; + execute_command( + Command::new(" /usr/sbin/diskutil") + .args(["unmount", "force"]) + .arg(name), + ) + .await + .map_err(Self::Error::Command)?; tracing::trace!("Stopped systemd unit"); *action_state = ActionState::Completed; diff --git a/src/actions/base/systemd_sysext_merge.rs b/src/actions/base/systemd_sysext_merge.rs index 5769111..d710189 100644 --- a/src/actions/base/systemd_sysext_merge.rs +++ b/src/actions/base/systemd_sysext_merge.rs @@ -58,7 +58,7 @@ impl Actionable for SystemdSysextMerge { execute_command(Command::new("systemd-sysext").arg("merge").arg(device)) .await - .map_err(SystemdSysextMergeError::Command)?; + .map_err(Self::Error::Command)?; tracing::trace!("Merged systemd-sysext"); *action_state = ActionState::Completed; diff --git a/src/actions/meta/darwin/create_apfs_volume.rs b/src/actions/meta/darwin/create_apfs_volume.rs index 455d776..4c4f546 100644 --- a/src/actions/meta/darwin/create_apfs_volume.rs +++ b/src/actions/meta/darwin/create_apfs_volume.rs @@ -7,8 +7,7 @@ use crate::actions::base::{ CreateVolume, CreateVolumeError, EnableOwnership, EnableOwnershipError, EncryptVolume, EncryptVolumeError, UnmountVolume, UnmountVolumeError, }, - CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError, CreateOrAppendFile, - CreateOrAppendFileError, + CreateOrAppendFile, CreateOrAppendFileError, }; use crate::actions::{base::darwin, Action, ActionDescription, ActionState, Actionable}; @@ -51,10 +50,9 @@ impl CreateApfsVolume { let create_synthetic_objects = CreateSyntheticObjects::plan().await?; - let unmount_volume = - UnmountVolume::plan(disk, name.clone(), case_sensitive, encrypt).await?; + let unmount_volume = UnmountVolume::plan(disk, name.clone()).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( "/etc/fstab", @@ -65,13 +63,13 @@ impl CreateApfsVolume { ) .await?; - let encrypt_volume = if let Some(password) = encrypt { - Some(EncryptVolume::plan(disk, password).await) + let encrypt_volume = if let Some(password) = encrypt.as_ref() { + Some(EncryptVolume::plan(disk, password.to_string()).await?) } else { 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?; Ok(Self { @@ -100,17 +98,8 @@ impl Actionable for CreateApfsVolume { let Self { disk, 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: _, + .. } = &self; if self.action_state == ActionState::Completed { vec![] @@ -127,10 +116,10 @@ impl Actionable for CreateApfsVolume { #[tracing::instrument(skip_all, fields(destination,))] async fn execute(&mut self) -> Result<(), Self::Error> { let Self { - disk, - name, - case_sensitive, - encrypt, + disk: _, + name: _, + case_sensitive: _, + encrypt: _, create_or_append_synthetic_conf, create_synthetic_objects, unmount_volume, @@ -152,7 +141,9 @@ impl Actionable for CreateApfsVolume { unmount_volume.execute().await?; create_volume.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?; enable_ownership.execute().await?; @@ -165,19 +156,10 @@ impl Actionable for CreateApfsVolume { let Self { disk, 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; - if self.action_state == ActionState::Uncompleted { + if *action_state == ActionState::Uncompleted { vec![] } else { vec![ActionDescription::new(