#!/bin/sh
#
# eudevd	init script to setup /dev
#
# chkconfig: 2345 02 95
# description: Manage user-space device nodes in /dev
# processname: eudevd
# config: /etc/eudev/udev.conf
#
### BEGIN INIT INFO
# Provides:          udevd
# Required-Start:
# Should-Start:
# Required-Stop:
# Should-Stop:
# Default-Start:  2 3 4 5
# Default-Stop:   0 1 6
# Short-Decription: Manage user-space device nodes in /dev
### END INIT INFO

WITHOUT_RC_COMPAT=1

. /etc/init.d/functions

SourceIfNotEmpty /etc/eudev/udev.conf

export SYSTEMD_LOG_TARGET=syslog

NAME=eudev
UDEVDNAME=eudevd
UDEVD=/sbin/$UDEVDNAME
UDEVADM=/sbin/eudevadm
RESTORECON=/sbin/restorecon

LOCKFILE=/var/lock/subsys/$UDEVDNAME
RETVAL=0

# If running from rc.sysinit, need to use mount -n (mtab is not yet writable)
mount_n="${RUN_FROM_SYSINIT:+-n}"

restore_context()
{
	[ -x "$RESTORECON" ] ||
		return 0

	mountpoint -q /selinux || mountpoint -q /sys/fs/selinux ||
		return 0

	"$RESTORECON" -R "$@"
}

mount_filesystem()
{
	# Warning: /dev/null is not present when this function is called.

	local mnt_fsname mnt_dir mnt_type mnt_opts

	mnt_fsname="$1"; shift
	mnt_dir="$1"; shift
	mnt_type="$1"; shift
	mnt_opts="$1"; shift

	! mountpoint -q "$mnt_dir" ||
		return 0

	mkdir -p -- "$mnt_dir" ||
		return

	if grep -qs "[[:space:]]$mnt_dir[[:space:]]" /proc/mounts; then
		mount -n --move "${mnt_dir##*/}" "$mnt_dir"
		return
	fi

	# If run from rc.sysinit, defer mount to the later step in
	# rc.sysinit to get correct mtab entry.
	[ -z "$RUN_FROM_SYSINIT" ] ||
		return 0

	if grep -qs "^[[:space:]]*[^#].*[[:space:]]$mnt_dir[[:space:]]" /etc/fstab; then
		# Listed in fstab - use options from there.
		mount $mount_n "$mnt_dir"
		return
	fi

	mount $mount_n -t "$mnt_type" -o "$mnt_opts" "$mnt_fsname" "$mnt_dir"
}

mount_pts_filesystem()
{
	mount_filesystem tmpfs /dev/shm shmfs rw
}

mount_shm_filesystem()
{
	mount_filesystem devpts /dev/pts devpts nosuid,noexec,gid=tty,mode=620
}

mount_dev_filesystem()
{
	mount_filesystem devtmpfs /dev udevfs "mode=755,${udevfs_options:-${tmpfs_options:-}}"
}

mount_run_filesystem()
{
	local rc=0 RUN_FROM_SYSINIT=

	mount_filesystem tmpfs /run runfs "mode=755,${runfs_options:-${tmpfs_options:-}}" ||
		return

	mkdir -p "/run/$NAME" 2>/dev/null ||
		rc=1

	create_static_inodes ||
		rc=1

	return $rc
}

umount_filesystem()
{
	! mountpoint -q "$1" ||
		umount "$1" 2>/dev/null ||
		umount -l "$1" 2>/dev/null ||:
}

detach_filesystem()
{
	umount_filesystem /dev/shm
	umount_filesystem /dev/pts
	umount_filesystem /dev
}

startup_failure()
{
	msg_starting "$UDEVDNAME"
	printf "%s" "$1"
	failure "$UDEVDNAME startup"
	echo
}

create_static_inodes()
{
	local systemd_tmpfiles

	systemd_tmpfiles="$(find_util systemd-tmpfiles)" ||
		return 0

	mkdir -p /run/tmpfiles.d

	if [ -x /bin/kmod ]; then
		/bin/kmod static-nodes --format=tmpfiles --output=/run/tmpfiles.d/kmod.conf
	fi

	"$systemd_tmpfiles" --prefix=/dev --create --boot
}

start_udevd()
{
	# We want to start udevd ourselves if it isn't already running.
	# This lets eudevd run at a sane nice level...
	if [ -z "$RUN_FROM_SYSINIT" ]; then
		start_daemon --lockfile "$LOCKFILE" --expect-user root -- $UDEVD --daemon
	else
		start_daemon --expect-user root -- $UDEVD --daemon
	fi
}

start()
{
	# Check if udevd is already running
	if start-stop-daemon --stop --user root --test --exec $UDEVD >/dev/null; then
		[ -n "$RUN_FROM_SYSINIT" ] ||
			touch "$LOCKFILE"
		return 0
	fi

	local rc=0

	# Check system before trying to start udev
	[ -d /sys/block ] && [ -d /sys/devices ] || {
		startup_failure "/sys is not mounted"
		return 1
	}

	[ -w /sys/class/mem/null/uevent ] || {
		startup_failure "kernel too old - no /sys/class/mem/null/uevent"
		return 1
	}

	[ -r /sys/kernel/uevent_seqnum ] || {
		startup_failure "kernel too old - no /sys/kernel/uevent_seqnum"
		return 1
	}

	mount_dev_filesystem || rc=1
	mount_run_filesystem || rc=1
	mount_pts_filesystem || rc=1
	mount_shm_filesystem || rc=1

	[ "$rc" = 0 ] ||
		return $rc

	# /dev is partially populated already,
	# so set SELinux context for it now.
	restore_context /dev ||
		rc=1

	if start_udevd; then
		$UDEVADM control --property="STARTUP=1" ||:
		$UDEVADM trigger --type=subsystems --action=add ||:
		$UDEVADM trigger --type=devices    --action=add ||:

		action "Populating /dev:" $UDEVADM settle ||
			rc=1

		$UDEVADM control --property="STARTUP=" ||:
	else
		rc=1
	fi

	return $rc
}

stop()
{
	stop_daemon --lockfile "$LOCKFILE" $UDEVD
}

restart()
{
	local rc=0
	stop || rc=1
	start_udevd || rc=1
	return $rc
}

case "${1-}" in
	start)
		start ||
			RETVAL=$?
		;;
	start_udevd_only)
		start_udevd ||
			RETVAL=$?
		;;
	stop)
		stop ||
			RETVAL=$?
		;;
	status)
		status --expect-user root -- $UDEVDNAME ||
			RETVAL=$?
		;;
	condrestart)
		[ ! -e "$LOCKFILE" ] ||
			restart ||
			RETVAL=$?
		;;
	condstop)
		msg=`status --expect-user root -- $UDEVDNAME` ||
			RETVAL=$?
		[ $RETVAL != 0 ] ||
			stop ||
			RETVAL=$?
		;;
	restart)
		restart ||
			RETVAL=$?
		;;
	reload)
		# nothing to do here
		;;
	umount)
		stop ||
			RETVAL=$?

		echo -n $"Removing udev device nodes: "

		detach_filesystem ||
			RETVAL=$?

		[ $RETVAL = 0 ] &&
			echo_success ||
			echo_failure
		echo
		;;
	*)
		echo "Usage: $0 {start|stop|status|restart|condrestart|condstop|umount}"
		RETVAL=1
esac

exit $RETVAL
