Skip to content

Commit

Permalink
add script to migrate macOS 15 Sequoia nixbld UIDs
Browse files Browse the repository at this point in the history
While we don't have any easy way to forcibly notify everyone about the
impending breakage (or forcibly migrate the users on their system),
this script enables those who do hear about the problem to migrate
their systems before they take the macOS update.

It should also enable people who only discover it after the update
when a build fails to ~fix their installs without a full reinstall.
  • Loading branch information
abathur committed Jul 31, 2024
1 parent caabdb0 commit de7b485
Showing 1 changed file with 92 additions and 17 deletions.
109 changes: 92 additions & 17 deletions scripts/sequoia-nixbld-user-migration.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,110 @@

((NEW_NIX_FIRST_BUILD_UID=331))

id_available(){
dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null
nix_user_n() {
printf "_nixbld%d" "$1"
}

change_nixbld_names_and_ids(){
local name uid next_id
id_unavailable(){
# treat UID as unavailable if a non-nixbld user is using it
# - list all users + UIDs
# - filter out all _nixbld\d+ users
# - check against remaining
dscl . list /Users UniqueID | grep -vE '\b_nixbld[0-9]+\b' | grep -E '\b'"$1"'\b' >/dev/null
}

any_nixbld(){
dscl . list /Users UniqueID | grep -E '\b_nixbld' >/dev/null
}

re_create_nixbld_user(){
local name uid

name="$1"
uid="$2"

sudo /usr/bin/dscl . -create "/Users/$name" UniqueID "$uid"
sudo /usr/bin/dscl . -create "/Users/$name" "IsHidden" "1"
sudo /usr/bin/dscl . -create "/Users/$name" "NFSHomeDirectory" "/var/empty"
sudo /usr/bin/dscl . -create "/Users/$name" "RealName" "Nix build user $uid"
sudo /usr/bin/dscl . -create "/Users/$name" "UserShell" "/sbin/nologin"
sudo /usr/bin/dscl . -create "/Users/$name" "PrimaryGroupID" "$NEW_NIX_FIRST_BUILD_UID"
}

hit_id_cap(){
echo "We've hit UID 400 without placing all of your users :("
echo "You should use the commands in this script as a starting"
echo "point to review your UID-space and manually move the"
echo "remaining users (or delete them, if you don't need them)."
}

change_nixbld_uids(){
local name next_id user_n

((next_id=NEW_NIX_FIRST_BUILD_UID))
((user_n=1))
name="$(nix_user_n "$user_n")"

echo "Attempting to migrate _nixbld users."

echo "Migrating nixbld group to GID $NEW_NIX_FIRST_BUILD_UID"
sudo dscl . -create "/Groups/nixbld" PrimaryGroupID "$NEW_NIX_FIRST_BUILD_UID"

# we know that we have *some* nixbld users, but macOS may have
# already clobbered the first few users if this system has been
# upgraded

echo "Checking first for missing early users."

until dscl . read "/Users/$name" &>/dev/null; do
# iterate for a clean ID
while id_unavailable "$next_id"; do
((next_id++))
if ((next_id >= 400)); then
hit_id_cap
exit 1
fi
done

re_create_nixbld_user "$name" "$next_id"
echo " $name was missing; created with uid: $next_id"

((user_n++))
name="$(nix_user_n "$user_n")"
done

echo "Each _nixbld# user should have its UID moved to $next_id+"
while read -r name uid; do
echo " Checking $name (uid: $uid)"

# start at _nixbld1 and increment until _nixbld<n> doesn't exist
while dscl . read "/Users/$name" &>/dev/null; do
# iterate for a clean ID
while id_available "$next_id"; do
while id_unavailable "$next_id"; do
((next_id++))
if ((next_id >= 400)); then
echo "We've hit UID 400 without placing all of your users :("
echo "You should use the commands in this script as a starting"
echo "point to review your UID-space and manually move the"
echo "remaining users (or delete them, if you don't need them)."
hit_id_cap
exit 1
fi
done

# first 2 are cleanup, it's OK if they aren't here
sudo dscl . delete "/Users/$name" dsAttrTypeNative:_writers_passwd &>/dev/null || true
sudo dscl . change "/Users/$name" NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true
sudo dscl . change "/Users/$name" UniqueID "$uid" "$next_id"
sudo dscl . -create "/Users/$name" UniqueID "$next_id"
echo " $name migrated to uid: $next_id"
done < <(dscl . list /Users UniqueID | grep _nixbld | sort -n -k2)

((user_n++))
name="$(nix_user_n "$user_n")"
done

if ((user_n == 1)); then
echo "Didn't find _nixbld1. Perhaps you have single-user Nix?"
exit 1
else
echo "Migrated $((user_n - 1)) users. If you want to double-check, try:"
echo "dscl . list /Users UniqueID | grep _nixbld | sort -n -k2"
fi
}

change_nixbld_names_and_ids
if any_nixbld; then
change_nixbld_uids
else
echo "Didn't find any _nixbld users. Perhaps you have single-user Nix?"
exit 1
fi

0 comments on commit de7b485

Please sign in to comment.