Restore user creation on Mac (#524)
* Restore user creation on Mac * Repair MacOS build * Pass clone not borrow * Fixup double group create * Add some links
This commit is contained in:
parent
db316614f2
commit
a962b3390b
330
src/action/base/add_user_to_group.rs
Normal file
330
src/action/base/add_user_to_group.rs
Normal file
|
@ -0,0 +1,330 @@
|
|||
use std::process::Stdio;
|
||||
|
||||
use nix::unistd::User;
|
||||
use target_lexicon::OperatingSystem;
|
||||
use tokio::process::Command;
|
||||
use tracing::{span, Span};
|
||||
|
||||
use crate::action::{ActionError, ActionErrorKind};
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||
|
||||
/**
|
||||
Create an operating system level user in the given group
|
||||
*/
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct AddUserToGroup {
|
||||
name: String,
|
||||
uid: u32,
|
||||
groupname: String,
|
||||
gid: u32,
|
||||
}
|
||||
|
||||
impl AddUserToGroup {
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
pub async fn plan(
|
||||
name: String,
|
||||
uid: u32,
|
||||
groupname: String,
|
||||
gid: u32,
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let this = Self {
|
||||
name: name.clone(),
|
||||
uid,
|
||||
groupname,
|
||||
gid,
|
||||
};
|
||||
|
||||
match OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX { .. } | OperatingSystem::Darwin => (),
|
||||
_ => {
|
||||
if !(which::which("addgroup").is_ok() || which::which("gpasswd").is_ok()) {
|
||||
return Err(Self::error(ActionErrorKind::MissingAddUserToGroupCommand));
|
||||
}
|
||||
if !(which::which("delgroup").is_ok() || which::which("gpasswd").is_ok()) {
|
||||
return Err(Self::error(
|
||||
ActionErrorKind::MissingRemoveUserFromGroupCommand,
|
||||
));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Ensure user does not exists
|
||||
if let Some(user) = User::from_name(name.as_str())
|
||||
.map_err(|e| ActionErrorKind::GettingUserId(name.clone(), e))
|
||||
.map_err(Self::error)?
|
||||
{
|
||||
if user.uid.as_raw() != uid {
|
||||
return Err(Self::error(ActionErrorKind::UserUidMismatch(
|
||||
name.clone(),
|
||||
user.uid.as_raw(),
|
||||
uid,
|
||||
)));
|
||||
}
|
||||
|
||||
if user.gid.as_raw() != gid {
|
||||
return Err(Self::error(ActionErrorKind::UserGidMismatch(
|
||||
name.clone(),
|
||||
user.gid.as_raw(),
|
||||
gid,
|
||||
)));
|
||||
}
|
||||
|
||||
// See if group membership needs to be done
|
||||
match OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX {
|
||||
major: _,
|
||||
minor: _,
|
||||
patch: _,
|
||||
}
|
||||
| OperatingSystem::Darwin => {
|
||||
let mut command = Command::new("/usr/sbin/dseditgroup");
|
||||
command.process_group(0);
|
||||
command.args(["-o", "checkmember", "-m"]);
|
||||
command.arg(&this.name);
|
||||
command.arg(&this.groupname);
|
||||
command.stdout(Stdio::piped());
|
||||
command.stderr(Stdio::piped());
|
||||
tracing::trace!("Executing `{:?}`", command.as_std());
|
||||
let output = command
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| ActionErrorKind::command(&command, e))
|
||||
.map_err(Self::error)?;
|
||||
match output.status.code() {
|
||||
Some(0) => {
|
||||
// yes {user} is a member of {groupname}
|
||||
// Since the user exists, and is already a member of the group, we have truly nothing to do here
|
||||
tracing::debug!(
|
||||
"Adding user `{}` to group `{}` already complete",
|
||||
this.name,
|
||||
this.groupname
|
||||
);
|
||||
return Ok(StatefulAction::completed(this));
|
||||
},
|
||||
Some(64) => {
|
||||
// 64 is the exit code for "Group not found"
|
||||
tracing::trace!(
|
||||
"Will add user `{}` to newly created group `{}`",
|
||||
this.name,
|
||||
this.groupname
|
||||
);
|
||||
// The group will be created by the installer
|
||||
()
|
||||
},
|
||||
_ => {
|
||||
// Some other issue
|
||||
return Err(Self::error(ActionErrorKind::command_output(
|
||||
&command, output,
|
||||
)));
|
||||
},
|
||||
};
|
||||
},
|
||||
_ => {
|
||||
let output = execute_command(
|
||||
Command::new("groups")
|
||||
.process_group(0)
|
||||
.arg(&this.name)
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
let output_str = String::from_utf8(output.stdout).map_err(Self::error)?;
|
||||
let user_in_group = output_str.split(" ").any(|v| v == &this.groupname);
|
||||
|
||||
if user_in_group {
|
||||
tracing::debug!(
|
||||
"Adding user `{}` to group `{}` already complete",
|
||||
this.name,
|
||||
this.groupname
|
||||
);
|
||||
return Ok(StatefulAction::completed(this));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(StatefulAction::uncompleted(this))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
#[typetag::serde(name = "add_user_to_group")]
|
||||
impl Action for AddUserToGroup {
|
||||
fn action_tag() -> crate::action::ActionTag {
|
||||
crate::action::ActionTag("add_user_to_group")
|
||||
}
|
||||
fn tracing_synopsis(&self) -> String {
|
||||
format!(
|
||||
"Add user `{}` (UID {}) to group `{}` (GID {})",
|
||||
self.name, self.uid, self.groupname, self.gid
|
||||
)
|
||||
}
|
||||
|
||||
fn tracing_span(&self) -> Span {
|
||||
span!(
|
||||
tracing::Level::DEBUG,
|
||||
"add_user_to_group",
|
||||
user = self.name,
|
||||
uid = self.uid,
|
||||
groupname = self.groupname,
|
||||
gid = self.gid,
|
||||
)
|
||||
}
|
||||
|
||||
fn execute_description(&self) -> Vec<ActionDescription> {
|
||||
vec![ActionDescription::new(
|
||||
self.tracing_synopsis(),
|
||||
vec![format!(
|
||||
"The Nix daemon requires the build users to be in a defined group"
|
||||
)],
|
||||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
name,
|
||||
uid: _,
|
||||
groupname,
|
||||
gid: _,
|
||||
} = self;
|
||||
|
||||
use target_lexicon::OperatingSystem;
|
||||
match OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX {
|
||||
major: _,
|
||||
minor: _,
|
||||
patch: _,
|
||||
}
|
||||
| OperatingSystem::Darwin => {
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
.args([
|
||||
".",
|
||||
"-append",
|
||||
&format!("/Groups/{groupname}"),
|
||||
"GroupMembership",
|
||||
])
|
||||
.arg(&name)
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
execute_command(
|
||||
Command::new("/usr/sbin/dseditgroup")
|
||||
.process_group(0)
|
||||
.args(["-o", "edit"])
|
||||
.arg("-a")
|
||||
.arg(&name)
|
||||
.arg("-t")
|
||||
.arg(&name)
|
||||
.arg(groupname)
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
},
|
||||
_ => {
|
||||
if which::which("gpasswd").is_ok() {
|
||||
execute_command(
|
||||
Command::new("gpasswd")
|
||||
.process_group(0)
|
||||
.args(["-a"])
|
||||
.args([name, groupname])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
} else if which::which("addgroup").is_ok() {
|
||||
execute_command(
|
||||
Command::new("addgroup")
|
||||
.process_group(0)
|
||||
.args([name, groupname])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
} else {
|
||||
return Err(Self::error(Self::error(
|
||||
ActionErrorKind::MissingAddUserToGroupCommand,
|
||||
)));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn revert_description(&self) -> Vec<ActionDescription> {
|
||||
vec![ActionDescription::new(
|
||||
format!(
|
||||
"Remove user `{}` (UID {}) from group {} (GID {})",
|
||||
self.name, self.uid, self.groupname, self.gid
|
||||
),
|
||||
vec![format!(
|
||||
"The Nix daemon requires system users it can act as in order to build"
|
||||
)],
|
||||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
name,
|
||||
uid: _,
|
||||
groupname,
|
||||
gid: _,
|
||||
} = self;
|
||||
|
||||
use target_lexicon::OperatingSystem;
|
||||
match target_lexicon::OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX {
|
||||
major: _,
|
||||
minor: _,
|
||||
patch: _,
|
||||
}
|
||||
| OperatingSystem::Darwin => {
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
.args([".", "-delete", &format!("/Groups/{groupname}"), "users"])
|
||||
.arg(&name)
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
},
|
||||
_ => {
|
||||
if which::which("gpasswd").is_ok() {
|
||||
execute_command(
|
||||
Command::new("gpasswd")
|
||||
.process_group(0)
|
||||
.args(["-d"])
|
||||
.args([&name.to_string(), &groupname.to_string()])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
} else if which::which("delgroup").is_ok() {
|
||||
execute_command(
|
||||
Command::new("delgroup")
|
||||
.process_group(0)
|
||||
.args([name, groupname])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
} else {
|
||||
return Err(Self::error(
|
||||
ActionErrorKind::MissingRemoveUserFromGroupCommand,
|
||||
));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
344
src/action/base/create_user.rs
Normal file
344
src/action/base/create_user.rs
Normal file
|
@ -0,0 +1,344 @@
|
|||
use nix::unistd::User;
|
||||
use target_lexicon::OperatingSystem;
|
||||
use tokio::process::Command;
|
||||
use tracing::{span, Span};
|
||||
|
||||
use crate::action::{ActionError, ActionErrorKind, ActionTag};
|
||||
use crate::execute_command;
|
||||
|
||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||
|
||||
/**
|
||||
Create an operating system level user in the given group
|
||||
*/
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateUser {
|
||||
name: String,
|
||||
uid: u32,
|
||||
groupname: String,
|
||||
gid: u32,
|
||||
comment: String,
|
||||
}
|
||||
|
||||
impl CreateUser {
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
pub async fn plan(
|
||||
name: String,
|
||||
uid: u32,
|
||||
groupname: String,
|
||||
gid: u32,
|
||||
comment: String,
|
||||
) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let this = Self {
|
||||
name: name.clone(),
|
||||
uid,
|
||||
groupname,
|
||||
gid,
|
||||
comment,
|
||||
};
|
||||
|
||||
match OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX { .. } | OperatingSystem::Darwin => (),
|
||||
_ => {
|
||||
if !(which::which("useradd").is_ok() || which::which("adduser").is_ok()) {
|
||||
return Err(Self::error(ActionErrorKind::MissingUserCreationCommand));
|
||||
}
|
||||
if !(which::which("userdel").is_ok() || which::which("deluser").is_ok()) {
|
||||
return Err(Self::error(ActionErrorKind::MissingUserDeletionCommand));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Ensure user does not exists
|
||||
if let Some(user) = User::from_name(name.as_str())
|
||||
.map_err(|e| ActionErrorKind::GettingUserId(name.clone(), e))
|
||||
.map_err(Self::error)?
|
||||
{
|
||||
if user.uid.as_raw() != uid {
|
||||
return Err(Self::error(ActionErrorKind::UserUidMismatch(
|
||||
name.clone(),
|
||||
user.uid.as_raw(),
|
||||
uid,
|
||||
)));
|
||||
}
|
||||
|
||||
if user.gid.as_raw() != gid {
|
||||
return Err(Self::error(ActionErrorKind::UserGidMismatch(
|
||||
name.clone(),
|
||||
user.gid.as_raw(),
|
||||
gid,
|
||||
)));
|
||||
}
|
||||
|
||||
tracing::debug!("Creating user `{}` already complete", this.name);
|
||||
return Ok(StatefulAction::completed(this));
|
||||
}
|
||||
|
||||
Ok(StatefulAction::uncompleted(this))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
#[typetag::serde(name = "create_user")]
|
||||
impl Action for CreateUser {
|
||||
fn action_tag() -> ActionTag {
|
||||
ActionTag("create_user")
|
||||
}
|
||||
fn tracing_synopsis(&self) -> String {
|
||||
format!(
|
||||
"Create user `{}` (UID {}) in group `{}` (GID {})",
|
||||
self.name, self.uid, self.groupname, self.gid
|
||||
)
|
||||
}
|
||||
|
||||
fn tracing_span(&self) -> Span {
|
||||
span!(
|
||||
tracing::Level::DEBUG,
|
||||
"create_user",
|
||||
user = self.name,
|
||||
uid = self.uid,
|
||||
groupname = self.groupname,
|
||||
gid = self.gid,
|
||||
)
|
||||
}
|
||||
|
||||
fn execute_description(&self) -> Vec<ActionDescription> {
|
||||
vec![ActionDescription::new(
|
||||
self.tracing_synopsis(),
|
||||
vec![format!(
|
||||
"The Nix daemon requires system users it can act as in order to build"
|
||||
)],
|
||||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
name,
|
||||
uid,
|
||||
groupname,
|
||||
gid,
|
||||
comment,
|
||||
} = self;
|
||||
|
||||
use OperatingSystem;
|
||||
match OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX {
|
||||
major: _,
|
||||
minor: _,
|
||||
patch: _,
|
||||
}
|
||||
| OperatingSystem::Darwin => {
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
.args([".", "-create", &format!("/Users/{name}")])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
.args([
|
||||
".",
|
||||
"-create",
|
||||
&format!("/Users/{name}"),
|
||||
"UniqueID",
|
||||
&format!("{uid}"),
|
||||
])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
.args([
|
||||
".",
|
||||
"-create",
|
||||
&format!("/Users/{name}"),
|
||||
"PrimaryGroupID",
|
||||
&format!("{gid}"),
|
||||
])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
.args([
|
||||
".",
|
||||
"-create",
|
||||
&format!("/Users/{name}"),
|
||||
"NFSHomeDirectory",
|
||||
"/var/empty",
|
||||
])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
.args([
|
||||
".",
|
||||
"-create",
|
||||
&format!("/Users/{name}"),
|
||||
"UserShell",
|
||||
"/sbin/nologin",
|
||||
])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
execute_command(
|
||||
Command::new("/usr/bin/dscl")
|
||||
.process_group(0)
|
||||
.args([".", "-create", &format!("/Users/{name}"), "IsHidden", "1"])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
},
|
||||
_ => {
|
||||
if which::which("useradd").is_ok() {
|
||||
execute_command(
|
||||
Command::new("useradd")
|
||||
.process_group(0)
|
||||
.args([
|
||||
"--home-dir",
|
||||
"/var/empty",
|
||||
"--comment",
|
||||
&comment,
|
||||
"--gid",
|
||||
&gid.to_string(),
|
||||
"--groups",
|
||||
&gid.to_string(),
|
||||
"--no-user-group",
|
||||
"--system",
|
||||
"--shell",
|
||||
"/sbin/nologin",
|
||||
"--uid",
|
||||
&uid.to_string(),
|
||||
"--password",
|
||||
"!",
|
||||
name,
|
||||
])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
} else if which::which("adduser").is_ok() {
|
||||
execute_command(
|
||||
Command::new("adduser")
|
||||
.process_group(0)
|
||||
.args([
|
||||
"--home",
|
||||
"/var/empty",
|
||||
"--gecos",
|
||||
&comment,
|
||||
"--ingroup",
|
||||
groupname,
|
||||
"--system",
|
||||
"--shell",
|
||||
"/sbin/nologin",
|
||||
"--uid",
|
||||
&uid.to_string(),
|
||||
"--disabled-password",
|
||||
name,
|
||||
])
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
} else {
|
||||
return Err(Self::error(ActionErrorKind::MissingUserCreationCommand));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn revert_description(&self) -> Vec<ActionDescription> {
|
||||
vec![ActionDescription::new(
|
||||
format!(
|
||||
"Delete user `{}` (UID {}) in group {} (GID {})",
|
||||
self.name, self.uid, self.groupname, self.gid
|
||||
),
|
||||
vec![format!(
|
||||
"The Nix daemon requires system users it can act as in order to build"
|
||||
)],
|
||||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
use OperatingSystem;
|
||||
match OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX {
|
||||
major: _,
|
||||
minor: _,
|
||||
patch: _,
|
||||
}
|
||||
| OperatingSystem::Darwin => {
|
||||
// MacOS is a "Special" case
|
||||
// It's only possible to delete users under certain conditions.
|
||||
// Documentation on https://it.megocollector.com/macos/cant-delete-a-macos-user-with-dscl-resolution/ and http://www.aixperts.co.uk/?p=214 suggested it was a secure token
|
||||
// That is correct, however it's a bit more nuanced. It appears to be that a user must be graphically logged in for some other user on the system to be deleted.
|
||||
let mut command = Command::new("/usr/bin/dscl");
|
||||
command.args([".", "-delete", &format!("/Users/{}", self.name)]);
|
||||
command.process_group(0);
|
||||
command.stdin(std::process::Stdio::null());
|
||||
|
||||
let output = command
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| ActionErrorKind::command(&command, e))
|
||||
.map_err(Self::error)?;
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
match output.status.code() {
|
||||
Some(0) => (),
|
||||
Some(40) if stderr.contains("-14120") => {
|
||||
// The user is on an ephemeral Mac, like detsys uses
|
||||
// These Macs cannot always delete users, as sometimes there is no graphical login
|
||||
tracing::warn!("Encountered an exit code 40 with -14120 error while removing user, this is likely because the initial executing user did not have a secure token, or that there was no graphical login session. To delete the user, log in graphically, then run `/usr/bin/dscl . -delete /Users/{}", self.name);
|
||||
},
|
||||
_ => {
|
||||
// Something went wrong
|
||||
return Err(Self::error(ActionErrorKind::command_output(
|
||||
&command, output,
|
||||
)));
|
||||
},
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if which::which("userdel").is_ok() {
|
||||
execute_command(
|
||||
Command::new("userdel")
|
||||
.process_group(0)
|
||||
.arg(&self.name)
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
} else if which::which("deluser").is_ok() {
|
||||
execute_command(
|
||||
Command::new("deluser")
|
||||
.process_group(0)
|
||||
.arg(&self.name)
|
||||
.stdin(std::process::Stdio::null()),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
} else {
|
||||
return Err(Self::error(ActionErrorKind::MissingUserDeletionCommand));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,21 +1,25 @@
|
|||
//! Base [`Action`](crate::action::Action)s that themselves have no other actions as dependencies
|
||||
|
||||
pub(crate) mod add_user_to_group;
|
||||
pub(crate) mod create_directory;
|
||||
pub(crate) mod create_file;
|
||||
pub(crate) mod create_group;
|
||||
pub(crate) mod create_or_insert_into_file;
|
||||
pub(crate) mod create_or_merge_nix_config;
|
||||
pub(crate) mod create_user;
|
||||
pub(crate) mod delete_user;
|
||||
pub(crate) mod fetch_and_unpack_nix;
|
||||
pub(crate) mod move_unpacked_nix;
|
||||
pub(crate) mod remove_directory;
|
||||
pub(crate) mod setup_default_profile;
|
||||
|
||||
pub use add_user_to_group::AddUserToGroup;
|
||||
pub use create_directory::CreateDirectory;
|
||||
pub use create_file::CreateFile;
|
||||
pub use create_group::CreateGroup;
|
||||
pub use create_or_insert_into_file::CreateOrInsertIntoFile;
|
||||
pub use create_or_merge_nix_config::CreateOrMergeNixConfig;
|
||||
pub use create_user::CreateUser;
|
||||
pub use delete_user::DeleteUser;
|
||||
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};
|
||||
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
||||
|
|
278
src/action/common/create_users_and_groups.rs
Normal file
278
src/action/common/create_users_and_groups.rs
Normal file
|
@ -0,0 +1,278 @@
|
|||
use crate::{
|
||||
action::{
|
||||
base::{AddUserToGroup, CreateGroup, CreateUser},
|
||||
Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction,
|
||||
},
|
||||
settings::CommonSettings,
|
||||
};
|
||||
use tracing::{span, Span};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateUsersAndGroups {
|
||||
nix_build_user_count: u32,
|
||||
nix_build_group_name: String,
|
||||
nix_build_group_id: u32,
|
||||
nix_build_user_prefix: String,
|
||||
nix_build_user_id_base: u32,
|
||||
create_group: StatefulAction<CreateGroup>,
|
||||
create_users: Vec<StatefulAction<CreateUser>>,
|
||||
add_users_to_groups: Vec<StatefulAction<AddUserToGroup>>,
|
||||
}
|
||||
|
||||
impl CreateUsersAndGroups {
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
pub async fn plan(settings: CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||
let create_group = CreateGroup::plan(
|
||||
settings.nix_build_group_name.clone(),
|
||||
settings.nix_build_group_id,
|
||||
)?;
|
||||
let mut create_users = Vec::with_capacity(settings.nix_build_user_count as usize);
|
||||
let mut add_users_to_groups = Vec::with_capacity(settings.nix_build_user_count as usize);
|
||||
for index in 1..=settings.nix_build_user_count {
|
||||
create_users.push(
|
||||
CreateUser::plan(
|
||||
format!("{}{index}", settings.nix_build_user_prefix),
|
||||
settings.nix_build_user_id_base + index,
|
||||
settings.nix_build_group_name.clone(),
|
||||
settings.nix_build_group_id,
|
||||
format!("Nix build user {index}"),
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?,
|
||||
);
|
||||
add_users_to_groups.push(
|
||||
AddUserToGroup::plan(
|
||||
format!("{}{index}", settings.nix_build_user_prefix),
|
||||
settings.nix_build_user_id_base + index,
|
||||
settings.nix_build_group_name.clone(),
|
||||
settings.nix_build_group_id,
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?,
|
||||
);
|
||||
}
|
||||
Ok(Self {
|
||||
nix_build_user_count: settings.nix_build_user_count,
|
||||
nix_build_group_name: settings.nix_build_group_name,
|
||||
nix_build_group_id: settings.nix_build_group_id,
|
||||
nix_build_user_prefix: settings.nix_build_user_prefix,
|
||||
nix_build_user_id_base: settings.nix_build_user_id_base,
|
||||
create_group,
|
||||
create_users,
|
||||
add_users_to_groups,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
#[typetag::serde(name = "create_users_and_group")]
|
||||
impl Action for CreateUsersAndGroups {
|
||||
fn action_tag() -> ActionTag {
|
||||
ActionTag("create_users_and_group")
|
||||
}
|
||||
fn tracing_synopsis(&self) -> String {
|
||||
format!(
|
||||
"Create build users (UID {}-{}) and group (GID {})",
|
||||
self.nix_build_user_id_base,
|
||||
self.nix_build_user_id_base + self.nix_build_user_count,
|
||||
self.nix_build_group_id
|
||||
)
|
||||
}
|
||||
|
||||
fn tracing_span(&self) -> Span {
|
||||
span!(
|
||||
tracing::Level::DEBUG,
|
||||
"create_users_and_group",
|
||||
nix_build_user_count = self.nix_build_user_count,
|
||||
nix_build_group_name = self.nix_build_group_name,
|
||||
nix_build_group_id = self.nix_build_group_id,
|
||||
nix_build_user_prefix = self.nix_build_user_prefix,
|
||||
nix_build_user_id_base = self.nix_build_user_id_base,
|
||||
)
|
||||
}
|
||||
|
||||
fn execute_description(&self) -> Vec<ActionDescription> {
|
||||
let Self {
|
||||
nix_build_user_count: _,
|
||||
nix_build_group_name: _,
|
||||
nix_build_group_id: _,
|
||||
nix_build_user_prefix: _,
|
||||
nix_build_user_id_base: _,
|
||||
create_group,
|
||||
create_users,
|
||||
add_users_to_groups,
|
||||
} = &self;
|
||||
|
||||
let mut create_users_descriptions = Vec::new();
|
||||
for create_user in create_users {
|
||||
if let Some(val) = create_user.describe_execute().iter().next() {
|
||||
create_users_descriptions.push(val.description.clone())
|
||||
}
|
||||
}
|
||||
|
||||
let mut add_user_to_group_descriptions = Vec::new();
|
||||
for add_user_to_group in add_users_to_groups {
|
||||
if let Some(val) = add_user_to_group.describe_execute().iter().next() {
|
||||
add_user_to_group_descriptions.push(val.description.clone())
|
||||
}
|
||||
}
|
||||
|
||||
let mut explanation = vec![
|
||||
format!("The Nix daemon requires system users (and a group they share) which it can act as in order to build"),
|
||||
];
|
||||
if let Some(val) = create_group.describe_execute().iter().next() {
|
||||
explanation.push(val.description.clone())
|
||||
}
|
||||
explanation.append(&mut create_users_descriptions);
|
||||
explanation.append(&mut add_user_to_group_descriptions);
|
||||
|
||||
vec![ActionDescription::new(self.tracing_synopsis(), explanation)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||
let Self {
|
||||
create_users,
|
||||
create_group,
|
||||
add_users_to_groups,
|
||||
nix_build_user_count: _,
|
||||
nix_build_group_name: _,
|
||||
nix_build_group_id: _,
|
||||
nix_build_user_prefix: _,
|
||||
nix_build_user_id_base: _,
|
||||
} = self;
|
||||
|
||||
// Create group
|
||||
create_group.try_execute().await?;
|
||||
|
||||
// Mac is apparently not threadsafe here...
|
||||
use target_lexicon::OperatingSystem;
|
||||
match OperatingSystem::host() {
|
||||
OperatingSystem::MacOSX {
|
||||
major: _,
|
||||
minor: _,
|
||||
patch: _,
|
||||
}
|
||||
| OperatingSystem::Darwin => {
|
||||
for create_user in create_users.iter_mut() {
|
||||
create_user.try_execute().await.map_err(Self::error)?;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
for create_user in create_users.iter_mut() {
|
||||
create_user.try_execute().await.map_err(Self::error)?;
|
||||
}
|
||||
// While we may be tempted to do something like this, it can break on many older OSes like Ubuntu 18.04:
|
||||
// ```
|
||||
// useradd: cannot lock /etc/passwd; try again later.
|
||||
// ```
|
||||
// So, instead, we keep this here in hopes one day we can enable it for some detected OS:
|
||||
//
|
||||
// let mut set = JoinSet::new();
|
||||
// let mut errors: Vec<Box<ActionError>> = Vec::new();
|
||||
// for (idx, create_user) in create_users.iter_mut().enumerate() {
|
||||
// let span = tracing::Span::current().clone();
|
||||
// let mut create_user_clone = create_user.clone();
|
||||
// let _abort_handle = set.spawn(async move {
|
||||
// create_user_clone.try_execute().instrument(span).await?;
|
||||
// Result::<_, _>::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(Box::new(e)),
|
||||
// Err(e) => return Err(ActionErrorKind::Join(e))?,
|
||||
// };
|
||||
// }
|
||||
|
||||
// if !errors.is_empty() {
|
||||
// if errors.len() == 1 {
|
||||
// return Err(errors.into_iter().next().unwrap().into());
|
||||
// } else {
|
||||
// return Err(ActionErrorKind::Children(errors));
|
||||
// }
|
||||
// }
|
||||
},
|
||||
};
|
||||
|
||||
for add_user_to_group in add_users_to_groups.iter_mut() {
|
||||
add_user_to_group.try_execute().await.map_err(Self::error)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn revert_description(&self) -> Vec<ActionDescription> {
|
||||
let Self {
|
||||
nix_build_user_count: _,
|
||||
nix_build_group_name: _,
|
||||
nix_build_group_id: _,
|
||||
nix_build_user_prefix: _,
|
||||
nix_build_user_id_base: _,
|
||||
create_group,
|
||||
create_users,
|
||||
add_users_to_groups,
|
||||
} = &self;
|
||||
let mut create_users_descriptions = Vec::new();
|
||||
for create_user in create_users {
|
||||
if let Some(val) = create_user.describe_revert().iter().next() {
|
||||
create_users_descriptions.push(val.description.clone())
|
||||
}
|
||||
}
|
||||
|
||||
let mut add_user_to_group_descriptions = Vec::new();
|
||||
for add_user_to_group in add_users_to_groups {
|
||||
if let Some(val) = add_user_to_group.describe_revert().iter().next() {
|
||||
add_user_to_group_descriptions.push(val.description.clone())
|
||||
}
|
||||
}
|
||||
|
||||
let mut explanation = vec![
|
||||
format!("The Nix daemon requires system users (and a group they share) which it can act as in order to build"),
|
||||
];
|
||||
if let Some(val) = create_group.describe_revert().iter().next() {
|
||||
explanation.push(val.description.clone())
|
||||
}
|
||||
explanation.append(&mut create_users_descriptions);
|
||||
explanation.append(&mut add_user_to_group_descriptions);
|
||||
|
||||
vec![ActionDescription::new(
|
||||
format!("Remove Nix users and group"),
|
||||
explanation,
|
||||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||
let mut errors = vec![];
|
||||
for create_user in self.create_users.iter_mut() {
|
||||
if let Err(err) = create_user.try_revert().await {
|
||||
errors.push(err);
|
||||
}
|
||||
}
|
||||
|
||||
// We don't actually need to do this, when a user is deleted they are removed from groups
|
||||
// for add_user_to_group in add_users_to_groups.iter_mut() {
|
||||
// add_user_to_group.try_revert().await?;
|
||||
// }
|
||||
|
||||
// Create group
|
||||
if let Err(err) = self.create_group.try_revert().await {
|
||||
errors.push(err);
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else if errors.len() == 1 {
|
||||
Err(errors
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("Expected 1 len Vec to have at least 1 item"))
|
||||
} else {
|
||||
Err(Self::error(ActionErrorKind::MultipleChildren(errors)))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ pub(crate) mod configure_init_service;
|
|||
pub(crate) mod configure_nix;
|
||||
pub(crate) mod configure_shell_profile;
|
||||
pub(crate) mod create_nix_tree;
|
||||
pub(crate) mod create_users_and_groups;
|
||||
pub(crate) mod delete_users;
|
||||
pub(crate) mod place_nix_configuration;
|
||||
pub(crate) mod provision_nix;
|
||||
|
@ -12,6 +13,7 @@ pub use configure_init_service::{ConfigureInitService, ConfigureNixDaemonService
|
|||
pub use configure_nix::ConfigureNix;
|
||||
pub use configure_shell_profile::ConfigureShellProfile;
|
||||
pub use create_nix_tree::CreateNixTree;
|
||||
pub use create_users_and_groups::CreateUsersAndGroups;
|
||||
pub use delete_users::DeleteUsersInGroup;
|
||||
pub use place_nix_configuration::PlaceNixConfiguration;
|
||||
pub use provision_nix::ProvisionNix;
|
||||
|
|
|
@ -57,6 +57,10 @@ impl PlaceNixConfiguration {
|
|||
"extra-nix-path".to_string(),
|
||||
"nixpkgs=flake:nixpkgs".to_string(),
|
||||
);
|
||||
|
||||
// Auto-allocate uids is broken on Mac. Tools like `whoami` don't work.
|
||||
// e.g. https://github.com/NixOS/nix/issues/8444
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
settings.insert("auto-allocate-uids".to_string(), "true".to_string());
|
||||
|
||||
let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, None, None, 0o0755, force)
|
||||
|
|
|
@ -17,8 +17,6 @@ Place Nix and it's requirements onto the target
|
|||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct ProvisionNix {
|
||||
fetch_nix: StatefulAction<FetchAndUnpackNix>,
|
||||
delete_users_in_group: Option<StatefulAction<DeleteUsersInGroup>>,
|
||||
create_group: StatefulAction<CreateGroup>,
|
||||
create_nix_tree: StatefulAction<CreateNixTree>,
|
||||
move_unpacked_nix: StatefulAction<MoveUnpackedNix>,
|
||||
}
|
||||
|
@ -34,49 +32,12 @@ impl ProvisionNix {
|
|||
)
|
||||
.await?;
|
||||
|
||||
let delete_users_in_group = if let Some(group) =
|
||||
Group::from_name(settings.nix_build_group_name.as_str())
|
||||
.map_err(|e| {
|
||||
ActionErrorKind::GettingGroupId(settings.nix_build_group_name.clone(), e)
|
||||
})
|
||||
.map_err(Self::error)?
|
||||
{
|
||||
if group.gid.as_raw() != settings.nix_build_group_id {
|
||||
return Err(Self::error(ActionErrorKind::GroupGidMismatch(
|
||||
settings.nix_build_group_name.clone(),
|
||||
group.gid.as_raw(),
|
||||
settings.nix_build_group_id,
|
||||
)));
|
||||
}
|
||||
if group.mem.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
DeleteUsersInGroup::plan(
|
||||
settings.nix_build_group_name.clone(),
|
||||
settings.nix_build_group_id,
|
||||
group.mem,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let create_group = CreateGroup::plan(
|
||||
settings.nix_build_group_name.clone(),
|
||||
settings.nix_build_group_id,
|
||||
)
|
||||
.map_err(Self::error)?;
|
||||
let create_nix_tree = CreateNixTree::plan().await.map_err(Self::error)?;
|
||||
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from(SCRATCH_DIR))
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
Ok(Self {
|
||||
fetch_nix,
|
||||
delete_users_in_group,
|
||||
create_group,
|
||||
create_nix_tree,
|
||||
move_unpacked_nix,
|
||||
}
|
||||
|
@ -101,8 +62,6 @@ impl Action for ProvisionNix {
|
|||
fn execute_description(&self) -> Vec<ActionDescription> {
|
||||
let Self {
|
||||
fetch_nix,
|
||||
delete_users_in_group,
|
||||
create_group,
|
||||
create_nix_tree,
|
||||
move_unpacked_nix,
|
||||
} = &self;
|
||||
|
@ -110,11 +69,6 @@ impl Action for ProvisionNix {
|
|||
let mut buf = Vec::default();
|
||||
buf.append(&mut fetch_nix.describe_execute());
|
||||
|
||||
if let Some(delete_users_in_group) = delete_users_in_group {
|
||||
buf.append(&mut delete_users_in_group.describe_execute());
|
||||
}
|
||||
|
||||
buf.append(&mut create_group.describe_execute());
|
||||
buf.append(&mut create_nix_tree.describe_execute());
|
||||
buf.append(&mut move_unpacked_nix.describe_execute());
|
||||
|
||||
|
@ -130,14 +84,6 @@ impl Action for ProvisionNix {
|
|||
Result::<_, ActionError>::Ok(fetch_nix_clone)
|
||||
});
|
||||
|
||||
if let Some(delete_users_in_group) = &mut self.delete_users_in_group {
|
||||
delete_users_in_group
|
||||
.try_execute()
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
}
|
||||
|
||||
self.create_group.try_execute().await.map_err(Self::error)?;
|
||||
self.create_nix_tree
|
||||
.try_execute()
|
||||
.await
|
||||
|
@ -158,8 +104,6 @@ impl Action for ProvisionNix {
|
|||
fn revert_description(&self) -> Vec<ActionDescription> {
|
||||
let Self {
|
||||
fetch_nix,
|
||||
delete_users_in_group,
|
||||
create_group,
|
||||
create_nix_tree,
|
||||
move_unpacked_nix,
|
||||
} = &self;
|
||||
|
@ -167,11 +111,6 @@ impl Action for ProvisionNix {
|
|||
let mut buf = Vec::default();
|
||||
buf.append(&mut move_unpacked_nix.describe_revert());
|
||||
buf.append(&mut create_nix_tree.describe_revert());
|
||||
buf.append(&mut create_group.describe_revert());
|
||||
|
||||
if let Some(delete_users_in_group) = delete_users_in_group {
|
||||
buf.append(&mut delete_users_in_group.describe_execute());
|
||||
}
|
||||
|
||||
buf.append(&mut fetch_nix.describe_revert());
|
||||
buf
|
||||
|
@ -185,16 +124,6 @@ impl Action for ProvisionNix {
|
|||
errors.push(err)
|
||||
}
|
||||
|
||||
if let Some(delete_users_in_group) = &mut self.delete_users_in_group {
|
||||
delete_users_in_group
|
||||
.try_revert()
|
||||
.await
|
||||
.map_err(Self::error)?;
|
||||
}
|
||||
|
||||
if let Err(err) = self.create_group.try_revert().await {
|
||||
errors.push(err)
|
||||
}
|
||||
if let Err(err) = self.create_nix_tree.try_revert().await {
|
||||
errors.push(err)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
action::{
|
||||
base::{CreateDirectory, RemoveDirectory},
|
||||
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
||||
common::{ConfigureInitService, ConfigureNix, CreateUsersAndGroups, ProvisionNix},
|
||||
linux::ProvisionSelinux,
|
||||
StatefulAction,
|
||||
},
|
||||
|
@ -65,6 +65,12 @@ impl Planner for Linux {
|
|||
.map_err(PlannerError::Action)?
|
||||
.boxed(),
|
||||
);
|
||||
plan.push(
|
||||
CreateUsersAndGroups::plan(self.settings.clone())
|
||||
.await
|
||||
.map_err(PlannerError::Action)?
|
||||
.boxed(),
|
||||
);
|
||||
plan.push(
|
||||
ConfigureNix::plan(ShellProfileLocations::default(), &self.settings)
|
||||
.await
|
||||
|
|
|
@ -9,7 +9,7 @@ use super::ShellProfileLocations;
|
|||
use crate::{
|
||||
action::{
|
||||
base::RemoveDirectory,
|
||||
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
||||
common::{ConfigureInitService, ConfigureNix, CreateUsersAndGroups, ProvisionNix},
|
||||
macos::{CreateNixVolume, SetTmutilExclusions},
|
||||
StatefulAction,
|
||||
},
|
||||
|
@ -143,6 +143,12 @@ impl Planner for Macos {
|
|||
.await
|
||||
.map_err(PlannerError::Action)?
|
||||
.boxed(),
|
||||
// Auto-allocate uids is broken on Mac. Tools like `whoami` don't work.
|
||||
// e.g. https://github.com/NixOS/nix/issues/8444
|
||||
CreateUsersAndGroups::plan(self.settings.clone())
|
||||
.await
|
||||
.map_err(PlannerError::Action)?
|
||||
.boxed(),
|
||||
SetTmutilExclusions::plan(vec![PathBuf::from("/nix/store"), PathBuf::from("/nix/var")])
|
||||
.await
|
||||
.map_err(PlannerError::Action)?
|
||||
|
|
|
@ -103,7 +103,7 @@ use tokio::process::Command;
|
|||
use crate::{
|
||||
action::{
|
||||
base::{CreateDirectory, CreateFile, RemoveDirectory},
|
||||
common::{ConfigureInitService, ConfigureNix, ProvisionNix},
|
||||
common::{ConfigureInitService, ConfigureNix, CreateUsersAndGroups, ProvisionNix},
|
||||
linux::{
|
||||
EnsureSteamosNixDirectory, RevertCleanSteamosNixOffload, StartSystemdUnit,
|
||||
SystemctlDaemonReload,
|
||||
|
@ -325,6 +325,10 @@ impl Planner for SteamDeck {
|
|||
.await
|
||||
.map_err(PlannerError::Action)?
|
||||
.boxed(),
|
||||
CreateUsersAndGroups::plan(self.settings.clone())
|
||||
.await
|
||||
.map_err(PlannerError::Action)?
|
||||
.boxed(),
|
||||
ConfigureNix::plan(shell_profile_locations, &self.settings)
|
||||
.await
|
||||
.map_err(PlannerError::Action)?
|
||||
|
|
|
@ -94,6 +94,55 @@ pub struct CommonSettings {
|
|||
)]
|
||||
pub nix_build_group_id: u32,
|
||||
|
||||
/// The Nix build user prefix (user numbers will be postfixed)
|
||||
#[cfg_attr(
|
||||
feature = "cli",
|
||||
clap(long, env = "NIX_INSTALLER_NIX_BUILD_USER_PREFIX", global = true)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
all(target_os = "macos", feature = "cli"),
|
||||
clap(default_value = "_nixbld")
|
||||
)]
|
||||
#[cfg_attr(
|
||||
all(target_os = "linux", feature = "cli"),
|
||||
clap(default_value = "nixbld")
|
||||
)]
|
||||
pub nix_build_user_prefix: String,
|
||||
|
||||
/// Number of build users to create
|
||||
#[cfg_attr(
|
||||
all(target_os = "linux", feature = "cli"),
|
||||
doc = "On Linux, Nix's `auto-allocate-uids` feature will be enabled, so users don't need to be created."
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "cli",
|
||||
clap(
|
||||
long,
|
||||
alias = "daemon-user-count",
|
||||
env = "NIX_INSTALLER_NIX_BUILD_USER_COUNT",
|
||||
global = true
|
||||
)
|
||||
)]
|
||||
#[cfg_attr(all(target_os = "macos", feature = "cli"), clap(default_value = "32"))]
|
||||
#[cfg_attr(all(target_os = "linux", feature = "cli"), clap(default_value = "0"))]
|
||||
pub nix_build_user_count: u32,
|
||||
|
||||
/// The Nix build user base UID (ascending)
|
||||
#[cfg_attr(
|
||||
feature = "cli",
|
||||
clap(long, env = "NIX_INSTALLER_NIX_BUILD_USER_ID_BASE", global = true)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
all(target_os = "macos", feature = "cli"),
|
||||
doc = "Service users on Mac should be between 200-400"
|
||||
)]
|
||||
#[cfg_attr(all(target_os = "macos", feature = "cli"), clap(default_value_t = 300))]
|
||||
#[cfg_attr(
|
||||
all(target_os = "linux", feature = "cli"),
|
||||
clap(default_value_t = 30_000)
|
||||
)]
|
||||
pub nix_build_user_id_base: u32,
|
||||
|
||||
/// The Nix package URL
|
||||
#[cfg_attr(
|
||||
feature = "cli",
|
||||
|
@ -189,30 +238,48 @@ impl CommonSettings {
|
|||
/// The default settings for the given Architecture & Operating System
|
||||
pub async fn default() -> Result<Self, InstallSettingsError> {
|
||||
let url;
|
||||
let nix_build_user_prefix;
|
||||
let nix_build_user_id_base;
|
||||
let nix_build_user_count;
|
||||
|
||||
use target_lexicon::{Architecture, OperatingSystem};
|
||||
match (Architecture::host(), OperatingSystem::host()) {
|
||||
#[cfg(target_os = "linux")]
|
||||
(Architecture::X86_64, OperatingSystem::Linux) => {
|
||||
url = NIX_X64_64_LINUX_URL;
|
||||
nix_build_user_prefix = "nixbld";
|
||||
nix_build_user_id_base = 30000;
|
||||
nix_build_user_count = 0;
|
||||
},
|
||||
#[cfg(target_os = "linux")]
|
||||
(Architecture::X86_32(_), OperatingSystem::Linux) => {
|
||||
url = NIX_I686_LINUX_URL;
|
||||
nix_build_user_prefix = "nixbld";
|
||||
nix_build_user_id_base = 30000;
|
||||
nix_build_user_count = 0;
|
||||
},
|
||||
#[cfg(target_os = "linux")]
|
||||
(Architecture::Aarch64(_), OperatingSystem::Linux) => {
|
||||
url = NIX_AARCH64_LINUX_URL;
|
||||
nix_build_user_prefix = "nixbld";
|
||||
nix_build_user_id_base = 30000;
|
||||
nix_build_user_count = 0;
|
||||
},
|
||||
#[cfg(target_os = "macos")]
|
||||
(Architecture::X86_64, OperatingSystem::MacOSX { .. })
|
||||
| (Architecture::X86_64, OperatingSystem::Darwin) => {
|
||||
url = NIX_X64_64_DARWIN_URL;
|
||||
nix_build_user_prefix = "_nixbld";
|
||||
nix_build_user_id_base = 300;
|
||||
nix_build_user_count = 32;
|
||||
},
|
||||
#[cfg(target_os = "macos")]
|
||||
(Architecture::Aarch64(_), OperatingSystem::MacOSX { .. })
|
||||
| (Architecture::Aarch64(_), OperatingSystem::Darwin) => {
|
||||
url = NIX_AARCH64_DARWIN_URL;
|
||||
nix_build_user_prefix = "_nixbld";
|
||||
nix_build_user_id_base = 300;
|
||||
nix_build_user_count = 32;
|
||||
},
|
||||
_ => {
|
||||
return Err(InstallSettingsError::UnsupportedArchitecture(
|
||||
|
@ -225,6 +292,9 @@ impl CommonSettings {
|
|||
modify_profile: true,
|
||||
nix_build_group_name: String::from("nixbld"),
|
||||
nix_build_group_id: 30_000,
|
||||
nix_build_user_id_base,
|
||||
nix_build_user_count,
|
||||
nix_build_user_prefix: nix_build_user_prefix.to_string(),
|
||||
nix_package_url: url.parse()?,
|
||||
proxy: Default::default(),
|
||||
extra_conf: Default::default(),
|
||||
|
@ -241,6 +311,9 @@ impl CommonSettings {
|
|||
modify_profile,
|
||||
nix_build_group_name,
|
||||
nix_build_group_id,
|
||||
nix_build_user_prefix,
|
||||
nix_build_user_id_base,
|
||||
nix_build_user_count,
|
||||
nix_package_url,
|
||||
proxy,
|
||||
extra_conf,
|
||||
|
@ -263,6 +336,18 @@ impl CommonSettings {
|
|||
"nix_build_group_id".into(),
|
||||
serde_json::to_value(nix_build_group_id)?,
|
||||
);
|
||||
map.insert(
|
||||
"nix_build_user_prefix".into(),
|
||||
serde_json::to_value(nix_build_user_prefix)?,
|
||||
);
|
||||
map.insert(
|
||||
"nix_build_user_id_base".into(),
|
||||
serde_json::to_value(nix_build_user_id_base)?,
|
||||
);
|
||||
map.insert(
|
||||
"nix_build_user_count".into(),
|
||||
serde_json::to_value(nix_build_user_count)?,
|
||||
);
|
||||
map.insert(
|
||||
"nix_package_url".into(),
|
||||
serde_json::to_value(nix_package_url)?,
|
||||
|
|
3
tests/fixtures/linux/linux.json
vendored
3
tests/fixtures/linux/linux.json
vendored
|
@ -343,6 +343,9 @@
|
|||
"modify_profile": true,
|
||||
"nix_build_group_name": "nixbld",
|
||||
"nix_build_group_id": 30000,
|
||||
"nix_build_user_count": 0,
|
||||
"nix_build_user_prefix": "nixbld",
|
||||
"nix_build_user_id_base": 30000,
|
||||
"nix_package_url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz",
|
||||
"proxy": null,
|
||||
"ssl_cert_file": null,
|
||||
|
|
3
tests/fixtures/linux/steam-deck.json
vendored
3
tests/fixtures/linux/steam-deck.json
vendored
|
@ -373,6 +373,9 @@
|
|||
"modify_profile": true,
|
||||
"nix_build_group_name": "nixbld",
|
||||
"nix_build_group_id": 30000,
|
||||
"nix_build_user_count": 0,
|
||||
"nix_build_user_prefix": "nixbld",
|
||||
"nix_build_user_id_base": 30000,
|
||||
"nix_package_url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-x86_64-linux.tar.xz",
|
||||
"proxy": null,
|
||||
"ssl_cert_file": null,
|
||||
|
|
3
tests/fixtures/macos/macos.json
vendored
3
tests/fixtures/macos/macos.json
vendored
|
@ -399,6 +399,9 @@
|
|||
"modify_profile": true,
|
||||
"nix_build_group_name": "nixbld",
|
||||
"nix_build_group_id": 30000,
|
||||
"nix_build_user_count": 32,
|
||||
"nix_build_user_prefix": "_nixbld",
|
||||
"nix_build_user_id_base": 300,
|
||||
"nix_package_url": "https://releases.nixos.org/nix/nix-2.15.0/nix-2.15.0-aarch64-darwin.tar.xz",
|
||||
"proxy": null,
|
||||
"ssl_cert_file": null,
|
||||
|
|
Loading…
Reference in a new issue