aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraham Christensen <graham@grahamc.com>2018-03-30 16:04:11 -0400
committerGitHub <noreply@github.com>2018-03-30 16:04:11 -0400
commit3fbaa230a23ef47100eb1f117008a1cee85fcf5e (patch)
treec50907afe38e1c3f6e844efd2f3ae6218ad25f21
parent4ee4fda521137fed6af0446948b3877e0c5db803 (diff)
parent4eb40c72ed21aa4b118811a1c099a5c0dd231145 (diff)
Merge pull request #2026 from grahamc/multi-user-linux
Expand the multi-user installer to support Linuxes with systemd
-rw-r--r--release.nix34
-rw-r--r--scripts/install-darwin-multi-user.sh831
-rw-r--r--scripts/install-multi-user.sh797
-rw-r--r--scripts/install-nix-from-closure.sh11
-rw-r--r--scripts/install-systemd-multi-user.sh154
5 files changed, 1067 insertions, 760 deletions
diff --git a/release.nix b/release.nix
index 91c782c51..492065df9 100644
--- a/release.nix
+++ b/release.nix
@@ -127,17 +127,39 @@ let
substitute ${./scripts/install-nix-from-closure.sh} $TMPDIR/install \
--subst-var-by nix ${toplevel} \
--subst-var-by cacert ${cacert}
- substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user \
+
+ substitute ${./scripts/install-darwin-multi-user.sh} $TMPDIR/install-darwin-multi-user.sh \
+ --subst-var-by nix ${toplevel} \
+ --subst-var-by cacert ${cacert}
+ substitute ${./scripts/install-systemd-multi-user.sh} $TMPDIR/install-systemd-multi-user.sh \
+ --subst-var-by nix ${toplevel} \
+ --subst-var-by cacert ${cacert}
+ substitute ${./scripts/install-multi-user.sh} $TMPDIR/install-multi-user \
--subst-var-by nix ${toplevel} \
--subst-var-by cacert ${cacert}
if type -p shellcheck; then
- shellcheck -e SC1090 $TMPDIR/install
- shellcheck -e SC1091,SC2002 $TMPDIR/install-darwin-multi-user
+ # SC1090: Don't worry about not being able to find
+ # $nix/etc/profile.d/nix.sh
+ shellcheck --exclude SC1090 $TMPDIR/install
+ shellcheck $TMPDIR/install-darwin-multi-user.sh
+ shellcheck $TMPDIR/install-systemd-multi-user.sh
+
+ # SC1091: Don't panic about not being able to source
+ # /etc/profile
+ # SC2002: Ignore "useless cat" "error", when loading
+ # .reginfo, as the cat is a much cleaner
+ # implementation, even though it is "useless"
+ # SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
+ # root's home directory
+ shellcheck --external-sources \
+ --exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
fi
chmod +x $TMPDIR/install
- chmod +x $TMPDIR/install-darwin-multi-user
+ chmod +x $TMPDIR/install-darwin-multi-user.sh
+ chmod +x $TMPDIR/install-systemd-multi-user.sh
+ chmod +x $TMPDIR/install-multi-user
dir=nix-${version}-${system}
fn=$out/$dir.tar.bz2
mkdir -p $out/nix-support
@@ -149,7 +171,9 @@ let
--transform "s,$TMPDIR/install,$dir/install," \
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
--transform "s,$NIX_STORE,$dir/store,S" \
- $TMPDIR/install $TMPDIR/install-darwin-multi-user $TMPDIR/reginfo \
+ $TMPDIR/install $TMPDIR/install-darwin-multi-user.sh \
+ $TMPDIR/install-systemd-multi-user.sh \
+ $TMPDIR/install-multi-user $TMPDIR/reginfo \
$(cat ${installerClosureInfo}/store-paths)
'');
diff --git a/scripts/install-darwin-multi-user.sh b/scripts/install-darwin-multi-user.sh
index 716b6e9bc..87c4c2b05 100644
--- a/scripts/install-darwin-multi-user.sh
+++ b/scripts/install-darwin-multi-user.sh
@@ -1,819 +1,144 @@
-#!/bin/bash
+#!/usr/bin/env bash
set -eu
set -o pipefail
-# Sourced from:
-# - https://github.com/LnL7/nix-darwin/blob/8c29d0985d74b4a990238497c47a2542a5616b3c/bootstrap.sh
-# - https://gist.github.com/expipiplus1/e571ce88c608a1e83547c918591b149f/ac504c6c1b96e65505fbda437a28ce563408ecb0
-# - https://github.com/NixOS/nixos-org-configurations/blob/a122f418797713d519aadf02e677fce0dc1cb446/delft/scripts/nix-mac-installer.sh
-# - https://github.com/matthewbauer/macNixOS/blob/f6045394f9153edea417be90c216788e754feaba/install-macNixOS.sh
-# - https://gist.github.com/LnL7/9717bd6cdcb30b086fd7f2093e5f8494/86b26f852ce563e973acd30f796a9a416248c34a
-#
-# however tracking which bits came from which would be impossible.
-
-readonly ESC='\033[0m'
-readonly BOLD='\033[38;1m'
-readonly BLUE='\033[38;34m'
-readonly BLUE_UL='\033[38;4;34m'
-readonly GREEN='\033[38;32m'
-readonly GREEN_UL='\033[38;4;32m'
-readonly RED='\033[38;31m'
-readonly RED_UL='\033[38;4;31m'
-readonly YELLOW='\033[38;33m'
-readonly YELLOW_UL='\033[38;4;33m'
-
-readonly CORES=$(sysctl -n hw.ncpu)
-readonly NIX_USER_COUNT="32"
-readonly NIX_BUILD_GROUP_ID="30000"
-readonly NIX_BUILD_GROUP_NAME="nixbld"
-readonly NIX_FIRST_BUILD_UID="30001"
-# Please don't change this. We don't support it, because the
-# default shell profile that comes with Nix doesn't support it.
-readonly NIX_ROOT="/nix"
readonly PLIST_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
-readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/zshrc")
-readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
-readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
-
-readonly NIX_INSTALLED_NIX="@nix@"
-readonly NIX_INSTALLED_CACERT="@cacert@"
-readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
-
-readonly ROOT_HOME="/var/root"
-
-if [ -t 0 ]; then
- readonly IS_HEADLESS='no'
-else
- readonly IS_HEADLESS='yes'
-fi
+dsclattr() {
+ /usr/bin/dscl . -read "$1" \
+ | awk "/$2/ { print \$2 }"
+}
-headless() {
- if [ "$IS_HEADLESS" = "yes" ]; then
- return 0
- else
- return 1
+poly_validate_assumptions() {
+ if [ "$(uname -s)" != "Darwin" ]; then
+ failure "This script is for use with macOS!"
fi
}
-contactme() {
- echo "We'd love to help if you need it."
- echo ""
- echo "If you can, open an issue at https://github.com/nixos/nix/issues"
- echo ""
- echo "Or feel free to contact the team,"
- echo " - on IRC #nixos on irc.freenode.net"
- echo " - on twitter @nixos_org"
+poly_service_installed_check() {
+ [ -e "$PLIST_DEST" ]
}
-uninstall_directions() {
- subheader "Uninstalling nix:"
- local step=0
-
- if [ -e "$PLIST_DEST" ]; then
- step=$((step + 1))
+poly_service_uninstall_directions() {
cat <<EOF
-$step. Delete $PLIST_DEST
+$1. Delete $PLIST_DEST
sudo launchctl unload $PLIST_DEST
sudo rm $PLIST_DEST
EOF
- fi
-
- for profile_target in "${PROFILE_TARGETS[@]}"; do
- if [ -e "$profile_target" ] && [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
- step=$((step + 1))
- cat <<EOF
-$step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target
-
- sudo mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
-
-(after this one, you may need to re-open any terminals that were
-opened while it existed.)
-
-EOF
- fi
- done
+}
- step=$((step + 1))
+poly_service_setup_note() {
cat <<EOF
-$step. Delete the files Nix added to your system:
-
- sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels
-
-and that is it.
+ - load and start a LaunchDaemon (at $PLIST_DEST) for nix-daemon
EOF
-
-}
-
-nix_user_for_core() {
- printf "nixbld%d" "$1"
-}
-
-nix_uid_for_core() {
- echo $((NIX_FIRST_BUILD_UID + $1 - 1))
}
-dsclattr() {
- /usr/bin/dscl . -read "$1" \
- | awk "/$2/ { print \$2 }"
-}
-
-_textout() {
- echo -en "$1"
- shift
- if [ "$*" = "" ]; then
- cat
- else
- echo "$@"
- fi
- echo -en "$ESC"
-}
-
-header() {
- follow="---------------------------------------------------------"
- header=$(echo "---- $* $follow$follow$follow" | head -c 80)
- echo ""
- _textout "$BLUE" "$header"
-}
-
-warningheader() {
- follow="---------------------------------------------------------"
- header=$(echo "---- $* $follow$follow$follow" | head -c 80)
- echo ""
- _textout "$RED" "$header"
-}
-
-subheader() {
- echo ""
- _textout "$BLUE_UL" "$*"
-}
-
-row() {
- printf "$BOLD%s$ESC:\\t%s\\n" "$1" "$2"
-}
-
-task() {
- echo ""
- ok "~~> $1"
-}
-
-bold() {
- echo "$BOLD$*$ESC"
-}
-
-ok() {
- _textout "$GREEN" "$@"
-}
-
-warning() {
- warningheader "warning!"
- cat
- echo ""
-}
-
-failure() {
- header "oh no!"
- _textout "$RED" "$@"
- echo ""
- _textout "$RED" "$(contactme)"
- trap finish_cleanup EXIT
- exit 1
-}
-
-ui_confirm() {
- _textout "$GREEN$GREEN_UL" "$1"
-
- if headless; then
- echo "No TTY, assuming you would say yes :)"
- return 0
- fi
-
- local prompt="[y/n] "
- echo -n "$prompt"
- while read -r y; do
- if [ "$y" = "y" ]; then
- echo ""
- return 0
- elif [ "$y" = "n" ]; then
- echo ""
- return 1
- else
- _textout "$RED" "Sorry, I didn't understand. I can only understand answers of y or n"
- echo -n "$prompt"
- fi
- done
- echo ""
- return 1
-}
-
-__sudo() {
- local expl="$1"
- local cmd="$2"
- shift
- header "sudo execution"
-
- echo "I am executing:"
- echo ""
- printf " $ sudo %s\\n" "$cmd"
- echo ""
- echo "$expl"
- echo ""
-
- return 0
-}
+poly_configure_nix_daemon_service() {
+ _sudo "to set up the nix-daemon as a LaunchDaemon" \
+ ln -sfn "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST"
-_sudo() {
- local expl="$1"
- shift
- if ! headless; then
- __sudo "$expl" "$*"
- fi
- sudo "$@"
-}
+ _sudo "to load the LaunchDaemon plist for nix-daemon" \
+ launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
+ _sudo "to start the nix-daemon" \
+ launchctl start org.nixos.nix-daemon
-readonly SCRATCH=$(mktemp -d -t tmp.XXXXXXXXXX)
-function finish_cleanup {
- rm -rf "$SCRATCH"
}
-function finish_fail {
- finish_cleanup
-
- failure <<EOF
-Jeeze, something went wrong. If you can take all the output and open
-an issue, we'd love to fix the problem so nobody else has this issue.
-
-:(
-EOF
+poly_group_exists() {
+ /usr/bin/dscl . -read "/Groups/$1" > /dev/null 2>&1
}
-trap finish_fail EXIT
-
-function finish_success {
- finish_cleanup
- ok "Alright! We're done!"
- cat <<EOF
-
-Before Nix will work in your existing shells, you'll need to close
-them and open them again. Other than that, you should be ready to go.
-
-Try it! Open a new terminal, and type:
-
- $ nix-shell -p nix-info --run "nix-info -m"
-
-Thank you for using this installer. If you have any feedback, don't
-hesitate:
-
-$(contactme)
-EOF
+poly_group_id_get() {
+ dsclattr "/Groups/$1" "PrimaryGroupID"
}
-
-validate_starting_assumptions() {
- if [ "$(uname -s)" != "Darwin" ]; then
- failure "This script is for use with macOS!"
- fi
-
- if [ $EUID -eq 0 ]; then
- failure <<EOF
-Please do not run this script with root privileges. We will call sudo
-when we need to.
-EOF
- fi
-
- if type nix-env 2> /dev/null >&2; then
- failure <<EOF
-Nix already appears to be installed, and this tool assumes it is
-_not_ yet installed.
-
-$(uninstall_directions)
-EOF
- fi
-
- if [ "${NIX_REMOTE:-}" != "" ]; then
- failure <<EOF
-For some reason, \$NIX_REMOTE is set. It really should not be set
-before this installer runs, and it hints that Nix is currently
-installed. Please delete the old Nix installation and start again.
-
-Note: You might need to close your shell window and open a new shell
-to clear the variable.
-EOF
- fi
-
- if echo "${SSL_CERT_FILE:-}" | grep -qE "(nix/var/nix|nix-profile)"; then
- failure <<EOF
-It looks like \$SSL_CERT_FILE is set to a path that used to be part of
-the old Nix installation. Please unset that variable and try again:
-
- $ unset SSL_CERT_FILE
-
-EOF
- fi
-
- for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do
- if [ -f "$file" ]; then
- if grep -l "^[^#].*.nix-profile" "$file"; then
- failure <<EOF
-I found a reference to a ".nix-profile" in $file.
-This has a high chance of breaking a new nix installation. It was most
-likely put there by a previous Nix installer.
-
-Please remove this reference and try running this again. You should
-also look for similar references in:
-
- - ~/.bash_profile
- - ~/.bash_login
- - ~/.profile
-
-or other shell init files that you may have.
-
-$(uninstall_directions)
-EOF
- fi
- fi
- done
-
- if [ -d /nix ]; then
- failure <<EOF
-There are some relics of a previous installation of Nix at /nix, and
-this scripts assumes Nix is _not_ yet installed. Please delete the old
-Nix installation and start again.
-
-$(uninstall_directions)
-EOF
- fi
-
- if [ -d /etc/nix ]; then
- failure <<EOF
-There are some relics of a previous installation of Nix at /etc/nix, and
-this scripts assumes Nix is _not_ yet installed. Please delete the old
-Nix installation and start again.
-
-$(uninstall_directions)
-EOF
- fi
-
- for profile_target in "${PROFILE_TARGETS[@]}"; do
- if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
- failure <<EOF
-When this script runs, it backs up the current $profile_target to
-$profile_target$PROFILE_BACKUP_SUFFIX. This backup file already exists, though.
-
-Please follow these instructions to clean up the old backup file:
-
-1. Copy $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX to another place, just
-in case.
-
-2. Take care to make sure that $profile_target$PROFILE_BACKUP_SUFFIX doesn't look like
-it has anything nix-related in it. If it does, something is probably
-quite wrong. Please open an issue or get in touch immediately.
-
-3. Take care to make sure that $profile_target doesn't look like it has
-anything nix-related in it. If it does, and $profile_target _did not_,
-run:
-
- $ /usr/bin/sudo /bin/mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
-
-and try again.
-EOF
- fi
-
- if grep -qi "nix" "$profile_target"; then
- failure <<EOF
-It looks like $profile_target already has some Nix configuration in
-there. There should be no reason to run this again. If you're having
-trouble, please open an issue.
-EOF
- fi
- done
-
- danger_paths=("$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.nix-profile")
- for danger_path in "${danger_paths[@]}"; do
- if _sudo "making sure that $danger_path doesn't exist" \
- test -e "$danger_path"; then
- failure <<EOF
-I found a file at $danger_path, which is a relic of a previous
-installation. You must first delete this file before continuing.
-
-$(uninstall_directions)
-EOF
- fi
- done
+poly_create_build_group() {
+ _sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
+ /usr/sbin/dseditgroup -o create \
+ -r "Nix build group for nix-daemon" \
+ -i "$NIX_BUILD_GROUP_ID" \
+ "$NIX_BUILD_GROUP_NAME" >&2
}
-setup_report() {
- header "hardware report"
- row " Cores" "$CORES"
-
- header "Nix config report"
- row " Temp Dir" "$SCRATCH"
- row " Nix Root" "$NIX_ROOT"
- row " Build Users" "$NIX_USER_COUNT"
- row " Build Group ID" "$NIX_BUILD_GROUP_ID"
- row "Build Group Name" "$NIX_BUILD_GROUP_NAME"
- if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" != "" ]; then
- row "Preexisting Install" "Allowed"
- fi
-
- subheader "build users:"
-
- row " Username" "UID"
- for i in $(seq 1 "$NIX_USER_COUNT"); do
- row " $(nix_user_for_core "$i")" "$(nix_uid_for_core "$i")"
- done
- echo ""
+poly_user_exists() {
+ /usr/bin/dscl . -read "/Users/$1" > /dev/null 2>&1
}
-create_build_group() {
- local primary_group_id
-
- task "Setting up the build group $NIX_BUILD_GROUP_NAME"
- if ! /usr/bin/dscl . -read "/Groups/$NIX_BUILD_GROUP_NAME" > /dev/null 2>&1; then
- _sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
- /usr/sbin/dseditgroup -o create \
- -r "Nix build group for nix-daemon" \
- -i "$NIX_BUILD_GROUP_ID" \
- "$NIX_BUILD_GROUP_NAME" >&2
- row " Created" "Yes"
- else
- primary_group_id=$(dsclattr "/Groups/$NIX_BUILD_GROUP_NAME" "PrimaryGroupID")
- if [ "$primary_group_id" -ne "$NIX_BUILD_GROUP_ID" ]; then
- failure <<EOF
-It seems the build group $NIX_BUILD_GROUP_NAME already exists, but
-with the UID $primary_group_id. This script can't really handle
-that right now, so I'm going to give up.
-
-You can fix this by editing this script and changing the
-NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID
-to $primary_group_id and re-run.
-EOF
- else
- row " Exists" "Yes"
- fi
- fi
+poly_user_id_get() {
+ dsclattr "/Users/$1" "UniqueID"
}
-create_build_user_for_core() {
- local coreid
- local username
- local uid
-
- coreid="$1"
- username=$(nix_user_for_core "$coreid")
- uid=$(nix_uid_for_core "$coreid")
- dsclpath="/Users/$username"
-
- task "Setting up the build user $username"
-
- if ! /usr/bin/dscl . -read "$dsclpath" > /dev/null 2>&1; then
- _sudo "Creating the Nix build user, $username" \
- /usr/bin/dscl . create "$dsclpath" \
- UniqueID "${uid}"
- row " Created" "Yes"
- else
- actual_uid=$(dsclattr "$dsclpath" "UniqueID")
- if [ "$actual_uid" -ne "$uid" ]; then
- failure <<EOF
-It seems the build user $username already exists, but with the UID
-with the UID $actual_uid. This script can't really handle that right
-now, so I'm going to give up.
-
-If you already created the users and you know they start from
-$actual_uid and go up from there, you can edit this script and change
-NIX_FIRST_BUILD_UID near the top of the file to $actual_uid and try
-again.
-EOF
- else
- row " Exists" "Yes"
- fi
- fi
-
- if [ "$(dsclattr "$dsclpath" "IsHidden")" = "1" ]; then
- row " IsHidden" "Yes"
- else
- _sudo "in order to make $username a hidden user" \
- /usr/bin/dscl . -create "$dsclpath" "IsHidden" "1"
- row " IsHidden" "Yes"
- fi
-
- if [ "$(dsclattr "$dsclpath" "NFSHomeDirectory")" = "/var/empty" ]; then
- row " NFSHomeDirectory" "/var/empty"
- else
- _sudo "in order to give $username a safe home directory" \
- /usr/bin/dscl . -create "$dsclpath" "NFSHomeDirectory" "/var/empty"
- row " NFSHomeDirectory" "/var/empty"
- fi
-
- if [ "$(dsclattr "$dsclpath" "RealName")" = "Nix build user $coreid" ]; then
- row " RealName" "Nix build user $coreid"
- else
- _sudo "in order to give $username a useful name" \
- /usr/bin/dscl . -create "$dsclpath" "RealName" "Nix build user $coreid"
- row " RealName" "Nix build user $coreid"
- fi
-
- if [ "$(dsclattr "$dsclpath" "UserShell")" = "/sbin/nologin" ]; then
- row " Logins Disabled" "Yes"
- else
- _sudo "in order to prevent $username from logging in" \
- /usr/bin/dscl . -create "$dsclpath" "UserShell" "/sbin/nologin"
- row " Logins Disabled" "Yes"
- fi
-
- if dseditgroup -o checkmember -m "$username" "$NIX_BUILD_GROUP_NAME" > /dev/null 2>&1 ; then
- row " Member of $NIX_BUILD_GROUP_NAME" "Yes"
- else
- _sudo "Add $username to the $NIX_BUILD_GROUP_NAME group"\
- /usr/sbin/dseditgroup -o edit -t user \
- -a "$username" "$NIX_BUILD_GROUP_NAME"
- row " Member of $NIX_BUILD_GROUP_NAME" "Yes"
- fi
-
- if [ "$(dsclattr "$dsclpath" "PrimaryGroupID")" = "$NIX_BUILD_GROUP_ID" ]; then
- row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
- else
- _sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
- /usr/bin/dscl . -create "$dsclpath" "PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
- row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
-
- fi
+poly_user_hidden_get() {
+ dsclattr "/Users/$1" "IsHidden"
}
-create_build_users() {
- for i in $(seq 1 "$NIX_USER_COUNT"); do
- create_build_user_for_core "$i"
- done
+poly_user_hidden_set() {
+ _sudo "in order to make $1 a hidden user" \
+ /usr/bin/dscl . -create "/Users/$1" "IsHidden" "1"
}
-create_directories() {
- _sudo "to make the basic directory structure of Nix (part 1)" \
- mkdir -pv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool}
-
- _sudo "to make the basic directory structure of Nix (part 2)" \
- mkdir -pv -m 1777 /nix/var/nix/{gcroots,profiles}/per-user
-
- _sudo "to make the basic directory structure of Nix (part 3)" \
- mkdir -pv -m 1775 /nix/store
-
- _sudo "to make the basic directory structure of Nix (part 4)" \
- chgrp "$NIX_BUILD_GROUP_NAME" /nix/store
-
- _sudo "to set up the root user's profile (part 1)" \
- mkdir -pv -m 0755 /nix/var/nix/profiles/per-user/root
-
- _sudo "to set up the root user's profile (part 2)" \
- mkdir -pv -m 0700 "$ROOT_HOME/.nix-defexpr"
-
- _sudo "to place the default nix daemon configuration (part 1)" \
- mkdir -pv -m 0555 /etc/nix
+poly_user_home_get() {
+ dsclattr "/Users/$1" "NFSHomeDirectory"
}
-place_channel_configuration() {
- echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels"
- _sudo "to set up the default system channel (part 1)" \
- install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels"
+poly_user_home_set() {
+ _sudo "in order to give $1 a safe home directory" \
+ /usr/bin/dscl . -create "/Users/$1" "NFSHomeDirectory" "$2"
}
-welcome_to_nix() {
- ok "Welcome to the Multi-User Nix Installation"
-
- cat <<EOF
-
-This installation tool will set up your computer with the Nix package
-manager. This will happen in a few stages:
-
-1. Make sure your computer doesn't already have Nix. If it does, I
- will show you instructions on how to clean up your old one.
-
-2. Show you what we are going to install and where. Then we will ask
- if you are ready to continue.
-
-3. Create the system users and groups that the Nix daemon uses to run
- builds.
-
-4. Perform the basic installation of the Nix files daemon.
-
-5. Configure your shell to import special Nix Profile files, so you
- can use Nix.
-
-6. Start the Nix daemon.
-
-EOF
-
- if ui_confirm "Would you like to see a more detailed list of what we will do?"; then
- cat <<EOF
-
-We will:
-
- - make sure your computer doesn't already have Nix files
- (if it does, I will tell you how to clean them up.)
- - create local users (see the list above for the users we'll make)
- - create a local group ($NIX_BUILD_GROUP_NAME)
- - install Nix in to $NIX_ROOT
- - create a configuration file in /etc/nix
- - set up the "default profile" by creating some Nix-related files in
- $ROOT_HOME
-EOF
- for profile_target in "${PROFILE_TARGETS[@]}"; do
- if [ -e "$profile_target" ]; then
- cat <<EOF
- - back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX
- - update $profile_target to include some Nix configuration
-EOF
- fi
- done
- cat <<EOF
- - load and start a LaunchDaemon (at $PLIST_DEST) for nix-daemon
-
-EOF
- if ! ui_confirm "Ready to continue?"; then
- failure <<EOF
-Okay, maybe you would like to talk to the team.
-EOF
- fi
- fi
+poly_user_note_get() {
+ dsclattr "/Users/$1" "RealName"
}
-chat_about_sudo() {
- header "let's talk about sudo"
-
- if headless; then
- cat <<EOF
-This script is going to call sudo a lot. Normally, it would show you
-exactly what commands it is running and why. However, the script is
-run in a headless fashion, like this:
-
- $ curl https://nixos.org/nix/install | sh
-
-or maybe in a CI pipeline. Because of that, we're going to skip the
-verbose output in the interest of brevity.
-
-If you would like to
-see the output, try like this:
-
- $ curl -o install-nix https://nixos.org/nix/install
- $ sh ./install-nix
-
-EOF
- return 0
- fi
-
- cat <<EOF
-This script is going to call sudo a lot. Every time we do, it'll
-output exactly what it'll do, and why.
-
-Just like this:
-EOF
-
- __sudo "to demonstrate how our sudo prompts look" \
- echo "this is a sudo prompt"
-
- cat <<EOF
-
-This might look scary, but everything can be undone by running just a
-few commands. We used to ask you to confirm each time sudo ran, but it
-was too many times. Instead, I'll just ask you this one time:
-
-EOF
- if ui_confirm "Can we use sudo?"; then
- ok "Yay! Thanks! Let's get going!"
- else
- failure <<EOF
-That is okay, but we can't install.
-EOF
- fi
+poly_user_note_set() {
+ _sudo "in order to give $username a useful note" \
+ /usr/bin/dscl . -create "/Users/$1" "RealName" "$2"
}
-install_from_extracted_nix() {
- (
- cd "$EXTRACTED_NIX_PATH"
-
- _sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \
- rsync -rlpt ./store/* "$NIX_ROOT/store/"
-
- if [ -d "$NIX_INSTALLED_NIX" ]; then
- echo " Alright! We have our first nix at $NIX_INSTALLED_NIX"
- else
- failure <<EOF
-Something went wrong, and I didn't find Nix installed at
-$NIX_INSTALLED_NIX.
-EOF
- fi
-
- _sudo "to initialize the Nix Database" \
- $NIX_INSTALLED_NIX/bin/nix-store --init
-
- cat ./.reginfo \
- | _sudo "to load data for the first time in to the Nix Database" \
- "$NIX_INSTALLED_NIX/bin/nix-store" --load-db
-
- echo " Just finished getting the nix database ready."
- )
+poly_user_shell_get() {
+ dsclattr "/Users/$1" "UserShell"
}
-shell_source_lines() {
- cat <<EOF
-
-# Nix
-if [ -e '$PROFILE_NIX_FILE' ]; then
- . '$PROFILE_NIX_FILE'
-fi
-# End Nix
-
-EOF
+poly_user_shell_set() {
+ _sudo "in order to give $1 a safe home directory" \
+ /usr/bin/dscl . -create "/Users/$1" "UserShell" "$2"
}
-configure_shell_profile() {
- for profile_target in "${PROFILE_TARGETS[@]}"; do
- if [ -e "$profile_target" ]; then
- _sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \
- cp "$profile_target" "$profile_target$PROFILE_BACKUP_SUFFIX"
-
- shell_source_lines \
- | _sudo "extend your $profile_target with nix-daemon settings" \
- tee -a "$profile_target"
- fi
- done
+poly_user_in_group_check() {
+ username=$1
+ group=$2
+ dseditgroup -o checkmember -m "$username" "$group" > /dev/null 2>&1
}
-setup_default_profile() {
- _sudo "to installing a bootstrapping Nix in to the default Profile" \
- HOME=$ROOT_HOME "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_NIX"
-
- _sudo "to installing a bootstrapping SSL certificate just for Nix in to the default Profile" \
- HOME=$ROOT_HOME "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_CACERT"
+poly_user_in_group_set() {
+ username=$1
+ group=$2
- _sudo "to update the default channel in the default profile" \
- HOME=$ROOT_HOME NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt "$NIX_INSTALLED_NIX/bin/nix-channel" --update nixpkgs
+ _sudo "Add $username to the $group group"\
+ /usr/sbin/dseditgroup -o edit -t user \
+ -a "$username" "$group"
}
-
-place_nix_configuration() {
- cat <<EOF > "$SCRATCH/nix.conf"
-build-users-group = $NIX_BUILD_GROUP_NAME
-
-max-jobs = $NIX_USER_COUNT
-cores = 1
-sandbox = false
-EOF
- _sudo "to place the default nix daemon configuration (part 2)" \
- install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf
+poly_user_primary_group_get() {
+ dsclattr "/Users/$1" "PrimaryGroupID"
}
-configure_nix_daemon_plist() {
- _sudo "to set up the nix-daemon as a LaunchDaemon" \
- ln -sfn "/nix/var/nix/profiles/default$PLIST_DEST" "$PLIST_DEST"
-
- _sudo "to load the LaunchDaemon plist for nix-daemon" \
- launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
-
- _sudo "to start the nix-daemon" \
- launchctl start org.nixos.nix-daemon
-
+poly_user_primary_group_set() {
+ _sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
+ /usr/bin/dscl . -create "/Users/$1" "PrimaryGroupID" "$2"
}
+poly_create_build_user() {
+ username=$1
+ uid=$2
+ builder_num=$3
-main() {
- welcome_to_nix
- chat_about_sudo
-
- if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" = "" ]; then
- validate_starting_assumptions
- fi
-
- setup_report
-
- if ! ui_confirm "Ready to continue?"; then
- ok "Alright, no changes have been made :)"
- contactme
- trap finish_cleanup EXIT
- exit 1
- fi
-
- create_build_group
- create_build_users
- create_directories
- place_channel_configuration
- install_from_extracted_nix
-
- configure_shell_profile
-
- set +eu
- . /etc/profile
- set -eu
-
- setup_default_profile
- place_nix_configuration
- configure_nix_daemon_plist
-
- trap finish_success EXIT
+ _sudo "Creating the Nix build user (#$builder_num), $username" \
+ /usr/bin/dscl . create "/Users/$username" \
+ UniqueID "${uid}"
}
-
-
-main
diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh
new file mode 100644
index 000000000..5f6542355
--- /dev/null
+++ b/scripts/install-multi-user.sh
@@ -0,0 +1,797 @@
+#!/usr/bin/env bash
+
+set -eu
+set -o pipefail
+
+# Sourced from:
+# - https://github.com/LnL7/nix-darwin/blob/8c29d0985d74b4a990238497c47a2542a5616b3c/bootstrap.sh
+# - https://gist.github.com/expipiplus1/e571ce88c608a1e83547c918591b149f/ac504c6c1b96e65505fbda437a28ce563408ecb0
+# - https://github.com/NixOS/nixos-org-configurations/blob/a122f418797713d519aadf02e677fce0dc1cb446/delft/scripts/nix-mac-installer.sh
+# - https://github.com/matthewbauer/macNixOS/blob/f6045394f9153edea417be90c216788e754feaba/install-macNixOS.sh
+# - https://gist.github.com/LnL7/9717bd6cdcb30b086fd7f2093e5f8494/86b26f852ce563e973acd30f796a9a416248c34a
+#
+# however tracking which bits came from which would be impossible.
+
+readonly ESC='\033[0m'
+readonly BOLD='\033[38;1m'
+readonly BLUE='\033[38;34m'
+readonly BLUE_UL='\033[38;4;34m'
+readonly GREEN='\033[38;32m'
+readonly GREEN_UL='\033[38;4;32m'
+readonly RED='\033[38;31m'
+readonly RED_UL='\033[38;4;31m'
+readonly YELLOW='\033[38;33m'
+readonly YELLOW_UL='\033[38;4;33m'
+
+readonly NIX_USER_COUNT="32"
+readonly NIX_BUILD_GROUP_ID="30000"
+readonly NIX_BUILD_GROUP_NAME="nixbld"
+readonly NIX_FIRST_BUILD_UID="30001"
+# Please don't change this. We don't support it, because the
+# default shell profile that comes with Nix doesn't support it.
+readonly NIX_ROOT="/nix"
+
+readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshrc")
+readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix"
+readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh"
+
+readonly NIX_INSTALLED_NIX="@nix@"
+readonly NIX_INSTALLED_CACERT="@cacert@"
+readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
+
+readonly ROOT_HOME=$(echo ~root)
+
+if [ -t 0 ]; then
+ readonly IS_HEADLESS='no'
+else
+ readonly IS_HEADLESS='yes'
+fi
+
+headless() {
+ if [ "$IS_HEADLESS" = "yes" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+contactme() {
+ echo "We'd love to help if you need it."
+ echo ""
+ echo "If you can, open an issue at https://github.com/nixos/nix/issues"
+ echo ""
+ echo "Or feel free to contact the team,"
+ echo " - on IRC #nixos on irc.freenode.net"
+ echo " - on twitter @nixos_org"
+}
+
+uninstall_directions() {
+ subheader "Uninstalling nix:"
+ local step=0
+
+ if poly_service_installed_check; then
+ step=$((step + 1))
+ poly_service_uninstall_directions "$step"
+ fi
+
+ for profile_target in "${PROFILE_TARGETS[@]}"; do
+ if [ -e "$profile_target" ] && [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
+ step=$((step + 1))
+ cat <<EOF
+$step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target
+
+ sudo mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
+
+(after this one, you may need to re-open any terminals that were
+opened while it existed.)
+
+EOF
+ fi
+ done
+
+ step=$((step + 1))
+ cat <<EOF
+$step. Delete the files Nix added to your system:
+
+ sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels
+
+and that is it.
+
+EOF
+
+}
+
+nix_user_for_core() {
+ printf "nixbld%d" "$1"
+}
+
+nix_uid_for_core() {
+ echo $((NIX_FIRST_BUILD_UID + $1 - 1))
+}
+
+_textout() {
+ echo -en "$1"
+ shift
+ if [ "$*" = "" ]; then
+ cat
+ else
+ echo "$@"
+ fi
+ echo -en "$ESC"
+}
+
+header() {
+ follow="---------------------------------------------------------"
+ header=$(echo "---- $* $follow$follow$follow" | head -c 80)
+ echo ""
+ _textout "$BLUE" "$header"
+}
+
+warningheader() {
+ follow="---------------------------------------------------------"
+ header=$(echo "---- $* $follow$follow$follow" | head -c 80)
+ echo ""
+ _textout "$RED" "$header"
+}
+
+subheader() {
+ echo ""
+ _textout "$BLUE_UL" "$*"
+}
+
+row() {
+ printf "$BOLD%s$ESC:\\t%s\\n" "$1" "$2"
+}
+
+task() {
+ echo ""
+ ok "~~> $1"
+}
+
+bold() {
+ echo "$BOLD$*$ESC"
+}
+
+ok() {
+ _textout "$GREEN" "$@"
+}
+
+warning() {
+ warningheader "warning!"
+ cat
+ echo ""
+}
+
+failure() {
+ header "oh no!"
+ _textout "$RED" "$@"
+ echo ""
+ _textout "$RED" "$(contactme)"
+ trap finish_cleanup EXIT
+ exit 1
+}
+
+ui_confirm() {
+ _textout "$GREEN$GREEN_UL" "$1"
+
+ if headless; then
+ echo "No TTY, assuming you would say yes :)"
+ return 0
+ fi
+
+ local prompt="[y/n] "
+ echo -n "$prompt"
+ while read -r y; do
+ if [ "$y" = "y" ]; then
+ echo ""
+ return 0
+ elif [ "$y" = "n" ]; then
+ echo ""
+ return 1
+ else
+ _textout "$RED" "Sorry, I didn't understand. I can only understand answers of y or n"
+ echo -n "$prompt"
+ fi
+ done
+ echo ""
+ return 1
+}
+
+__sudo() {
+ local expl="$1"
+ local cmd="$2"
+ shift
+ header "sudo execution"
+
+ echo "I am executing:"
+ echo ""
+ printf " $ sudo %s\\n" "$cmd"
+ echo ""
+ echo "$expl"
+ echo ""
+
+ return 0
+}
+
+_sudo() {
+ local expl="$1"
+ shift
+ if ! headless; then
+ __sudo "$expl" "$*"
+ fi
+ sudo "$@"
+}
+
+
+readonly SCRATCH=$(mktemp -d -t tmp.XXXXXXXXXX)
+function finish_cleanup {
+ rm -rf "$SCRATCH"
+}
+
+function finish_fail {
+ finish_cleanup
+
+ failure <<EOF
+Jeeze, something went wrong. If you can take all the output and open
+an issue, we'd love to fix the problem so nobody else has this issue.
+
+:(
+EOF
+}
+trap finish_fail EXIT
+
+function finish_success {
+ finish_cleanup
+
+ ok "Alright! We're done!"
+ cat <<EOF
+
+Before Nix will work in your existing shells, you'll need to close
+them and open them again. Other than that, you should be ready to go.
+
+Try it! Open a new terminal, and type:
+
+ $ nix-shell -p nix-info --run "nix-info -m"
+
+Thank you for using this installer. If you have any feedback, don't
+hesitate:
+
+$(contactme)
+EOF
+}
+
+
+validate_starting_assumptions() {
+ poly_validate_assumptions
+
+ if [ $EUID -eq 0 ]; then
+ failure <<EOF
+Please do not run this script with root privileges. We will call sudo
+when we need to.
+EOF
+ fi
+
+ if type nix-env 2> /dev/null >&2; then
+ failure <<EOF
+Nix already appears to be installed, and this tool assumes it is
+_not_ yet installed.
+
+$(uninstall_directions)
+EOF
+ fi
+
+ if [ "${NIX_REMOTE:-}" != "" ]; then
+ failure <<EOF
+For some reason, \$NIX_REMOTE is set. It really should not be set
+before this installer runs, and it hints that Nix is currently
+installed. Please delete the old Nix installation and start again.
+
+Note: You might need to close your shell window and open a new shell
+to clear the variable.
+EOF
+ fi
+
+ if echo "${SSL_CERT_FILE:-}" | grep -qE "(nix/var/nix|nix-profile)"; then
+ failure <<EOF
+It looks like \$SSL_CERT_FILE is set to a path that used to be part of
+the old Nix installation. Please unset that variable and try again:
+
+ $ unset SSL_CERT_FILE
+
+EOF
+ fi
+
+ for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do
+ if [ -f "$file" ]; then
+ if grep -l "^[^#].*.nix-profile" "$file"; then
+ failure <<EOF
+I found a reference to a ".nix-profile" in $file.
+This has a high chance of breaking a new nix installation. It was most
+likely put there by a previous Nix installer.
+
+Please remove this reference and try running this again. You should
+also look for similar references in:
+
+ - ~/.bash_profile
+ - ~/.bash_login
+ - ~/.profile
+
+or other shell init files that you may have.
+
+$(uninstall_directions)
+EOF
+ fi
+ fi
+ done
+
+ if [ -d /nix ]; then
+ failure <<EOF
+There are some relics of a previous installation of Nix at /nix, and
+this scripts assumes Nix is _not_ yet installed. Please delete the old
+Nix installation and start again.
+
+$(uninstall_directions)
+EOF
+ fi
+
+ if [ -d /etc/nix ]; then
+ failure <<EOF
+There are some relics of a previous installation of Nix at /etc/nix, and
+this scripts assumes Nix is _not_ yet installed. Please delete the old
+Nix installation and start again.
+
+$(uninstall_directions)
+EOF
+ fi
+
+ for profile_target in "${PROFILE_TARGETS[@]}"; do
+ if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
+ failure <<EOF
+When this script runs, it backs up the current $profile_target to
+$profile_target$PROFILE_BACKUP_SUFFIX. This backup file already exists, though.
+
+Please follow these instructions to clean up the old backup file:
+
+1. Copy $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX to another place, just
+in case.
+
+2. Take care to make sure that $profile_target$PROFILE_BACKUP_SUFFIX doesn't look like
+it has anything nix-related in it. If it does, something is probably
+quite wrong. Please open an issue or get in touch immediately.
+
+3. Take care to make sure that $profile_target doesn't look like it has
+anything nix-related in it. If it does, and $profile_target _did not_,
+run:
+
+ $ /usr/bin/sudo /bin/mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
+
+and try again.
+EOF
+ fi
+
+ if [ -e "$profile_target" ] && grep -qi "nix" "$profile_target"; then
+ failure <<EOF
+It looks like $profile_target already has some Nix configuration in
+there. There should be no reason to run this again. If you're having
+trouble, please open an issue.
+EOF
+ fi
+ done
+
+ danger_paths=("$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.nix-profile")
+ for danger_path in "${danger_paths[@]}"; do
+ if _sudo "making sure that $danger_path doesn't exist" \
+ test -e "$danger_path"; then
+ failure <<EOF
+I found a file at $danger_path, which is a relic of a previous
+installation. You must first delete this file before continuing.
+
+$(uninstall_directions)
+EOF
+ fi
+ done
+}
+
+setup_report() {
+ header "Nix config report"
+ row " Temp Dir" "$SCRATCH"
+ row " Nix Root" "$NIX_ROOT"
+ row " Build Users" "$NIX_USER_COUNT"
+ row " Build Group ID" "$NIX_BUILD_GROUP_ID"
+ row "Build Group Name" "$NIX_BUILD_GROUP_NAME"
+ if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" != "" ]; then
+ row "Preexisting Install" "Allowed"
+ fi
+
+ subheader "build users:"
+
+ row " Username" "UID"
+ for i in $(seq 1 "$NIX_USER_COUNT"); do
+ row " $(nix_user_for_core "$i")" "$(nix_uid_for_core "$i")"
+ done
+ echo ""
+}
+
+create_build_group() {
+ local primary_group_id
+
+ task "Setting up the build group $NIX_BUILD_GROUP_NAME"
+ if ! poly_group_exists "$NIX_BUILD_GROUP_NAME"; then
+ poly_create_build_group
+ row " Created" "Yes"
+ else
+ primary_group_id=$(poly_group_id_get "$NIX_BUILD_GROUP_NAME")
+ if [ "$primary_group_id" -ne "$NIX_BUILD_GROUP_ID" ]; then
+ failure <<EOF
+It seems the build group $NIX_BUILD_GROUP_NAME already exists, but
+with the UID $primary_group_id. This script can't really handle
+that right now, so I'm going to give up.
+
+You can fix this by editing this script and changing the
+NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID
+to $primary_group_id and re-run.
+EOF
+ else
+ row " Exists" "Yes"
+ fi
+ fi
+}
+
+create_build_user_for_core() {
+ local coreid
+ local username
+ local uid
+
+ coreid="$1"
+ username=$(nix_user_for_core "$coreid")
+ uid=$(nix_uid_for_core "$coreid")
+
+ task "Setting up the build user $username"
+
+ if ! poly_user_exists "$username"; then
+ poly_create_build_user "$username" "$uid" "$coreid"
+ row " Created" "Yes"
+ else
+ actual_uid=$(poly_user_id_get "$username")
+ if [ "$actual_uid" != "$uid" ]; then
+ failure <<EOF
+It seems the build user $username already exists, but with the UID
+with the UID '$actual_uid'. This script can't really handle that right
+now, so I'm going to give up.
+
+If you already created the users and you know they start from
+$actual_uid and go up from there, you can edit this script and change
+NIX_FIRST_BUILD_UID near the top of the file to $actual_uid and try
+again.
+EOF
+ else
+ row " Exists" "Yes"
+ fi
+ fi
+
+ if [ "$(poly_user_hidden_get "$username")" = "1" ]; then
+ row " Hidden" "Yes"
+ else
+ poly_user_hidden_set "$username"
+ row " Hidden" "Yes"
+ fi
+
+ if [ "$(poly_user_home_get "$username")" = "/var/empty" ]; then
+ row " Home Directory" "/var/empty"
+ else
+ poly_user_home_set "$username" "/var/empty"
+ row " Home Directory" "/var/empty"
+ fi
+
+ # We use grep instead of an equality check because it is difficult
+ # to extract _just_ the user's note, instead it is prefixed with
+ # some plist junk. This was causing the user note to always be set,
+ # even if there was no reason for it.
+ if ! poly_user_note_get "$username" | grep -q "Nix build user $coreid"; then
+ row " Note" "Nix build user $coreid"
+ else
+ poly_user_note_set "$username" "Nix build user $coreid"
+ row " Note" "Nix build user $coreid"
+ fi
+
+ if [ "$(poly_user_shell_get "$username")" = "/sbin/nologin" ]; then
+ row " Logins Disabled" "Yes"
+ else
+ poly_user_shell_set "$username" "/sbin/nologin"
+ row " Logins Disabled" "Yes"
+ fi
+
+ if poly_user_in_group_check "$username" "$NIX_BUILD_GROUP_NAME"; then
+ row " Member of $NIX_BUILD_GROUP_NAME" "Yes"
+ else
+ poly_user_in_group_set "$username" "$NIX_BUILD_GROUP_NAME"
+ row " Member of $NIX_BUILD_GROUP_NAME" "Yes"
+ fi
+
+ if [ "$(poly_user_primary_group_get "$username")" = "$NIX_BUILD_GROUP_ID" ]; then
+ row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
+ else
+ poly_user_primary_group_set "$username" "$NIX_BUILD_GROUP_ID"
+ row " PrimaryGroupID" "$NIX_BUILD_GROUP_ID"
+ fi
+}
+
+create_build_users() {
+ for i in $(seq 1 "$NIX_USER_COUNT"); do
+ create_build_user_for_core "$i"
+ done
+}
+
+create_directories() {
+ _sudo "to make the basic directory structure of Nix (part 1)" \
+ mkdir -pv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool}
+
+ _sudo "to make the basic directory structure of Nix (part 2)" \
+ mkdir -pv -m 1777 /nix/var/nix/{gcroots,profiles}/per-user
+
+ _sudo "to make the basic directory structure of Nix (part 3)" \
+ mkdir -pv -m 1775 /nix/store
+
+ _sudo "to make the basic directory structure of Nix (part 4)" \
+ chgrp "$NIX_BUILD_GROUP_NAME" /nix/store
+
+ _sudo "to set up the root user's profile (part 1)" \
+ mkdir -pv -m 0755 /nix/var/nix/profiles/per-user/root
+
+ _sudo "to set up the root user's profile (part 2)" \
+ mkdir -pv -m 0700 "$ROOT_HOME/.nix-defexpr"
+
+ _sudo "to place the default nix daemon configuration (part 1)" \
+ mkdir -pv -m 0555 /etc/nix
+}
+
+place_channel_configuration() {
+ echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels"
+ _sudo "to set up the default system channel (part 1)" \
+ install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels"
+}
+
+welcome_to_nix() {
+ ok "Welcome to the Multi-User Nix Installation"
+
+ cat <<EOF
+
+This installation tool will set up your computer with the Nix package
+manager. This will happen in a few stages:
+
+1. Make sure your computer doesn't already have Nix. If it does, I
+ will show you instructions on how to clean up your old one.
+
+2. Show you what we are going to install and where. Then we will ask
+ if you are ready to continue.
+
+3. Create the system users and groups that the Nix daemon uses to run
+ builds.
+
+4. Perform the basic installation of the Nix files daemon.
+
+5. Configure your shell to import special Nix Profile files, so you
+ can use Nix.
+
+6. Start the Nix daemon.
+
+EOF
+
+ if ui_confirm "Would you like to see a more detailed list of what we will do?"; then
+ cat <<EOF
+
+We will:
+
+ - make sure your computer doesn't already have Nix files
+ (if it does, I will tell you how to clean them up.)
+ - create local users (see the list above for the users we'll make)
+ - create a local group ($NIX_BUILD_GROUP_NAME)
+ - install Nix in to $NIX_ROOT
+ - create a configuration file in /etc/nix
+ - set up the "default profile" by creating some Nix-related files in
+ $ROOT_HOME
+EOF
+ for profile_target in "${PROFILE_TARGETS[@]}"; do
+ if [ -e "$profile_target" ]; then
+ cat <<EOF
+ - back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX
+ - update $profile_target to include some Nix configuration
+EOF
+ fi
+ done
+ poly_service_setup_note
+ if ! ui_confirm "Ready to continue?"; then
+ failure <<EOF
+Okay, maybe you would like to talk to the team.
+EOF
+ fi
+ fi
+}
+
+chat_about_sudo() {
+ header "let's talk about sudo"
+
+ if headless; then
+ cat <<EOF
+This script is going to call sudo a lot. Normally, it would show you
+exactly what commands it is running and why. However, the script is
+run in a headless fashion, like this:
+
+ $ curl https://nixos.org/nix/install | sh
+
+or maybe in a CI pipeline. Because of that, we're going to skip the
+verbose output in the interest of brevity.
+
+If you would like to
+see the output, try like this:
+
+ $ curl -o install-nix https://nixos.org/nix/install
+ $ sh ./install-nix
+
+EOF
+ return 0
+ fi
+
+ cat <<EOF
+This script is going to call sudo a lot. Every time we do, it'll
+output exactly what it'll do, and why.
+
+Just like this:
+EOF
+
+ __sudo "to demonstrate how our sudo prompts look" \
+ echo "this is a sudo prompt"
+
+ cat <<EOF
+
+This might look scary, but everything can be undone by running just a
+few commands. We used to ask you to confirm each time sudo ran, but it
+was too many times. Instead, I'll just ask you this one time:
+
+EOF
+ if ui_confirm "Can we use sudo?"; then
+ ok "Yay! Thanks! Let's get going!"
+ else
+ failure <<EOF
+That is okay, but we can't install.
+EOF
+ fi
+}
+
+install_from_extracted_nix() {
+ (
+ cd "$EXTRACTED_NIX_PATH"
+
+ _sudo "to copy the basic Nix files to the new store at $NIX_ROOT/store" \
+ rsync -rlpt ./store/* "$NIX_ROOT/store/"
+
+ if [ -d "$NIX_INSTALLED_NIX" ]; then
+ echo " Alright! We have our first nix at $NIX_INSTALLED_NIX"
+ else
+ failure <<EOF
+Something went wrong, and I didn't find Nix installed at
+$NIX_INSTALLED_NIX.
+EOF
+ fi
+
+ _sudo "to initialize the Nix Database" \
+ $NIX_INSTALLED_NIX/bin/nix-store --init
+
+ cat ./.reginfo \
+ | _sudo "to load data for the first time in to the Nix Database" \
+ "$NIX_INSTALLED_NIX/bin/nix-store" --load-db
+
+ echo " Just finished getting the nix database ready."
+ )
+}
+
+shell_source_lines() {
+ cat <<EOF
+
+# Nix
+if [ -e '$PROFILE_NIX_FILE' ]; then
+ . '$PROFILE_NIX_FILE'
+fi
+# End Nix
+
+EOF
+}
+
+configure_shell_profile() {
+ # If there is an /etc/profile.d directory, we want to ensure there
+ # is a nix.sh within it, so we can use the following loop to add
+ # the source lines to it. Note that I'm _not_ adding the source
+ # lines here, because we want to be using the regular machinery.
+ #
+ # If we go around that machinery, it becomes more complicated and
+ # adds complications to the uninstall instruction generator and
+ # old instruction sniffer as well.
+ if [ -d /etc/profile.d ]; then
+ _sudo "create a stub /etc/profile.d/nix.sh which will be updated" \
+ touch /etc/profile.d/nix.sh
+ fi
+
+ for profile_target in "${PROFILE_TARGETS[@]}"; do
+ if [ -e "$profile_target" ]; then
+ _sudo "to back up your current $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX" \
+ cp "$profile_target" "$profile_target$PROFILE_BACKUP_SUFFIX"
+
+ shell_source_lines \
+ | _sudo "extend your $profile_target with nix-daemon settings" \
+ tee -a "$profile_target"
+ fi
+ done
+}
+
+setup_default_profile() {
+ _sudo "to installing a bootstrapping Nix in to the default Profile" \
+ HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_NIX"
+
+ _sudo "to installing a bootstrapping SSL certificate just for Nix in to the default Profile" \
+ HOME="$ROOT_HOME" "$NIX_INSTALLED_NIX/bin/nix-env" -i "$NIX_INSTALLED_CACERT"
+
+ _sudo "to update the default channel in the default profile" \
+ HOME="$ROOT_HOME" NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt "$NIX_INSTALLED_NIX/bin/nix-channel" --update nixpkgs
+}
+
+
+place_nix_configuration() {
+ cat <<EOF > "$SCRATCH/nix.conf"
+build-users-group = $NIX_BUILD_GROUP_NAME
+
+max-jobs = $NIX_USER_COUNT
+cores = 1
+sandbox = false
+EOF
+ _sudo "to place the default nix daemon configuration (part 2)" \
+ install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf
+}
+
+main() {
+ if [ "$(uname -s)" = "Darwin" ]; then
+ # shellcheck source=./install-darwin-multi-user.sh
+ . "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh"
+ elif [ "$(uname -s)" = "Linux" ] && [ -e /run/systemd/system ]; then
+ # shellcheck source=./install-systemd-multi-user.sh
+ . "$EXTRACTED_NIX_PATH/install-systemd-multi-user.sh"
+ else
+ failure "Sorry, I don't know what to do on $(uname)"
+ fi
+
+ welcome_to_nix
+ chat_about_sudo
+
+ if [ "${ALLOW_PREEXISTING_INSTALLATION:-}" = "" ]; then
+ validate_starting_assumptions
+ fi
+
+ setup_report
+
+ if ! ui_confirm "Ready to continue?"; then
+ ok "Alright, no changes have been made :)"
+ contactme
+ trap finish_cleanup EXIT
+ exit 1
+ fi
+
+ create_build_group
+ create_build_users
+ create_directories
+ place_channel_configuration
+ install_from_extracted_nix
+
+ configure_shell_profile
+
+ set +eu
+ . /etc/profile
+ set -eu
+
+ setup_default_profile
+ place_nix_configuration
+ poly_configure_nix_daemon_service
+
+ trap finish_success EXIT
+}
+
+
+main
diff --git a/scripts/install-nix-from-closure.sh b/scripts/install-nix-from-closure.sh
index 3e5676f41..8416fb967 100644
--- a/scripts/install-nix-from-closure.sh
+++ b/scripts/install-nix-from-closure.sh
@@ -29,8 +29,15 @@ if [ "$(uname -s)" = "Darwin" ]; then
exit 1
fi
- printf '\e[1;31mSwitching to the Multi-User Darwin Installer\e[0m\n'
- exec "$self/install-darwin-multi-user"
+ printf '\e[1;31mSwitching to the Daemon-based Installer\e[0m\n'
+ exec "$self/install-multi-user"
+ exit 0
+fi
+
+# Linux & Systemd support
+if [ "$(uname -s)" = "Linux" ] && [ -e /run/systemd/system ]; then
+ printf '\e[1;31mSwitching to the Daemon-based Installer\e[0m\n'
+ exec "$self/install-multi-user"
exit 0
fi
diff --git a/scripts/install-systemd-multi-user.sh b/scripts/install-systemd-multi-user.sh
new file mode 100644
index 000000000..04bc539a1
--- /dev/null
+++ b/scripts/install-systemd-multi-user.sh
@@ -0,0 +1,154 @@
+#!/usr/bin/env bash
+
+set -eu
+set -o pipefail
+
+readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service
+readonly SERVICE_DEST=/etc/systemd/system/nix-daemon.service
+
+readonly SOCKET_SRC=/lib/systemd/system/nix-daemon.socket
+readonly SOCKET_DEST=/etc/systemd/system/nix-daemon.socket
+
+poly_validate_assumptions() {
+ if [ "$(uname -s)" != "Linux" ]; then
+ failure "This script is for use with Linux!"
+ fi
+}
+
+poly_service_installed_check() {
+ [ "$(systemctl is-enabled nix-daemon.service)" = "linked" ] \
+ || [ "$(systemctl is-enabled nix-daemon.socket)" = "enabled" ]
+}
+
+poly_service_uninstall_directions() {
+ cat <<EOF
+$1. Delete the systemd service and socket units
+
+ sudo systemctl stop nix-daemon.socket
+ sudo systemctl stop nix-daemon.service
+ sudo systemctl disable nix-daemon.socket
+ sudo systemctl disable nix-daemon.service
+ sudo systemctl daemon-reload
+EOF
+}
+
+poly_service_setup_note() {
+ cat <<EOF
+ - load and start a service (at $SERVICE_DEST
+ and $SOCKET_DEST) for nix-daemon
+
+EOF
+}
+
+poly_configure_nix_daemon_service() {
+ _sudo "to set up the nix-daemon service" \
+ systemctl link "/nix/var/nix/profiles/default$SERVICE_SRC"
+
+ _sudo "to set up the nix-daemon socket service" \
+ systemctl enable "/nix/var/nix/profiles/default$SOCKET_SRC"
+
+ _sudo "to load the systemd unit for nix-daemon" \
+ systemctl daemon-reload
+
+ _sudo "to start the nix-daemon.socket" \
+ systemctl start nix-daemon.socket
+
+ _sudo "to start the nix-daemon.service" \
+ systemctl start nix-daemon.service
+
+}
+
+poly_group_exists() {
+ getent group "$1" > /dev/null 2>&1
+}
+
+poly_group_id_get() {
+ getent group "$1" | cut -d: -f3
+}
+
+poly_create_build_group() {
+ _sudo "Create the Nix build group, $NIX_BUILD_GROUP_NAME" \
+ groupadd -g "$NIX_BUILD_GROUP_ID" --system \
+ "$NIX_BUILD_GROUP_NAME" >&2
+}
+
+poly_user_exists() {
+ getent passwd "$1" > /dev/null 2>&1
+}
+
+poly_user_id_get() {
+ getent passwd "$1" | cut -d: -f3
+}
+
+poly_user_hidden_get() {
+ echo "1"
+}
+
+poly_user_hidden_set() {
+ true
+}
+
+poly_user_home_get() {
+ getent passwd "$1" | cut -d: -f6
+}
+
+poly_user_home_set() {
+ _sudo "in order to give $1 a safe home directory" \
+ usermod --home "$2" "$1"
+}
+
+poly_user_note_get() {
+ getent passwd "$1" | cut -d: -f5
+}
+
+poly_user_note_set() {
+ _sudo "in order to give $1 a useful comment" \
+ usermod --comment "$2" "$1"
+}
+
+poly_user_shell_get() {
+ getent passwd "$1" | cut -d: -f7
+}
+
+poly_user_shell_set() {
+ _sudo "in order to prevent $1 from logging in" \
+ usermod --shell "$2" "$1"
+}
+
+poly_user_in_group_check() {
+ groups "$1" | grep -q "$2" > /dev/null 2>&1
+}
+
+poly_user_in_group_set() {
+ _sudo "Add $1 to the $2 group"\
+ usermod --append --groups "$2" "$1"
+}
+
+poly_user_primary_group_get() {
+ getent passwd "$1" | cut -d: -f4
+}
+
+poly_user_primary_group_set() {
+ _sudo "to let the nix daemon use this user for builds (this might seem redundant, but there are two concepts of group membership)" \
+ usermod --gid "$2" "$1"
+
+}
+
+poly_create_build_user() {
+ username=$1
+ uid=$2
+ builder_num=$3
+
+ _sudo "Creating the Nix build user, $username" \
+ useradd \
+ --home-dir /var/empty \
+ --comment "Nix build user $builder_num" \
+ --gid "$NIX_BUILD_GROUP_ID" \
+ --groups "$NIX_BUILD_GROUP_NAME" \
+ --no-user-group \
+ --system \
+ --shell /sbin/nologin \
+ --uid "$uid" \
+ --password "!" \
+ "$username"
+}