-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathschroot
executable file
·237 lines (214 loc) · 6 KB
/
schroot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#!/bin/bash
# vi:sw=2:ts=2:noexpandtab:nowrap
chroot=
chrootsu=1
chrootuser= # autodetect
chrootuser_grant_sudo=1
chrootshell="$SHELL"
chroot_pre_cmd=
# In bash style, we do some simple transformations for other shells
chrootPS1='\[[1;36m\][chroot]\[[0m\]\w \[[36m\]\$\[[0m\] '
chrootPS1root='\[[1;31m\][chroot]\[[0m\]\w \[[31m\]#\[[0m\] '
bindmounts="/dev /sys /proc /home"
tmpmounts="/tmp"
bindroot="/orig_root"
copyfiles="/etc/resolv.conf /etc/hostname"
# Ensure the correct ~/.schrootrc is expanded after sudo $0:
[ -z "$SCHROOTRC" ] && export SCHROOTRC=~/.schrootrc
if [ -f "$SCHROOTRC" ]; then
. "$SCHROOTRC"
fi
warn()
{
echo "[1;33m$@[0m"
}
die()
{
echo "[1;31m$@[0m"
exit 1
}
die_if_root()
{
if [ `id -u` == 0 ]; then
die "$@"
else
warn "$@"
echo "Continuing anyway in case of a false-negative caused by insufficient permissions since we aren't root yet..."
fi
}
usage()
{
echo "Usage: $0 [-r] [-s shell] [chroot]"
exit 1
}
parse_args()
{
while [ $# -ge 1 ]; do
case "$1" in
'-r') chrootsu=0 ;;
'-s') chrootshell="$2";shift;;
*) break
esac
shift
done
if [ -n "$1" ]; then
chroot="$1"; shift
fi
[ $# -ge 1 ] && usage
}
check_params()
{
[ -z "$chroot" ] && die No chroot specified
[ "`readlink -f "$chroot"`" = "/" ] && die Attempted to chroot to /
[ -d "$chroot" ] || die_if_root chroot target is not a directory
[ -x "$chroot$chrootshell" ] || die_if_root "$chrootshell not found or not executable inside chroot"
}
become_root()
{
if [ `id -u` != 0 ]; then
echo Becomming root...
if ( sudo -E 2>&1 | grep 'illegal option' > /dev/null ); then
sudo `readlink -f "$0"` "$@"
else
sudo -E `readlink -f "$0"` "$@"
fi
exit
fi
}
setup_user()
{
if [ "$chrootsu" != "1" ]; then
chrootuser=
return
fi
if [ -z "$chrootuser" ]; then
if [ -n "$SUDO_USER" ]; then
chrootuser="$SUDO_USER"
else
echo SUDO_USER not set - remaining as root in chroot
fi
fi
echo Setting up "$chrootuser" in chroot...
tmp=`mktemp /tmp/tmp.XXXXXXXXXX`
for f in /etc/passwd /etc/shadow; do
if [ ! -f "$f" -o ! -f "$chroot$f" ]; then
echo "Skipping update to $chroot$f - file does not exist"
continue
fi
echo "Updating $chrootuser in $chroot$f"
grep -v "^$chrootuser:" "$chroot$f" > "$tmp"
grep "^$chrootuser:" "$f" | cat "$tmp" - > "$chroot$f"
done
if [ $chrootuser_grant_sudo -eq 1 ]; then
if [ -f "/etc/sudoers" -a -f "$chroot/etc/sudoers" ]; then
echo "Granting $chrootuser sudo access in chroot"
grep -v "^$chrootuser ALL=(ALL) ALL$" "$chroot/etc/sudoers" > "$tmp"
echo "$chrootuser ALL=(ALL) ALL" | cat "$tmp" - > "$chroot/etc/sudoers"
else
echo "Skipping update to /etc/sudoers - file does not exist"
fi
fi
rm "$tmp"
}
chrootinuse()
{
ls -l /proc/*/root|grep "\s$chroot$" >/dev/null
}
setup()
{
chrootinuse && return
if mount | grep "$chroot$bindroot"; then
echo Stale chroot environment detected, cleaning up...
cleanup
fi
echo Setting up chroot environment...
if [ -n "$bindroot" ]; then
echo "Binding / to $chroot$bindroot..."
mkdir -p "$chroot$bindroot" 2>/dev/null
mount --rbind / "$chroot$bindroot"
fi
for mountpoint in $bindmounts; do
echo "Binding $mountpoint to $chroot$mountpoint..."
mkdir -p "$chroot$mountpoint" 2>/dev/null
mount --rbind "$mountpoint" "$chroot$mountpoint" || die "Failed to bind mount $mountpoint"
done
for mountpoint in $tmpmounts; do
echo "Mounting tmpfs on $chroot$mountpoint..."
mkdir -p "$chroot$mountpoint" 2>/dev/null
mount -t tmpfs nodev "$chroot$mountpoint" || die "Failed to mount $mountpoint"
done
for file in $copyfiles; do
echo "Copying $file into $chroot$file..."
cp -f "$file" "$chroot$file" || die "Failed to copy $file"
done
echo "Updating $chroot/etc/mtab..."
rm $chroot/etc/mtab
sub_bindroot=$(echo $bindroot | sed 's/\//\\\//g')
sub_chroot=$(echo $chroot | sed 's/\//\\\//g')
sed "s/^\(\S*\) \(\S*\) /\1 $sub_bindroot\2 /g; s/^\(\S*\) $sub_bindroot$sub_chroot/\1 /" </proc/mounts > "$chroot/etc/mtab" || die "Failed to update $chroot/etc/mtab"
echo "Updating $chroot/etc/hosts entry for $(hostname)..."
tmp=`mktemp /tmp/tmp.XXXXXXXXXX`
grep -v "^127.0.0.1 $(hostname)$" "$chroot/etc/hosts" > "$tmp"
echo "127.0.0.1 $(hostname)" | cat "$tmp" - > "$chroot/etc/hosts"
rm $tmp
}
cleanup()
{
if chrootinuse; then
echo chroot still in use, not cleaning up.
return
fi
echo "Cleaning up chroot environment..."
for mountpoint in $(echo $bindmounts $tmpmounts $bindroot | tr ' ' '\n' | tac); do
echo "Unmounting $chroot$mountpoint..."
umount -l "$chroot$mountpoint" || warn "Failed to unmount $mountpoint"
# In case it has been mounted multiple times:
while umount -l "$chroot$mountpoint" 2>/dev/null;do true;done
done
}
_sanitise_PS1_other()
{
# - Remove the '\[' and '\]' bashisms
# - Replace '\w' with '$PWD'
chrootPS1=$(echo "$chrootPS1" | sed 's/\\\[//g; s/\\\]//g; s/\\w/$PWD/g')
chrootPS1root=$(echo "$chrootPS1root" | sed 's/\\\[//g; s/\\\]//g; s/\\w/$PWD/g')
}
_sanitise_PS1_zsh()
{
# NOTE: Largely untested
# - Replace '\[' and '\]' with zsh equivalents '%{' and '%}'
# - Replace '\w' with '%~'
chrootPS1=$(echo "$chrootPS1" | sed 's/\\\[/%{/g; s/\\\]/%}/g; s/\\w/%~/g')
chrootPS1root=$(echo "$chrootPS1root" | sed 's/\\\[/%{/g; s/\\\]/%}/g; s/\\w/%~/g')
}
sanitise_PS1()
{
s=$(basename $(readlink -f "$chroot$chrootshell"))
case "$s" in
'zsh') _sanitise_PS1_zsh ;;
'bash') ;; # Do nothing - strings are already in bash format
*) _sanitise_PS1_other;;
esac
}
enter_chroot()
{
if [ -n "$chrootuser" ]; then
echo "Entering $chroot as $chrootuser..."
# FIXME: This no longer gives a TTY (damn moving parts):
# chroot "$chroot" su $chrootuser -l -s "$chrootshell" -c "env PS1='$chrootPS1' SUDO_PS1='$chrootPS1root' $chrootshell"
# Workaround:
$chroot_pre_cmd chroot "$chroot" su $chrootuser -l -s "$chrootshell"
else
echo "Entering $chroot..."
# chroot "$chroot" su -l -c "env PS1='$chrootPS1root' $chrootshell"
$chroot_pre_cmd chroot "$chroot" su -l
fi
}
parse_args "$@"
check_params
become_root "$@"
sanitise_PS1
trap cleanup EXIT
setup
setup_user
enter_chroot