-
Notifications
You must be signed in to change notification settings - Fork 0
/
vm-init
executable file
·252 lines (220 loc) · 6.64 KB
/
vm-init
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-only
#
# vm-init: actual init script
#
# Copyright (C) 2019-2023 Vitaly Chikunov <[email protected]>
#
[ $$ != 1 ] && exit 1
export PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
mount -n -t proc -o nosuid,noexec,nodev proc /proc
read -r cmdline < /proc/cmdline
cmdline+=" " # Add separator so we can easily match complete words
if [ -z "${cmdline/* VERBOSE *}" ]; then
V=-v
V() { echo "- $*"; "$@"; }
else
V=
V() { "$@"; }
fi
SCRIPT=${cmdline#* SCRIPT=}
if [ "$SCRIPT" = "$cmdline" ]; then
echo "vm-init: SCRIPT= is not set."
SCRIPT=
fi
SCRIPT=${SCRIPT%% *}
V mount -n -t tmpfs -o mode=755,nodev,nosuid,strictatime tmpfs /run
V mount -t sysfs -o nosuid,noexec,nodev sys /sys
if ! grep -sq '^\S\+ / 9p ' /proc/mounts; then
mount_tag_f=( /sys/bus/virtio/drivers/9pnet_virtio/virtio*/mount_tag )
if [ -f "$mount_tag_f" ]; then
read mount_tag < $mount_tag_f
mkdir -p /mnt/9p
V mount -t 9p -o "9p2000.L,trans=virtio,access=any,msize=262144" "$mount_tag" /mnt/9p
unset mount_tag mount_tag_f
[ -n "$SCRIPT" ] && SCRIPT="/mnt/9p/$SCRIPT"
fi
fi
eval "$(grep -s -o ' MODPROBE_OPTIONS=.*' "$SCRIPT")"
# Last MODPROBE_OPTIONS will win.
export MODPROBE_OPTIONS
if ! modprobe -q ext4 >/dev/null 2>&1; then
V /sbin/depmod
fi
# Additional early commands like 'mount --bind'.
eval "$(grep -s -Pxo '# CMD: \K.*' "$SCRIPT")"
# Save script if it's on /tmp before mounting tmpfs over it.
case "$SCRIPT" in /tmp/*) SCRIPT_TEXT=$(cat $SCRIPT);; esac
V mount -t tmpfs -o size='100%' tmpfs /tmp
if [ -n "${SCRIPT_TEXT-}" ]; then
echo "$SCRIPT_TEXT" > "$SCRIPT"
chmod a+x "$SCRIPT"
unset SCRIPT_TEXT
fi
V mount -t tmpfs tmpfs /var/log
V mount -t devtmpfs -o mode=0755,nosuid,noexec devtmpfs /dev
CONSOLE=${cmdline#console=}
CONSOLE=${CONSOLE%% *}
if [ -c /dev/"$CONSOLE" ]; then
exec 0<>/dev/"$CONSOLE" 1>&0 2>&0
elif [ -c /dev/console ]; then
exec 0<>/dev/console 1>&0 2>&0
else
# Visible if /dev is inaccessible.
echo "vm-init: console=$CONSOLE is not accessible."
fi
if [ -n "$STDOUT" ]; then
for vport in /sys/class/virtio-ports/*; do
read name < "$vport/name"
if [ "$name" = stdout ]; then
name=$(basename "$vport")
exec 1>/dev/"$name"
fi
done
unset STDOUT vport name
fi
install -g utmp -m660 /dev/null /var/log/btmp
install -g utmp -m664 /dev/null /var/log/wtmp
ln -sf /proc/kcore /dev/core
ln -sf /proc/self/fd /dev/fd
ln -sf /proc/self/fd/0 /dev/stdin
ln -sf /proc/self/fd/1 /dev/stdout
ln -sf /proc/self/fd/2 /dev/stderr
V mount -t configfs configfs /sys/kernel/config
V mount -t debugfs debugfs /sys/kernel/debug
V mount -t securityfs securityfs /sys/kernel/security
mkdir -p /dev/pts /dev/shm /dev/disk/by-id /dev/disk/by-path /dev/disk/by-uuid
V mount -t devpts -o gid=tty,mode=620,noexec,nosuid devpts /dev/pts
V mount -t tmpfs -o mode=1777,size=1M,noexec,nosuid,nodev tmpfs /dev/shm
V ip link set dev lo up 2>/dev/null
if grep -q UDEVD=y /proc/cmdline; then
V udevd --daemon --resolve-names=never
V udevadm trigger --type=subsystems --action=add
V udevadm trigger --type=devices --action=add
V udevadm settle
else
# try to load some modules
V modprobe --quiet --all virtio_blk scsi_mod ata_piix pata_acpi \
ata_generic sd_mod virtio_net
fi
V modprobe virtio-rng
if SWAPDEV=$(blkid -t TYPE=swap -o device); then
V /sbin/swapon $SWAPDEV
SWAPSIZE=$(awk '$1 == "SwapTotal:" {print $2}' /proc/meminfo)
V mount -t tmpfs -o remount,size="$SWAPSIZE"K /tmp
fi
unset SWAPSIZE SWAPDEV
# test writability
if tmp=$(mktemp -p /usr/src 2>/dev/null); then
rm $tmp
else
echo >&2 " Error: Impossible to create files in /usr/src"
echo >&2 " Exit code from the command will be lost!"
fi
# Prepare network
network_conf() {
local iface=$1
if type ip >/dev/null 2>&1; then
V ip link set $iface up
V ip addr add 10.0.2.1/24 dev $iface
V ip rout add default via 10.0.2.2
elif type ifconfig >/dev/null 2>&1; then
V ifconfig lo up
V ifconfig $iface 10.0.2.1/24 up
V route add default gw 10.0.2.2
fi
}
if [ $(wc -l < /proc/net/dev) -gt 3 ]; then
network_conf $(grep -E -vw 'Inter-|face|lo:' /proc/net/dev | head -1 | cut -d: -f1 | tr -d ' ')
fi
if [ -n "$RESIZE2FS" ]; then
RESIZE2FS=$(blkid --uuid "$RESIZE2FS")
if [ -n "$V" ]; then
V resize2fs -p "$RESIZE2FS"
else
resize2fs "$RESIZE2FS" >/dev/null 2>&1
fi
unset RESIZE2FS
fi
if [ -n "$RSYNC" ]; then
V rsync -a $V ${RSYNC//:/ }
fi
# Handle --overlay option
UMOUNT=
ov_count=0
img_count=0
prev_cmdline=$cmdline
while [ -z "${prev_cmdline/* OVERLAY=*}" ]; do
prev_cmdline=${prev_cmdline#* OVERLAY=}
OVERLAY=${prev_cmdline%% *}
IFS=: read ovfs ovpath <<< "$OVERLAY"
IFS=, read ovfs ovopts <<< "$ovfs"
mnt=/mnt/$ov_count
ov_count=$((ov_count+1))
mkdir -p $mnt
if [ -b "$ovfs" ]; then
# full device path
V mount $V -n "$ovfs" $mnt
elif [ -b "/dev/$ovfs" ]; then
# short device name
V mount $V -n "/dev/$ovfs" $mnt
elif [ -d "$ovfs" ]; then
# use existing dir
mnt=$ovfs
elif [ "$ovfs" = tmpfs ]; then
# auto-create tmpfs mount
V mount $V -t tmpfs ${ovopts:+-o $ovopts} tmpfs $mnt
elif [ "$ovfs" = ext4 ]; then
# auto-create ext4 partition
size=${ovopts#*size=}
size=${size%%,*}
img=/usr/src/ext4.$img_count.img
# do not delete data if it already exists
if ! /sbin/tune2fs -l $img >/dev/null 2>&1; then
> $img
V truncate -s ${size:-11M} $img
V /sbin/mkfs.ext4 -q $img
fi
V mount $V -t ext4 -o loop $img $mnt
img_count=$((img_count+1))
else
echo >&2 "Unknown how to overlay $OVERLAY"
unset mnt
fi
if [ "$mnt" ]; then
mkdir -p $mnt/upper $mnt/work
: ${ovpath:=/usr/src}
V modprobe overlay
V mount $V -t overlay -olowerdir=$ovpath,upperdir=$mnt/upper,workdir=$mnt/work overlay $ovpath
UMOUNT=$ovpath
fi
unset mnt ovfs ovopts ovpath
done
unset ov_count img_count prev_cmdline
if [ -n "$FAKESUDO" ]; then
V install -pm4755 /usr/lib/vm-run/vm-fakesudo /usr/bin/sudo
fi
if [ -n "$RUNUSER" ]; then
RUNUSER="/usr/lib/vm-run/vm-fakesudo -u $RUNUSER -i exec"
fi
[ -z "$NOTTY" ] && [ -t 1 ] && V vm-resize ${V:--q}
if [ -f "$SCRIPT" ]; then
set -- $SCRIPT
else
echo >&2 "vm-init: SCRIPT=$SCRIPT not found, running rescue shell."
unset RUNUSER
export PS1='(rescue)# '
set -- /bin/bash
fi
$RUNUSER setsid $(test -t 1 && echo --ctty) --wait "$@"
RET=$?
# TODO: We can miss some stderr output here, for example script is not
# executable due to incorrect TMPDIR.
[ "$UMOUNT" ] && umount $V -n --lazy $UMOUNT
case "$SCRIPT" in /tmp/*) umount $V -n --lazy /tmp;; esac
echo $RET 2>/dev/null > "$SCRIPT.ret" ||
echo >&2 "Exit code $RET is lost"
# Disable `reboot: Power down' message
[ -z "${cmdline/* quiet *}" ] && echo 0 > /proc/sys/kernel/printk
V sync
V exec -a poweroff /usr/lib/vm-run/initrd-init