#!/bin/bash # Init script for build2 buildos. # # Loosely based on the one that comes in Debian initrd.img (since we are # using its kernel image as is). # trap "exit 1" ERR set -o errtrace # Trap in functions. # Note: diagnostics goes to stdout. # function info () { echo "$*"; } function error () { info "$*"; exit 1; } export PATH=/sbin:/usr/sbin:/bin:/usr/bin # One would expect rootflags=size=1g to work but it doesn't (perhaps init # is expected to interpret it)? # mount -o remount,size=1G / mkdir -p /sys /proc mount -t sysfs -o nodev,noexec,nosuid sysfs /sys mount -t proc -o nodev,noexec,nosuid proc /proc info "init starting up..." mount -t devtmpfs -o nosuid,mode=0755 udev /dev mkdir -p /dev/pts mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts || true mkdir -p /run mount -t tmpfs -o "noexec,nosuid,size=10%,mode=0755" tmpfs /run mkdir -p /tmp mount -t tmpfs -o "nodev,nosuid,size=10%,mode=1777" tmpfs /tmp # Start udev. # # Based on Debian initrd's init-top/udev. Note that we don't stop it at # at the end of init. # info "starting udev..." if [ -w /sys/kernel/uevent_helper ]; then echo >/sys/kernel/uevent_helper fi SYSTEMD_LOG_LEVEL=info /lib/systemd/systemd-udevd --daemon --resolve-names=never udevadm trigger --action=add udevadm settle || true # Parse the kernel command line. This is complicated by the fact that the # values can be quoted, for example: # # foo='foo fox' # bar="bar 'box'" # # First we separete quoted variables and arguments with newlines (giving # priority to assignments). Then we replace whitespaces with newline on # lines that don't contain quites. Finally, clean up by removing blank # lines. # readarray -t cmdline < <(cat /proc/cmdline | \ sed -r -e "s/([^ ]+=)?('[^']*'|\"[^\"]*\")/\n\1\2\n/g" | \ sed -r -e "/['\"]/!s/ /\n/g" | sed -r -e '/^\s*$/d') # Enter all buildos variables as bash variables. # info "command line:" for v in "${cmdline[@]}"; do var="$(sed -r -n -e 's/^buildos\.([^=]+)=.*$/\1/p' <<<"$v")" # Extract name. if [ -n "$var" ]; then val="$(sed -r -e 's/^[^=]+=(.*)$/\1/' <<<"$v")" # Extract value. val="$(sed -r -e "s/^('(.*)'|\"(.*)\")$/\2\3/" <<<"$val")" # Strip quoted. info " $var=$val" declare "$var=$val" fi done # Figure out network configuration and generate the corresponding # /etc/network/interfaces. # info "starting network..." # We are using udev's predictable interface names. The two character prefixes # based on the type of interface: # # en -- ethernet # sl -- serial line IP (slip) # wl -- wlan # ww -- wwan # eth_all="$(cd /sys/class/net && ls -d en?*)" if [ -z "$eth_all" ]; then info "no ethernet interfaces found among:" ip link show fi eth= eth_up= for s in 1 2 4 8; do # Try to bring them all up and find the one that has carrier. # for i in $eth_all; do ip link set "$i" up || true done sleep "$s" for i in $eth_all; do if [ "$(cat "/sys/class/net/$i/carrier")" -eq "1" ]; then info "detected carrier on $i" eth_up+=" $i" fi done # Bring them all down. # for i in $eth_all; do ip link set "$i" down || true done # If we didn't find anything, try to wait for carrier longer. # if [ -z "$eth_up" ]; then continue fi # If we end up with several interfaces we simply unleash dhcp on all of # them and use the first that gets configured. # # Note also that it's possible the interface that we want is not yet ready # in which case we will try to wait for carrier a bit longer. # for i in $eth_up; do if dhclient -v "$i"; then eth="$i" break fi done if [ -n "$eth" ]; then break fi done if [ -z "$eth_up" ]; then error "no ethernet interfaces with carrier among:" ip link show fi if [ -z "$eth" ]; then error "no ethernet interfaces with DHCP among:" ip link show fi mac="$(cat "/sys/class/net/$eth/address")" mid="$(sed -e 's/://g' <<<"$mac")" # Machine id. info "configured $eth ($mac)" # Set the hostname. # hname="$(hostname)" if [ "$hname" = "(none)" ]; then hname="build-$mid" hostname "$hname" fi echo "$hname" >/etc/hostname info "hostname $hname" # Stop DHCP client without releasing the lease and deconfigure the interface. # The plan is to generate a bridge-based /etc/network/interfaces configuration # based on what we have discovered and then let the systemd networking bringup # to configure everything (at which point we will hopefully reuse the lease). # dhclient -q -x # @@ Need to be make configurable. # priv_network="172.16.123.0" priv_netmask="255.255.255.0" priv_netbase="$(sed -e 's/^\(.*\)\.0$/\1/' <<<"$priv_network")" cat </etc/network/interfaces auto lo iface lo inet loopback # Public bridge. # auto br0 iface br0 inet dhcp bridge_ports $eth bridge_stp off bridge_maxwait 0 bridge_fd 0 bridge_mac $mac # Private bridge with NAT to br0. # auto br1 iface br1 inet static address ${priv_netbase}.1 netmask $priv_netmask bridge_ports none bridge_stp off bridge_maxwait 0 bridge_fd 0 post-up iptables -t nat -A POSTROUTING -o br0 -j MASQUERADE post-up iptables -A FORWARD -i br0 -o br1 -m state --state RELATED,ESTABLISHED -j ACCEPT post-up iptables -A FORWARD -i br1 -o br0 -j ACCEPT EOF cat </etc/dnsmasq.d/br1-dhcp interface=br1 bind-interfaces dhcp-range=${priv_netbase}.10,${priv_netbase}.250,12h EOF # Configure Postfix. # cat <<<"$hname" >/etc/mailname sed -r -i \ -e "s%^(myhostname).*%\1 = $hname%" \ -e 's%^(mydestination).*%\1 = $myhostname, localhost.localdomain, localhost%' \ -e 's%^(mynetworks).*%\1 = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128%' \ -e "s%^(relayhost).*%\1 = $smtp_relay%" \ /etc/postfix/main.cf # Make admin alias for buildos.admin_email, redirect root to admin. # cat <>/etc/aliases admin: $admin_email root: admin EOF newaliases /bin/bash # Hand off to systemd. But first arrange to keep console output (which # becomes tty1). # mkdir -p /etc/systemd/system/getty@tty1.service.d cat </etc/systemd/system/getty@tty1.service.d/noclear.conf [Service] TTYVTDisallocate=no EOF exec /lib/systemd/systemd \ --show-status=1 \ --machine-id="00000000000000000000$mid" \ /dev/console 2>&1