#!/bin/sh -e
### BEGIN INIT INFO
# Provides:          udev
# Required-Start:    mountkernfs 
# Required-Stop:     
# Default-Start:     S
# Default-Stop:
# Short-Description: Start udevd, populate /dev and load drivers.
### END INIT INFO

# we need to unmount /dev/pts/ and remount it later over the tmpfs
unmount_devpts() {
  if mountpoint -q /dev/pts/; then
    umount -n -l /dev/pts/
  fi

  if mountpoint -q /dev/shm/; then
    umount -n -l /dev/shm/
  fi
}

# mount a tmpfs over /dev, if somebody did not already do it
mount_tmpfs() {
  if grep -E -q "^[^[:space:]]+ /dev tmpfs" /proc/mounts; then
    return
  fi

  if ! mount -n -o size=$tmpfs_size,mode=0755 -t tmpfs tmpfs /dev; then
    log_failure_msg "udev requires tmpfs support, not started."
    log_end_msg 1
  fi

  # relabel the new tmpfs accordingly
  [ -x /sbin/restorecon ] && /sbin/restorecon /dev

  return 0
}

create_dev_makedev() {
  if [ -e /sbin/MAKEDEV ]; then
    ln -sf /sbin/MAKEDEV /dev/MAKEDEV
  else
    ln -sf /bin/true /dev/MAKEDEV
  fi
}

supported_kernel() {
  case "$(uname -r)" in
    2.[012345].*|2.6.[0-9]|2.6.[0-9][!0-9]*) return 1 ;;
    2.6.1[0-7]|2.6.1[0-7][!0-9]*) return 1 ;;
  esac
  return 0
}

# shell version of /usr/bin/tty
my_tty() {
  [ -x /bin/readlink ] || return 0
  [ -e /proc/self/fd/0 ] || return 0
  readlink --silent /proc/self/fd/0 || true
}

warn_if_interactive() {
  if [ "$RUNLEVEL" = "S" -a "$PREVLEVEL" = "N" ]; then
    return
  fi

  TTY=$(my_tty)
  if [ -z "$TTY" -o "$TTY" = "/dev/console" -o "$TTY" = "/dev/null" ]; then
    return
  fi

  printf "\n\n\nIt has been detected that the command\n\n\t$0 $*\n\n"
  printf "has been run from an interactive shell.\n"
  printf "It will probably not do what you expect, so this script will wait\n"
  printf "60 seconds before continuing. Press ^C to stop it.\n"
  printf "RUNNING THIS COMMAND IS HIGHLY DISCOURAGED!\n\n\n\n"
  sleep 60
}

create_dev_root_rule() {
  local udevroot="$1"
  [ -e $udevroot/rules.d/61-dev-root-link.rules ] && return 0

  eval $(udevadm info --export --export-prefix=ROOT_ --device-id-of-file=/ \
    || true)
  [ "$ROOT_MAJOR" -a "$ROOT_MINOR" ] || return 0

  echo 'ACTION=="add", SUBSYSTEM=="block", ENV{MAJOR}=="'$ROOT_MAJOR'", ENV{MINOR}=="'$ROOT_MINOR'", SYMLINK+="root"' \
    > $udevroot/rules.d/61-dev-root-link.rules
}

##############################################################################

[ -x /sbin/udevd ] || exit 0

PATH="/sbin:/bin"

# defaults
tmpfs_size="10M"
udev_root="/dev"

if [ -e /etc/udev/udev.conf ]; then
  . /etc/udev/udev.conf
fi

. /lib/lsb/init-functions

if ! supported_kernel; then
  log_failure_msg "udev requires a kernel >= 2.6.18, not started."
  log_end_msg 1
fi

if [ ! -e /proc/filesystems ]; then
  log_failure_msg "udev requires a mounted procfs, not started."
  log_end_msg 1
fi

if ! grep -q '[[:space:]]tmpfs$' /proc/filesystems; then
  log_failure_msg "udev requires tmpfs support, not started."
  log_end_msg 1
fi

if [ ! -d /sys/class/ ]; then
  log_failure_msg "udev requires a mounted sysfs, not started."
  log_end_msg 1
fi

if [ ! -e /sys/kernel/uevent_helper ]; then
  log_failure_msg "udev requires hotplug support, not started."
  log_end_msg 1
fi

##############################################################################
# grml specific stuff:
# - support bootoption noudev
# - allow execution of initscript through FORCE=1 when booting with noudev
# - inform user how to skip execution of udev (being bootoption noudev)

# do not display anything when bootoption *splash is present:
if grep -qe ' splash' -qe ' tsplash' -qe ' textsplash' /proc/cmdline ; then
   exec >/dev/null </dev/null
fi

# are we running within init (non_interactive) or within shell (interactive)?
check_for_non_interactive() {
  if [ "$RUNLEVEL" = "S" -a "$PREVLEVEL" = "N" ]; then
    return
  fi

  TTY=$(my_tty)
  if [ -z "$TTY" -o "$TTY" = "/dev/console" -o "$TTY" = "/dev/null" ]; then
    return
  fi

  return 1
}

# do not flood console with kernel driver messages
grep -q debug /proc/cmdline || echo 0 > /proc/sys/kernel/printk

# support bootoption blacklist, must be executed *before* udev is present
( zsh -c '. /etc/grml/autoconfig.functions && config_blacklist || echo "Error when trying to run config_blacklist"' )

# finally check for bootoption noudev:
case "$1" in start)
      if ! grep -q noudev /proc/cmdline ; then
         check_for_non_interactive && \
      	 log_warning_msg "If your system hangs now skip execution of udev via bootoption noudev"
      else
         if [ -z "$FORCE" ] ; then
            log_failure_msg "Bootoption noudev found. Skipping execution of udev init script."
            if ! check_for_non_interactive ; then
               printf "\nIt has been detected that the udev init script\n"
               printf "has been run from an interactive shell.\n"
               printf "You booted your system using bootoption noudev.\n"
               printf "To force startup of udev please run:\n\n"
               printf "\tFORCE=1 Start udev\n\n"
            fi
            exit 1
         fi
      fi
esac
##############################################################################

# this is experimental and may not work well
if [ "$UDEV_DISABLED" = "yes" ]; then
  udev_root=/etc/udev/.dev
  export UDEV_ROOT=$udev_root
fi

udev_root=${udev_root%/}

if [ "$udev_root" != "/dev" ]; then
  log_warning_msg "udev_root != /dev/"

case "$1" in
    start)
    if [ -e "$udev_root/.udev/" ]; then
	if mountpoint -q $udev_root/; then
	    log_failure_msg "udev is already active on $udev_root."
	    log_end_msg 1
	else
	    log_warning_msg ".udev/ already exists on the static $udev_root!"
	fi
    fi

    echo > /sys/kernel/uevent_helper

    mount -n -o size=$tmpfs_size,mode=0755 -t tmpfs tmpfs $udev_root
    mkdir -p $udev_root/.udev/db/

    log_daemon_msg "Starting the hotplug events dispatcher" "udevd"
    if udevd --daemon; then
	log_end_msg $?
    else
	log_end_msg $?
    fi

    mkdir -p /dev/.udev/queue/ /dev/.udev/rules.d/
    create_dev_root_rule $udev_root

    log_action_begin_msg "Synthesizing initial hotplug events"
    if udevadm trigger; then
	log_action_end_msg $?
    else
	log_action_end_msg $?
    fi

    ;;
    stop)
    log_daemon_msg "Stopping the hotplug events dispatcher" "udevd"
    if start-stop-daemon --stop --name udevd --quiet --oknodo --retry 5; then
	log_end_msg $?
    else
	log_end_msg $?
    fi

    log_action_begin_msg "Unmounting $udev_root"
    # unmounting with -l should never fail
    if umount -n -l $udev_root; then
	log_action_end_msg $?
    else
	log_action_end_msg $?
    fi
    ;;

    restart)
    $0 stop
    $0 start
    ;;

    reload|force-reload)
    udevadm control --reload_rules
    ;;

    *)
    echo "Usage: /etc/init.d/udev {start|stop|restart|reload|force-reload}"
    exit 1
    ;;
esac

  exit 0
fi # udev_root != /dev

##############################################################################

# When modifying this script, do not forget that between the time that the
# new /dev has been mounted and udevadm trigger has been run there will be
# no /dev/null. This also means that you cannot use the "&" shell command.

case "$1" in
    start)
    if [ -e "$udev_root/.udev/" ]; then
	if mountpoint -q $udev_root/; then
	    TMPFS_MOUNTED=1
	else
	    log_warning_msg ".udev/ already exists on the static $udev_root!"
	fi
    else
	warn_if_interactive
    fi

    echo > /sys/kernel/uevent_helper

    if [ -z "$TMPFS_MOUNTED" ]; then
	unmount_devpts
	mount_tmpfs
	[ -d /proc/1 ] || mount -n /proc
    else
	# set the SELinux context for devices created in the initramfs
	[ -x /sbin/restorecon ] && /sbin/restorecon -R /dev
	# and clean up the database of the initramfs udev
	rm -rf /dev/.udev/
    fi

    # /dev/null must be created before udevd is started
    /lib/udev/create_static_nodes

    # if this directory is not present /dev will not be updated by udev
    mkdir -p /dev/.udev/db/

    log_daemon_msg "Starting the hotplug events dispatcher" "udevd"
    if udevd --daemon; then
	log_end_msg $?
    else
	log_end_msg $?
    fi

    mkdir -p /dev/.udev/queue/ /dev/.udev/rules.d/
    create_dev_root_rule /dev/.udev/

    log_action_begin_msg "Synthesizing the initial hotplug events"
    if udevadm trigger; then
	log_action_end_msg $?
    else
	log_action_end_msg $?
    fi

    create_dev_makedev

    # wait for the udevd childs to finish
    log_action_begin_msg "Waiting for /dev to be fully populated"
    if udevadm settle; then
	log_action_end_msg 0
    else
	log_action_end_msg 0 'timeout'
    fi
    ;;

    stop)
    log_daemon_msg "Stopping the hotplug events dispatcher" "udevd"
    if start-stop-daemon --stop --name udevd --quiet --oknodo --retry 5; then
	log_end_msg $?
    else
	log_end_msg $?
    fi
    ;;

    restart)
    log_daemon_msg "Stopping the hotplug events dispatcher" "udevd"
    if start-stop-daemon --stop --name udevd --quiet --oknodo --retry 5; then
	log_end_msg $?
    else
	log_end_msg $? || true
    fi

    log_daemon_msg "Starting the hotplug events dispatcher" "udevd"
    if udevd --daemon; then
	log_end_msg $?
    else
	log_end_msg $?
    fi
    ;;

    reload|force-reload)
    udevadm control --reload_rules
    ;;

    *)
    echo "Usage: /etc/init.d/udev {start|stop|restart|reload|force-reload}"
    exit 1
    ;;
esac

# grml hack:
echo 6 > /proc/sys/kernel/printk

exit 0

