Skip to content

Commit 6accf86

Browse files
authored
Merge pull request #11075 from abathur/macos_sequoia_proto_migration_script
add UID migration script for macOS Sequoia 15
2 parents ce4e4a1 + 0fabb34 commit 6accf86

File tree

2 files changed

+165
-2
lines changed

2 files changed

+165
-2
lines changed

scripts/bigsur-nixbld-user-migration.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
((NEW_NIX_FIRST_BUILD_UID=301))
44

5-
id_available(){
5+
id_unavailable(){
66
dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null
77
}
88

@@ -15,7 +15,7 @@ change_nixbld_names_and_ids(){
1515
while read -r name uid; do
1616
echo " Checking $name (uid: $uid)"
1717
# iterate for a clean ID
18-
while id_available "$next_id"; do
18+
while id_unavailable "$next_id"; do
1919
((next_id++))
2020
if ((next_id >= 400)); then
2121
echo "We've hit UID 400 without placing all of your users :("
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#!/usr/bin/env bash
2+
3+
set -x
4+
5+
((NEW_NIX_FIRST_BUILD_UID=350))
6+
((TEMP_NIX_FIRST_BUILD_UID=31000))
7+
8+
nix_user_n() {
9+
printf "_nixbld%d" "$1"
10+
}
11+
12+
id_unavailable(){
13+
dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null
14+
}
15+
16+
any_nixbld(){
17+
dscl . list /Users UniqueID | grep -E '\b_nixbld' >/dev/null
18+
}
19+
20+
re_create_nixbld_user(){
21+
local name uid
22+
23+
name="$1"
24+
uid="$2"
25+
26+
sudo /usr/bin/dscl . -create "/Users/$name" "UniqueID" "$uid"
27+
sudo /usr/bin/dscl . -create "/Users/$name" "IsHidden" "1"
28+
sudo /usr/bin/dscl . -create "/Users/$name" "NFSHomeDirectory" "/var/empty"
29+
sudo /usr/bin/dscl . -create "/Users/$name" "RealName" "Nix build user $name"
30+
sudo /usr/bin/dscl . -create "/Users/$name" "UserShell" "/sbin/nologin"
31+
sudo /usr/bin/dscl . -create "/Users/$name" "PrimaryGroupID" "30001"
32+
}
33+
34+
hit_id_cap(){
35+
echo "We've hit UID 400 without placing all of your users :("
36+
echo "You should use the commands in this script as a starting"
37+
echo "point to review your UID-space and manually move the"
38+
echo "remaining users (or delete them, if you don't need them)."
39+
}
40+
41+
# evacuate the role-uid space to simplify final placement logic
42+
temporarily_move_existing_nixbld_uids(){
43+
local name uid next_id user_n
44+
45+
((next_id=TEMP_NIX_FIRST_BUILD_UID))
46+
47+
echo ""
48+
echo "Step 1: move existing _nixbld users out of the destination UID range."
49+
50+
while read -r name uid; do
51+
# iterate for a clean ID
52+
while id_unavailable "$next_id"; do
53+
((next_id++))
54+
# We really want to get these all placed, but I guess there's
55+
# some risk we iterate forever--so we'll give up after 9k uids.
56+
if ((next_id >= 40000)); then
57+
echo "We've hit UID 40000 without temporarily placing all of your users :("
58+
echo "You should use the commands in this script as a starting"
59+
echo "point to review your UID-space and manually move the"
60+
echo "remaining users to any open UID over 1000."
61+
exit 1
62+
fi
63+
done
64+
sudo dscl . -create "/Users/$name" UniqueID "$next_id"
65+
echo " Temporarily moved $name from uid $uid -> $next_id"
66+
67+
done < <(dscl . list /Users UniqueID | grep _nixbld | sort -n -k2)
68+
}
69+
70+
change_nixbld_uids(){
71+
local name next_id user_n
72+
73+
((next_id=NEW_NIX_FIRST_BUILD_UID))
74+
((user_n=1))
75+
name="$(nix_user_n "$user_n")"
76+
77+
# we know that we have *some* nixbld users, but macOS may have
78+
# already clobbered the first few users if this system has been
79+
# upgraded
80+
81+
echo ""
82+
echo "Step 2: re-create missing early _nixbld# users."
83+
84+
until dscl . read "/Users/$name" &>/dev/null; do
85+
# iterate for a clean ID
86+
while id_unavailable "$next_id"; do
87+
((next_id++))
88+
if ((next_id >= 400)); then
89+
hit_id_cap
90+
exit 1
91+
fi
92+
done
93+
94+
re_create_nixbld_user "$name" "$next_id"
95+
echo " $name was missing; created with uid: $next_id"
96+
97+
((user_n++))
98+
name="$(nix_user_n "$user_n")"
99+
done
100+
101+
echo ""
102+
echo "Step 3: relocate remaining _nixbld# UIDs to $next_id+"
103+
104+
# start at first _nixbld# not re-created above and increment
105+
# until _nixbld<n> doesn't exist
106+
while dscl . read "/Users/$name" &>/dev/null; do
107+
# iterate for a clean ID
108+
while id_unavailable "$next_id"; do
109+
((next_id++))
110+
if ((next_id >= 400)); then
111+
hit_id_cap
112+
exit 1
113+
fi
114+
done
115+
116+
sudo dscl . -create "/Users/$name" UniqueID "$next_id"
117+
echo " $name migrated to uid: $next_id"
118+
119+
((user_n++))
120+
name="$(nix_user_n "$user_n")"
121+
done
122+
123+
if ((user_n == 1)); then
124+
echo "Didn't find _nixbld1. Perhaps you have single-user Nix?"
125+
exit 1
126+
else
127+
echo "Migrated $((user_n - 1)) users. If you want to double-check, try:"
128+
echo "dscl . list /Users UniqueID | grep _nixbld | sort -n -k2"
129+
fi
130+
}
131+
needs_migration(){
132+
local name uid next_id user_n
133+
134+
((next_id=NEW_NIX_FIRST_BUILD_UID))
135+
((user_n=1))
136+
137+
while read -r name uid; do
138+
expected_name="$(nix_user_n "$user_n")"
139+
if [[ "$expected_name" != "$name" ]]; then
140+
return 0
141+
fi
142+
if [[ "$next_id" != "$uid" ]]; then
143+
return 0
144+
fi
145+
((next_id++))
146+
((user_n++))
147+
done < <(dscl . list /Users UniqueID | grep _nixbld | sort -n -k2)
148+
return 1
149+
}
150+
151+
152+
if any_nixbld; then
153+
if needs_migration; then
154+
echo "Attempting to migrate _nixbld users."
155+
temporarily_move_existing_nixbld_uids
156+
change_nixbld_uids
157+
else
158+
echo "_nixbld users already appear to be migrated."
159+
fi
160+
else
161+
echo "Didn't find any _nixbld users. Perhaps you have single-user Nix?"
162+
exit 1
163+
fi

0 commit comments

Comments
 (0)