blob: cd3caa4ac1bee9496bec98b253f7009e09439ac3 [file] [log] [blame]
#!/bin/bash
# virtme-init: virtme's basic init (PID 1) process
# Copyright © 2014 Andy Lutomirski
# Licensed under the GPLv2, which is available in the virtme distribution
# as a file called LICENSE with SHA-256 hash:
# 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
log() {
if [[ -e /dev/kmsg ]]; then
echo "<6>virtme-init: $*" >/dev/kmsg
else
echo "virtme-init: $*"
fi
}
mount -t sysfs -o nosuid,noexec,nodev sys /sys/
declare -A mount_tags
for i in /sys/bus/virtio/drivers/9pnet_virtio/virtio*/mount_tag; do
# mount_tag is terminated with a NUL byte, which leads to a
# "command substitution: ignored null byte in input" warning from
# bash; use sed instead of a bare 'cat' here to strip it off.
mount_tags["`sed '$s/\x00$//;' "$i"`"]=1
done
kver="`uname -r`"
if [[ -n "${mount_tags[virtme.moddir]}" ]]; then
mount -t tmpfs none /lib/modules
mkdir /lib/modules/"$kver"
mount -n -t 9p -o ro,version=9p2000.L,trans=virtio,access=any virtme.moddir /lib/modules/"$kver"
elif [[ -d "/lib/modules/$kver" ]]; then
# We may have mismatched modules. Mask them off.
mount -n -t tmpfs -o ro,mode=0000 disallow_modules "/lib/modules/$kver"
fi
mount -t tmpfs tmpfs /tmp/
[[ -w /var/log ]] || mount -t tmpfs tmpfs /var/log/
# Fix up /etc a little bit
touch /tmp/fstab
mount --bind /tmp/fstab /etc/fstab
rm /tmp/fstab
# Find udevd
if [[ -x /usr/lib/systemd/systemd-udevd ]]; then
udevd=/usr/lib/systemd/systemd-udevd
elif [[ -x /lib/systemd/systemd-udevd ]]; then
udevd=/lib/systemd/systemd-udevd
else
udevd=`which udevd`
fi
# Mount proc (needed for stat, sadly)
mount -t proc -o nosuid,noexec,nodev proc /proc/
# devtmpfs might be automounted; if not, mount it.
if ! findmnt --kernel --mountpoint /dev &>/dev/null; then
# Ideally we'll use devtmpfs (but don't rely on /dev/null existing).
if [[ -c /dev/null ]]; then
mount -n -t devtmpfs -o mode=0755,nosuid,noexec devtmpfs /dev \
&>/dev/null
else
mount -n -t devtmpfs -o mode=0755,nosuid,noexec devtmpfs /dev
fi
if (( $? != 0 )); then
# The running kernel doesn't have devtmpfs. Use regular tmpfs.
mount -t tmpfs -o mode=0755,nosuid,noexec none /dev
# Make some basic devices first, and let udev handle the rest
mknod -m 0666 /dev/null c 1 3
mknod -m 0660 /dev/kmsg c 1 11
mknod -m 0600 /dev/console c 5 1
fi
fi
for tag in "${!virtme_initmount@}"; do
if [[ ! -d "${!tag}" ]]; then
mkdir -p "${!tag}"
fi
mount -t 9p -o version=9p2000.L,trans=virtio,access=any "virtme.initmount${tag:16}" "${!tag}" || exit 1
done
if [[ -n "virtme_chdir" ]]; then
cd -- "${virtme_chdir}"
fi
log "basic initialization done"
######## The remainder of this script is a very simple init (PID 1) ########
# Does the system use systemd-tmpfiles?
tmpfiles=`which systemd-tmpfiles 2>/dev/null` && {
log "running systemd-tmpfiles"
systemd-tmpfiles --create --boot --exclude-prefix="/dev"
}
# Make dbus work (if tmpfiles wasn't there or didn't create the directory).
install -d /run/dbus
# Try to get udevd to coldplug everything.
# We could use virtme-loadmods as a lightweight alternative.
if [[ -n "$udevd" ]]; then
if [[ -e '/sys/kernel/uevent_helper' ]]; then
# This kills boot performance.
log "you have CONFIG_UEVENT_HELPER on; turn it off"
echo '' >/sys/kernel/uevent_helper
fi
log "starting udevd"
"$udevd" --daemon --resolve-names=never
log "triggering udev coldplug"
udevadm trigger --type=subsystems --action=add >/dev/null 2>&1
udevadm trigger --type=devices --action=add >/dev/null 2>&1
log "waiting for udev to settle"
udevadm settle
log "udev is done"
else
log "udevd not found"
fi
# Set up useful things in /sys, assuming our kernel supports it.
mount -t configfs configfs /sys/kernel/config &>/dev/null
mount -t debugfs debugfs /sys/kernel/debug &>/dev/null
# Set up cgroup mount points (mount cgroupv2 hierarchy by default)
if mount -t tmpfs tmpfs /sys/fs/cgroup &>/dev/null; then
mkdir /sys/fs/cgroup/unified
mount -t cgroup2 cgroup2 /sys/fs/cgroup/unified
fi
# Set up filesystems that live in /dev
mkdir -p -m 0755 /dev/shm /dev/pts
mount -t devpts -o gid=tty,mode=620,noexec,nosuid devpts /dev/pts
mount -t tmpfs -o mode=1777,nosuid,nodev tmpfs /dev/shm
# Install /proc/self/fd symlinks into /dev if not already present
declare -r -A fdlinks=(["/dev/fd"]="/proc/self/fd"
["/dev/stdin"]="/proc/self/fd/0"
["/dev/stdout"]="/proc/self/fd/1"
["/dev/stderr"]="/proc/self/fd/2")
for p in "${!fdlinks[@]}"; do
[[ -e "$p" ]] || ln -s "${fdlinks[$p]}" "$p"
done
if [[ -n "$virtme_hostname" ]]; then
log "Setting hostname to $virtme_hostname..."
hostname "$virtme_hostname"
fi
# Bring up networking
ip link set dev lo up
if cat /proc/cmdline |grep -q -E '(^| )virtme.dhcp($| )'; then
real_resolv_conf=/etc/resolv.conf
if [[ -L "$real_resolv_conf" ]]; then
real_resolv_conf="`readlink /etc/resolv.conf`"
if [[ ! -e $real_resolv_conf ]]; then
mkdir -p "`dirname $real_resolv_conf`"
touch $real_resolv_conf
fi
fi
if [[ -f "$real_resolv_conf" ]]; then
tmpfile="`mktemp --tmpdir=/tmp`"
chmod 644 "$tmpfile"
mount --bind "$tmpfile" "$real_resolv_conf"
rm "$tmpfile"
fi
# udev is liable to rename the interface out from under us.
virtme_net=`ls "$(ls -d /sys/bus/virtio/drivers/virtio_net/virtio* |sort -g |head -n1)"/net`
busybox udhcpc -i "$virtme_net" -n -q -f -s "$(dirname $0)/virtme-udhcpc-script"
fi
if [[ -x /run/virtme/data/script ]]; then
if [[ ! -e "/dev/virtio-ports/virtme.scriptio" ]]; then
echo "virtme-init: cannot find script I/O port; make sure virtio-serial is available"
poweroff -f
exit 1
fi
log 'starting script'
setsid /run/virtme/data/script <>/dev/virtio-ports/virtme.scriptio 1>&0 2>&0
log "script returned $?"
# Hmm. We should expose the return value somehow.
sync
poweroff -f
exit 1
fi
# Figure out what the main console is
consdev="`grep ' ... (.C' /proc/consoles |cut -d' ' -f1`"
if [[ -z "$consdev" ]]; then
log "can't deduce console device"
exec bash --login # At least try to be helpful
fi
deallocvt
if [[ "$consdev" == "tty0" ]]; then
# Create some VTs
openvt -c 2 -- /bin/bash
openvt -c 3 -- /bin/bash
openvt -c 4 -- /bin/bash
consdev=tty1 # sigh
fi
if [[ ! -e "/dev/$consdev" ]]; then
log "/dev/$consdev doesn't exist."
exec bash --login
fi
# Parameters that start with virtme_ shouldn't pollute the environment
for p in "${!virtme_@}"; do export -n "$p"; done
echo "virtme-init: console is $consdev"
# Set up a basic environment
install -d -m 0755 /tmp/roothome
export HOME=/tmp/roothome
# Bring up a functioning shell on the console. This is a bit magical:
# We have no controlling terminal because we're attached to a fake
# console device (probably something like /dev/console), which can't
# be a controlling terminal. We are also not a member of a session.
# Init apparently can't setsid (whether that's a limitation of the
# setsid binary or the system call, I don't know).
while true; do
# Program the console sensibly
if [[ -n "${virtme_stty_con}" ]]; then
stty ${virtme_stty_con} <"/dev/$consdev"
fi
setsid bash 0<>"/dev/$consdev" 1>&0 2>&0
echo "Shell died. Will respawn."
sleep 0.5
done