#!/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

  # /dev/.static/dev/ is used by MAKEDEV to access the real /dev/ directory.
  # /lib/udev/devices/ is recycled as a temporary mount point.
  if [ -z "$no_static_dev" ]; then
    mount -n --bind /dev /lib/udev/devices
  fi

  if ! mount -n -o size=$tmpfs_size,mode=0755 -t tmpfs tmpfs /dev; then
    if [ -z "$no_static_dev" ]; then
      umount -n /lib/udev/devices
    fi
    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

  if [ -z "$no_static_dev" ]; then
    mkdir -p /dev/.static/dev
    chmod 700 /dev/.static/
    mount -n --move /lib/udev/devices /dev/.static/dev
  fi
}

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

# I hate this hack.  -- Md
make_extra_nodes() {
  if [ "$(echo /lib/udev/devices/*)" != "/lib/udev/devices/*" ]; then
    cp --archive --update /lib/udev/devices/* /dev/
  fi

  [ -e /etc/udev/links.conf ] || return 0
  grep '^[^#]' /etc/udev/links.conf | \
  while read type name arg1; do
    [ "$type" -a "$name" -a ! -e "/dev/$name" -a ! -L "/dev/$name" ] ||continue
    case "$type" in
      L) ln -s $arg1 /dev/$name ;;
      D) mkdir -p /dev/$name ;;
      M) mknod -m 600 /dev/$name $arg1 ;;
      *) log_warning_msg "links.conf: unparseable line ($type $name $arg1)" ;;
    esac
    if [ -x /sbin/restorecon ]; then
      /sbin/restorecon /dev/$name
    fi
  done
}

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

load_input_modules() {
  case "$(uname -r)" in
    2.6.1[0-5]|2.6.1[0-5][!0-9]*) ;; # <= 2.6.15
    *) return 0
  esac

  for module in mousedev evdev joydev; do
    modprobe -q $module || true
  done
}

# 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
}

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

[ -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.15, 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 /proc/sys/kernel/hotplug ]; 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 at this stage 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
  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 > /proc/sys/kernel/hotplug

    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

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

    load_input_modules

    ;;
    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)
    udevcontrol 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 udevtrigger 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 > /proc/sys/kernel/hotplug

    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
    fi

    # /dev/null must be created before udevd is started
    make_extra_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/

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

    load_input_modules
    create_dev_makedev

    # wait for the udevd childs to finish
    log_action_begin_msg "Waiting for /dev to be fully populated"
    if udevsettle; 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)
    udevcontrol 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

