#!/bin/sh
#
# This program is run by /hurd/init at boot time after the essential
# servers are up, and is responsible for running the "userland" parts of a
# normal system.  This includes running the single-user shell as well as a
# multi-user system.  This program is expected never to exit.
#


###
### Where to find programs, etc.
###

PATH=/bin:/sbin
export PATH

umask 022

# If we lose badly, try to exec each of these in turn.
fallback_shells='/bin/bash /bin/sh /bin/dash /bin/csh /bin/ash /bin/shd'

# Shell used for normal single-user startup.
SHELL=/bin/sh

# Programs that do multi-user startup.
prefix=
exec_prefix=${prefix}
RUNCOM=${exec_prefix}/libexec/rc
RUNTTYS=${exec_prefix}/libexec/runttys
# Signals that we should pass down to runttys.
runttys_sigs='TERM INT HUP TSTP'

###


# If we get a SIGLOST, attempt to reopen the console in case
# our console ports were revoked.  This lets us print messages.
reopen_console ()
{
  exec 1>/dev/console 2>&1 || exit 3
}
trap 'reopen_console' 32 # SIGLOST = server died on GNU


# Call this when we are losing badly enough that we want to punt normal
# startup entirely.  We exec a single-user shell, so we will not come back
# here.  The only way to get to multi-user from that shell will be
# explicitly exec this script or something like that.
singleuser ()
{
  test $# -eq 0 || echo "$0: $*"
  for try in ${fallback_shells}; do
    SHELL=${try}
    exec ${SHELL}
  done
  exit 127
}

# Print a newline and banner.
echo
echo Starting runsystem

# See whether pflocal is set up already, and do so if not (install case)
#
# Normally this should be the case, but we better make sure since
# without the pflocal server, pipe(2) does not work.
if ! test -c /servers/socket/1 && command -v settrans >/dev/null ; then
  echo Setting up pflocal
  # The root filesystem should be read-only at this point.
  if fsysopts / --update --writable ; then
    settrans -c /servers/socket/1 /hurd/pflocal
  else
    singleuser "Failed to create /servers/socket/1."
  fi
fi

# We expect to be started by console-run, which gives us no arguments and
# puts FALLBACK_CONSOLE=file-name in the environment if our console is
# other than a normal /dev/console.

if [ "${FALLBACK_CONSOLE+set}" = set ]; then
  singleuser "Running on fallback console ${FALLBACK_CONSOLE}"
fi


###
### Normal startup procedures
###

# Parse the multiboot command line.  We only pay attention to -s and -f.
flags=
while [ $# -gt 0 ]; do
  arg="$1"
  shift
  case "$arg" in
  --*) ;;
  *=*) ;;
  -*)
    flags="${flags}${arg#-}"
    ;;
  'single')
    flags="${flags}s"
    ;;
  'fastboot'|'emergency')
    flags="${flags}f"
    ;;
  esac
done

# Check boot flags.
case "$flags" in
*s*)
  rc=false			# force single-user
  ;;
*f*)
  rc="${RUNCOM}"		# fastboot
  ;;
*)
  rc="${RUNCOM} autoboot"	# multi-user default
  ;;
esac

# Large infinite loop.  If this script ever exits, init considers that
# a serious bogosity and punts to a fallback single-user shell.
# We handle here the normal transitions between single-user and multi-user.
while : ; do

  # Run the rc script.  As long as it exits nonzero, punt to single-user.
  # After the single-user shell exits, we will start over attempting to
  # run rc; but later invocations strip the `autoboot' argument.
  until $rc; do
    rc=${RUNCOM}

    # Run single-user shell and repeat as long as it dies with a signal.
    until ${SHELL} || test $? -lt 128; do
      :
    done
  done

  # Now we are officially ready for normal multi-user operation.

  # Trap certain signals and send them on to runttys.  For this to work, we
  # must run it asynchronously and wait for it with the `wait' built-in.
  runttys_pid=0
  for sig in $runttys_sigs; do
    trap "kill -$sig \${runttys_pid}" $sig
  done

  # This program reads /etc/ttys and starts the programs it says to.
  ${RUNTTYS} &
  runttys_pid=$!

  # Wait for runttys to die, meanwhile handling trapped signals.
  wait

  # Go back to the top of the infinite loop, as if booting single-user.
  rc=false

done
