#!/bin/sh
# Rewrites /etc/issue, the serial-console login banner: the lines the
# image build wrote, then one https:// URL per global address of wlan0
# (and eth0 when the machine has one). The address is the only way in
# that the banner gives, because it is the one that works on every
# network. Called by the init script at boot and by the udhcpc hook on
# every lease event. Idempotent: the file is written only when the text
# changes.
#
# The rootfs is read-only: at the first change after boot the file is
# bind-mounted from a copy under /run/forgefirm (tmpfs) and the banner
# is written through the mount. Before that the image's own file shows.
# The image's own text is kept at that moment in a second copy, so the
# banner needs no marker line in the file and shows nothing but itself.

PATH=/sbin:/usr/sbin:/bin:/usr/bin

ISSUE=/etc/issue
STATE=/run/forgefirm/issue
BASE=/run/forgefirm/issue.base

# One URL per global address; an IPv6 address gets its URL brackets.
# Tentative, deprecated and temporary addresses are left out.
addresses () {
  command -v ip >/dev/null 2>&1 || return 0
  for dev in wlan0 eth0; do
    [ -d "/sys/class/net/$dev" ] || continue
    ip addr show dev "$dev" 2>/dev/null | awk '
      ($1 == "inet" || $1 == "inet6") && / scope global/ \
          && !/tentative/ && !/deprecated/ && !/temporary/ {
        a = $2
        sub(/\/.*/, "", a)
        if ($1 == "inet6") a = "[" a "]"
        print "  https://" a "/"
      }'
  done
}

# A mount at the file, read from /proc/mounts (mountpoint(1) judges a
# file by its device numbers alone).
is_mounted () {
  awk -v t="$1" '$2 == t { f = 1 } END { exit !f }' /proc/mounts
}

[ -f "$ISSUE" ] || exit 0

# The image's own text: the file as it is before the first write of the
# boot. /run is a tmpfs, so the copy is made once per boot, and the
# first caller is the one that finds the file untouched.
mkdir -p "${STATE%/*}" || exit 1
[ -f "$BASE" ] || cp -p "$ISSUE" "$BASE" || exit 1
if ! is_mounted "$ISSUE"; then
  cp -p "$BASE" "$STATE" \
    && mount --bind "$STATE" "$ISSUE" || exit 1
fi

base=$(cat "$BASE")
addrs=$(addresses)
if [ -n "$addrs" ]; then
  new=$(printf '%s\n\nControl panel:\n%s\n' "$base" "$addrs")
else
  new=$(printf '%s\n\nControl panel: no network address yet\n' "$base")
fi
[ "$new" = "$(cat "$ISSUE")" ] && exit 0

tmp="$STATE.tmp.$$"
printf '%s\n\n' "$new" > "$tmp" || { rm -f "$tmp"; exit 1; }
cat "$tmp" > "$ISSUE"
rm -f "$tmp"
exit 0
