launchctl bootstrap
fails with disabled in a dirty state (#555)
* Handle a MacOS service being disabled during bootstrap * Handle service disabled in configure_init_service * Fixup missed line * Fix import * Don't deref pointer * Tweak detection and re-enablement
This commit is contained in:
parent
f8b3e29751
commit
ba841149e7
|
@ -189,13 +189,31 @@ impl Action for ConfigureInitService {
|
|||
.await
|
||||
.map_err(Self::error)?;
|
||||
|
||||
let domain = "system";
|
||||
let service = "org.nixos.nix-daemon";
|
||||
|
||||
let is_disabled = crate::action::macos::service_is_disabled(&domain, &service)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
if is_disabled {
|
||||
execute_command(
|
||||
Command::new("launchctl")
|
||||
.process_group(0)
|
||||
.arg("enable")
|
||||
.arg(&format!("{domain}/{service}"))
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
}
|
||||
|
||||
if *start_daemon {
|
||||
execute_command(
|
||||
Command::new("launchctl")
|
||||
.process_group(0)
|
||||
.arg("kickstart")
|
||||
.arg("-k")
|
||||
.arg("system/org.nixos.nix-daemon")
|
||||
.arg(&format!("{domain}/{service}"))
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -8,6 +8,8 @@ use crate::execute_command;
|
|||
|
||||
use crate::action::{Action, ActionDescription};
|
||||
|
||||
use super::service_is_disabled;
|
||||
|
||||
/**
|
||||
Bootstrap and kickstart an APFS volume
|
||||
*/
|
||||
|
@ -16,6 +18,8 @@ pub struct BootstrapLaunchctlService {
|
|||
domain: String,
|
||||
service: String,
|
||||
path: PathBuf,
|
||||
is_present: bool,
|
||||
is_disabled: bool,
|
||||
}
|
||||
|
||||
impl BootstrapLaunchctlService {
|
||||
|
@ -29,6 +33,7 @@ impl BootstrapLaunchctlService {
|
|||
let service = service.as_ref().to_string();
|
||||
let path = path.as_ref().to_path_buf();
|
||||
|
||||
let is_present = {
|
||||
let mut command = Command::new("launchctl");
|
||||
command.process_group(0);
|
||||
command.arg("print");
|
||||
|
@ -37,16 +42,29 @@ impl BootstrapLaunchctlService {
|
|||
command.stdin(std::process::Stdio::null());
|
||||
command.stdout(std::process::Stdio::piped());
|
||||
command.stderr(std::process::Stdio::piped());
|
||||
let output = command
|
||||
let command_output = command
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| Self::error(ActionErrorKind::command(&command, e)))?;
|
||||
if output.status.success() || output.status.code() == Some(37) {
|
||||
// We presume that success means it's found
|
||||
if command_output.status.success() || command_output.status.code() == Some(37) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
let is_disabled = service_is_disabled(&domain, &service)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
|
||||
if is_present && !is_disabled {
|
||||
return Ok(StatefulAction::completed(Self {
|
||||
service,
|
||||
domain,
|
||||
path,
|
||||
is_present,
|
||||
is_disabled,
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -54,6 +72,8 @@ impl BootstrapLaunchctlService {
|
|||
domain,
|
||||
service,
|
||||
path,
|
||||
is_present,
|
||||
is_disabled,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +99,8 @@ impl Action for BootstrapLaunchctlService {
|
|||
"bootstrap_launchctl_service",
|
||||
domain = self.domain,
|
||||
path = %self.path.display(),
|
||||
is_disabled = self.is_disabled,
|
||||
is_present = self.is_present,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -90,20 +112,36 @@ impl Action for BootstrapLaunchctlService {
|
|||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
domain,
|
||||
service: _,
|
||||
service,
|
||||
path,
|
||||
is_present,
|
||||
is_disabled,
|
||||
} = self;
|
||||
|
||||
if *is_disabled {
|
||||
execute_command(
|
||||
Command::new("launchctl")
|
||||
.process_group(0)
|
||||
.arg("bootstrap")
|
||||
.arg(domain)
|
||||
.arg(path)
|
||||
.arg("enable")
|
||||
.arg(&format!("{domain}/{service}"))
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
}
|
||||
|
||||
if !*is_present {
|
||||
execute_command(
|
||||
Command::new("launchctl")
|
||||
.process_group(0)
|
||||
.arg("bootstrap")
|
||||
.arg(&domain)
|
||||
.arg(&path)
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ use tokio::process::Command;
|
|||
pub use unmount_apfs_volume::UnmountApfsVolume;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::execute_command;
|
||||
|
||||
use super::ActionErrorKind;
|
||||
|
||||
async fn get_uuid_for_label(apfs_volume_label: &str) -> Result<Option<Uuid>, ActionErrorKind> {
|
||||
|
@ -75,3 +77,23 @@ struct DiskUtilApfsInfoOutput {
|
|||
#[serde(rename = "VolumeUUID")]
|
||||
volume_uuid: Option<Uuid>,
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub(crate) async fn service_is_disabled(
|
||||
domain: &str,
|
||||
service: &str,
|
||||
) -> Result<bool, ActionErrorKind> {
|
||||
let output = execute_command(
|
||||
Command::new("launchctl")
|
||||
.arg("print-disabled")
|
||||
.arg(&domain)
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped()),
|
||||
)
|
||||
.await?;
|
||||
let utf8_output = String::from_utf8_lossy(&output.stdout);
|
||||
let is_disabled = utf8_output.contains(&format!("\"{service}\" => disabled"));
|
||||
tracing::trace!(is_disabled, "Service disabled status");
|
||||
Ok(is_disabled)
|
||||
}
|
||||
|
|
4
tests/fixtures/macos/macos.json
vendored
4
tests/fixtures/macos/macos.json
vendored
|
@ -61,7 +61,9 @@
|
|||
"action": {
|
||||
"domain": "system",
|
||||
"service": "org.nixos.darwin-store",
|
||||
"path": "/Library/LaunchDaemons/org.nixos.darwin-store.plist"
|
||||
"path": "/Library/LaunchDaemons/org.nixos.darwin-store.plist",
|
||||
"is_present": false,
|
||||
"is_disabled": false
|
||||
},
|
||||
"state": "Uncompleted"
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue