Hobbling along

This commit is contained in:
Ana Hobden 2022-10-24 16:16:18 -07:00
parent 86a518f2fe
commit 8924bf5cb7
14 changed files with 235 additions and 79 deletions

View file

@ -81,7 +81,7 @@ impl Actionable for ConfigureNixDaemonService {
const DARWIN_NIX_DAEMON_DEST: &str =
"/Library/LaunchDaemons/org.nixos.nix-daemon.plist";
let src = Path::new("/nix/var/nix/profiles/default").join(DARWIN_NIX_DAEMON_DEST);
let src = Path::new("/nix/var/nix/profiles/default/Library/LaunchDaemons/org.nixos.nix-daemon.plist");
tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST)
.await
.map_err(|e| {

View file

@ -9,16 +9,18 @@ use crate::actions::{Action, ActionDescription, ActionState, Actionable};
pub struct CreateUser {
name: String,
uid: usize,
groupname: String,
gid: usize,
action_state: ActionState,
}
impl CreateUser {
#[tracing::instrument(skip_all)]
pub fn plan(name: String, uid: usize, gid: usize) -> Self {
pub fn plan(name: String, uid: usize, groupname: String, gid: usize) -> Self {
Self {
name,
uid,
groupname,
gid,
action_state: ActionState::Uncompleted,
}
@ -36,6 +38,7 @@ impl Actionable for CreateUser {
let Self {
name,
uid,
groupname: _,
gid,
action_state: _,
} = self;
@ -52,12 +55,14 @@ impl Actionable for CreateUser {
#[tracing::instrument(skip_all, fields(
user = self.name,
uid = self.uid,
groupname = self.groupname,
gid = self.gid,
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
let Self {
name,
uid,
groupname,
gid,
action_state,
} = self;
@ -77,15 +82,79 @@ impl Actionable for CreateUser {
| OperatingSystem::Darwin => {
execute_command(Command::new("/usr/bin/dscl").args([
".",
"create",
"-create",
&format!("/Users/{name}"),
"UniqueId",
]))
.await
.map_err(Self::Error::Command)?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
&format!("/Users/{name}"),
"UniqueID",
&format!("{uid}"),
]))
.await
.map_err(Self::Error::Command)?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
&format!("/Users/{name}"),
"PrimaryGroupID",
&format!("{gid}"),
]))
.await
.map_err(Self::Error::Command)?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
&format!("/Users/{name}"),
"NFSHomeDirectory",
"/var/empty",
]))
.await
.map_err(Self::Error::Command)?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
&format!("/Users/{name}"),
"UserShell",
"/sbin/nologin",
]))
.await
.map_err(Self::Error::Command)?;
execute_command(
Command::new("/usr/bin/dscl")
.args([
".",
"-append",
&format!("/Groups/{groupname}"),
"GroupMembership",
])
.arg(&name),
)
.await
.map_err(Self::Error::Command)?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
&format!("/Users/{name}"),
"IsHidden",
"1",
]))
.await
.map_err(Self::Error::Command)?;
execute_command(
Command::new("/usr/sbin/dseditgroup")
.args(["-o", "edit"])
.arg("-a")
.arg(&name)
.arg("-t")
.arg(&name)
.arg(groupname),
)
.await
.map_err(Self::Error::Command)?;
},
_ => {
execute_command(Command::new("useradd").args([
@ -124,6 +193,7 @@ impl Actionable for CreateUser {
let Self {
name,
uid,
groupname: _,
gid,
action_state: _,
} = self;
@ -146,6 +216,7 @@ impl Actionable for CreateUser {
let Self {
name,
uid: _,
groupname: _,
gid: _,
action_state,
} = self;
@ -163,7 +234,13 @@ impl Actionable for CreateUser {
patch: _,
}
| OperatingSystem::Darwin => {
todo!()
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-delete",
&format!("/Users/{name}"),
]))
.await
.map_err(Self::Error::Command)?;
},
_ => {
execute_command(Command::new("userdel").args([&name.to_string()]))

View file

@ -56,9 +56,13 @@ impl Actionable for BootstrapVolume {
)
.await
.map_err(Self::Error::Command)?;
execute_command(Command::new("launchctl").args(["-k", "system/org.nixos.darwin-store"]))
.await
.map_err(Self::Error::Command)?;
execute_command(Command::new("launchctl").args([
"kickstart",
"-k",
"system/org.nixos.darwin-store",
]))
.await
.map_err(Self::Error::Command)?;
tracing::trace!("Bootstrapped volume");
*action_state = ActionState::Completed;

View file

@ -121,14 +121,9 @@ impl Actionable for CreateVolume {
}
tracing::debug!("Deleting volume");
execute_command(Command::new("/usr/sbin/diskutil").args([
"apfs",
"deleteVolume",
&format!("{}", disk.display()),
name,
]))
.await
.map_err(Self::Error::Command)?;
execute_command(Command::new("/usr/sbin/diskutil").args(["apfs", "deleteVolume", name]))
.await
.map_err(Self::Error::Command)?;
tracing::trace!("Deleted volume");
*action_state = ActionState::Completed;

View file

@ -73,15 +73,6 @@ impl Actionable for SetupDefaultProfile {
return Err(Self::Error::NoNssCacert); // TODO(@hoverbear): Fix this error
};
// Install `nix` itself into the store
execute_command(
Command::new(nix_pkg.join("bin/nix-env"))
.arg("-i")
.arg(&nix_pkg),
)
.await
.map_err(SetupDefaultProfileError::Command)?;
// Find an `nss-cacert` package, add it too.
let nss_ca_cert_pkg_glob = "/nix/store/*-nss-cacert-*";
let mut found_nss_ca_cert_pkg = None;
@ -101,11 +92,29 @@ impl Actionable for SetupDefaultProfile {
return Err(Self::Error::NoNssCacert);
};
// Install `nix` itself into the store
execute_command(
Command::new(nix_pkg.join("bin/nix-env"))
.arg("-i")
.arg(&nix_pkg)
.env("HOME", dirs::home_dir().ok_or(Self::Error::NoRootHome)?)
.env(
"NIX_SSL_CERT_FILE",
nss_ca_cert_pkg.join("etc/ssl/certs/ca-bundle.crt"),
), /* This is apparently load bearing... */
)
.await
.map_err(SetupDefaultProfileError::Command)?;
// Install `nss-cacert` into the store
execute_command(
Command::new(nix_pkg.join("bin/nix-env"))
.arg("-i")
.arg(&nss_ca_cert_pkg),
.arg(&nss_ca_cert_pkg)
.env(
"NIX_SSL_CERT_FILE",
nss_ca_cert_pkg.join("etc/ssl/certs/ca-bundle.crt"),
),
)
.await
.map_err(SetupDefaultProfileError::Command)?;
@ -199,4 +208,6 @@ pub enum SetupDefaultProfileError {
#[serde(serialize_with = "crate::serialize_error_to_display")]
std::io::Error,
),
#[error("No root home found to place channel configuration in")]
NoRootHome,
}

View file

@ -32,6 +32,7 @@ impl CreateUsersAndGroup {
CreateUser::plan(
format!("{}{count}", settings.nix_build_user_prefix),
settings.nix_build_user_id_base + count,
settings.nix_build_group_name.clone(),
settings.nix_build_group_id,
)
})
@ -108,35 +109,30 @@ impl Actionable for CreateUsersAndGroup {
// Create group
create_group.execute().await?;
// Create users
// TODO(@hoverbear): Abstract this, it will be common
let mut set = JoinSet::new();
let mut errors = Vec::default();
for (idx, create_user) in create_users.iter().enumerate() {
let mut create_user_clone = create_user.clone();
let _abort_handle = set.spawn(async move {
create_user_clone.execute().await?;
Result::<_, CreateUserError>::Ok((idx, create_user_clone))
});
// Mac is apparently not threadsafe here...
for create_user in create_users.iter_mut() {
// let mut create_user_clone = create_user.clone();
// let _abort_handle = set.spawn(async move {
create_user.execute().await?;
// Result::<_, CreateUserError>::Ok((idx, create_user_clone))
// });
}
while let Some(result) = set.join_next().await {
match result {
Ok(Ok((idx, success))) => create_users[idx] = success,
Ok(Err(e)) => errors.push(e),
Err(e) => return Err(e)?,
};
}
// while let Some(result) = set.join_next().await {
// match result {
// Ok(Ok((idx, success))) => create_users[idx] = success,
// Ok(Err(e)) => errors.push(e),
// Err(e) => return Err(e)?,
// };
// }
if !errors.is_empty() {
if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into());
} else {
return Err(CreateUsersAndGroupError::CreateUsers(errors));
}
}
// if !errors.is_empty() {
// if errors.len() == 1 {
// return Err(errors.into_iter().next().unwrap().into());
// } else {
// return Err(CreateUsersAndGroupError::CreateUsers(errors));
// }
// }
tracing::trace!("Created users and groups");
*action_state = ActionState::Completed;

View file

@ -1,5 +1,8 @@
use serde::Serialize;
use std::path::{Path, PathBuf};
use std::{
path::{Path, PathBuf},
time::Duration,
};
use crate::actions::base::{
darwin::{
@ -7,7 +10,7 @@ use crate::actions::base::{
CreateVolume, CreateVolumeError, EnableOwnership, EnableOwnershipError, EncryptVolume,
EncryptVolumeError, UnmountVolume, UnmountVolumeError,
},
CreateOrAppendFile, CreateOrAppendFileError,
CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError,
};
use crate::actions::{base::darwin, Action, ActionDescription, ActionState, Actionable};
@ -25,6 +28,7 @@ pub struct CreateApfsVolume {
create_volume: CreateVolume,
create_or_append_fstab: CreateOrAppendFile,
encrypt_volume: Option<EncryptVolume>,
setup_volume_daemon: CreateFile,
bootstrap_volume: BootstrapVolume,
enable_ownership: EnableOwnership,
action_state: ActionState,
@ -39,9 +43,14 @@ impl CreateApfsVolume {
encrypt: Option<String>,
) -> Result<Self, CreateApfsVolumeError> {
let disk = disk.as_ref();
let create_or_append_synthetic_conf =
CreateOrAppendFile::plan("/etc/synthetic.conf", None, None, 0o0655, "nix".into())
.await?;
let create_or_append_synthetic_conf = CreateOrAppendFile::plan(
"/etc/synthetic.conf",
None,
None,
0o0655,
"nix\n".into(), /* The newline is required otherwise it segfaults */
)
.await?;
let create_synthetic_objects = CreateSyntheticObjects::plan().await?;
@ -64,6 +73,49 @@ impl CreateApfsVolume {
None
};
let mount_command = if encrypt.is_some() {
vec![
"/bin/sh",
"-c",
"/usr/bin/security find-generic-password",
"-s",
"{name}",
"-w",
"|",
"/usr/sbin/diskutil",
"apfs",
"unlockVolume",
&name,
"-mountpoint",
"/nix",
"-stdinpassphrase",
]
} else {
vec!["/usr/sbin/diskutil", "mount", "-mountPoint", "/nix", &name]
};
// TODO(@hoverbear): Use plist lib we have in tree...
let mount_plist = format!(
"\
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\
<plist version=\"1.0\">\n\
<dict>\n\
<key>RunAtLoad</key>\n\
<true/>\n\
<key>Label</key>\n\
<string>org.nixos.darwin-store</string>\n\
<key>ProgramArguments</key>\n\
<array>\n\
{}\
</array>\n\
</dict>\n\
</plist>\n\
\
", mount_command.iter().map(|v| format!("<string>{v}</string>\n")).collect::<Vec<_>>().join("\n")
);
let setup_volume_daemon =
CreateFile::plan(NIX_VOLUME_MOUNTD_DEST, None, None, None, mount_plist, false).await?;
let bootstrap_volume = BootstrapVolume::plan(NIX_VOLUME_MOUNTD_DEST).await?;
let enable_ownership = EnableOwnership::plan("/nix").await?;
@ -78,6 +130,7 @@ impl CreateApfsVolume {
create_volume,
create_or_append_fstab,
encrypt_volume,
setup_volume_daemon,
bootstrap_volume,
enable_ownership,
action_state: ActionState::Uncompleted,
@ -121,6 +174,7 @@ impl Actionable for CreateApfsVolume {
create_volume,
create_or_append_fstab,
encrypt_volume,
setup_volume_daemon,
bootstrap_volume,
enable_ownership,
action_state,
@ -139,7 +193,13 @@ impl Actionable for CreateApfsVolume {
if let Some(encrypt_volume) = encrypt_volume {
encrypt_volume.execute().await?;
}
setup_volume_daemon.execute().await?;
bootstrap_volume.execute().await?;
// TODO: Check wait
tokio::time::sleep(Duration::from_millis(5000)).await;
enable_ownership.execute().await?;
tracing::trace!("Created APFS volume");
@ -179,6 +239,7 @@ impl Actionable for CreateApfsVolume {
create_volume,
create_or_append_fstab,
encrypt_volume,
setup_volume_daemon,
bootstrap_volume,
enable_ownership,
action_state,
@ -191,6 +252,7 @@ impl Actionable for CreateApfsVolume {
enable_ownership.revert().await?;
bootstrap_volume.revert().await?;
setup_volume_daemon.revert().await?;
if let Some(encrypt_volume) = encrypt_volume {
encrypt_volume.revert().await?;
}
@ -217,6 +279,8 @@ impl From<CreateApfsVolume> for Action {
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateApfsVolumeError {
#[error(transparent)]
CreateFile(#[from] CreateFileError),
#[error(transparent)]
DarwinBootstrapVolume(#[from] BootstrapVolumeError),
#[error(transparent)]

View file

@ -26,7 +26,7 @@ impl PlaceChannelConfiguration {
let create_file = CreateFile::plan(
dirs::home_dir()
.ok_or(PlaceChannelConfigurationError::NoRootHome)?
.join("/.nix-channels"),
.join(".nix-channels"),
None,
None,
0o0664,

View file

@ -15,7 +15,6 @@ use super::{CreateNixTree, CreateNixTreeError, CreateUsersAndGroup, CreateUsersA
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ProvisionNix {
create_nix_dir: CreateDirectory,
fetch_nix: FetchNix,
create_users_and_group: CreateUsersAndGroup,
create_nix_tree: CreateNixTree,
@ -26,8 +25,6 @@ pub struct ProvisionNix {
impl ProvisionNix {
#[tracing::instrument(skip_all)]
pub async fn plan(settings: InstallSettings) -> Result<Self, ProvisionNixError> {
let create_nix_dir = CreateDirectory::plan("/nix", None, None, 0o0755, true).await?;
let fetch_nix = FetchNix::plan(
settings.nix_package_url.clone(),
PathBuf::from("/nix/temp-install-dir"),
@ -38,7 +35,6 @@ impl ProvisionNix {
let move_unpacked_nix =
MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")).await?;
Ok(Self {
create_nix_dir,
fetch_nix,
create_users_and_group,
create_nix_tree,
@ -53,7 +49,6 @@ impl Actionable for ProvisionNix {
type Error = ProvisionNixError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
create_nix_dir,
fetch_nix,
create_users_and_group,
create_nix_tree,
@ -64,7 +59,6 @@ impl Actionable for ProvisionNix {
vec![]
} else {
let mut buf = Vec::default();
buf.append(&mut create_nix_dir.describe_execute());
buf.append(&mut fetch_nix.describe_execute());
buf.append(&mut create_users_and_group.describe_execute());
buf.append(&mut create_nix_tree.describe_execute());
@ -77,7 +71,6 @@ impl Actionable for ProvisionNix {
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Self::Error> {
let Self {
create_nix_dir,
fetch_nix,
create_nix_tree,
create_users_and_group,
@ -91,8 +84,6 @@ impl Actionable for ProvisionNix {
*action_state = ActionState::Progress;
tracing::debug!("Provisioning Nix");
create_nix_dir.execute().await?;
// We fetch nix while doing the rest, then move it over.
let mut fetch_nix_clone = fetch_nix.clone();
let fetch_nix_handle = tokio::task::spawn(async {
@ -116,7 +107,6 @@ impl Actionable for ProvisionNix {
fn describe_revert(&self) -> Vec<ActionDescription> {
let Self {
create_nix_dir,
fetch_nix,
create_users_and_group,
create_nix_tree,
@ -131,7 +121,6 @@ impl Actionable for ProvisionNix {
buf.append(&mut create_nix_tree.describe_revert());
buf.append(&mut create_users_and_group.describe_revert());
buf.append(&mut fetch_nix.describe_revert());
buf.append(&mut create_nix_dir.describe_revert());
buf
}
}
@ -139,7 +128,6 @@ impl Actionable for ProvisionNix {
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Self::Error> {
let Self {
create_nix_dir,
fetch_nix,
create_nix_tree,
create_users_and_group,
@ -172,8 +160,6 @@ impl Actionable for ProvisionNix {
*fetch_nix = fetch_nix_handle.await.map_err(ProvisionNixError::from)??;
move_unpacked_nix.revert().await?;
create_nix_dir.revert().await?;
tracing::trace!("Unprovisioned Nix");
*action_state = ActionState::Uncompleted;
Ok(())

View file

@ -28,7 +28,10 @@ async fn execute_command(command: &mut Command) -> Result<Output, std::io::Error
true => Ok(output),
false => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Command `{command_str}` failed status"),
format!(
"Command `{command_str}` failed status, stderr:\n{}\n",
String::from_utf8(output.stderr).unwrap_or_else(|_e| String::from("<Non-UTF-8>"))
),
)),
}
}

View file

@ -1,6 +1,6 @@
use crate::{
actions::{
base::StartSystemdUnit,
base::{CreateDirectory, StartSystemdUnit},
meta::{ConfigureNix, ProvisionNix},
Action, ActionError,
},
@ -21,6 +21,10 @@ impl Plannable for LinuxMultiUser {
planner: Self.into(),
settings: settings.clone(),
actions: vec![
CreateDirectory::plan("/nix", None, None, 0o0755, true)
.await
.map(Action::from)
.map_err(ActionError::from)?,
ProvisionNix::plan(settings.clone())
.await
.map(Action::from)

View file

@ -20,8 +20,10 @@ impl Planner {
match (Architecture::host(), OperatingSystem::host()) {
(Architecture::X86_64, OperatingSystem::Linux) => Ok(Self::LinuxMultiUser),
(Architecture::Aarch64(_), OperatingSystem::Linux) => Ok(Self::LinuxMultiUser),
(Architecture::X86_64, OperatingSystem::MacOSX { .. }) => Ok(Self::DarwinMultiUser),
(Architecture::Aarch64(_), OperatingSystem::MacOSX { .. }) => Ok(Self::DarwinMultiUser),
(Architecture::X86_64, OperatingSystem::MacOSX { .. })
| (Architecture::X86_64, OperatingSystem::Darwin) => Ok(Self::DarwinMultiUser),
(Architecture::Aarch64(_), OperatingSystem::MacOSX { .. })
| (Architecture::Aarch64(_), OperatingSystem::Darwin) => Ok(Self::DarwinMultiUser),
_ => Err(PlannerError::UnsupportedArchitecture(target_lexicon::HOST)),
}
}

View file

@ -1,6 +1,6 @@
use crate::{
actions::{
base::StartSystemdUnit,
base::{CreateDirectory, StartSystemdUnit},
meta::{CreateSystemdSysext, ProvisionNix},
Action, ActionError,
},
@ -27,6 +27,10 @@ impl Plannable for SteamDeck {
.await
.map(Action::from)
.map_err(ActionError::from)?,
CreateDirectory::plan("/nix", None, None, 0o0755, true)
.await
.map(Action::from)
.map_err(ActionError::from)?,
ProvisionNix::plan(settings.clone())
.await
.map(Action::from)

View file

@ -22,22 +22,32 @@ pub struct InstallSettings {
impl InstallSettings {
pub fn default() -> Result<Self, InstallSettingsError> {
let url;
let nix_build_user_prefix;
let nix_build_user_id_base;
use target_lexicon::{Architecture, OperatingSystem};
match (Architecture::host(), OperatingSystem::host()) {
(Architecture::X86_64, OperatingSystem::Linux) => {
url = "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-x86_64-linux.tar.xz";
nix_build_user_prefix = "nixbld";
nix_build_user_id_base = 3000;
},
(Architecture::Aarch64(_), OperatingSystem::Linux) => {
url = "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-aarch64-linux.tar.xz";
nix_build_user_prefix = "nixbld";
nix_build_user_id_base = 3000;
},
(Architecture::X86_64, OperatingSystem::MacOSX { .. })
| (Architecture::X86_64, OperatingSystem::Darwin) => {
url = "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-x86_64-darwin.tar.xz";
nix_build_user_prefix = "_nixbld";
nix_build_user_id_base = 300;
},
(Architecture::Aarch64(_), OperatingSystem::MacOSX { .. })
| (Architecture::Aarch64(_), OperatingSystem::Darwin) => {
url = "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-aarch64-darwin.tar.xz";
nix_build_user_prefix = "_nixbld";
nix_build_user_id_base = 300;
},
_ => {
return Err(InstallSettingsError::UnsupportedArchitecture(
@ -53,8 +63,8 @@ impl InstallSettings {
modify_profile: Default::default(),
nix_build_group_name: String::from("nixbld"),
nix_build_group_id: 3000,
nix_build_user_prefix: String::from("nixbld"),
nix_build_user_id_base: 3001,
nix_build_user_prefix: nix_build_user_prefix.to_string(),
nix_build_user_id_base,
nix_package_url: url
.parse()
.expect("Could not parse default Nix archive url, please report this issue"),